Skip to content

Commit 345d924

Browse files
Merge branch 'release/1.8.1'
2 parents 2cc272c + 317b0a9 commit 345d924

13 files changed

+96
-67
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ before_script:
1313
- mvn -B --update-snapshots dependency-check:check -Pdependency-check
1414
script:
1515
- mvn -B clean test jacoco:report verify -Pcoverage -Dorg.slf4j.simpleLogger.defaultLogLevel=debug
16+
- |
17+
if [[ "$TRAVIS_BRANCH" =~ ^release/.* ]]; then
18+
mvn -B javadoc:jar;
19+
fi
1620
after_success:
1721
- curl -o ~/codacy-coverage-reporter.jar https://oss.sonatype.org/service/local/repositories/releases/content/com/codacy/codacy-coverage-reporter/4.0.3/codacy-coverage-reporter-4.0.3-assembly.jar
1822
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r target/site/jacoco/jacoco.xml

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>org.cryptomator</groupId>
44
<artifactId>cryptofs</artifactId>
5-
<version>1.8.0</version>
5+
<version>1.8.1</version>
66
<name>Cryptomator Crypto Filesystem</name>
77
<description>This library provides the Java filesystem provider used by Cryptomator.</description>
88
<url>https://github.com/cryptomator/cryptofs</url>

src/main/java/org/cryptomator/cryptofs/ch/CleartextFileChannel.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.cryptomator.cryptofs.fh.ChunkCache;
88
import org.cryptomator.cryptofs.fh.ChunkData;
99
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
10+
import org.cryptomator.cryptofs.fh.FileHeaderLoader;
1011
import org.cryptomator.cryptofs.fh.OpenFileModifiedDate;
1112
import org.cryptomator.cryptofs.fh.OpenFileSize;
1213
import org.cryptomator.cryptolib.Cryptors;
@@ -41,6 +42,7 @@ public class CleartextFileChannel extends AbstractFileChannel {
4142
private static final Logger LOG = LoggerFactory.getLogger(CleartextFileChannel.class);
4243

4344
private final FileChannel ciphertextFileChannel;
45+
private final FileHeaderLoader fileHeaderLoader;
4446
private final Cryptor cryptor;
4547
private final ChunkCache chunkCache;
4648
private final EffectiveOpenOptions options;
@@ -50,11 +52,13 @@ public class CleartextFileChannel extends AbstractFileChannel {
5052
private final ExceptionsDuringWrite exceptionsDuringWrite;
5153
private final ChannelCloseListener closeListener;
5254
private final CryptoFileSystemStats stats;
55+
private boolean headerWritten;
5356

5457
@Inject
55-
public CleartextFileChannel(FileChannel ciphertextFileChannel, ReadWriteLock readWriteLock, Cryptor cryptor, ChunkCache chunkCache, EffectiveOpenOptions options, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, Supplier<BasicFileAttributeView> attrViewProvider, ExceptionsDuringWrite exceptionsDuringWrite, ChannelCloseListener closeListener, CryptoFileSystemStats stats) {
58+
public CleartextFileChannel(FileChannel ciphertextFileChannel, FileHeaderLoader fileHeaderLoader, ReadWriteLock readWriteLock, Cryptor cryptor, ChunkCache chunkCache, EffectiveOpenOptions options, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, Supplier<BasicFileAttributeView> attrViewProvider, ExceptionsDuringWrite exceptionsDuringWrite, ChannelCloseListener closeListener, CryptoFileSystemStats stats) {
5659
super(readWriteLock);
5760
this.ciphertextFileChannel = ciphertextFileChannel;
61+
this.fileHeaderLoader = fileHeaderLoader;
5862
this.cryptor = cryptor;
5963
this.chunkCache = chunkCache;
6064
this.options = options;
@@ -68,6 +72,7 @@ public CleartextFileChannel(FileChannel ciphertextFileChannel, ReadWriteLock rea
6872
if (options.append()) {
6973
position = fileSize.get();
7074
}
75+
headerWritten = !options.writable();
7176
}
7277

7378
private void updateFileSize() {
@@ -147,6 +152,8 @@ protected int writeLocked(ByteBuffer src, long position) throws IOException {
147152
private long writeLockedInternal(ByteSource src, long position) throws IOException {
148153
Preconditions.checkArgument(position <= fileSize.get());
149154

155+
writeHeaderIfNeeded();
156+
150157
int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize();
151158
long written = 0;
152159
while (src.hasRemaining()) {
@@ -179,6 +186,14 @@ private long writeLockedInternal(ByteSource src, long position) throws IOExcepti
179186
return written;
180187
}
181188

189+
private void writeHeaderIfNeeded() throws IOException {
190+
if (!headerWritten) {
191+
LOG.trace("{} - Writing file header.", this);
192+
ciphertextFileChannel.write(cryptor.fileHeaderCryptor().encryptHeader(fileHeaderLoader.get()), 0);
193+
headerWritten = true;
194+
}
195+
}
196+
182197
@Override
183198
protected void truncateLocked(long newSize) throws IOException {
184199
Preconditions.checkArgument(newSize >= 0);

src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@
66
import javax.inject.Inject;
77
import java.io.IOException;
88
import java.nio.ByteBuffer;
9-
import java.nio.channels.FileChannel;
109

1110
@OpenFileScoped
1211
class ChunkLoader {
1312

1413
private final Cryptor cryptor;
1514
private final ChunkIO ciphertext;
16-
private final FileHeaderHandler headerHandler;
15+
private final FileHeaderLoader headerLoader;
1716
private final CryptoFileSystemStats stats;
1817

1918
@Inject
20-
public ChunkLoader(Cryptor cryptor, ChunkIO ciphertext, FileHeaderHandler headerHandler, CryptoFileSystemStats stats) {
19+
public ChunkLoader(Cryptor cryptor, ChunkIO ciphertext, FileHeaderLoader headerLoader, CryptoFileSystemStats stats) {
2120
this.cryptor = cryptor;
2221
this.ciphertext = ciphertext;
23-
this.headerHandler = headerHandler;
22+
this.headerLoader = headerLoader;
2423
this.stats = stats;
2524
}
2625

@@ -36,7 +35,7 @@ public ChunkData load(Long chunkIndex) throws IOException {
3635
return ChunkData.emptyWithSize(payloadSize);
3736
} else {
3837
ciphertextBuf.flip();
39-
ByteBuffer cleartextBuf = cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, headerHandler.get(), true);
38+
ByteBuffer cleartextBuf = cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, headerLoader.get(), true);
4039
stats.addBytesDecrypted(cleartextBuf.remaining());
4140
ByteBuffer cleartextBufWhichCanHoldFullChunk;
4241
if (cleartextBuf.capacity() < payloadSize) {

src/main/java/org/cryptomator/cryptofs/fh/ChunkSaver.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ class ChunkSaver {
1212

1313
private final Cryptor cryptor;
1414
private final ChunkIO ciphertext;
15-
private final FileHeaderHandler headerHandler;
15+
private final FileHeaderLoader headerLoader;
1616
private final ExceptionsDuringWrite exceptionsDuringWrite;
1717
private final CryptoFileSystemStats stats;
1818

1919
@Inject
20-
public ChunkSaver(Cryptor cryptor, ChunkIO ciphertext, FileHeaderHandler headerHandler, ExceptionsDuringWrite exceptionsDuringWrite, CryptoFileSystemStats stats) {
20+
public ChunkSaver(Cryptor cryptor, ChunkIO ciphertext, FileHeaderLoader headerLoader, ExceptionsDuringWrite exceptionsDuringWrite, CryptoFileSystemStats stats) {
2121
this.cryptor = cryptor;
2222
this.ciphertext = ciphertext;
23-
this.headerHandler = headerHandler;
23+
this.headerLoader = headerLoader;
2424
this.exceptionsDuringWrite = exceptionsDuringWrite;
2525
this.stats = stats;
2626
}
@@ -30,9 +30,8 @@ public void save(long chunkIndex, ChunkData chunkData) throws IOException {
3030
long ciphertextPos = chunkIndex * cryptor.fileContentCryptor().ciphertextChunkSize() + cryptor.fileHeaderCryptor().headerSize();
3131
ByteBuffer cleartextBuf = chunkData.asReadOnlyBuffer();
3232
stats.addBytesEncrypted(cleartextBuf.remaining());
33-
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, headerHandler.get());
33+
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, headerLoader.get());
3434
try {
35-
headerHandler.persistIfNeeded();
3635
ciphertext.write(ciphertextBuf, ciphertextPos);
3736
} catch (IOException e) {
3837
exceptionsDuringWrite.add(e);

src/main/java/org/cryptomator/cryptofs/fh/FileHeaderHandler.java renamed to src/main/java/org/cryptomator/cryptofs/fh/FileHeaderLoader.java

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,17 @@
1414
import java.util.concurrent.atomic.AtomicReference;
1515

1616
@OpenFileScoped
17-
class FileHeaderHandler {
17+
public class FileHeaderLoader {
1818

19-
private static final Logger LOG = LoggerFactory.getLogger(FileHeaderHandler.class);
19+
private static final Logger LOG = LoggerFactory.getLogger(FileHeaderLoader.class);
2020

2121
private final ChunkIO ciphertext;
2222
private final Cryptor cryptor;
2323
private final AtomicReference<Path> path;
2424
private final AtomicReference<FileHeader> header = new AtomicReference<>();
25-
private boolean dirty;
2625

2726
@Inject
28-
public FileHeaderHandler(ChunkIO ciphertext, Cryptor cryptor, @CurrentOpenFilePath AtomicReference<Path> path) {
27+
public FileHeaderLoader(ChunkIO ciphertext, Cryptor cryptor, @CurrentOpenFilePath AtomicReference<Path> path) {
2928
this.ciphertext = ciphertext;
3029
this.cryptor = cryptor;
3130
this.path = path;
@@ -43,12 +42,12 @@ private FileHeader load() throws UncheckedIOException {
4342
try {
4443
if (ciphertext.size() == 0) { // i.e. TRUNCATE_EXISTING, CREATE OR CREATE_NEW
4544
LOG.trace("Generating file header for {}", path.get());
46-
dirty = true;
4745
return cryptor.fileHeaderCryptor().create();
4846
} else {
4947
LOG.trace("Reading file header from {}", path.get());
5048
ByteBuffer existingHeaderBuf = ByteBuffer.allocate(cryptor.fileHeaderCryptor().headerSize());
51-
ciphertext.read(existingHeaderBuf, 0);
49+
int read = ciphertext.read(existingHeaderBuf, 0);
50+
assert read == existingHeaderBuf.capacity();
5251
existingHeaderBuf.flip();
5352
try {
5453
return cryptor.fileHeaderCryptor().decryptHeader(existingHeaderBuf);
@@ -61,13 +60,4 @@ private FileHeader load() throws UncheckedIOException {
6160
}
6261
}
6362

64-
public void persistIfNeeded() throws IOException {
65-
FileHeader header = get(); // make sure to invoke get(), as this sets dirty as a side effect
66-
if (dirty) {
67-
LOG.trace("Writing file header to {}", path.get());
68-
ciphertext.write(cryptor.fileHeaderCryptor().encryptHeader(header), 0);
69-
dirty = false;
70-
}
71-
}
72-
7363
}

src/main/java/org/cryptomator/cryptofs/fh/OpenCryptoFile.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ public class OpenCryptoFile implements Closeable {
2929

3030
private final FileCloseListener listener;
3131
private final AtomicReference<Instant> lastModified;
32+
private final ChunkCache chunkCache;
3233
private final ChunkIO chunkIO;
33-
private final FileHeaderHandler headerHandler;
3434
private final AtomicReference<Path> currentFilePath;
3535
private final AtomicLong fileSize;
3636
private final OpenCryptoFileComponent component;
3737
private final ConcurrentMap<CleartextFileChannel, FileChannel> openChannels = new ConcurrentHashMap<>();
3838

3939
@Inject
40-
public OpenCryptoFile(FileCloseListener listener, ChunkIO chunkIO, FileHeaderHandler headerHandler, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
40+
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, ChunkIO chunkIO, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
4141
this.listener = listener;
42+
this.chunkCache = chunkCache;
4243
this.chunkIO = chunkIO;
43-
this.headerHandler = headerHandler;
4444
this.currentFilePath = currentFilePath;
4545
this.fileSize = fileSize;
4646
this.component = component;
@@ -50,6 +50,10 @@ public OpenCryptoFile(FileCloseListener listener, ChunkIO chunkIO, FileHeaderHan
5050
public synchronized FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException {
5151
Path path = currentFilePath.get();
5252

53+
if (options.truncateExisting()) {
54+
chunkCache.invalidateAll();
55+
}
56+
5357
FileChannel ciphertextFileChannel = null;
5458
CleartextFileChannel cleartextFileChannel = null;
5559
try {
@@ -96,7 +100,6 @@ private synchronized void channelClosed(CleartextFileChannel cleartextFileChanne
96100
try {
97101
FileChannel ciphertextFileChannel = openChannels.remove(cleartextFileChannel);
98102
if (ciphertextFileChannel != null) {
99-
headerHandler.persistIfNeeded();
100103
chunkIO.unregisterChannel(ciphertextFileChannel);
101104
ciphertextFileChannel.close();
102105
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.io.IOException;
2121
import java.nio.ByteBuffer;
2222
import java.nio.channels.FileChannel;
23+
import java.nio.charset.StandardCharsets;
2324
import java.nio.file.FileSystem;
2425
import java.nio.file.Files;
2526
import java.nio.file.Path;
@@ -28,7 +29,9 @@
2829
import static java.lang.String.format;
2930
import static java.nio.file.StandardOpenOption.APPEND;
3031
import static java.nio.file.StandardOpenOption.CREATE;
32+
import static java.nio.file.StandardOpenOption.CREATE_NEW;
3133
import static java.nio.file.StandardOpenOption.READ;
34+
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
3235
import static java.nio.file.StandardOpenOption.WRITE;
3336
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties;
3437
import static org.cryptomator.cryptofs.CryptoFileSystemUri.create;
@@ -55,6 +58,26 @@ public static void teardownClass() throws IOException {
5558
inMemoryFs.close();
5659
}
5760

61+
// tests https://github.com/cryptomator/cryptofs/issues/48
62+
@Test
63+
public void testTruncateExistingWhileStillOpen() throws IOException {
64+
Path file = filePath(nextFileId());
65+
66+
try (FileChannel ch1 = FileChannel.open(file, CREATE_NEW, WRITE)) {
67+
ch1.write(StandardCharsets.UTF_8.encode("goodbye world"), 0);
68+
ch1.force(true); // will generate a file header
69+
try (FileChannel ch2 = FileChannel.open(file, CREATE, WRITE, TRUNCATE_EXISTING)) { // reuse existing file header, but will not re-write it
70+
ch2.write(StandardCharsets.UTF_8.encode("hello world"), 0);
71+
}
72+
}
73+
74+
try (FileChannel ch1 = FileChannel.open(file, READ)) {
75+
ByteBuffer buf = ByteBuffer.allocate((int) ch1.size());
76+
ch1.read(buf);
77+
Assertions.assertArrayEquals("hello world".getBytes(), buf.array());
78+
}
79+
}
80+
5881
// tests https://github.com/cryptomator/cryptofs/issues/22
5982
@Test
6083
public void testFileSizeIsZeroAfterCreatingFileChannel() throws IOException {

src/test/java/org/cryptomator/cryptofs/ch/CleartextFileChannelTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.cryptomator.cryptofs.fh.ChunkCache;
66
import org.cryptomator.cryptofs.fh.ChunkData;
77
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
8+
import org.cryptomator.cryptofs.fh.FileHeaderLoader;
89
import org.cryptomator.cryptolib.api.Cryptor;
910
import org.cryptomator.cryptolib.api.FileContentCryptor;
1011
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
@@ -54,6 +55,7 @@ public class CleartextFileChannelTest {
5455
private FileHeaderCryptor fileHeaderCryptor = mock(FileHeaderCryptor.class);
5556
private FileContentCryptor fileContentCryptor = mock(FileContentCryptor.class);
5657
private FileChannel ciphertextFileChannel = mock(FileChannel.class);
58+
private FileHeaderLoader headerLoader = mock(FileHeaderLoader.class);
5759
private EffectiveOpenOptions options = mock(EffectiveOpenOptions.class);
5860
private AtomicLong fileSize = new AtomicLong(100);
5961
private AtomicReference<Instant> lastModified = new AtomicReference(Instant.ofEpochMilli(0));
@@ -78,7 +80,7 @@ public void setUp() throws IOException {
7880
when(readWriteLock.readLock()).thenReturn(readLock);
7981
when(readWriteLock.writeLock()).thenReturn(writeLock);
8082

81-
inTest = new CleartextFileChannel(ciphertextFileChannel, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
83+
inTest = new CleartextFileChannel(ciphertextFileChannel, headerLoader, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
8284
}
8385

8486
@Test
@@ -292,7 +294,7 @@ public void testReadFromMultipleChunks() throws IOException {
292294
when(ciphertextFileChannel.size()).thenReturn(5_500_000_160l); // initial cleartext size will be 5_000_000_100l
293295
when(options.readable()).thenReturn(true);
294296

295-
inTest = new CleartextFileChannel(ciphertextFileChannel, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
297+
inTest = new CleartextFileChannel(ciphertextFileChannel, headerLoader, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
296298
ByteBuffer buf = ByteBuffer.allocate(10);
297299

298300
// A read from frist chunk:

src/test/java/org/cryptomator/cryptofs/fh/ChunkLoaderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class ChunkLoaderTest {
3535
private final Cryptor cryptor = mock(Cryptor.class);
3636
private final CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
3737
private final FileHeader header = mock(FileHeader.class);
38-
private final FileHeaderHandler headerLoader = mock(FileHeaderHandler.class);
38+
private final FileHeaderLoader headerLoader = mock(FileHeaderLoader.class);
3939
private final ChunkLoader inTest = new ChunkLoader(cryptor, chunkIO, headerLoader, stats);
4040

4141
@BeforeEach

0 commit comments

Comments
 (0)