1- /*******************************************************************************
2- * Copyright (c) 2016 Sebastian Stenzel and others.
3- * All rights reserved. This program and the accompanying materials
4- * are made available under the terms of the accompanying LICENSE.txt.
5- *
6- * Contributors:
7- * Sebastian Stenzel - initial API and implementation
8- *******************************************************************************/
91package org .cryptomator .cryptofs .fh ;
102
113import org .cryptomator .cryptofs .EffectiveOpenOptions ;
2315import java .nio .file .attribute .FileTime ;
2416import java .time .Instant ;
2517import java .util .Optional ;
26- import java .util .concurrent .ConcurrentHashMap ;
27- import java .util .concurrent .ConcurrentMap ;
18+ import java .util .concurrent .atomic .AtomicInteger ;
2819import java .util .concurrent .atomic .AtomicLong ;
2920import java .util .concurrent .atomic .AtomicReference ;
3021
@@ -42,10 +33,13 @@ public class OpenCryptoFile implements Closeable {
4233 private final AtomicReference <Path > currentFilePath ;
4334 private final AtomicLong fileSize ;
4435 private final OpenCryptoFileComponent component ;
45- private final ConcurrentMap <CleartextFileChannel , FileChannel > openChannels = new ConcurrentHashMap <>();
36+
37+ private final AtomicInteger openChannelsCount = new AtomicInteger (0 );
4638
4739 @ Inject
48- public OpenCryptoFile (FileCloseListener listener , ChunkCache chunkCache , Cryptor cryptor , FileHeaderHolder headerHolder , ChunkIO chunkIO , @ CurrentOpenFilePath AtomicReference <Path > currentFilePath , @ OpenFileSize AtomicLong fileSize , @ OpenFileModifiedDate AtomicReference <Instant > lastModified , OpenCryptoFileComponent component ) {
40+ public OpenCryptoFile (FileCloseListener listener , ChunkCache chunkCache , Cryptor cryptor , FileHeaderHolder headerHolder , ChunkIO chunkIO , //
41+ @ CurrentOpenFilePath AtomicReference <Path > currentFilePath , @ OpenFileSize AtomicLong fileSize , //
42+ @ OpenFileModifiedDate AtomicReference <Instant > lastModified , OpenCryptoFileComponent component ) {
4943 this .listener = listener ;
5044 this .chunkCache = chunkCache ;
5145 this .cryptor = cryptor ;
@@ -71,6 +65,8 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, Fil
7165 }
7266 FileChannel ciphertextFileChannel = null ;
7367 CleartextFileChannel cleartextFileChannel = null ;
68+
69+ openChannelsCount .incrementAndGet (); // synchronized context, hence we can proactively increase the number
7470 try {
7571 ciphertextFileChannel = path .getFileSystem ().provider ().newFileChannel (path , options .createOpenOptionsForEncryptedFile (), attrs );
7672 initFileHeader (options , ciphertextFileChannel );
@@ -81,20 +77,16 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, Fil
8177 }
8278 initFileSize (ciphertextFileChannel );
8379 cleartextFileChannel = component .newChannelComponent () //
84- .create (ciphertextFileChannel , options , this ::channelClosed ) //
80+ .create (ciphertextFileChannel , options , this ::cleartextChannelClosed ) //
8581 .channel ();
8682 } finally {
8783 if (cleartextFileChannel == null ) { // i.e. something didn't work
84+ cleartextChannelClosed (ciphertextFileChannel );
8885 closeQuietly (ciphertextFileChannel );
89- // is this the first file channel to be opened?
90- if (openChannels .isEmpty ()) {
91- close (); // then also close the file again.
92- }
9386 }
9487 }
9588
9689 assert cleartextFileChannel != null ; // otherwise there would have been an exception
97- openChannels .put (cleartextFileChannel , ciphertextFileChannel );
9890 chunkIO .registerChannel (ciphertextFileChannel , options .writable ());
9991 return cleartextFileChannel ;
10092 }
@@ -183,12 +175,11 @@ public void updateCurrentFilePath(Path newFilePath) {
183175 currentFilePath .updateAndGet (p -> p == null ? null : newFilePath );
184176 }
185177
186- private synchronized void channelClosed (CleartextFileChannel cleartextFileChannel ) {
187- FileChannel ciphertextFileChannel = openChannels .remove (cleartextFileChannel );
178+ private synchronized void cleartextChannelClosed (FileChannel ciphertextFileChannel ) {
188179 if (ciphertextFileChannel != null ) {
189180 chunkIO .unregisterChannel (ciphertextFileChannel );
190181 }
191- if (openChannels . isEmpty () ) {
182+ if (openChannelsCount . decrementAndGet () == 0 ) {
192183 close ();
193184 }
194185 }
0 commit comments