88 *******************************************************************************/
99package org .cryptomator .cryptofs ;
1010
11+ import static org .cryptomator .cryptofs .ChunkData .readChunkData ;
12+ import static org .cryptomator .cryptofs .ChunkData .writtenChunkData ;
13+
1114import java .io .IOException ;
1215import java .nio .ByteBuffer ;
1316import java .nio .MappedByteBuffer ;
3639import com .google .common .cache .RemovalNotification ;
3740
3841/**
39- * Not thread-safe.
42+ * TODO Not thread-safe.
4043 */
4144class 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}
0 commit comments