Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relaxed inUse definition #80

Merged
merged 7 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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");
}
Expand All @@ -80,13 +89,24 @@ public int write(ByteBuffer buf, long num, long offset) throws IOException {
return written;
}

/**
* Tests, if this OpenFile is <em>dirty</em>.
* 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();
}

public void fsync(boolean metaData) throws IOException {
channel.force(metaData);
dirty = false;
}

public void truncate(long size) throws IOException {
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/org/cryptomator/frontend/fuse/OpenFileFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <em>dirty</em>, 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());
}

/**
Expand Down
11 changes: 4 additions & 7 deletions src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,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 {

Expand All @@ -48,7 +47,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;
Expand All @@ -61,7 +59,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) {
Expand Down Expand Up @@ -305,7 +302,7 @@ public void destroy() {
@Override
public boolean isInUse() {
try (PathLock pLock = lockManager.tryLockForWriting("/")) {
return hasOpenFiles.getAsBoolean();
return openFiles.hasDirtyFiles();
} catch (AlreadyLockedException e) {
return true;
}
Expand All @@ -320,7 +317,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
infeo marked this conversation as resolved.
Show resolved Hide resolved
* @param opDesc A human-friendly string describing what operation was attempted (for logging purposes)
* @return A specific error code or -EIO.
*/
Expand All @@ -331,8 +328,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();
infeo marked this conversation as resolved.
Show resolved Hide resolved
// }
}
}