diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..b257aaa
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,22 @@
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "monthly"
+ day: "monday"
+ time: "06:00"
+ timezone: "UTC"
+ groups:
+ maven-dependencies:
+ patterns:
+ - "*"
+
+ - package-ecosystem: "github-actions"
+ directory: "/" # even for `.github/workflows`
+ schedule:
+ interval: "monthly"
+ groups:
+ github-actions:
+ patterns:
+ - "*"
\ No newline at end of file
diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
index 5fc4dd9..3bba864 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
@@ -19,11 +19,19 @@ public class OpenFile implements Closeable {
private final Path path;
private final FileChannel channel;
+ /**
+ * Whether any data has been changed on this file.
+ *
+ * This value only changes while holding a write lock, see {@link org.cryptomator.frontend.fuse.locks.LockManager}.
+ */
+ private volatile boolean dirty;
+
private OpenFile(Path path, FileChannel channel) {
this.path = path;
this.channel = channel;
+ this.dirty = false;
}
-
+
static OpenFile create(Path path, Set extends OpenOption> options, FileAttribute>... attrs) throws IOException {
FileChannel ch = FileChannel.open(path, options, attrs);
return new OpenFile(path, ch);
@@ -69,6 +77,7 @@ public int read(ByteBuffer buf, long num, long offset) throws IOException {
* @throws IOException If an exception occurs during write.
*/
public int write(ByteBuffer buf, long num, long offset) throws IOException {
+ dirty = true;
if (num > Integer.MAX_VALUE) {
throw new IOException("Requested too many bytes");
}
@@ -80,6 +89,16 @@ public int write(ByteBuffer buf, long num, long offset) throws IOException {
return written;
}
+ /**
+ * Tests, if this OpenFile is dirty.
+ * An OpenFile is dirty, if its write method is called at least once.
+ *
+ * @return {@code true} if {@link OpenFile#write(ByteBuffer, long, long)} was called on this object, otherwise {@code false}
+ */
+ boolean isDirty() {
+ return dirty;
+ }
+
@Override
public void close() throws IOException {
channel.close();
@@ -87,6 +106,7 @@ public void close() throws IOException {
public void fsync(boolean metaData) throws IOException {
channel.force(metaData);
+ dirty = false;
}
public void truncate(long size) throws IOException {
diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
index f1471cb..04ab047 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
@@ -67,8 +67,15 @@ public void close(long fileHandle) throws ClosedChannelException, IOException {
}
}
- public int getOpenFileCount(){
- return openFiles.size();
+ /**
+ * Tests, if any {@link OpenFile} is considered dirty, e.g. might have pending changes.
+ * This method is neither atomic regarding the set of OpenFiles nor regarding each OpenFile individually. Therefore, external synchronization is required.
+ *
+ * @return {@code true} if and only if at least one dirty {@link OpenFile} exists. Otherwise {@code false}.
+ * @see OpenFile#isDirty()
+ */
+ boolean hasDirtyFiles() {
+ return openFiles.entrySet().stream().anyMatch(entry -> entry.getValue().isDirty());
}
/**
diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
index 9c2d833..0efc945 100644
--- a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
+++ b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
@@ -35,7 +35,6 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
-import java.util.function.BooleanSupplier;
public sealed class ReadOnlyAdapter implements FuseNioAdapter permits ReadWriteAdapter {
@@ -52,7 +51,6 @@ public sealed class ReadOnlyAdapter implements FuseNioAdapter permits ReadWriteA
private final ReadOnlyDirectoryHandler dirHandler;
private final ReadOnlyFileHandler fileHandler;
private final ReadOnlyLinkHandler linkHandler;
- private final BooleanSupplier hasOpenFiles;
protected ReadOnlyAdapter(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder, FileStore fileStore, OpenFileFactory openFiles, ReadOnlyDirectoryHandler dirHandler, ReadOnlyFileHandler fileHandler, boolean enableXattr) {
this.errno = errno;
@@ -66,7 +64,6 @@ protected ReadOnlyAdapter(Errno errno, Path root, int maxFileNameLength, FileNam
this.dirHandler = dirHandler;
this.fileHandler = fileHandler;
this.linkHandler = new ReadOnlyLinkHandler(fileNameTranscoder);
- this.hasOpenFiles = () -> openFiles.getOpenFileCount() != 0;
}
public static ReadOnlyAdapter create(Errno errno, Path root, int maxFileNameLength, FileNameTranscoder fileNameTranscoder, boolean enableXattr) {
@@ -377,7 +374,7 @@ public void destroy() {
@Override
public boolean isInUse() {
try (PathLock pLock = lockManager.tryLockForWriting("/")) {
- return hasOpenFiles.getAsBoolean();
+ return openFiles.hasDirtyFiles();
} catch (AlreadyLockedException e) {
return true;
}