Skip to content

Commit

Permalink
Merge branch 'release/0.1.6'
Browse files Browse the repository at this point in the history
# Conflicts:
#	pom.xml
  • Loading branch information
overheadhunter committed Nov 19, 2016
2 parents b3e65c0 + 940e5b8 commit 8bd2fab
Show file tree
Hide file tree
Showing 17 changed files with 330 additions and 68 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>0.1.5</version>
<version>0.1.6</version>
<name>Cryptomator Crypto Filesystem</name>
<description>This library provides the Java filesystem provider used by Cryptomator.</description>
<url>https://github.com/cryptomator/cryptofs</url>
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/ChunkData.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ public boolean wasWritten() {
return written;
}

public void truncate(int length) {
if (this.length > length) {
this.length = length;
this.written = true;
}
}

public CopyWithoutDirection copyData() {
return copyDataStartingAt(0);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/cryptomator/cryptofs/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ final class Constants {
public static final String DATA_DIR_NAME = "d";
public static final String METADATA_DIR_NAME = "m";
public static final String DIR_PREFIX = "0";
public static final int NAME_SHORTENING_THRESHOLD = 80; // TODO markuskreusch: set correct value
public static final int VAULT_VERSION = 4;
public static final int NAME_SHORTENING_THRESHOLD = 129;
public static final int VAULT_VERSION = 5;

public static final String SEPARATOR = "/";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.DosFileAttributeView;
import java.nio.file.attribute.DosFileAttributes;
import java.nio.file.attribute.FileAttributeView;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.UserPrincipal;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -48,10 +52,10 @@ class CryptoFileAttributeByNameProvider {
attribute("basic:size", BasicFileAttributes.class, BasicFileAttributes::size);
attribute("basic:fileKey", BasicFileAttributes.class, BasicFileAttributes::fileKey);

attribute("dos:isReadOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly);
attribute("dos:isHidden", DosFileAttributes.class, DosFileAttributes::isHidden);
attribute("dos:isArchive", DosFileAttributes.class, DosFileAttributes::isArchive);
attribute("dos:isSystem", DosFileAttributes.class, DosFileAttributes::isSystem);
attribute("dos:readOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly);
attribute("dos:hidden", DosFileAttributes.class, DosFileAttributes::isHidden);
attribute("dos:archive", DosFileAttributes.class, DosFileAttributes::isArchive);
attribute("dos:system", DosFileAttributes.class, DosFileAttributes::isSystem);

attribute("posix:owner", PosixFileAttributes.class, PosixFileAttributes::owner);
attribute("posix:group", PosixFileAttributes.class, PosixFileAttributes::group);
Expand All @@ -64,14 +68,14 @@ class CryptoFileAttributeByNameProvider {
attribute("basic:lastAccessTime", BasicFileAttributeView.class, FileTime.class, (view, lastAccessTime) -> view.setTimes(null, lastAccessTime, null));
attribute("basic:creationTime", BasicFileAttributeView.class, FileTime.class, (view, creationTime) -> view.setTimes(null, null, creationTime));

attribute("dos:isReadOnly", DosFileAttributes.class, DosFileAttributes::isReadOnly);
attribute("dos:isHidden", DosFileAttributes.class, DosFileAttributes::isHidden);
attribute("dos:isArchive", DosFileAttributes.class, DosFileAttributes::isArchive);
attribute("dos:isSystem", DosFileAttributes.class, DosFileAttributes::isSystem);
attribute("dos:readOnly", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setReadOnly);
attribute("dos:hidden", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setHidden);
attribute("dos:archive", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setArchive);
attribute("dos:system", DosFileAttributeView.class, Boolean.class, DosFileAttributeView::setSystem);

attribute("posix:owner", PosixFileAttributes.class, PosixFileAttributes::owner);
attribute("posix:group", PosixFileAttributes.class, PosixFileAttributes::group);
attribute("posix:permissions", PosixFileAttributes.class, PosixFileAttributes::permissions);
attribute("posix:owner", PosixFileAttributeView.class, UserPrincipal.class, PosixFileAttributeView::setOwner);
attribute("posix:group", PosixFileAttributeView.class, GroupPrincipal.class, PosixFileAttributeView::setGroup);
attribute("posix:permissions", PosixFileAttributeView.class, Set.class, PosixFileAttributeView::setPermissions);
}

private <T extends BasicFileAttributes> void attribute(String name, Class<T> type, Function<T, ?> getter) {
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/org/cryptomator/cryptofs/CryptoFileSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ void createDirectory(CryptoPath cleartextDir, FileAttribute<?>... attrs) throws
} finally {
if (!success) {
Files.delete(ciphertextDirFile);
dirIdProvider.invalidate(ciphertextDirFile);
dirIdProvider.delete(ciphertextDirFile);
}
}
}
Expand Down Expand Up @@ -350,7 +350,7 @@ void delete(CryptoPath cleartextPath) throws IOException {
// should not happen. Nevertheless this is a valid state, so who no big deal...
LOG.warn("Successfully deleted dir {}, but didn't find corresponding dir file {}", ciphertextDir, ciphertextDirFile);
}
dirIdProvider.invalidate(ciphertextDirFile);
dirIdProvider.delete(ciphertextDirFile);
} catch (NoSuchFileException e) {
// translate ciphertext path to cleartext path
throw new NoSuchFileException(cleartextPath.toString());
Expand Down Expand Up @@ -458,11 +458,10 @@ void move(CryptoPath cleartextSource, CryptoPath cleartextTarget, CopyOption...
}
}
Files.delete(ciphertextTargetDir);
dirIdProvider.invalidate(ciphertextTargetDirFile);
}
Files.move(ciphertextSourceDirFile, ciphertextTargetDirFile, options);
}
dirIdProvider.invalidate(ciphertextSourceDirFile);
dirIdProvider.move(ciphertextSourceDirFile, ciphertextTargetDirFile);
} else {
throw new NoSuchFileException(cleartextSource.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public AsynchronousFileChannel newAsynchronousFileChannel(Path cleartextPath, Se

@Override
public FileChannel newFileChannel(Path cleartextPath, Set<? extends OpenOption> optionsSet, FileAttribute<?>... attrs) throws IOException {
return fileSystem(cleartextPath).newFileChannel(CryptoPath.cast(cleartextPath), optionsSet, attrs);
return fileSystem(cleartextPath).newFileChannel(CryptoPath.castAndAssertAbsolute(cleartextPath), optionsSet, attrs);
}

@Override
Expand All @@ -166,27 +166,27 @@ public SeekableByteChannel newByteChannel(Path cleartextPath, Set<? extends Open

@Override
public DirectoryStream<Path> newDirectoryStream(Path cleartextDir, Filter<? super Path> filter) throws IOException {
return fileSystem(cleartextDir).newDirectoryStream(CryptoPath.cast(cleartextDir), filter);
return fileSystem(cleartextDir).newDirectoryStream(CryptoPath.castAndAssertAbsolute(cleartextDir), filter);
}

@Override
public void createDirectory(Path cleartextDir, FileAttribute<?>... attrs) throws IOException {
fileSystem(cleartextDir).createDirectory(CryptoPath.cast(cleartextDir), attrs);
fileSystem(cleartextDir).createDirectory(CryptoPath.castAndAssertAbsolute(cleartextDir), attrs);
}

@Override
public void delete(Path cleartextPath) throws IOException {
fileSystem(cleartextPath).delete(CryptoPath.cast(cleartextPath));
fileSystem(cleartextPath).delete(CryptoPath.castAndAssertAbsolute(cleartextPath));
}

@Override
public void copy(Path cleartextSource, Path cleartextTarget, CopyOption... options) throws IOException {
copyAndMoveOperations.copy(CryptoPath.cast(cleartextSource), CryptoPath.cast(cleartextTarget), options);
copyAndMoveOperations.copy(CryptoPath.castAndAssertAbsolute(cleartextSource), CryptoPath.castAndAssertAbsolute(cleartextTarget), options);
}

@Override
public void move(Path cleartextSource, Path cleartextTarget, CopyOption... options) throws IOException {
copyAndMoveOperations.move(CryptoPath.cast(cleartextSource), CryptoPath.cast(cleartextTarget), options);
copyAndMoveOperations.move(CryptoPath.castAndAssertAbsolute(cleartextSource), CryptoPath.castAndAssertAbsolute(cleartextTarget), options);
}

@Override
Expand All @@ -197,7 +197,7 @@ public boolean isSameFile(Path cleartextPath, Path cleartextPath2) throws IOExce

@Override
public boolean isHidden(Path cleartextPath) throws IOException {
return fileSystem(cleartextPath).isHidden(CryptoPath.cast(cleartextPath));
return fileSystem(cleartextPath).isHidden(CryptoPath.castAndAssertAbsolute(cleartextPath));
}

@Override
Expand All @@ -207,27 +207,27 @@ public FileStore getFileStore(Path cleartextPath) throws IOException {

@Override
public void checkAccess(Path cleartextPath, AccessMode... modes) throws IOException {
fileSystem(cleartextPath).checkAccess(CryptoPath.cast(cleartextPath), modes);
fileSystem(cleartextPath).checkAccess(CryptoPath.castAndAssertAbsolute(cleartextPath), modes);
}

@Override
public <V extends FileAttributeView> V getFileAttributeView(Path cleartextPath, Class<V> type, LinkOption... options) {
return fileSystem(cleartextPath).getFileAttributeView(CryptoPath.cast(cleartextPath), type, options);
return fileSystem(cleartextPath).getFileAttributeView(CryptoPath.castAndAssertAbsolute(cleartextPath), type, options);
}

@Override
public <A extends BasicFileAttributes> A readAttributes(Path cleartextPath, Class<A> type, LinkOption... options) throws IOException {
return fileSystem(cleartextPath).readAttributes(CryptoPath.cast(cleartextPath), type, options);
return fileSystem(cleartextPath).readAttributes(CryptoPath.castAndAssertAbsolute(cleartextPath), type, options);
}

@Override
public Map<String, Object> readAttributes(Path cleartextPath, String attributes, LinkOption... options) throws IOException {
return fileSystem(cleartextPath).readAttributes(CryptoPath.cast(cleartextPath), attributes, options);
return fileSystem(cleartextPath).readAttributes(CryptoPath.castAndAssertAbsolute(cleartextPath), attributes, options);
}

@Override
public void setAttribute(Path cleartextPath, String attribute, Object value, LinkOption... options) throws IOException {
fileSystem(cleartextPath).setAttribute(CryptoPath.cast(cleartextPath), attribute, value, options);
fileSystem(cleartextPath).setAttribute(CryptoPath.castAndAssertAbsolute(cleartextPath), attribute, value, options);
}

private CryptoFileSystem fileSystem(Path path) {
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/CryptoPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public CryptoPath(CryptoFileSystem fileSystem, List<String> elements, boolean ab
this.absolute = absolute;
}

public static CryptoPath castAndAssertAbsolute(Path path) {
CryptoPath result = cast(path);
if (!result.isAbsolute()) {
throw new IllegalArgumentException("Path must be absolute but was " + path);
}
return result;
}

public static CryptoPath cast(Path path) {
if (path instanceof CryptoPath) {
CryptoPath cryptoPath = (CryptoPath) path;
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/org/cryptomator/cryptofs/DirectoryIdProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,29 @@ public String load(Path dirFilePath) throws IOException {
}
}

public void invalidate(Path dirFilePath) {
/**
* Removes the id currently associated with <code>dirFilePath</code> from cache. Useful during folder delete operations.
* This method has no effect if the content of the given dirFile is not currently cached.
*
* @param dirFilePath The dirFile for which the cache should be deleted.
*/
public void delete(Path dirFilePath) {
ids.invalidate(dirFilePath);
}

/**
* Transfers ownership from the id currently associated with <code>srcDirFilePath</code> to <code>dstDirFilePath</code>. Usefule during folder move operations.
* This method has no effect if the content of the source dirFile is not currently cached.
*
* @param srcDirFilePath The dirFile that contained the cached id until now.
* @param dstDirFilePath The dirFile that will contain the id from now on.
*/
public void move(Path srcDirFilePath, Path dstDirFilePath) {
String id = ids.getIfPresent(srcDirFilePath);
if (id != null) {
ids.put(dstDirFilePath, id);
ids.invalidate(srcDirFilePath);
}
}

}
16 changes: 12 additions & 4 deletions src/main/java/org/cryptomator/cryptofs/OpenCryptoFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import static java.lang.Math.min;
import static org.cryptomator.cryptofs.OpenCounter.OpenState.ALREADY_CLOSED;
import static org.cryptomator.cryptofs.OpenCounter.OpenState.WAS_OPEN;
import static org.cryptomator.cryptolib.Cryptors.ciphertextSize;

import java.io.IOException;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -51,8 +52,6 @@ public OpenCryptoFile(EffectiveOpenOptions options, Cryptor cryptor, FileChannel
this.header = header;
this.size = size;
this.stats = stats;

size.set(header.getFilesize());
}

public FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException {
Expand Down Expand Up @@ -135,13 +134,22 @@ public long size() {
}

public synchronized void truncate(long size) throws IOException {
// TODO
long originalSize = this.size.getAndUpdate(current -> min(size, current));
if (originalSize > size) {
int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize();
long indexOfLastChunk = (size + cleartextChunkSize - 1) / cleartextChunkSize - 1;
int sizeOfIncompleteChunk = (int) (size % cleartextChunkSize);
if (sizeOfIncompleteChunk > 0) {
chunkCache.get(indexOfLastChunk).truncate(sizeOfIncompleteChunk);
}
long ciphertextFileSize = cryptor.fileHeaderCryptor().headerSize() + ciphertextSize(size, cryptor);
channel.truncate(ciphertextFileSize);
}
}

public synchronized void force(boolean metaData, EffectiveOpenOptions options) throws IOException {
chunkCache.invalidateAll(); // TODO increase performance by writing chunks but keeping them cached
if (options.writable()) {
header.setFilesize(size.get());
channel.write(cryptor.fileHeaderCryptor().encryptHeader(header), 0);
}
channel.force(metaData);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.cryptomator.cryptofs;

import static org.cryptomator.cryptofs.UncheckedThrows.rethrowUnchecked;
import static org.cryptomator.cryptolib.Cryptors.cleartextSize;

import java.io.IOException;
import java.nio.ByteBuffer;
Expand All @@ -26,8 +27,16 @@ public FileChannel provideFileChannel(@OpenFilePath Path path, EffectiveOpenOpti
@Provides
@PerOpenFile
@OpenFileSize
public AtomicLong provideFileSize() {
return new AtomicLong();
public AtomicLong provideFileSize(FileChannel channel, Cryptor cryptor) {
return rethrowUnchecked(IOException.class).from(() -> {
long size = channel.size();
if (size == 0) {
return new AtomicLong();
} else {
int headerSize = cryptor.fileHeaderCryptor().headerSize();
return new AtomicLong(cleartextSize(size - headerSize, cryptor));
}
});
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ public PatternPathMatcher(Pattern pattern) {
this.pattern = pattern;
}

/**
* @deprecated for testing
*/
@Deprecated
Pattern getPattern() {
return pattern;
}

@Override
public boolean matches(Path path) {
return pattern.matcher(path.toString()).matches();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*******************************************************************************/
package org.cryptomator.cryptofs;

import static java.lang.Math.min;
import static java.lang.String.format;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
Expand Down Expand Up @@ -88,6 +89,38 @@ public void testWriteAndReadNothing() throws IOException {
}
}

@Theory
public void testWriteDataAndTruncateToOffset(@FromDataPoints("dataSizes") int cleartextSize, @FromDataPoints("writeOffsets") int truncateToSize) throws IOException {
long fileId = nextFileId();

int targetSize = min(truncateToSize, cleartextSize);

try (FileChannel channel = writableChannel(fileId)) {
assertEquals(0, channel.size());
channel.write(repeat(1).times(cleartextSize).asByteBuffer());
assertEquals(cleartextSize, channel.size());
}

try (FileChannel channel = writableChannel(fileId)) {
assertEquals(cleartextSize, channel.size());
channel.truncate(truncateToSize);
assertEquals(targetSize, channel.size());
}

try (FileChannel channel = readableChannel(fileId)) {
if (targetSize > 0) {
ByteBuffer buffer = ByteBuffer.allocate(targetSize);
int result = channel.read(buffer);
assertEquals(targetSize, result);
buffer.flip();
for (int i = 0; i < targetSize; i++) {
assertEquals(format("byte(%d) = 1", i), 1, buffer.get(i));
}
}
assertEquals(EOF, channel.read(ByteBuffer.allocate(0)));
}
}

@Theory
public void testWithWritingOffset(@FromDataPoints("dataSizes") int dataSize, @FromDataPoints("writeOffsets") int writeOffset) throws IOException {
assumeTrue(dataSize != 0 || writeOffset != 0);
Expand Down
Loading

0 comments on commit 8bd2fab

Please sign in to comment.