diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java index 5fc4dd9..a25ee55 100644 --- a/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java +++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFile.java @@ -19,11 +19,14 @@ public class OpenFile implements Closeable { private final Path path; private final FileChannel channel; + private volatile boolean writeCalled; + private OpenFile(Path path, FileChannel channel) { this.path = path; this.channel = channel; + this.writeCalled = false; } - + static OpenFile create(Path path, Set options, FileAttribute... attrs) throws IOException { FileChannel ch = FileChannel.open(path, options, attrs); return new OpenFile(path, ch); @@ -69,6 +72,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 { + writeCalled = true; if (num > Integer.MAX_VALUE) { throw new IOException("Requested too many bytes"); } @@ -80,6 +84,15 @@ public int write(ByteBuffer buf, long num, long offset) throws IOException { return written; } + /** + * Tests, if the write method of this open file was called at least once. + * + * @return {@code true} if {@link OpenFile#write(ByteBuffer, long, long)} was called on this object, otherwise {@code false} + */ + public boolean isWrittenTo() { + return writeCalled; + } + @Override public void close() throws IOException { channel.close(); diff --git a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java index f1471cb..f7b6cf1 100644 --- a/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java +++ b/src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java @@ -67,8 +67,14 @@ public void close(long fileHandle) throws ClosedChannelException, IOException { } } - public int getOpenFileCount(){ - return openFiles.size(); + /** + * Tests, if there exists {@link OpenFile}s, which has been written to. + * The check performed in this method is not atomic and therefore requires external synchronization. + * + * @return {@code true} if and only if at least one {@link OpenFile} exists, where {@link OpenFile#isWrittenTo()} returns {@code true}. Otherwise {@code false}. + */ + public boolean hasWrittenToOpenFiles(){ + return openFiles.entrySet().stream().anyMatch(entry -> entry.getValue().isWrittenTo()); } /** diff --git a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java index a4c8064..ff9fcbd 100644 --- a/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java +++ b/src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java @@ -48,7 +48,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) { this.errno = errno; @@ -61,7 +60,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) { @@ -305,7 +303,7 @@ public void destroy() { @Override public boolean isInUse() { try (PathLock pLock = lockManager.tryLockForWriting("/")) { - return hasOpenFiles.getAsBoolean(); + return openFiles.hasWrittenToOpenFiles(); } catch (AlreadyLockedException e) { return true; } @@ -320,7 +318,7 @@ public void close() throws IOException { * Attempts to get a specific error code that best describes the given exception. * As a side effect this logs the error. * - * @param e An exception + * @param e An exception * @param opDesc A human-friendly string describing what operation was attempted (for logging purposes) * @return A specific error code or -EIO. */ @@ -331,8 +329,8 @@ protected int getErrorCodeForGenericFileSystemException(FileSystemException e, S // LOG.warn("{} {} failed, name too long.", opDesc); // return -ErrorCodes.ENAMETOOLONG(); // } else { - LOG.error(opDesc + " failed.", e); - return -errno.eio(); + LOG.error(opDesc + " failed.", e); + return -errno.eio(); // } } }