diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ed115ca..cf8004f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
with:
- java-version: 17
- distribution: 'temurin'
+ java-version: 19
+ distribution: 'zulu'
cache: 'maven'
- name: Ensure to use tagged version
if: startsWith(github.ref, 'refs/tags/')
@@ -25,16 +25,14 @@ jobs:
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
continue-on-error: true
- - uses: actions/upload-artifact@v2
+ - uses: actions/upload-artifact@v3
with:
name: artifacts
path: target/*.jar
- name: Create Release
- uses: actions/create-release@v1
+ uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
- env:
- GITHUB_TOKEN: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }} # release as "cryptobot"
with:
- tag_name: ${{ github.ref }}
- release_name: Release ${{ github.ref }}
- prerelease: true
\ No newline at end of file
+ prerelease: true
+ token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
+ generate_release_notes: true
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index ec9182c..0342aac 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -15,19 +15,19 @@ jobs:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
fetch-depth: 2
- - uses: actions/setup-java@v2
+ - uses: actions/setup-java@v3
with:
- java-version: 17
- distribution: 'temurin'
+ java-version: 19
+ distribution: 'zulu'
cache: 'maven'
- name: Initialize CodeQL
- uses: github/codeql-action/init@v1
+ uses: github/codeql-action/init@v2
with:
languages: java
- name: Build
run: mvn -B compile
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v1
\ No newline at end of file
+ uses: github/codeql-action/analyze@v2
\ No newline at end of file
diff --git a/.github/workflows/publish-central.yml b/.github/workflows/publish-central.yml
index fcbd408..9ad01e7 100644
--- a/.github/workflows/publish-central.yml
+++ b/.github/workflows/publish-central.yml
@@ -10,13 +10,13 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
ref: "refs/tags/${{ github.event.inputs.tag }}"
- - uses: actions/setup-java@v2
+ - uses: actions/setup-java@v3
with:
- java-version: 17
- distribution: 'temurin'
+ java-version: 19
+ distribution: 'zulu'
cache: 'maven'
server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml
server-username: MAVEN_USERNAME # env variable for username in deploy
diff --git a/.github/workflows/publish-github.yml b/.github/workflows/publish-github.yml
index ff89e5a..b233f6c 100644
--- a/.github/workflows/publish-github.yml
+++ b/.github/workflows/publish-github.yml
@@ -7,11 +7,11 @@ jobs:
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/') # only allow publishing tagged versions
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-java@v2
+ - uses: actions/checkout@v3
+ - uses: actions/setup-java@v3
with:
- java-version: 17
- distribution: 'temurin'
+ java-version: 19
+ distribution: 'zulu'
cache: 'maven'
gpg-private-key: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
diff --git a/.gitignore b/.gitignore
index 8b96cab..7496fc9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,39 +1,25 @@
-# Compiled class file
*.class
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
-
# Package Files #
*.jar
*.war
*.ear
-*.zip
-*.tar.gz
-*.rar
-
-# Maven #
-target/
-pom.xml.versionsBackup
# Eclipse Settings Files #
.settings
.project
.classpath
-test-output/
-# IntelliJ Settings Files #
-.idea/
-out/
-.idea_modules/
-*.iml
-*.iws
+# Maven #
+target/
+pom.xml.versionsBackup
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
+# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+.idea/compiler.xml
+.idea/encodings.xml
+.idea/jarRepositories.xml
+.idea/**/libraries/
+*.iml
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..5ce7f62
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123c
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..146ab09
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..b385302
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..c612358
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/CryptoFS_Mirror.xml b/.idea/runConfigurations/CryptoFS_Mirror.xml
new file mode 100644
index 0000000..bb49f9d
--- /dev/null
+++ b/.idea/runConfigurations/CryptoFS_Mirror.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Mirror.xml b/.idea/runConfigurations/Mirror.xml
new file mode 100644
index 0000000..e07961a
--- /dev/null
+++ b/.idea/runConfigurations/Mirror.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 6b5a3c5..eb93eb4 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
# fuse-nio-adapter
Provides directory contents specified by a `java.nio.file.Path` via a FUSE filesystem.
-Uses [jnr-fuse](https://github.com/SerCeMan/jnr-fuse), i.e. you need to install the specified fuse drivers for your OS.
+Uses [jfuse](https://github.com/cryptomator/jfuse), i.e. you need to install the specified fuse drivers for your OS.
## Configuration Parameters
The following system properties are used:
diff --git a/pom.xml b/pom.xml
index c62cd04..882e526 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,332 +1,363 @@
-
- 4.0.0
- org.cryptomator
- fuse-nio-adapter
- 1.3.4
- FUSE-NIO-Adapter
- Access resources at a given NIO path via FUSE.
- https://github.com/cryptomator/fuse-nio-adapter
+
+ 4.0.0
+ org.cryptomator
+ fuse-nio-adapter
+ 2.0.0
+ FUSE-NIO-Adapter
+ Access resources at a given NIO path via FUSE.
+ https://github.com/cryptomator/fuse-nio-adapter
-
- scm:git:git@github.com:cryptomator/fuse-nio-adapter.git
- scm:git:git@github.com:cryptomator/fuse-nio-adapter.git
- git@github.com:cryptomator/fuse-nio-adapter.git
-
+
+ scm:git:git@github.com:cryptomator/fuse-nio-adapter.git
+ scm:git:git@github.com:cryptomator/fuse-nio-adapter.git
+ git@github.com:cryptomator/fuse-nio-adapter.git
+
-
- UTF-8
- 11
+
+ UTF-8
+ 19
-
- 0.5.7
-
- 2.39.1
- 30.1.1-jre
- 1.7.32
-
-
- 5.8.2
- 4.5.1
- 2.4.1
-
+
+ 1.2.0
+ 0.3.3
+ 31.1-jre
+ 2.0.3
+ 3.1.4
-
-
- GNU Affero General Public License (AGPL) version 3.0
- https://www.gnu.org/licenses/agpl.txt
- repo
-
-
+
+ 5.9.0
+ 4.7.0
+ 2.4.3
-
-
- Sebastian Stenzel
- sebastian.stenzel@gmail.com
- +1
- cryptomator.org
- http://cryptomator.org
-
-
+
+ 8.1.0
+ 3.1.0
+
-
-
-
- com.github.serceman
- jnr-fuse
- ${jnrfuse.version}
-
+
+
+ GNU Affero General Public License (AGPL) version 3.0
+ https://www.gnu.org/licenses/agpl.txt
+ repo
+
+
-
-
- com.google.dagger
- dagger
- ${dagger.version}
-
+
+
+ Sebastian Stenzel
+ sebastian.stenzel@gmail.com
+ +1
+ cryptomator.org
+ http://cryptomator.org
+
+
-
-
- com.google.guava
- guava
- ${guava.version}
-
+
+
+
+ org.cryptomator
+ jfuse
+ ${jfuse.version}
+
-
-
- org.slf4j
- slf4j-api
- ${slf4j.version}
-
-
- org.slf4j
- slf4j-simple
- ${slf4j.version}
- test
-
+
+
+ org.cryptomator
+ integrations-api
+ ${integrations-api.version}
+
-
-
- org.junit.jupiter
- junit-jupiter-api
- ${junit.jupiter.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-engine
- ${junit.jupiter.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-params
- ${junit.jupiter.version}
- test
-
-
- org.mockito
- mockito-core
- ${mockito.version}
- test
-
-
- org.mockito
- mockito-inline
- ${mockito.version}
- test
-
-
- org.cryptomator
- cryptofs
- ${cryptofs.version}
- test
-
-
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+ ${caffeine.version}
+
+
+ org.checkerframework
+ checker-qual
+
+
+ com.google.errorprone
+ error_prone_annotations
+
+
+
-
-
- maven-central
- Maven Central Repo
- https://repo.maven.apache.org/maven2
-
-
+
+
+ org.jetbrains
+ annotations
+ 23.0.0
+ provided
+
-
-
-
- maven-compiler-plugin
- 3.8.1
-
- true
-
-
- com.google.dagger
- dagger-compiler
- ${dagger.version}
-
-
-
-
-
- maven-surefire-plugin
- 3.0.0-M5
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 3.2.0
-
-
-
- org.cryptomator.frontend.fuse
-
-
-
-
-
- maven-source-plugin
- 3.2.1
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- maven-javadoc-plugin
- 3.3.1
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
- **/*_*
- **/Dagger*
-
-
-
-
- jakarta.inject
- jakarta.inject-api
- 1.0.3
-
-
-
-
--add-modules
-
java.xml,java.compiler
-
-
-
-
-
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j.version}
+
+
+ org.slf4j
+ slf4j-simple
+ ${slf4j.version}
+ test
+
-
-
- dependency-check
-
-
-
- org.owasp
- dependency-check-maven
- 6.4.1
-
- 24
- 0
- true
- true
-
- suppression.xml
-
-
-
-
-
- check
-
-
-
-
-
-
-
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.jupiter.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.mockito
+ mockito-inline
+ ${mockito.version}
+ test
+
+
+ org.cryptomator
+ cryptofs
+ ${cryptofs.version}
+ test
+
+
-
- coverage
-
-
-
- org.jacoco
- jacoco-maven-plugin
- 0.8.7
-
-
- prepare-agent
-
- prepare-agent
-
-
-
- report
-
- report
-
-
-
-
-
-
-
+
+
+ maven-central
+ Maven Central Repo
+ https://repo.maven.apache.org/maven2
+
+
-
- sign
-
-
-
- maven-gpg-plugin
- 3.0.1
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
- --pinentry-mode
- loopback
-
-
-
-
-
-
-
-
+
+
+
+ maven-compiler-plugin
+ 3.10.1
+
+ true
+ ${project.build.jdk}
+
+ --enable-preview
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+ prepare-agent
+
+ prepare-agent
+
+
+ surefire.jacoco.args
+
+
+
+
+
+ maven-surefire-plugin
+ 3.0.0-M7
+
+ @{surefire.jacoco.args} --enable-preview --enable-native-access=ALL-UNNAMED
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+ maven-source-plugin
+ 3.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ maven-javadoc-plugin
+ 3.4.1
+
+
+ attach-javadocs
+
+ jar
+
+
+ ${project.build.jdk}
+ --enable-preview
+
+
+
+
+
+ **/*_*
+ **/Dagger*
+
+
+
+
+
-
- deploy-central
-
-
- ossrh
- Maven Central
- https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.8
- true
-
- ossrh
- https://s01.oss.sonatype.org/
- true
-
-
-
-
-
+
+
+ dependency-check
+
+
+
+ org.owasp
+ dependency-check-maven
+ ${dependency-check.version}
+
+ 24
+ 0
+ true
+ true
+
+ suppression.xml
+
+
+
+
+
+ check
+
+
+
+
+
+
+
-
- deploy-github
-
-
- github
- GitHub Packages
- https://maven.pkg.github.com/cryptomator/fuse-nio-adapter
-
-
-
-
+
+ coverage
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ report
+
+ report
+
+
+
+
+
+
+
+
+
+ sign
+
+
+
+ maven-gpg-plugin
+ 3.0.1
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+
+
+
+
+ deploy-central
+
+
+ ossrh
+ Maven Central
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.8
+ true
+
+ ossrh
+ https://s01.oss.sonatype.org/
+ true
+
+
+
+
+
+
+
+ deploy-github
+
+
+ github
+ GitHub Packages
+ https://maven.pkg.github.com/cryptomator/fuse-nio-adapter
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven.deploy.version}
+
+
+
+
+
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..50a84e8
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,17 @@
+import org.cryptomator.frontend.fuse.mount.FuseTMountProvider;
+import org.cryptomator.frontend.fuse.mount.LinuxFuseMountProvider;
+import org.cryptomator.frontend.fuse.mount.MacFuseMountProvider;
+import org.cryptomator.frontend.fuse.mount.WinFspNetworkMountProvider;
+import org.cryptomator.integrations.mount.MountService;
+import org.cryptomator.frontend.fuse.mount.WinFspMountProvider;
+
+module org.cryptomator.frontend.fuse {
+ requires org.cryptomator.jfuse;
+ requires org.cryptomator.integrations.api;
+ requires org.slf4j;
+ requires com.google.common; // TODO try to remove
+ requires com.github.benmanes.caffeine;
+ requires static org.jetbrains.annotations;
+
+ provides MountService with LinuxFuseMountProvider, MacFuseMountProvider, FuseTMountProvider, WinFspMountProvider, WinFspNetworkMountProvider;
+}
\ No newline at end of file
diff --git a/src/main/java/org/cryptomator/frontend/fuse/AdapterFactory.java b/src/main/java/org/cryptomator/frontend/fuse/AdapterFactory.java
deleted file mode 100644
index 3ff9d88..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/AdapterFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import java.nio.file.Path;
-
-public class AdapterFactory {
-
- /**
- * The default value for the maximum supported filename length.
- */
- public static final int DEFAULT_MAX_FILENAMELENGTH = 254; // 255 is preferred, but nautilus checks for this value + 1
-
- private AdapterFactory() {
- }
-
- /**
- * Creates a read-only fuse-nio filesystem with a maximum file name length of {@value DEFAULT_MAX_FILENAMELENGTH} and an assumed filename encoding of UTF-8 NFC for FUSE and the NIO filesystem.
- * @param root the root path of the NIO filesystem.
- * @return an adapter mapping FUSE callbacks to the nio interface
- * @see ReadOnlyAdapter
- * @see FileNameTranscoder
- */
- public static FuseNioAdapter createReadOnlyAdapter(Path root) {
- return createReadOnlyAdapter(root, DEFAULT_MAX_FILENAMELENGTH, FileNameTranscoder.transcoder() );
- }
-
- public static FuseNioAdapter createReadOnlyAdapter(Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder) {
- FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder()
- .root(root)
- .maxFileNameLength(maxFileNameLength)
- .fileNameTranscoder(fileNameTranscoder)
- .build();
- return comp.readOnlyAdapter();
- }
-
- /**
- * Creates a fuse-nio-filesystem with a maximum file name length of {@value DEFAULT_MAX_FILENAMELENGTH} and an assumed filename encoding of UTF-8 NFC for FUSE and the NIO filesystem.
- * @param root the root path of the NIO filesystem.
- * @return an adapter mapping FUSE callbacks to the nio interface
- * @see ReadWriteAdapter
- * @see FileNameTranscoder
- */
- public static FuseNioAdapter createReadWriteAdapter(Path root) {
- return createReadWriteAdapter(root, DEFAULT_MAX_FILENAMELENGTH);
- }
-
- /**
- * Creates a fuse-nio-filesystem with an assumed filename encoding of UTF-8 NFC for FUSE and the NIO filesystem.
- * @param root the root path of the NIO filesystem.
- * @return an adapter mapping FUSE callbacks to the nio interface
- * @see ReadWriteAdapter
- * @see FileNameTranscoder
- */
- public static FuseNioAdapter createReadWriteAdapter(Path root, int maxFileNameLength) {
- return createReadWriteAdapter(root,maxFileNameLength,FileNameTranscoder.transcoder());
- }
-
- public static FuseNioAdapter createReadWriteAdapter(Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder) {
- FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder().root(root).maxFileNameLength(maxFileNameLength).fileNameTranscoder(fileNameTranscoder).build();
- return comp.readWriteAdapter();
- }
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/BitMaskEnumUtil.java b/src/main/java/org/cryptomator/frontend/fuse/BitMaskEnumUtil.java
deleted file mode 100644
index 20eadf2..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/BitMaskEnumUtil.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import java.util.EnumSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import jnr.constants.Constant;
-
-@PerAdapter
-public class BitMaskEnumUtil {
-
- @Inject
- public BitMaskEnumUtil() {
- }
-
- public Set bitMaskToSet(Class clazz, long mask) {
- Set result = EnumSet.noneOf(clazz);
- for (E e : clazz.getEnumConstants()) {
- if ((e.longValue() & mask) == e.longValue()) {
- result.add(e);
- }
- }
- return result;
- }
-
- public long setToBitMask(Set set) {
- long mask = 0;
- for (E value : set) {
- mask |= value.longValue();
- }
- return mask;
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/FileAttributesUtil.java b/src/main/java/org/cryptomator/frontend/fuse/FileAttributesUtil.java
index 144c29b..acde66a 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/FileAttributesUtil.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/FileAttributesUtil.java
@@ -1,38 +1,34 @@
package org.cryptomator.frontend.fuse;
-import jnr.posix.util.Platform;
-import ru.serce.jnrfuse.flags.AccessConstants;
-import ru.serce.jnrfuse.struct.FileStat;
+import org.cryptomator.jfuse.api.Stat;
-import javax.inject.Inject;
import java.nio.file.AccessMode;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.EnumSet;
import java.util.Set;
-@PerAdapter
+@SuppressWarnings("OctalInteger")
public class FileAttributesUtil {
+ // TODO: are default UID/GID system-dependent?
// uid/gid are overwritten by fuse mount options -ouid=...
private static final int DUMMY_UID = 65534; // usually nobody
private static final int DUMMY_GID = 65534; // usually nobody
- @Inject
- public FileAttributesUtil() {
- }
+ private FileAttributesUtil(){}
- public Set accessModeMaskToSet(int mask) {
+ public static Set accessModeMaskToSet(int mask) {
Set accessModes = EnumSet.noneOf(AccessMode.class);
// @formatter:off
- if ((mask & AccessConstants.R_OK) == AccessConstants.R_OK) accessModes.add(AccessMode.READ);
- if ((mask & AccessConstants.W_OK) == AccessConstants.W_OK) accessModes.add(AccessMode.WRITE);
- if ((mask & AccessConstants.X_OK) == AccessConstants.X_OK) accessModes.add(AccessMode.EXECUTE);
+ if ((mask & 4) == 4) accessModes.add(AccessMode.READ);
+ if ((mask & 2) == 2) accessModes.add(AccessMode.WRITE);
+ if ((mask & 1) == 1) accessModes.add(AccessMode.EXECUTE);
// @formatter:on
return accessModes;
}
- public Set octalModeToPosixPermissions(long mode) {
+ public static Set octalModeToPosixPermissions(long mode) {
Set result = EnumSet.noneOf(PosixFilePermission.class);
// @formatter:off
if ((mode & 0400) == 0400) result.add(PosixFilePermission.OWNER_READ);
@@ -48,49 +44,36 @@ public Set octalModeToPosixPermissions(long mode) {
return result;
}
- public void copyBasicFileAttributesFromNioToFuse(BasicFileAttributes attrs, FileStat stat) {
+ public static void copyBasicFileAttributesFromNioToFuse(BasicFileAttributes attrs, Stat stat) {
+ stat.unsetModeBits(Stat.S_IFMT);
if (attrs.isDirectory()) {
- stat.st_mode.set(stat.st_mode.longValue() | FileStat.S_IFDIR);
+ stat.setModeBits(Stat.S_IFDIR);
} else if (attrs.isRegularFile()) {
- stat.st_mode.set(stat.st_mode.longValue() | FileStat.S_IFREG);
+ stat.setModeBits(Stat.S_IFREG);
} else if (attrs.isSymbolicLink()) {
- stat.st_mode.set(stat.st_mode.longValue() | FileStat.S_IFLNK);
- }
- stat.st_uid.set(DUMMY_UID);
- stat.st_gid.set(DUMMY_GID);
- stat.st_mtim.tv_sec.set(attrs.lastModifiedTime().toInstant().getEpochSecond());
- stat.st_mtim.tv_nsec.set(attrs.lastModifiedTime().toInstant().getNano());
- stat.st_ctim.tv_sec.set(attrs.creationTime().toInstant().getEpochSecond());
- stat.st_ctim.tv_nsec.set(attrs.creationTime().toInstant().getNano());
- if (Platform.IS_MAC || Platform.IS_WINDOWS) {
- assert stat.st_birthtime != null;
- stat.st_birthtime.tv_sec.set(attrs.creationTime().toInstant().getEpochSecond());
- stat.st_birthtime.tv_nsec.set(attrs.creationTime().toInstant().getNano());
- }
- stat.st_atim.tv_sec.set(attrs.lastAccessTime().toInstant().getEpochSecond());
- stat.st_atim.tv_nsec.set(attrs.lastAccessTime().toInstant().getNano());
- stat.st_size.set(attrs.size());
- stat.st_nlink.set(1);
- // make sure to nil certain fields known to contain garbage from uninitialized memory
- // fixes alleged permission bugs, see https://github.com/cryptomator/fuse-nio-adapter/issues/19
- if (Platform.IS_MAC) {
- stat.st_flags.set(0);
- stat.st_gen.set(0);
+ stat.setModeBits(Stat.S_IFLNK);
}
+ stat.setUid(DUMMY_UID);
+ stat.setGid(DUMMY_GID);
+ stat.mTime().set(attrs.lastModifiedTime().toInstant());
+ stat.birthTime().set(attrs.creationTime().toInstant());
+ stat.aTime().set(attrs.lastAccessTime().toInstant());
+ stat.setSize(attrs.size());
+ stat.setNLink((short) 1);
}
- public long posixPermissionsToOctalMode(Set permissions) {
+ public static long posixPermissionsToOctalMode(Set permissions) {
long mode = 0;
// @formatter:off
- if (permissions.contains(PosixFilePermission.OWNER_READ)) mode = mode | FileStat.S_IRUSR;
- if (permissions.contains(PosixFilePermission.GROUP_READ)) mode = mode | FileStat.S_IRGRP;
- if (permissions.contains(PosixFilePermission.OTHERS_READ)) mode = mode | FileStat.S_IROTH;
- if (permissions.contains(PosixFilePermission.OWNER_WRITE)) mode = mode | FileStat.S_IWUSR;
- if (permissions.contains(PosixFilePermission.GROUP_WRITE)) mode = mode | FileStat.S_IWGRP;
- if (permissions.contains(PosixFilePermission.OTHERS_WRITE)) mode = mode | FileStat.S_IWOTH;
- if (permissions.contains(PosixFilePermission.OWNER_EXECUTE)) mode = mode | FileStat.S_IXUSR;
- if (permissions.contains(PosixFilePermission.GROUP_EXECUTE)) mode = mode | FileStat.S_IXGRP;
- if (permissions.contains(PosixFilePermission.OTHERS_EXECUTE)) mode = mode | FileStat.S_IXOTH;
+ if (permissions.contains(PosixFilePermission.OWNER_READ)) mode = mode | 0400;
+ if (permissions.contains(PosixFilePermission.GROUP_READ)) mode = mode | 0040;
+ if (permissions.contains(PosixFilePermission.OTHERS_READ)) mode = mode | 0004;
+ if (permissions.contains(PosixFilePermission.OWNER_WRITE)) mode = mode | 0200;
+ if (permissions.contains(PosixFilePermission.GROUP_WRITE)) mode = mode | 0020;
+ if (permissions.contains(PosixFilePermission.OTHERS_WRITE)) mode = mode | 0002;
+ if (permissions.contains(PosixFilePermission.OWNER_EXECUTE)) mode = mode | 0100;
+ if (permissions.contains(PosixFilePermission.GROUP_EXECUTE)) mode = mode | 0010;
+ if (permissions.contains(PosixFilePermission.OTHERS_EXECUTE)) mode = mode | 0001;
// @formatter:on
return mode;
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapter.java
index 989881f..f651ee8 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapter.java
@@ -1,12 +1,15 @@
package org.cryptomator.frontend.fuse;
-import ru.serce.jnrfuse.FuseFS;
+import org.cryptomator.jfuse.api.FuseOperations;
-import java.util.concurrent.TimeoutException;
+import java.io.IOException;
-public interface FuseNioAdapter extends FuseFS, AutoCloseable {
+public interface FuseNioAdapter extends FuseOperations, AutoCloseable {
- boolean isMounted();
+ /**
+ * The default value for the maximum supported filename length.
+ */
+ int DEFAULT_MAX_FILENAMELENGTH = 254; // 255 is preferred, but nautilus checks for this value + 1
/**
* Checks if the filesystem is in use (and therefore an unmount attempt should be avoided).
@@ -20,20 +23,6 @@ public interface FuseNioAdapter extends FuseFS, AutoCloseable {
*/
boolean isInUse();
- /**
- * Sets mounted to false.
- *
- * Allows custom unmount implementations to prevent subsequent invocations of {@link #umount()} to run into illegal states.
- */
- void setUnmounted();
-
- /**
- * If the init() callback of fuse_operations is implemented, this method blocks until it is called or a specified timeout is hit. Otherwise returns directly.
- *
- * @param timeOutMillis the timeout in milliseconds to wait until the init() call
- * @throws InterruptedException If the waiting thread is interrupted.
- * @throws TimeoutException If the waiting thread waits longer than the specified {@code timeout}.
- */
- void awaitInitCall(long timeOutMillis) throws InterruptedException, TimeoutException;
-
+ @Override
+ void close() throws IOException;
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterComponent.java b/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterComponent.java
deleted file mode 100644
index ee4a1dd..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterComponent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import dagger.BindsInstance;
-import dagger.Component;
-
-import javax.inject.Named;
-import java.nio.file.Path;
-
-@PerAdapter
-@Component(modules = {FuseNioAdapterModule.class})
-public interface FuseNioAdapterComponent {
-
- ReadOnlyAdapter readOnlyAdapter();
-
- ReadWriteAdapter readWriteAdapter();
-
- @Component.Builder
- interface Builder {
-
- @BindsInstance
- Builder root(@Named("root") Path root);
-
- @BindsInstance
- Builder maxFileNameLength(@Named("maxFileNameLength") int maxFileNameLength);
-
- @BindsInstance
- Builder fileNameTranscoder(FileNameTranscoder fileNameTranscoder);
-
- FuseNioAdapterComponent build();
- }
-
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterModule.java b/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterModule.java
deleted file mode 100644
index 1887645..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/FuseNioAdapterModule.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.FileStore;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import javax.inject.Named;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class FuseNioAdapterModule {
-
- @Provides
- @PerAdapter
- protected FileStore provideRootFileStore(@Named("root") Path root) {
- try {
- return Files.getFileStore(root);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
index 84c645b..381b7d6 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
@@ -1,5 +1,9 @@
package org.cryptomator.frontend.fuse;
+import com.google.common.base.MoreObjects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -9,11 +13,6 @@
import java.nio.file.attribute.FileAttribute;
import java.util.Set;
-import com.google.common.base.MoreObjects;
-import jnr.ffi.Pointer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
public class OpenFile implements Closeable {
private static final Logger LOG = LoggerFactory.getLogger(OpenFile.class);
@@ -41,28 +40,24 @@ static OpenFile create(Path path, Set extends OpenOption> options, FileAttribu
* @return Actual number of bytes read (can be less than {@code size} if reached EOF).
* @throws IOException If an exception occurs during read.
*/
- public synchronized int read(Pointer buf, long num, long offset) throws IOException {
+ public int read(ByteBuffer buf, long num, long offset) throws IOException {
long size = channel.size();
if (offset >= size) {
return 0;
+ } else if (num > Integer.MAX_VALUE) {
+ throw new IOException("Requested too many bytes");
} else {
- ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);
- long pos = 0;
- channel.position(offset);
- LOG.trace("Attempting to read {}-{}:", offset, offset + num);
- do {
- long remaining = num - pos;
- int read = readNext(bb, remaining);
- if (read == -1) {
+ int read = 0;
+ int toRead = (int) Math.min(num, buf.limit());
+ while (read < toRead) {
+ int r = channel.read(buf, offset + read);
+ if (r == -1) {
LOG.trace("Reached EOF");
- return (int) pos; // reached EOF TODO: wtf cast
- } else {
- LOG.trace("Reading {}-{}", offset + pos, offset + pos + read);
- buf.put(pos, bb.array(), 0, read);
- pos += read;
+ break;
}
- } while (pos < num);
- return (int) pos; // TODO wtf cast
+ read += r;
+ }
+ return read;
}
}
@@ -73,29 +68,18 @@ public synchronized int read(Pointer buf, long num, long offset) throws IOExcept
* @param num Number of bytes to write
* @param offset Position of first byte to write at
* @return Actual number of bytes written
- * TODO: only the bytes which contains information or also some filling zeros?
* @throws IOException If an exception occurs during write.
*/
- public synchronized int write(Pointer buf, long num, long offset) throws IOException {
- ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);
- long written = 0;
- channel.position(offset);
- do {
- long remaining = num - written;
- bb.clear();
- int len = (int) Math.min(remaining, bb.capacity());
- buf.get(written, bb.array(), 0, len);
- bb.limit(len);
- channel.write(bb); // TODO check return value
- written += len;
- } while (written < num);
- return (int) written; // TODO wtf cast
- }
-
- private int readNext(ByteBuffer readBuf, long num) throws IOException {
- readBuf.clear();
- readBuf.limit((int) Math.min(readBuf.capacity(), num));
- return channel.read(readBuf);
+ public int write(ByteBuffer buf, long num, long offset) throws IOException {
+ if (num > Integer.MAX_VALUE) {
+ throw new IOException("Requested too many bytes");
+ }
+ int written = 0;
+ int toWrite = (int) Math.min(num, buf.limit());
+ while (written < toWrite) {
+ written += channel.write(buf, offset + written);
+ }
+ return written;
}
@Override
diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
index 4747503..c223fca 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
@@ -1,14 +1,14 @@
package org.cryptomator.frontend.fuse;
+import com.google.common.collect.Sets;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
-import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
-import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
-import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -16,13 +16,6 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
-import javax.inject.Inject;
-
-import com.google.common.collect.Sets;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@PerAdapter
public class OpenFileFactory implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(OpenFileFactory.class);
@@ -30,10 +23,6 @@ public class OpenFileFactory implements AutoCloseable {
private final ConcurrentMap openFiles = new ConcurrentHashMap<>();
private final AtomicLong fileHandleGen = new AtomicLong(1l);
- @Inject
- public OpenFileFactory() {
- }
-
/**
* @param path path of the file to open
* @param options file open options
@@ -94,8 +83,8 @@ public int getOpenFileCount(){
@Override
public synchronized void close() throws IOException {
IOException exception = new IOException("At least one open file could not be closed.");
- for (Iterator> it = openFiles.entrySet().iterator(); it.hasNext();) {
- Map.Entry entry = it.next();
+ for (var it = openFiles.entrySet().iterator(); it.hasNext();) {
+ var entry = it.next();
OpenFile openFile = entry.getValue();
LOG.warn("Closing unclosed file {}", openFile);
try {
diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenOptionsUtil.java b/src/main/java/org/cryptomator/frontend/fuse/OpenOptionsUtil.java
deleted file mode 100644
index 5af40c4..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/OpenOptionsUtil.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import java.nio.file.OpenOption;
-import java.nio.file.StandardOpenOption;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-import jnr.constants.platform.OpenFlags;
-
-@PerAdapter
-public class OpenOptionsUtil {
-
- private final BitMaskEnumUtil bitMaskUtil;
-
- @Inject
- public OpenOptionsUtil(BitMaskEnumUtil bitMaskUtil) {
- this.bitMaskUtil = bitMaskUtil;
- }
-
- public Set fuseOpenFlagsToNioOpenOptions(long mask) {
- Set flags = bitMaskUtil.bitMaskToSet(OpenFlags.class, mask);
- return fuseOpenFlagsToNioOpenOptions(flags);
- }
-
- public Set fuseOpenFlagsToNioOpenOptions(Set flags) {
- Set result = new HashSet<>();
- // https://linux.die.net/man/3/open:
- if (flags.contains(OpenFlags.O_RDWR)) {
- result.add(StandardOpenOption.READ);
- result.add(StandardOpenOption.WRITE);
- } else if (flags.contains(OpenFlags.O_WRONLY)) {
- result.add(StandardOpenOption.WRITE);
- } else if (flags.contains(OpenFlags.O_RDONLY)) {
- result.add(StandardOpenOption.READ);
- }
- if (flags.contains(OpenFlags.O_APPEND)) {
- result.add(StandardOpenOption.APPEND);
- }
- if (flags.contains(OpenFlags.O_TRUNC)) {
- result.add(StandardOpenOption.TRUNCATE_EXISTING);
- }
- return result;
- }
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/PerAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/PerAdapter.java
deleted file mode 100644
index 3d27e6a..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/PerAdapter.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Scope;
-
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-@Scope
-@Documented
-@Retention(RUNTIME)
-public @interface PerAdapter {
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
index 873f278..3dc7b52 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
@@ -3,25 +3,21 @@
import com.google.common.base.CharMatcher;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
-import jnr.ffi.Pointer;
-import jnr.ffi.types.off_t;
-import jnr.ffi.types.size_t;
import org.cryptomator.frontend.fuse.locks.AlreadyLockedException;
import org.cryptomator.frontend.fuse.locks.DataLock;
import org.cryptomator.frontend.fuse.locks.LockManager;
import org.cryptomator.frontend.fuse.locks.PathLock;
+import org.cryptomator.jfuse.api.DirFiller;
+import org.cryptomator.jfuse.api.Errno;
+import org.cryptomator.jfuse.api.FileInfo;
+import org.cryptomator.jfuse.api.Stat;
+import org.cryptomator.jfuse.api.Statvfs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import ru.serce.jnrfuse.ErrorCodes;
-import ru.serce.jnrfuse.FuseFillDir;
-import ru.serce.jnrfuse.FuseStubFS;
-import ru.serce.jnrfuse.struct.FileStat;
-import ru.serce.jnrfuse.struct.FuseFileInfo;
-import ru.serce.jnrfuse.struct.Statvfs;
-import javax.inject.Inject;
-import javax.inject.Named;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
@@ -39,44 +35,71 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.function.BooleanSupplier;
-/**
- * Read-Only FUSE-NIO-Adapter based on Sergey Tselovalnikov's HelloFuse
- */
-@PerAdapter
-public class ReadOnlyAdapter extends FuseStubFS implements FuseNioAdapter {
+public sealed class ReadOnlyAdapter implements FuseNioAdapter permits ReadWriteAdapter {
private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyAdapter.class);
private static final int BLOCKSIZE = 4096;
+ protected final Errno errno;
protected final Path root;
private final int maxFileNameLength;
protected final FileStore fileStore;
protected final LockManager lockManager;
+ protected final OpenFileFactory openFiles;
protected final FileNameTranscoder fileNameTranscoder;
private final ReadOnlyDirectoryHandler dirHandler;
private final ReadOnlyFileHandler fileHandler;
private final ReadOnlyLinkHandler linkHandler;
- private final FileAttributesUtil attrUtil;
private final BooleanSupplier hasOpenFiles;
- private final CountDownLatch initSignaler;
- @Inject
- public ReadOnlyAdapter(@Named("root") Path root, @Named("maxFileNameLength") int maxFileNameLength, FileNameTranscoder fileNameTranscoder, FileStore fileStore, LockManager lockManager, ReadOnlyDirectoryHandler dirHandler, ReadOnlyFileHandler fileHandler, ReadOnlyLinkHandler linkHandler, FileAttributesUtil attrUtil, OpenFileFactory fileFactory) {
+ protected ReadOnlyAdapter(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder, FileStore fileStore, OpenFileFactory openFiles, ReadOnlyDirectoryHandler dirHandler, ReadOnlyFileHandler fileHandler) {
+ this.errno = errno;
this.root = root;
this.maxFileNameLength = maxFileNameLength;
this.fileNameTranscoder = fileNameTranscoder;
this.fileStore = fileStore;
- this.lockManager = lockManager;
+ this.lockManager = new LockManager();
+ this.openFiles = openFiles;
this.dirHandler = dirHandler;
this.fileHandler = fileHandler;
- this.linkHandler = linkHandler;
- this.attrUtil = attrUtil;
- this.hasOpenFiles = () -> fileFactory.getOpenFileCount() != 0;
- this.initSignaler = new CountDownLatch(1);
+ this.linkHandler = new ReadOnlyLinkHandler(fileNameTranscoder);
+ this.hasOpenFiles = () -> openFiles.getOpenFileCount() != 0;
+ }
+
+ public static ReadOnlyAdapter create(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder) {
+ try {
+ var fileStore = Files.getFileStore(root);
+ var openFiles = new OpenFileFactory();
+ var dirHandler = new ReadOnlyDirectoryHandler(fileNameTranscoder);
+ var fileHandler = new ReadOnlyFileHandler(openFiles);
+ return new ReadOnlyAdapter(errno, root, maxFileNameLength, fileNameTranscoder, fileStore, openFiles, dirHandler, fileHandler);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public Errno errno() {
+ return errno;
+ }
+
+ @Override
+ public Set supportedOperations() {
+ return Set.of(Operation.ACCESS,
+ Operation.CHMOD,
+ Operation.CREATE,
+ Operation.DESTROY,
+ Operation.GET_ATTR,
+ Operation.INIT,
+ Operation.OPEN,
+ Operation.OPEN_DIR,
+ Operation.READ,
+ Operation.READLINK,
+ Operation.READ_DIR,
+ Operation.RELEASE,
+ Operation.RELEASE_DIR,
+ Operation.STATFS);
}
protected Path resolvePath(String absolutePath) {
@@ -91,17 +114,17 @@ public int statfs(String path, Statvfs stbuf) {
long avail = fileStore.getUsableSpace();
long tBlocks = total / BLOCKSIZE;
long aBlocks = avail / BLOCKSIZE;
- stbuf.f_bsize.set(BLOCKSIZE);
- stbuf.f_frsize.set(BLOCKSIZE);
- stbuf.f_blocks.set(tBlocks);
- stbuf.f_bavail.set(aBlocks);
- stbuf.f_bfree.set(aBlocks);
- stbuf.f_namemax.set(maxFileNameLength);
+ stbuf.setBsize(BLOCKSIZE);
+ stbuf.setFrsize(BLOCKSIZE);
+ stbuf.setBlocks(tBlocks);
+ stbuf.setBavail(aBlocks);
+ stbuf.setBfree(aBlocks);
+ stbuf.setNameMax(maxFileNameLength);
LOG.trace("statfs {} ({} / {})", path, avail, total);
return 0;
} catch (IOException | RuntimeException e) {
LOG.error("statfs " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@@ -109,11 +132,11 @@ public int statfs(String path, Statvfs stbuf) {
public int access(String path, int mask) {
try {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
- Set accessModes = attrUtil.accessModeMaskToSet(mask);
+ Set accessModes = FileAttributesUtil.accessModeMaskToSet(mask);
return checkAccess(node, accessModes);
} catch (RuntimeException e) {
LOG.error("checkAccess failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@@ -129,33 +152,33 @@ protected int checkAccess(Path path, Set requiredAccessModes, Set() {
@@ -77,9 +66,7 @@ public int readdir(Path path, Pointer buf, FuseFillDir filler, long offset, Fuse
Iterator iter = Iterators.concat(sameAndParent, ds.iterator());
while (iter.hasNext()) {
String fileName = iter.next().getFileName().toString();
- if (filler.apply(buf, fileNameTranscoder.nioToFuse(fileName), null, 0) != 0) {
- return -ErrorCodes.ENOMEM();
- }
+ filler.fill(fileNameTranscoder.nioToFuse(fileName));
}
return 0;
} catch (DirectoryIteratorException e) {
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyFileHandler.java b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyFileHandler.java
index 7346b5a..435d93a 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyFileHandler.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyFileHandler.java
@@ -1,39 +1,31 @@
package org.cryptomator.frontend.fuse;
-import jnr.ffi.Pointer;
-import ru.serce.jnrfuse.struct.FileStat;
-import ru.serce.jnrfuse.struct.FuseFileInfo;
+import org.cryptomator.jfuse.api.FileInfo;
+import org.cryptomator.jfuse.api.Stat;
-import javax.inject.Inject;
import java.io.Closeable;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.file.AccessDeniedException;
-import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
import java.util.Set;
-@PerAdapter
public class ReadOnlyFileHandler implements Closeable {
protected final OpenFileFactory openFiles;
- protected final FileAttributesUtil attrUtil;
- private final OpenOptionsUtil openOptionsUtil;
- @Inject
- public ReadOnlyFileHandler(OpenFileFactory openFiles, FileAttributesUtil attrUtil, OpenOptionsUtil openOptionsUtil) {
+ public ReadOnlyFileHandler(OpenFileFactory openFiles) {
this.openFiles = openFiles;
- this.attrUtil = attrUtil;
- this.openOptionsUtil = openOptionsUtil;
}
- public void open(Path path, FuseFileInfo fi) throws IOException {
- Set openOptions = openOptionsUtil.fuseOpenFlagsToNioOpenOptions(fi.flags.longValue());
+ public void open(Path path, FileInfo fi) throws IOException {
+ var openOptions = fi.getOpenFlags();
long fileHandle = open(path, openOptions);
- fi.fh.set(fileHandle);
+ fi.setFh(fileHandle);
}
/**
@@ -43,7 +35,7 @@ public void open(Path path, FuseFileInfo fi) throws IOException {
* @throws AccessDeniedException Thrown if the requested openOptions are not supported
* @throws IOException
*/
- protected long open(Path path, Set openOptions) throws AccessDeniedException, IOException {
+ protected long open(Path path, Set openOptions) throws AccessDeniedException, IOException {
if (openOptions.contains(StandardOpenOption.WRITE)) {
throw new AccessDeniedException(path.toString(), null, "Unsupported open options: WRITE");
} else {
@@ -62,8 +54,8 @@ protected long open(Path path, Set openOptions) throws AccessDeniedE
* @throws ClosedChannelException If no open file could be found for the given file handle
* @throws IOException
*/
- public int read(Pointer buf, long size, long offset, FuseFileInfo fi) throws IOException {
- OpenFile file = openFiles.get(fi.fh.get());
+ public int read(ByteBuffer buf, long size, long offset, FileInfo fi) throws IOException {
+ OpenFile file = openFiles.get(fi.getFh());
if (file == null) {
throw new ClosedChannelException();
}
@@ -77,20 +69,17 @@ public int read(Pointer buf, long size, long offset, FuseFileInfo fi) throws IOE
* @throws ClosedChannelException If no channel for the given fileHandle has been found.
* @throws IOException
*/
- public void release(FuseFileInfo fi) throws IOException {
- openFiles.close(fi.fh.get());
+ public void release(FileInfo fi) throws IOException {
+ openFiles.close(fi.getFh());
}
- public int getattr(Path node, BasicFileAttributes attrs, FileStat stat) {
- if (attrs instanceof PosixFileAttributes) {
- PosixFileAttributes posixAttrs = (PosixFileAttributes) attrs;
- long mode = attrUtil.posixPermissionsToOctalMode(posixAttrs.permissions());
- mode = mode & 0555;
- stat.st_mode.set(FileStat.S_IFREG | mode);
+ public int getattr(Path node, BasicFileAttributes attrs, Stat stat) {
+ if (attrs instanceof PosixFileAttributes posixAttrs) {
+ stat.setPermissions(posixAttrs.permissions());
} else {
- stat.st_mode.set(FileStat.S_IFREG | 0444);
+ stat.setMode(0555);
}
- attrUtil.copyBasicFileAttributesFromNioToFuse(attrs, stat);
+ FileAttributesUtil.copyBasicFileAttributesFromNioToFuse(attrs, stat);
return 0;
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyLinkHandler.java b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyLinkHandler.java
index 190206c..85242f8 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyLinkHandler.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyLinkHandler.java
@@ -1,42 +1,33 @@
package org.cryptomator.frontend.fuse;
-import jnr.ffi.Pointer;
+import org.cryptomator.jfuse.api.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import ru.serce.jnrfuse.struct.FileStat;
-import javax.inject.Inject;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
+import java.nio.file.NotLinkException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
-@PerAdapter
class ReadOnlyLinkHandler {
- private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyLinkHandler.class);
-
- private final FileAttributesUtil attrUtil;
private final FileNameTranscoder fileNameTranscoder;
- @Inject
- public ReadOnlyLinkHandler(FileAttributesUtil attrUtil, FileNameTranscoder fileNameTranscoder) {
- this.attrUtil = attrUtil;
+ public ReadOnlyLinkHandler(FileNameTranscoder fileNameTranscoder) {
this.fileNameTranscoder = fileNameTranscoder;
}
- public int getattr(Path path, BasicFileAttributes attrs, FileStat stat) {
- if (attrs instanceof PosixFileAttributes) {
- PosixFileAttributes posixAttrs = (PosixFileAttributes) attrs;
- long mode = attrUtil.posixPermissionsToOctalMode(posixAttrs.permissions());
- mode = mode & 0555;
- stat.st_mode.set(FileStat.S_IFLNK | mode);
+ public int getattr(Path path, BasicFileAttributes attrs, Stat stat) {
+ if (attrs instanceof PosixFileAttributes posixAttrs) {
+ stat.setPermissions(posixAttrs.permissions());
} else {
- stat.st_mode.set(FileStat.S_IFLNK | 0555);
+ stat.setMode(0555);
}
- attrUtil.copyBasicFileAttributesFromNioToFuse(attrs, stat);
+ stat.setModeBits(Stat.S_IFLNK);
+ FileAttributesUtil.copyBasicFileAttributesFromNioToFuse(attrs, stat);
return 0;
}
@@ -48,13 +39,16 @@ public int getattr(Path path, BasicFileAttributes attrs, FileStat stat) {
* @return
* @throws IOException
*/
- public int readlink(Path path, Pointer buf, long size) throws IOException {
+ public int readlink(Path path, ByteBuffer buf, long size) throws IOException {
+ if(path.getParent() == null) {
+ throw new NotLinkException("Root cannot be a link");
+ }
Path target = Files.readSymbolicLink(path);
ByteBuffer fuseEncodedTarget = fileNameTranscoder.interpretAsFuseString(fileNameTranscoder.nioToFuse(target.toString()));
int len = (int) Math.min(fuseEncodedTarget.remaining(), size - 1);
assert len < size;
buf.put(0, fuseEncodedTarget.array(), 0, len);
- buf.putByte(len, (byte) 0x00); // add null terminator
+ buf.put(len, (byte) 0x00); // add null terminator
return 0;
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteAdapter.java
index 8190aa6..98cc96a 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteAdapter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteAdapter.java
@@ -1,24 +1,16 @@
package org.cryptomator.frontend.fuse;
-import jnr.constants.platform.OpenFlags;
-import jnr.ffi.Pointer;
-import jnr.ffi.types.gid_t;
-import jnr.ffi.types.mode_t;
-import jnr.ffi.types.off_t;
-import jnr.ffi.types.size_t;
-import jnr.ffi.types.uid_t;
import org.cryptomator.frontend.fuse.locks.DataLock;
-import org.cryptomator.frontend.fuse.locks.LockManager;
import org.cryptomator.frontend.fuse.locks.PathLock;
+import org.cryptomator.jfuse.api.Errno;
+import org.cryptomator.jfuse.api.FileInfo;
+import org.cryptomator.jfuse.api.TimeSpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import ru.serce.jnrfuse.ErrorCodes;
-import ru.serce.jnrfuse.struct.FuseFileInfo;
-import ru.serce.jnrfuse.struct.Timespec;
-import javax.inject.Inject;
-import javax.inject.Named;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.file.AccessMode;
import java.nio.file.DirectoryNotEmptyException;
@@ -35,27 +27,50 @@
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermissions;
-import java.time.DateTimeException;
import java.util.EnumSet;
import java.util.Set;
/**
*
*/
-@PerAdapter
-public class ReadWriteAdapter extends ReadOnlyAdapter {
+public final class ReadWriteAdapter extends ReadOnlyAdapter {
private static final Logger LOG = LoggerFactory.getLogger(ReadWriteAdapter.class);
private final ReadWriteFileHandler fileHandler;
- private final FileAttributesUtil attrUtil;
- private final BitMaskEnumUtil bitMaskUtil;
- @Inject
- public ReadWriteAdapter(@Named("root") Path root, @Named("maxFileNameLength") int maxFileNameLength, FileNameTranscoder fileNameTranscoder, FileStore fileStore, LockManager lockManager, ReadWriteDirectoryHandler dirHandler, ReadWriteFileHandler fileHandler, ReadOnlyLinkHandler linkHandler, FileAttributesUtil attrUtil, BitMaskEnumUtil bitMaskUtil, OpenFileFactory fileFactory) {
- super(root, maxFileNameLength, fileNameTranscoder, fileStore, lockManager, dirHandler, fileHandler, linkHandler, attrUtil, fileFactory);
+ private ReadWriteAdapter(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder, FileStore fileStore, OpenFileFactory openFiles, ReadWriteDirectoryHandler dirHandler, ReadWriteFileHandler fileHandler) {
+ super(errno, root, maxFileNameLength, fileNameTranscoder, fileStore, openFiles, dirHandler, fileHandler);
this.fileHandler = fileHandler;
- this.attrUtil = attrUtil;
- this.bitMaskUtil = bitMaskUtil;
+ }
+
+ public static ReadWriteAdapter create(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder) {
+ try {
+ var fileStore = Files.getFileStore(root);
+ var openFiles = new OpenFileFactory();
+ var dirHandler = new ReadWriteDirectoryHandler(fileNameTranscoder);
+ var fileHandler = new ReadWriteFileHandler(openFiles);
+ return new ReadWriteAdapter(errno, root, maxFileNameLength, fileNameTranscoder, fileStore, openFiles, dirHandler, fileHandler);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public Set supportedOperations() {
+ var ops = EnumSet.copyOf(super.supportedOperations());
+ ops.add(Operation.CHMOD);
+ //ops.add(Operation.CHOWN);
+ ops.add(Operation.CREATE);
+ //ops.add(Operation.FSYNC);
+ ops.add(Operation.MKDIR);
+ ops.add(Operation.RENAME);
+ ops.add(Operation.RMDIR);
+ ops.add(Operation.SYMLINK);
+ ops.add(Operation.TRUNCATE);
+ ops.add(Operation.UNLINK);
+ ops.add(Operation.UTIMENS);
+ ops.add(Operation.WRITE);
+ return ops;
}
@Override
@@ -64,8 +79,8 @@ protected int checkAccess(Path path, Set requiredAccessModes) {
}
@Override
- public int mkdir(String path, @mode_t long mode) {
- try (PathLock pathLock = lockManager.createPathLock(path).forWriting();
+ public int mkdir(String path, int mode) {
+ try (PathLock pathLock = lockManager.lockForWriting(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
LOG.trace("mkdir {} ({})", path, mode);
@@ -73,18 +88,18 @@ public int mkdir(String path, @mode_t long mode) {
return 0;
} catch (FileAlreadyExistsException e) {
LOG.warn("mkdir {} failed, file already exists.", path);
- return -ErrorCodes.EEXIST();
+ return -errno.eexist();
} catch (FileSystemException e) {
return getErrorCodeForGenericFileSystemException(e, "mkdir " + path);
} catch (IOException | RuntimeException e) {
LOG.error("mkdir " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
public int symlink(String targetPath, String linkPath) {
- try (PathLock pathLock = lockManager.createPathLock(linkPath).forWriting();
+ try (PathLock pathLock = lockManager.lockForWriting(linkPath);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path link = resolvePath(fileNameTranscoder.fuseToNio(linkPath));
Path target = link.getFileSystem().getPath(fileNameTranscoder.fuseToNio(targetPath));
@@ -93,24 +108,24 @@ public int symlink(String targetPath, String linkPath) {
return 0;
} catch (FileAlreadyExistsException e) {
LOG.warn("symlink {} -> {} failed, file already exists.", linkPath, targetPath);
- return -ErrorCodes.EEXIST();
+ return -errno.eexist();
} catch (FileSystemException e) {
return getErrorCodeForGenericFileSystemException(e, "symlink " + targetPath + " -> " + linkPath);
} catch (IOException | RuntimeException e) {
LOG.error("symlink failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int create(String path, @mode_t long mode, FuseFileInfo fi) {
- try (PathLock pathLock = lockManager.createPathLock(path).forWriting();
+ public int create(String path, int mode, FileInfo fi) {
+ try (PathLock pathLock = lockManager.lockForWriting(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
- Set flags = bitMaskUtil.bitMaskToSet(OpenFlags.class, fi.flags.longValue());
+ var flags = fi.getOpenFlags();
LOG.trace("create {} with flags {}", path, flags);
if (fileStore.supportsFileAttributeView(PosixFileAttributeView.class)) {
- FileAttribute> attrs = PosixFilePermissions.asFileAttribute(attrUtil.octalModeToPosixPermissions(mode));
+ FileAttribute> attrs = PosixFilePermissions.asFileAttribute(FileAttributesUtil.octalModeToPosixPermissions(mode));
fileHandler.createAndOpen(node, fi, attrs);
} else {
fileHandler.createAndOpen(node, fi);
@@ -118,65 +133,65 @@ public int create(String path, @mode_t long mode, FuseFileInfo fi) {
return 0;
} catch (FileAlreadyExistsException e) {
LOG.warn("create {} failed, file already exists.", path);
- return -ErrorCodes.EEXIST();
+ return -errno.eexist();
} catch (FileSystemException e) {
return getErrorCodeForGenericFileSystemException(e, "create " + path);
} catch (IOException | RuntimeException e) {
LOG.error("create " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int chown(String path, @uid_t long uid, @gid_t long gid) {
+ public int chown(String path, int uid, int gid, FileInfo fi) {
LOG.trace("Ignoring chown(uid={}, gid={}) call. Files will be served with static uid/gid.", uid, gid);
return 0;
}
@Override
- public int chmod(String path, @mode_t long mode) {
- try (PathLock pathLock = lockManager.createPathLock(path).forReading();
+ public int chmod(String path, int mode, FileInfo fi) {
+ try (PathLock pathLock = lockManager.lockForReading(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
LOG.trace("chmod {} ({})", path, mode);
- Files.setPosixFilePermissions(node, attrUtil.octalModeToPosixPermissions(mode));
+ Files.setPosixFilePermissions(node, FileAttributesUtil.octalModeToPosixPermissions(mode));
return 0;
} catch (NoSuchFileException e) {
LOG.warn("chmod {} failed, file not found.", path);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (UnsupportedOperationException e) {
LOG.warn("Setting posix permissions not supported by underlying file system.");
- return -ErrorCodes.ENOSYS();
+ return -errno.enosys();
} catch (IOException | RuntimeException e) {
LOG.error("chmod " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
public int unlink(String path) {
- try (PathLock pathLock = lockManager.createPathLock(path).forWriting();
+ try (PathLock pathLock = lockManager.lockForWriting(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
if (Files.isDirectory(node, LinkOption.NOFOLLOW_LINKS)) {
LOG.warn("unlink {} failed, node is a directory.", path);
- return -ErrorCodes.EISDIR();
+ return -errno.eisdir();
}
LOG.trace("unlink {}", path);
Files.delete(node);
return 0;
} catch (NoSuchFileException e) {
LOG.warn("unlink {} failed, file not found.", path);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (IOException | RuntimeException e) {
LOG.error("unlink " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
public int rmdir(String path) {
- try (PathLock pathLock = lockManager.createPathLock(path).forWriting();
+ try (PathLock pathLock = lockManager.lockForWriting(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
if (!Files.isDirectory(node, LinkOption.NOFOLLOW_LINKS)) {
@@ -189,21 +204,21 @@ public int rmdir(String path) {
return 0;
} catch (NotDirectoryException e) {
LOG.warn("rmdir {} failed, node is not a directory.", path);
- return -ErrorCodes.ENOTDIR();
+ return -errno.enotdir();
} catch (NoSuchFileException e) {
LOG.warn("rmdir {} failed, file not found.", path);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (DirectoryNotEmptyException e) {
LOG.warn("rmdir {} failed, directory not empty.", path);
- return -ErrorCodes.ENOTEMPTY();
+ return -errno.enotempty();
} catch (IOException | RuntimeException e) {
LOG.error("rmdir " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
/**
- * Specialised method on MacOS due to the usage of the -noappledouble option in the {@link org.cryptomator.frontend.fuse.mount.MacMounter} and the possible existence of AppleDouble or DSStore-Files.
+ * Specialised method on MacOS due to the usage of the -noappledouble option in the {@link org.cryptomator.frontend.fuse.mount.MacFuseMountProvider} and the possible existence of AppleDouble or DSStore-Files.
*
* @param node the directory path for which is checked for such files
* @throws IOException if an AppleDouble file cannot be deleted or opening of a directory stream fails
@@ -218,10 +233,10 @@ private void deleteAppleDoubleFiles(Path node) throws IOException {
}
@Override
- public int rename(String oldPath, String newPath) {
- try (PathLock oldPathLock = lockManager.createPathLock(oldPath).forWriting();
+ public int rename(String oldPath, String newPath, int flags) {
+ try (PathLock oldPathLock = lockManager.lockForWriting(oldPath);
DataLock oldDataLock = oldPathLock.lockDataForWriting();
- PathLock newPathLock = lockManager.createPathLock(newPath).forWriting();
+ PathLock newPathLock = lockManager.lockForWriting(newPath);
DataLock newDataLock = newPathLock.lockDataForWriting()) {
// TODO: recursively check for open file handles
Path nodeOld = resolvePath(fileNameTranscoder.fuseToNio(oldPath));
@@ -231,107 +246,86 @@ public int rename(String oldPath, String newPath) {
return 0;
} catch (NoSuchFileException e) {
LOG.warn("rename {} to {} failed, file not found.", oldPath, newPath);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (DirectoryNotEmptyException e) {
LOG.warn("rename {} to {} failed, directory not empty.", oldPath, newPath);
- return -ErrorCodes.ENOTEMPTY();
+ return -errno.enotempty();
} catch (FileSystemException e) {
return getErrorCodeForGenericFileSystemException(e, "rename " + oldPath + " -> " + newPath);
} catch (IOException | RuntimeException e) {
LOG.error("rename " + oldPath + " to " + newPath + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int utimens(String path, Timespec[] timespec) {
- /*
- * From utimensat(2) man page:
- * the array times: times[0] specifies the new "last access time" (atime);
- * times[1] specifies the new "last modification time" (mtime).
- */
- assert timespec.length == 2;
- try (PathLock pathLock = lockManager.createPathLock(path).forReading();
+ public int utimens(String path, TimeSpec atime, TimeSpec mtime, FileInfo fi) {
+ try (PathLock pathLock = lockManager.lockForReading(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
- LOG.trace("utimens {} (last modification {} sec {} nsec, last access {} sec {} nsec)", path, timespec[1].tv_sec.get(), timespec[1].tv_nsec.longValue(), timespec[0].tv_sec.get(), timespec[0].tv_nsec.longValue());
- fileHandler.utimens(node, timespec[1], timespec[0]);
+ LOG.trace("utimens {} (last modification {}, last access {})", path, mtime, atime);
+ fileHandler.utimens(node, mtime, atime);
return 0;
- } catch (DateTimeException | ArithmeticException e) {
- LOG.warn("utimens {} failed, invalid argument.", e);
- return -ErrorCodes.EINVAL();
} catch (NoSuchFileException e) {
LOG.warn("utimens {} failed, file not found.", path);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (IOException | RuntimeException e) {
LOG.error("utimens " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int write(String path, Pointer buf, @size_t long size, @off_t long offset, FuseFileInfo fi) {
- try (PathLock pathLock = lockManager.createPathLock(path).forReading();
+ public int write(String path, ByteBuffer buf, long size, long offset, FileInfo fi) {
+ try (PathLock pathLock = lockManager.lockForReading(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
LOG.trace("write {} bytes to file {} starting at {}...", size, path, offset);
int written = fileHandler.write(buf, size, offset, fi);
LOG.trace("wrote {} bytes to file {}.", written, path);
return written;
} catch (ClosedChannelException e) {
- LOG.warn("write {} failed, invalid file handle {}", path, fi.fh.get());
- return -ErrorCodes.EBADF();
+ LOG.warn("write {} failed, invalid file handle {}", path, fi.getFh());
+ return -errno.ebadf();
} catch (IOException | RuntimeException e) {
LOG.error("write " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int truncate(String path, @off_t long size) {
- try (PathLock pathLock = lockManager.createPathLock(path).forReading();
+ public int truncate(String path, long size, FileInfo fi) {
+ try (PathLock pathLock = lockManager.lockForReading(path);
DataLock dataLock = pathLock.lockDataForWriting()) {
Path node = resolvePath(fileNameTranscoder.fuseToNio(path));
LOG.trace("truncate {} {}", path, size);
- fileHandler.truncate(node, size);
+ if (fi != null) {
+ fileHandler.ftruncate(size, fi);
+ } else {
+ fileHandler.truncate(node, size);
+ }
return 0;
} catch (NoSuchFileException e) {
LOG.warn("utimens {} failed, file not found.", path);
- return -ErrorCodes.ENOENT();
+ return -errno.enoent();
} catch (IOException | RuntimeException e) {
LOG.error("truncate " + path + " failed.", e);
- return -ErrorCodes.EIO();
- }
- }
-
- @Override
- public int ftruncate(String path, long size, FuseFileInfo fi) {
- try (PathLock pathLock = lockManager.createPathLock(path).forReading();
- DataLock dataLock = pathLock.lockDataForWriting()) {
- LOG.trace("ftruncate {} to size: {}", path, size);
- fileHandler.ftruncate(size, fi);
- return 0;
- } catch (ClosedChannelException e) {
- LOG.warn("ftruncate {} failed, invalid file handle {}", path, fi.fh.get());
- return -ErrorCodes.EBADF();
- } catch (IOException | RuntimeException e) {
- LOG.error("ftruncate " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
@Override
- public int fsync(String path, int isdatasync, FuseFileInfo fi) {
+ public int fsync(String path, int isdatasync, FileInfo fi) {
try {
boolean metaData = isdatasync == 0;
LOG.trace("fsync {}", path);
fileHandler.fsync(fi, metaData);
return 0;
} catch (ClosedChannelException e) {
- LOG.warn("fsync {} failed, invalid file handle {}", path, fi.fh.get());
- return -ErrorCodes.EBADF();
+ LOG.warn("fsync {} failed, invalid file handle {}", path, fi.getFh());
+ return -errno.ebadf();
} catch (IOException | RuntimeException e) {
LOG.error("fsync " + path + " failed.", e);
- return -ErrorCodes.EIO();
+ return -errno.eio();
}
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteDirectoryHandler.java b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteDirectoryHandler.java
index 8be4cc5..a5a690b 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteDirectoryHandler.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteDirectoryHandler.java
@@ -1,29 +1,24 @@
package org.cryptomator.frontend.fuse;
-import ru.serce.jnrfuse.struct.FileStat;
+import org.cryptomator.jfuse.api.Stat;
-import javax.inject.Inject;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFileAttributes;
-@PerAdapter
public class ReadWriteDirectoryHandler extends ReadOnlyDirectoryHandler {
- @Inject
- public ReadWriteDirectoryHandler(FileAttributesUtil attrUtil, FileNameTranscoder fileNameTranscoder) {
- super(attrUtil, fileNameTranscoder);
+ public ReadWriteDirectoryHandler(FileNameTranscoder fileNameTranscoder) {
+ super(fileNameTranscoder);
}
@Override
- public int getattr(Path node, BasicFileAttributes attrs, FileStat stat) {
+ public int getattr(Path node, BasicFileAttributes attrs, Stat stat) {
int result = super.getattr(node, attrs, stat);
- if (attrs instanceof PosixFileAttributes) {
- PosixFileAttributes posixAttrs = (PosixFileAttributes) attrs;
- long mode = attrUtil.posixPermissionsToOctalMode(posixAttrs.permissions());
- stat.st_mode.set(FileStat.S_IFDIR | mode);
+ if (attrs instanceof PosixFileAttributes posixAttrs) {
+ stat.setPermissions(posixAttrs.permissions());
} else {
- stat.st_mode.set(FileStat.S_IFDIR | 0755);
+ stat.setModeBits(0755);
}
return result;
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteFileHandler.java b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteFileHandler.java
index 5df263d..e59e30d 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadWriteFileHandler.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadWriteFileHandler.java
@@ -1,19 +1,16 @@
package org.cryptomator.frontend.fuse;
-import jnr.ffi.Pointer;
-import ru.serce.jnrfuse.struct.FileStat;
-import ru.serce.jnrfuse.struct.FuseFileInfo;
-import ru.serce.jnrfuse.struct.Timespec;
+import org.cryptomator.jfuse.api.FileInfo;
+import org.cryptomator.jfuse.api.Stat;
+import org.cryptomator.jfuse.api.TimeSpec;
-import javax.inject.Inject;
import java.io.Closeable;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
-import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.LinkOption;
-import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributeView;
@@ -21,44 +18,36 @@
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFileAttributes;
-import java.time.Instant;
import java.util.EnumSet;
import java.util.Set;
-@PerAdapter
public class ReadWriteFileHandler extends ReadOnlyFileHandler implements Closeable {
- private static final long UTIME_NOW = -1l; // https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/sys/stat.h#L538
- private static final long UTIME_OMIT = -2l; // https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/sys/stat.h#L539
-
- @Inject
- public ReadWriteFileHandler(OpenFileFactory openFiles, FileAttributesUtil attrUtil, FileStore fileStore, OpenOptionsUtil openOptionsUtil) {
- super(openFiles, attrUtil, openOptionsUtil);
+ public ReadWriteFileHandler(OpenFileFactory openFiles) {
+ super(openFiles);
}
@Override
- public int getattr(Path node, BasicFileAttributes attrs, FileStat stat) {
+ public int getattr(Path node, BasicFileAttributes attrs, Stat stat) {
int result = super.getattr(node, attrs, stat);
- if (result == 0 && attrs instanceof PosixFileAttributes) {
- PosixFileAttributes posixAttrs = (PosixFileAttributes) attrs;
- long mode = attrUtil.posixPermissionsToOctalMode(posixAttrs.permissions());
- stat.st_mode.set(FileStat.S_IFREG | mode);
+ if (result == 0 && attrs instanceof PosixFileAttributes posixAttrs) {
+ stat.setPermissions(posixAttrs.permissions());
} else if (result == 0) {
- stat.st_mode.set(FileStat.S_IFREG | 0777);
+ stat.setModeBits(0777);
}
return result;
}
- public void createAndOpen(Path path, FuseFileInfo fi, FileAttribute>... attrs) throws IOException {
+ public void createAndOpen(Path path, FileInfo fi, FileAttribute>... attrs) throws IOException {
long fileHandle = openFiles.open(path, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.READ, StandardOpenOption.WRITE), attrs);
- fi.fh.set(fileHandle);
+ fi.setFh(fileHandle);
}
/**
* {@inheritDoc}
*/
@Override
- protected long open(Path path, Set openOptions) throws IOException {
+ protected long open(Path path, Set openOptions) throws IOException {
return openFiles.open(path, openOptions);
}
@@ -73,8 +62,8 @@ protected long open(Path path, Set openOptions) throws IOException {
* @throws ClosedChannelException If no open file could be found for the given file handle
* @throws IOException If an exception occurs during write.
*/
- public int write(Pointer buf, long size, long offset, FuseFileInfo fi) throws IOException {
- OpenFile file = openFiles.get(fi.fh.get());
+ public int write(ByteBuffer buf, long size, long offset, FileInfo fi) throws IOException {
+ OpenFile file = openFiles.get(fi.getFh());
if (file == null) {
throw new ClosedChannelException();
}
@@ -89,8 +78,8 @@ public int write(Pointer buf, long size, long offset, FuseFileInfo fi) throws IO
* @throws ClosedChannelException If no open file could be found for the given file handle
* @throws IOException If an exception occurs during write.
*/
- public void fsync(FuseFileInfo fi, boolean metaData) throws IOException {
- OpenFile file = openFiles.get(fi.fh.get());
+ public void fsync(FileInfo fi, boolean metaData) throws IOException {
+ OpenFile file = openFiles.get(fi.getFh());
if (file == null) {
throw new ClosedChannelException();
}
@@ -111,30 +100,19 @@ public void truncate(Path path, long size) throws IOException {
* @throws ClosedChannelException If no open file could be found for the given file handle
* @throws IOException If an exception occurs during write.
*/
- public void ftruncate(long size, FuseFileInfo fi) throws IOException {
- OpenFile file = openFiles.get(fi.fh.get());
+ public void ftruncate(long size, FileInfo fi) throws IOException {
+ OpenFile file = openFiles.get(fi.getFh());
if (file == null) {
throw new ClosedChannelException();
}
file.truncate(size);
}
- public void utimens(Path node, Timespec mTimeSpec, Timespec aTimeSpec) throws IOException {
- FileTime mTime = toFileTime(mTimeSpec);
- FileTime aTime = toFileTime(aTimeSpec);
+ public void utimens(Path node, TimeSpec mTimeSpec, TimeSpec aTimeSpec) throws IOException {
+ FileTime mTime = mTimeSpec.getOptional().map(FileTime::from).orElse(null);
+ FileTime aTime = aTimeSpec.getOptional().map(FileTime::from).orElse(null);
BasicFileAttributeView view = Files.getFileAttributeView(node, BasicFileAttributeView.class, LinkOption.NOFOLLOW_LINKS);
- view.setTimes(mTime, aTime, null); // might fail on JDK < 13, see https://bugs.openjdk.java.net/browse/JDK-8220793
+ view.setTimes(mTime, aTime, null);
}
- private FileTime toFileTime(Timespec timespec) {
- long seconds = timespec.tv_sec.longValue();
- long nanoseconds = timespec.tv_nsec.longValue();
- if (nanoseconds == UTIME_NOW) {
- return FileTime.from(Instant.now());
- } else if (nanoseconds == UTIME_OMIT) {
- return null;
- } else {
- return FileTime.from(Instant.ofEpochSecond(seconds, nanoseconds));
- }
- }
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/VersionCompare.java b/src/main/java/org/cryptomator/frontend/fuse/VersionCompare.java
deleted file mode 100644
index c52cc8f..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/VersionCompare.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.cryptomator.frontend.fuse;
-
-/**
- * from: https://www.baeldung.com/java-comparing-versions#customSolution
- */
-public class VersionCompare {
-
- public static int compareVersions(String version1, String version2) {
- int comparisonResult = 0;
-
- String[] version1Splits = version1.split("\\.");
- String[] version2Splits = version2.split("\\.");
- int maxLengthOfVersionSplits = Math.max(version1Splits.length, version2Splits.length);
-
- for (int i = 0; i < maxLengthOfVersionSplits; i++){
- Integer v1 = i < version1Splits.length ? Integer.parseInt(version1Splits[i]) : 0;
- Integer v2 = i < version2Splits.length ? Integer.parseInt(version2Splits[i]) : 0;
- int compare = v1.compareTo(v2);
- if (compare != 0) {
- comparisonResult = compare;
- break;
- }
- }
- return comparisonResult;
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/DataLock.java b/src/main/java/org/cryptomator/frontend/fuse/locks/DataLock.java
index 2832658..06991ed 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/DataLock.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/locks/DataLock.java
@@ -1,7 +1,42 @@
package org.cryptomator.frontend.fuse.locks;
-public interface DataLock extends AutoCloseable {
+import org.jetbrains.annotations.Unmodifiable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+
+/**
+ * A data lock, either for reading (shared) or writing (exclusive).
+ *
+ * @param pathComponents The path, split into path components
+ * @param rwLock The read-write-lock. We need to store a strong reference while in use, because LockManager works with weeak references
+ * @param lock Either the {@code rwLock}'s read or its write lock
+ */
+public record DataLock(@Unmodifiable List pathComponents, ReadWriteLock rwLock, Lock lock) implements AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DataLock.class);
+
+ static DataLock readLock(List pathComponents, ReadWriteLock rwLock) {
+ var lock = rwLock.readLock();
+ lock.lock();
+ LOG.trace("Acquired read data lock for '{}'", pathComponents);
+ return new DataLock(pathComponents, rwLock, lock);
+ }
+
+ static DataLock writeLock(List pathComponents, ReadWriteLock rwLock) {
+ var lock = rwLock.writeLock();
+ lock.lock();
+ LOG.trace("Acquired write data lock for '{}'", pathComponents);
+ return new DataLock(pathComponents, rwLock, lock);
+ }
@Override
- void close();
+ public void close() {
+ LOG.trace("Released data lock for '{}'", pathComponents);
+ lock.unlock();
+ }
+
}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/DataLockImpl.java b/src/main/java/org/cryptomator/frontend/fuse/locks/DataLockImpl.java
deleted file mode 100644
index 56766dd..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/DataLockImpl.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.cryptomator.frontend.fuse.locks;
-
-import java.util.List;
-import java.util.concurrent.locks.ReadWriteLock;
-
-abstract class DataLockImpl implements DataLock {
-
- protected final List pathComponents;
- protected final ReadWriteLock lock; // keep reference to avoid lock being GC'ed out of the LockManager's cache
-
- protected DataLockImpl(List pathComponents, ReadWriteLock lock) {
- this.pathComponents = pathComponents;
- this.lock = lock;
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/DataRLockImpl.java b/src/main/java/org/cryptomator/frontend/fuse/locks/DataRLockImpl.java
deleted file mode 100644
index 296490b..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/DataRLockImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.cryptomator.frontend.fuse.locks;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.concurrent.locks.ReadWriteLock;
-
-class DataRLockImpl extends DataLockImpl {
-
- private static final Logger LOG = LoggerFactory.getLogger(DataRLockImpl.class);
-
- private DataRLockImpl(List pathComponents, ReadWriteLock lock) {
- super(pathComponents, lock);
- }
-
- static DataRLockImpl create(List pathComponents, ReadWriteLock lock) {
- lock.readLock().lock();
- LOG.trace("Acquired read data lock for '{}'", pathComponents);
- return new DataRLockImpl(pathComponents, lock);
- }
-
- @Override
- public void close() {
- LOG.trace("Released read data lock for '{}'", pathComponents);
- lock.readLock().unlock();
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/DataWLockImpl.java b/src/main/java/org/cryptomator/frontend/fuse/locks/DataWLockImpl.java
deleted file mode 100644
index 9832f4c..0000000
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/DataWLockImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.cryptomator.frontend.fuse.locks;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.concurrent.locks.ReadWriteLock;
-
-class DataWLockImpl extends DataLockImpl {
-
- private static final Logger LOG = LoggerFactory.getLogger(DataWLockImpl.class);
-
- private DataWLockImpl(List pathComponents, ReadWriteLock lock) {
- super(pathComponents, lock);
- }
-
- static DataWLockImpl create(List pathComponents, ReadWriteLock lock) {
- lock.writeLock().lock();
- LOG.trace("Acquired write data lock for '{}'", pathComponents);
- return new DataWLockImpl(pathComponents, lock);
- }
-
- @Override
- public void close() {
- LOG.trace("Released write data lock for '{}'", pathComponents);
- lock.writeLock().unlock();
- }
-
-}
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/FilePaths.java b/src/main/java/org/cryptomator/frontend/fuse/locks/FilePaths.java
index e65d84e..77b5ea0 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/FilePaths.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/locks/FilePaths.java
@@ -2,10 +2,10 @@
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
+import org.jetbrains.annotations.Unmodifiable;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import java.util.stream.Stream;
class FilePaths {
@@ -14,10 +14,9 @@ class FilePaths {
private static final Splitter PATH_SPLITTER = Splitter.on(PATH_SEP).omitEmptyStrings();
private static final Joiner PATH_JOINER = Joiner.on(PATH_SEP);
+ @Unmodifiable
public static List toComponents(String pathRelativeToRoot) {
- List pathComponents = new ArrayList<>(PATH_SPLITTER.splitToList(pathRelativeToRoot));
- pathComponents.add(0, ROOT);
- return Collections.unmodifiableList(pathComponents);
+ return Stream.concat(Stream.of(ROOT), PATH_SPLITTER.splitToStream(pathRelativeToRoot)).toList();
}
public static String toPath(List pathComponents) {
diff --git a/src/main/java/org/cryptomator/frontend/fuse/locks/LockManager.java b/src/main/java/org/cryptomator/frontend/fuse/locks/LockManager.java
index 853960c..3812997 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/locks/LockManager.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/locks/LockManager.java
@@ -1,19 +1,17 @@
package org.cryptomator.frontend.fuse.locks;
-import com.google.common.base.Supplier;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.cache.RemovalNotification;
-import org.cryptomator.frontend.fuse.PerAdapter;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.github.benmanes.caffeine.cache.RemovalCause;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.inject.Inject;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Supplier;
/**
* Provides a path-based locking mechanism as described by
@@ -21,25 +19,24 @@
*
*
* Usage Example 1:
- *
- * try (PathLock pathLock = lockManager.createPathLock("/foo/bar/baz").forReading(); // path is not manipulated, thus read-locking
- * DataLock dataLock = pathLock.lockDataForWriting()) { // content is manipulated, thus write-locking
- * // write to file
+ * {@snippet :
+ * try (var pathLock = lockManager.lockForReading("/foo/bar/baz"); // path is not manipulated, thus read-locking
+ * var dataLock = pathLock.lockDataForWriting()) { // content is manipulated, thus write-locking
+ * // write to file
* }
- *
+ *}
*
*
* Usage Example 2:
- *
- * try (PathLock srcPathLock = lockManager.createPathLock("/foo/bar/original").forReading();
- * DataLock srcDataLock = srcPathLock.lockDataForReading(); // content will only be read, thus read-locking
- * PathLock dstPathLock = lockManager.createPathLock("/foo/bar/copy").forWriting(); // file will be created, thus write-locking
- * DataLock dstDataLock = srcPathLock.lockDataForWriting()) {
+ * {@snippet :
+ * try (var srcPathLock = lockManager.lockForReading("/foo/bar/original");
+ * var srcDataLock = srcPathLock.lockDataForReading(); // content will only be read, thus read-locking
+ * var dstPathLock = lockManager.lockForWriting("/foo/bar/copy"); // file will be created, thus write-locking
+ * var dstDataLock = srcPathLock.lockDataForWriting()) {
* // copy from /foo/bar/original to /foo/bar/copy
* }
- *
+ *}
*/
-@PerAdapter
public class LockManager {
private static final Logger LOG = LoggerFactory.getLogger(LockManager.class);
@@ -47,31 +44,45 @@ public class LockManager {
private final LoadingCache, ReadWriteLock> pathLocks;
private final LoadingCache, ReadWriteLock> dataLocks;
- @Inject
public LockManager() {
- CacheBuilder