Skip to content

Commit 0077f98

Browse files
Merge pull request #1 from markuskreusch/develop
Write only modified chunks
2 parents b4a63a8 + a75fa3e commit 0077f98

File tree

4 files changed

+61
-15
lines changed

4 files changed

+61
-15
lines changed

src/main/java/org/cryptomator/cryptofs/AbstractFileChannel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import java.nio.channels.WritableByteChannel;
1616

1717
/**
18-
* Not thread-safe.
18+
* TODO Not thread-safe.
1919
*/
2020
abstract class AbstractFileChannel extends FileChannel {
2121

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.cryptomator.cryptofs;
2+
3+
import java.nio.ByteBuffer;
4+
5+
class ChunkData {
6+
7+
private final ByteBuffer bytes;
8+
private final boolean written;
9+
10+
private ChunkData(ByteBuffer bytes, boolean written) {
11+
this.bytes = bytes;
12+
this.written = written;
13+
}
14+
15+
public static ChunkData writtenChunkData(ByteBuffer bytes) {
16+
return new ChunkData(bytes, true);
17+
}
18+
19+
public static ChunkData readChunkData(ByteBuffer bytes) {
20+
return new ChunkData(bytes, false);
21+
}
22+
23+
public ByteBuffer bytes() {
24+
return bytes;
25+
}
26+
27+
public boolean wasWritten() {
28+
return written;
29+
}
30+
31+
}

src/main/java/org/cryptomator/cryptofs/CryptoFileChannel.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
*******************************************************************************/
99
package org.cryptomator.cryptofs;
1010

11+
import static org.cryptomator.cryptofs.ChunkData.readChunkData;
12+
import static org.cryptomator.cryptofs.ChunkData.writtenChunkData;
13+
1114
import java.io.IOException;
1215
import java.nio.ByteBuffer;
1316
import java.nio.MappedByteBuffer;
@@ -36,7 +39,7 @@
3639
import com.google.common.cache.RemovalNotification;
3740

3841
/**
39-
* Not thread-safe.
42+
* TODO Not thread-safe.
4043
*/
4144
class CryptoFileChannel extends AbstractFileChannel {
4245

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

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

179182
private ByteBuffer loadCleartextChunk(long chunkIndex) {
180183
try {
181-
return cleartextChunks.get(chunkIndex);
184+
return cleartextChunks.get(chunkIndex).bytes();
182185
} catch (ExecutionException e) {
183186
if (e.getCause() instanceof AuthenticationFailedException) {
184187
// TODO
@@ -189,10 +192,10 @@ private ByteBuffer loadCleartextChunk(long chunkIndex) {
189192
}
190193
}
191194

192-
private class CleartextChunkLoader extends CacheLoader<Long, ByteBuffer> {
195+
private class CleartextChunkLoader extends CacheLoader<Long, ChunkData> {
193196

194197
@Override
195-
public ByteBuffer load(Long chunkIndex) throws Exception {
198+
public ChunkData load(Long chunkIndex) throws Exception {
196199
LOG.debug("load chunk" + chunkIndex);
197200
int payloadSize = cryptor.fileContentCryptor().cleartextChunkSize();
198201
int chunkSize = cryptor.fileContentCryptor().ciphertextChunkSize();
@@ -201,24 +204,27 @@ public ByteBuffer load(Long chunkIndex) throws Exception {
201204
int read = ch.read(ciphertextBuf, ciphertextPos);
202205
if (read == -1) {
203206
// append
204-
return ByteBuffer.allocate(payloadSize);
207+
return writtenChunkData(ByteBuffer.allocate(payloadSize));
205208
} else {
206209
ciphertextBuf.flip();
207-
return cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, header, true);
210+
return readChunkData(cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, header, true));
208211
}
209212
}
210213

211214
}
212215

213-
private class CleartextChunkSaver implements RemovalListener<Long, ByteBuffer> {
216+
private class CleartextChunkSaver implements RemovalListener<Long, ChunkData> {
214217

215218
@Override
216-
public void onRemoval(RemovalNotification<Long, ByteBuffer> notification) {
217-
long chunkIndex = notification.getKey();
218-
if (openOptions.contains(StandardOpenOption.WRITE) && chunkIndex * cryptor.fileContentCryptor().cleartextChunkSize() < size()) {
219+
public void onRemoval(RemovalNotification<Long, ChunkData> notification) {
220+
onRemoval(notification.getKey(), notification.getValue());
221+
}
222+
223+
private void onRemoval(long chunkIndex, ChunkData chunkData) {
224+
if (channelIsWritable() && chunkLiesInFile(chunkIndex) && chunkData.wasWritten()) {
219225
LOG.debug("save chunk" + chunkIndex);
220226
long ciphertextPos = chunkIndex * cryptor.fileContentCryptor().ciphertextChunkSize() + cryptor.fileHeaderCryptor().headerSize();
221-
ByteBuffer cleartextBuf = notification.getValue().asReadOnlyBuffer();
227+
ByteBuffer cleartextBuf = chunkData.bytes().asReadOnlyBuffer();
222228
cleartextBuf.flip();
223229
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, header);
224230
try {
@@ -229,6 +235,14 @@ public void onRemoval(RemovalNotification<Long, ByteBuffer> notification) {
229235
}
230236
}
231237

238+
private boolean chunkLiesInFile(long chunkIndex) {
239+
return chunkIndex * cryptor.fileContentCryptor().cleartextChunkSize() < size();
240+
}
241+
242+
private boolean channelIsWritable() {
243+
return openOptions.contains(StandardOpenOption.WRITE);
244+
}
245+
232246
}
233247

234248
}

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemProviderTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import java.io.IOException;
1212
import java.net.URI;
13+
import java.net.URLEncoder;
1314
import java.nio.ByteBuffer;
1415
import java.nio.channels.FileChannel;
1516
import java.nio.file.FileSystem;
@@ -74,7 +75,7 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
7475

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

0 commit comments

Comments
 (0)