-
Notifications
You must be signed in to change notification settings - Fork 35
Description
When opening a filechannel to a not previously opened file in cryptofs, an OpenCryptoFile for this file (path) is instantiated and OpenCryptoFile::newFileChannel-method is called on it.
There the ciphertext filechannel (which is the one actually writing to the underlying filesystem) is created and linked to the returned cleartext file channel. When closing the cleartext file channel via channel.close(), the "close chain" runs over JDKs AbstractInteruptibleChannel.close() and the implementations of the implCloseChannel() methods in AbstractFileChannel and last and most importantly, the CleartextFileChannel:
cryptofs/src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java
Lines 322 to 337 in 6f04f2a
| protected void implCloseChannel() throws IOException { | |
| try { | |
| flush(); | |
| try { | |
| persistLastModified(); | |
| } catch (NoSuchFileException nsfe) { | |
| //no-op, see https://github.com/cryptomator/cryptofs/issues/169 | |
| } catch (IOException e) { | |
| //only best effort attempt | |
| LOG.warn("Failed to persist last modified timestamp for encrypted file: {}", e.getMessage()); | |
| } | |
| } finally { | |
| super.implCloseChannel(); | |
| closeListener.closed(this); | |
| } | |
| } |
- The
flush()-method writes, if necessary the file header and otherwise empties the chunk cache. - Afterwards, the
lastModifiedtimestamp is corrected (might be wrong on storage due to caching). - Finally, the super impl of
implCloseChannel(does not perform any IO) - and a close listener is called. The latter is a method in
OpenCryptoFile, set via the DaggerChannelComponent:
cryptofs/src/main/java/org/cryptomator/cryptofs/fh/OpenCryptoFile.java
Lines 186 to 198 in 6f04f2a
private synchronized void channelClosed(CleartextFileChannel cleartextFileChannel) throws IOException { try { FileChannel ciphertextFileChannel = openChannels.remove(cleartextFileChannel); if (ciphertextFileChannel != null) { chunkIO.unregisterChannel(ciphertextFileChannel); ciphertextFileChannel.close(); } } finally { if (openChannels.isEmpty()) { close(); } } }
It basically deregisters the channel from some pools/queues and closes the ciphertext file channel.
Even though we explicitly write the last modifed time to storage, it might be changed afterwards because we never flush the ciphertextchannel explicitly before setting the last modified time. Hence, if there are unwritten changes in the channel, it is only written on close, which is after correcting the last modified time, effectively nullifying the correction.
The flush call only regards the chunk cache, which might use other (ciphertext) file channels.