Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.nio.channels.WritableByteChannel;

/**
* Not thread-safe.
* TODO Not thread-safe.
*/
abstract class AbstractFileChannel extends FileChannel {

Expand Down
31 changes: 31 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/ChunkData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.cryptomator.cryptofs;

import java.nio.ByteBuffer;

class ChunkData {

private final ByteBuffer bytes;
private final boolean written;

private ChunkData(ByteBuffer bytes, boolean written) {
this.bytes = bytes;
this.written = written;
}

public static ChunkData writtenChunkData(ByteBuffer bytes) {
return new ChunkData(bytes, true);
}

public static ChunkData readChunkData(ByteBuffer bytes) {
return new ChunkData(bytes, false);
}

public ByteBuffer bytes() {
return bytes;
}

public boolean wasWritten() {
return written;
}

}
40 changes: 27 additions & 13 deletions src/main/java/org/cryptomator/cryptofs/CryptoFileChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
*******************************************************************************/
package org.cryptomator.cryptofs;

import static org.cryptomator.cryptofs.ChunkData.readChunkData;
import static org.cryptomator.cryptofs.ChunkData.writtenChunkData;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
Expand Down Expand Up @@ -36,7 +39,7 @@
import com.google.common.cache.RemovalNotification;

/**
* Not thread-safe.
* TODO Not thread-safe.
*/
class CryptoFileChannel extends AbstractFileChannel {

Expand All @@ -46,7 +49,7 @@ class CryptoFileChannel extends AbstractFileChannel {
private final Cryptor cryptor;
private final FileChannel ch;
private final FileHeader header;
private final LoadingCache<Long, ByteBuffer> cleartextChunks;
private final LoadingCache<Long, ChunkData> cleartextChunks;
private final Set<OpenOption> openOptions;
private final Collection<IOException> ioExceptionsDuringWrite = new ArrayList<>();

Expand Down Expand Up @@ -104,7 +107,7 @@ public int write(ByteBuffer src, long position) throws IOException {
}
if (len == payloadSize) {
// complete chunk, no need to load and decrypt from file:
cleartextChunks.put(chunkIndex, ByteBuffer.allocate(payloadSize));
cleartextChunks.put(chunkIndex, writtenChunkData(ByteBuffer.allocate(payloadSize)));
}
final ByteBuffer chunkBuf = loadCleartextChunk(chunkIndex);
chunkBuf.position(offset).limit(Math.max(chunkBuf.limit(), len));
Expand Down Expand Up @@ -178,7 +181,7 @@ protected void implCloseChannel() throws IOException {

private ByteBuffer loadCleartextChunk(long chunkIndex) {
try {
return cleartextChunks.get(chunkIndex);
return cleartextChunks.get(chunkIndex).bytes();
} catch (ExecutionException e) {
if (e.getCause() instanceof AuthenticationFailedException) {
// TODO
Expand All @@ -189,10 +192,10 @@ private ByteBuffer loadCleartextChunk(long chunkIndex) {
}
}

private class CleartextChunkLoader extends CacheLoader<Long, ByteBuffer> {
private class CleartextChunkLoader extends CacheLoader<Long, ChunkData> {

@Override
public ByteBuffer load(Long chunkIndex) throws Exception {
public ChunkData load(Long chunkIndex) throws Exception {
LOG.debug("load chunk" + chunkIndex);
int payloadSize = cryptor.fileContentCryptor().cleartextChunkSize();
int chunkSize = cryptor.fileContentCryptor().ciphertextChunkSize();
Expand All @@ -201,24 +204,27 @@ public ByteBuffer load(Long chunkIndex) throws Exception {
int read = ch.read(ciphertextBuf, ciphertextPos);
if (read == -1) {
// append
return ByteBuffer.allocate(payloadSize);
return writtenChunkData(ByteBuffer.allocate(payloadSize));
} else {
ciphertextBuf.flip();
return cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, header, true);
return readChunkData(cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, header, true));
}
}

}

private class CleartextChunkSaver implements RemovalListener<Long, ByteBuffer> {
private class CleartextChunkSaver implements RemovalListener<Long, ChunkData> {

@Override
public void onRemoval(RemovalNotification<Long, ByteBuffer> notification) {
long chunkIndex = notification.getKey();
if (openOptions.contains(StandardOpenOption.WRITE) && chunkIndex * cryptor.fileContentCryptor().cleartextChunkSize() < size()) {
public void onRemoval(RemovalNotification<Long, ChunkData> notification) {
onRemoval(notification.getKey(), notification.getValue());
}

private void onRemoval(long chunkIndex, ChunkData chunkData) {
if (channelIsWritable() && chunkLiesInFile(chunkIndex) && chunkData.wasWritten()) {
LOG.debug("save chunk" + chunkIndex);
long ciphertextPos = chunkIndex * cryptor.fileContentCryptor().ciphertextChunkSize() + cryptor.fileHeaderCryptor().headerSize();
ByteBuffer cleartextBuf = notification.getValue().asReadOnlyBuffer();
ByteBuffer cleartextBuf = chunkData.bytes().asReadOnlyBuffer();
cleartextBuf.flip();
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, header);
try {
Expand All @@ -229,6 +235,14 @@ public void onRemoval(RemovalNotification<Long, ByteBuffer> notification) {
}
}

private boolean chunkLiesInFile(long chunkIndex) {
return chunkIndex * cryptor.fileContentCryptor().cleartextChunkSize() < size();
}

private boolean channelIsWritable() {
return openOptions.contains(StandardOpenOption.WRITE);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.FileSystem;
Expand Down Expand Up @@ -74,7 +75,7 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx

@Test
public void testGetFsViaNioApi() throws IOException {
URI fsUri = URI.create("cryptomator://" + tmpPath.toString());
URI fsUri = URI.create("cryptomator://" + URLEncoder.encode(tmpPath.toString(), "UTF-8"));
FileSystem fs = FileSystems.newFileSystem(fsUri, ImmutableMap.of(CryptoFileSystemProvider.FS_ENV_PW, "asd"));
Assert.assertTrue(fs instanceof CryptoFileSystem);
FileSystem fs2 = FileSystems.getFileSystem(fsUri);
Expand Down