Skip to content

Commit

Permalink
Merge branch 'release/1.2.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Apr 29, 2020
2 parents 38ab72c + 2c36355 commit ae5c85e
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 187 deletions.
16 changes: 8 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>fuse-nio-adapter</artifactId>
<version>1.2.3</version>
<version>1.2.4</version>
<name>FUSE-NIO-Adapter</name>
<description>Access resources at a given NIO path via FUSE.</description>
<url>https://github.com/cryptomator/fuse-nio-adapter</url>
Expand All @@ -16,12 +16,12 @@

<properties>
<jnrfuse.version>0.5.4</jnrfuse.version>
<dagger.version>2.26</dagger.version>
<guava.version>28.2-jre</guava.version>
<dagger.version>2.27</dagger.version>
<guava.version>29.0-jre</guava.version>
<slf4j.version>1.7.30</slf4j.version>

<junit.jupiter.version>5.6.0</junit.jupiter.version>
<mockito.version>3.3.0</mockito.version>
<junit.jupiter.version>5.6.2</junit.jupiter.version>
<mockito.version>3.3.3</mockito.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Expand Down Expand Up @@ -114,7 +114,7 @@
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>1.9.3</version>
<version>1.9.7</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down Expand Up @@ -151,7 +151,7 @@
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>5.3.1</version>
<version>5.3.2</version>
<configuration>
<cveValidForHours>24</cveValidForHours>
<failBuildOnCVSS>0</failBuildOnCVSS>
Expand Down Expand Up @@ -224,7 +224,7 @@
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/org/cryptomator/frontend/fuse/AdapterFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@

public class AdapterFactory {

private static final int DEFAULT_NAME_MAX = 254; // 255 is preferred, but nautilus checks for this value + 1

private AdapterFactory() {
}

public static FuseNioAdapter createReadOnlyAdapter(Path root) {
FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder().root(root).build();
return createReadOnlyAdapter(root, DEFAULT_NAME_MAX);
}

public static FuseNioAdapter createReadOnlyAdapter(Path root, int maxFileNameLength) {
FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder().root(root).maxFileNameLength(maxFileNameLength).build();
return comp.readOnlyAdapter();
}

public static FuseNioAdapter createReadWriteAdapter(Path root) {
FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder().root(root).build();
return createReadWriteAdapter(root, DEFAULT_NAME_MAX);
}

public static FuseNioAdapter createReadWriteAdapter(Path root, int maxFileNameLength) {
FuseNioAdapterComponent comp = DaggerFuseNioAdapterComponent.builder().root(root).maxFileNameLength(maxFileNameLength).build();
return comp.readWriteAdapter();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ interface Builder {
@BindsInstance
Builder root(@Named("root") Path root);

@BindsInstance
Builder maxFileNameLength(@Named("maxFileNameLength") int maxFileNameLength);

FuseNioAdapterComponent build();
}

Expand Down
9 changes: 7 additions & 2 deletions src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ public class OpenFile implements Closeable {
private final Path path;
private final FileChannel channel;

public OpenFile(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
private OpenFile(Path path, FileChannel channel) {
this.path = path;
this.channel = FileChannel.open(path, options, attrs);
this.channel = channel;
}

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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

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;
Expand Down Expand Up @@ -50,7 +54,7 @@ public long open(Path path, OpenOption... options) throws IOException {
*/
public long open(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
long fileHandle = fileHandleGen.getAndIncrement();
OpenFile file = new OpenFile(path, options, attrs);
OpenFile file = OpenFile.create(path, options, attrs);
openFiles.put(fileHandle, file);
LOG.trace("Opening {} {}", fileHandle, file);
return fileHandle;
Expand Down
80 changes: 55 additions & 25 deletions src/main/java/org/cryptomator/frontend/fuse/ReadOnlyAdapter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cryptomator.frontend.fuse;

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;
Expand All @@ -20,9 +21,11 @@
import javax.inject.Inject;
import javax.inject.Named;
import java.io.IOException;
import java.nio.channels.ClosedChannelException;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
import java.nio.file.FileStore;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
Expand All @@ -44,8 +47,8 @@ public class ReadOnlyAdapter extends FuseStubFS implements FuseNioAdapter {

private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyAdapter.class);
private static final int BLOCKSIZE = 4096;
private static final int FUSE_NAME_MAX = 254; // 255 is preferred, but nautilus checks for this value + 1
protected final Path root;
private final int maxFileNameLength;
protected final FileStore fileStore;
protected final LockManager lockManager;
private final ReadOnlyDirectoryHandler dirHandler;
Expand All @@ -54,8 +57,9 @@ public class ReadOnlyAdapter extends FuseStubFS implements FuseNioAdapter {
private final FileAttributesUtil attrUtil;

@Inject
public ReadOnlyAdapter(@Named("root") Path root, FileStore fileStore, LockManager lockManager, ReadOnlyDirectoryHandler dirHandler, ReadOnlyFileHandler fileHandler, ReadOnlyLinkHandler linkHandler, FileAttributesUtil attrUtil) {
public ReadOnlyAdapter(@Named("root") Path root, @Named("maxFileNameLength") int maxFileNameLength, FileStore fileStore, LockManager lockManager, ReadOnlyDirectoryHandler dirHandler, ReadOnlyFileHandler fileHandler, ReadOnlyLinkHandler linkHandler, FileAttributesUtil attrUtil) {
this.root = root;
this.maxFileNameLength = maxFileNameLength;
this.fileStore = fileStore;
this.lockManager = lockManager;
this.dirHandler = dirHandler;
Expand All @@ -81,7 +85,7 @@ public int statfs(String path, Statvfs stbuf) {
stbuf.f_blocks.set(tBlocks);
stbuf.f_bavail.set(aBlocks);
stbuf.f_bfree.set(aBlocks);
stbuf.f_namemax.set(FUSE_NAME_MAX);
stbuf.f_namemax.set(maxFileNameLength);
LOG.trace("statfs {} ({} / {})", path, avail, total);
return 0;
} catch (IOException | RuntimeException e) {
Expand Down Expand Up @@ -163,6 +167,8 @@ public int getattr(String path, FileStat stat) {
// see Files.notExists
LOG.trace("getattr {} failed, node not found", path);
return -ErrorCodes.ENOENT();
} catch (FileSystemException e) {
return getErrorCodeForGenericFileSystemException(e, "getattr " + path);
} catch (IOException | RuntimeException e) {
LOG.error("getattr failed.", e);
return -ErrorCodes.EIO();
Expand Down Expand Up @@ -190,19 +196,17 @@ public int open(String path, FuseFileInfo fi) {
try (PathLock pathLock = lockManager.createPathLock(path).forReading();
DataLock dataLock = pathLock.lockDataForReading()) {
Path node = resolvePath(path);
// TODO do we need to distinguish files vs. dirs? https://github.com/libfuse/libfuse/wiki/Invariants
if (Files.isDirectory(node)) {
LOG.error("open {} failed, node is a directory.", path);
return -ErrorCodes.EISDIR();
} else if (Files.exists(node)) {
LOG.trace("open {} ({})", path, fi.fh.get());
return fileHandler.open(node, fi);
} else {
LOG.error("open {} failed, file not found.", path);
return -ErrorCodes.ENOENT();
}
} catch (RuntimeException e) {
LOG.error("open failed.", e);
LOG.trace("open {} ({})", path, fi.fh.get());
fileHandler.open(node, fi);
return 0;
} catch (NoSuchFileException e) {
LOG.warn("open {} failed, file not found.", path);
return -ErrorCodes.ENOENT();
} catch (AccessDeniedException e) {
LOG.warn("Attempted to open file with unsupported flags.", e);
return -ErrorCodes.EROFS();
} catch (IOException | RuntimeException e) {
LOG.error("open " + path + " failed.", e);
return -ErrorCodes.EIO();
}
}
Expand All @@ -211,11 +215,15 @@ public int open(String path, FuseFileInfo fi) {
public int read(String path, Pointer buf, @size_t long size, @off_t long offset, FuseFileInfo fi) {
try (PathLock pathLock = lockManager.createPathLock(path).forReading();
DataLock dataLock = pathLock.lockDataForReading()) {
Path node = resolvePath(path);
assert Files.exists(node);
return fileHandler.read(node, buf, size, offset, fi);
} catch (RuntimeException e) {
LOG.error("read failed.", e);
LOG.trace("read {} bytes from file {} starting at {}...", size, path, offset);
int read = fileHandler.read(buf, size, offset, fi);
LOG.trace("read {} bytes from file {}", read, path);
return read;
} catch (ClosedChannelException e) {
LOG.warn("read {} failed, invalid file handle {}", path, fi.fh.get());
return -ErrorCodes.EBADF();
} catch (IOException | RuntimeException e) {
LOG.error("read " + path + " failed.", e);
return -ErrorCodes.EIO();
}
}
Expand All @@ -224,11 +232,14 @@ public int read(String path, Pointer buf, @size_t long size, @off_t long offset,
public int release(String path, FuseFileInfo fi) {
try (PathLock pathLock = lockManager.createPathLock(path).forReading();
DataLock dataLock = pathLock.lockDataForReading()) {
Path node = resolvePath(path);
LOG.trace("release {} ({})", path, fi.fh.get());
return fileHandler.release(node, fi);
} catch (RuntimeException e) {
LOG.error("release failed.", e);
fileHandler.release(fi);
return 0;
} catch (ClosedChannelException e) {
LOG.warn("release {} failed, invalid file handle {}", path, fi.fh.get());
return -ErrorCodes.EBADF();
} catch (IOException | RuntimeException e) {
LOG.error("release " + path + " failed.", e);
return -ErrorCodes.EIO();
}
}
Expand Down Expand Up @@ -265,4 +276,23 @@ public void umount() {
public void close() throws IOException {
fileHandler.close();
}

/**
* 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 opDesc A human-friendly string describing what operation was attempted (for logging purposes)
* @return A specific error code or -EIO.
*/
protected int getErrorCodeForGenericFileSystemException(FileSystemException e, String opDesc) {
String reason = Strings.nullToEmpty(e.getReason());
if (reason.contains("path too long") || reason.contains("name too long")) {
LOG.warn("{} {} failed, name too long.", opDesc);
return -ErrorCodes.ENAMETOOLONG();
} else {
LOG.error(opDesc + " failed.", e);
return -ErrorCodes.EIO();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package org.cryptomator.frontend.fuse;

import jnr.ffi.Pointer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.serce.jnrfuse.ErrorCodes;
import ru.serce.jnrfuse.struct.FileStat;
import ru.serce.jnrfuse.struct.FuseFileInfo;

Expand All @@ -22,8 +19,6 @@
@PerAdapter
public class ReadOnlyFileHandler implements Closeable {

private static final Logger LOG = LoggerFactory.getLogger(ReadOnlyFileHandler.class);

protected final OpenFileFactory openFiles;
protected final FileAttributesUtil attrUtil;
private final OpenOptionsUtil openOptionsUtil;
Expand All @@ -35,23 +30,14 @@ public ReadOnlyFileHandler(OpenFileFactory openFiles, FileAttributesUtil attrUti
this.openOptionsUtil = openOptionsUtil;
}

public int open(Path path, FuseFileInfo fi) {
try {
Set<OpenOption> openOptions = openOptionsUtil.fuseOpenFlagsToNioOpenOptions(fi.flags.longValue());
long fileHandle = open(path, openOptions);
fi.fh.set(fileHandle);
return 0;
} catch (AccessDeniedException e) {
LOG.warn("Attempted to open file with unsupported flags.", e);
return -ErrorCodes.EROFS();
} catch (IOException e) {
LOG.error("Error opening file.", e);
return -ErrorCodes.EIO();
}
public void open(Path path, FuseFileInfo fi) throws IOException {
Set<OpenOption> openOptions = openOptionsUtil.fuseOpenFlagsToNioOpenOptions(fi.flags.longValue());
long fileHandle = open(path, openOptions);
fi.fh.set(fileHandle);
}

/**
* @param path path of the file to open
* @param path path of the file to open
* @param openOptions file open options
* @return file handle used to identify and close open files.
* @throws AccessDeniedException Thrown if the requested openOptions are not supported
Expand All @@ -65,31 +51,34 @@ protected long open(Path path, Set<OpenOption> openOptions) throws AccessDeniedE
}
}

public int read(Path path, Pointer buf, long size, long offset, FuseFileInfo fi) {
/**
* Reads up to {@code num} bytes beginning at {@code offset} into {@code buf}
*
* @param buf Buffer
* @param size Number of bytes to read
* @param offset Position of first byte to read
* @param fi contains the file handle
* @return Actual number of bytes read (can be less than {@code size} if reached EOF).
* @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());
if (file == null) {
LOG.warn("Attempted to read from file with illegal fileHandle {}: {}", fi.fh.get(), path);
return -ErrorCodes.EBADF();
}
try {
return file.read(buf, size, offset);
} catch (IOException e) {
LOG.error("Reading file failed.", e);
return -ErrorCodes.EIO();
throw new ClosedChannelException();
}
return file.read(buf, size, offset);
}

public int release(Path path, FuseFileInfo fi) {
try {
openFiles.close(fi.fh.get());
return 0;
} catch (ClosedChannelException e) {
LOG.warn("Attempted to close file with illegal fileHandle {}: {}", fi.fh.get(), path);
return -ErrorCodes.EBADF();
} catch (IOException e) {
LOG.error("Error closing file.", e);
return -ErrorCodes.EIO();
}
/**
* Closes the channel identified by the given fileHandle
*
* @param fi contains the file handle
* @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 int getattr(Path node, BasicFileAttributes attrs, FileStat stat) {
Expand Down
Loading

0 comments on commit ae5c85e

Please sign in to comment.