Skip to content

Commit

Permalink
Merge branch 'release/1.8.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Apr 24, 2019
2 parents 2cc272c + 317b0a9 commit 345d924
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 67 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ before_script:
- mvn -B --update-snapshots dependency-check:check -Pdependency-check
script:
- mvn -B clean test jacoco:report verify -Pcoverage -Dorg.slf4j.simpleLogger.defaultLogLevel=debug
- |
if [[ "$TRAVIS_BRANCH" =~ ^release/.* ]]; then
mvn -B javadoc:jar;
fi
after_success:
- 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
- $JAVA_HOME/bin/java -jar ~/codacy-coverage-reporter.jar report -l Java -r target/site/jacoco/jacoco.xml
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>1.8.0</version>
<version>1.8.1</version>
<name>Cryptomator Crypto Filesystem</name>
<description>This library provides the Java filesystem provider used by Cryptomator.</description>
<url>https://github.com/cryptomator/cryptofs</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.cryptomator.cryptofs.fh.ChunkCache;
import org.cryptomator.cryptofs.fh.ChunkData;
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
import org.cryptomator.cryptofs.fh.FileHeaderLoader;
import org.cryptomator.cryptofs.fh.OpenFileModifiedDate;
import org.cryptomator.cryptofs.fh.OpenFileSize;
import org.cryptomator.cryptolib.Cryptors;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class CleartextFileChannel extends AbstractFileChannel {
private static final Logger LOG = LoggerFactory.getLogger(CleartextFileChannel.class);

private final FileChannel ciphertextFileChannel;
private final FileHeaderLoader fileHeaderLoader;
private final Cryptor cryptor;
private final ChunkCache chunkCache;
private final EffectiveOpenOptions options;
Expand All @@ -50,11 +52,13 @@ public class CleartextFileChannel extends AbstractFileChannel {
private final ExceptionsDuringWrite exceptionsDuringWrite;
private final ChannelCloseListener closeListener;
private final CryptoFileSystemStats stats;
private boolean headerWritten;

@Inject
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) {
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) {
super(readWriteLock);
this.ciphertextFileChannel = ciphertextFileChannel;
this.fileHeaderLoader = fileHeaderLoader;
this.cryptor = cryptor;
this.chunkCache = chunkCache;
this.options = options;
Expand All @@ -68,6 +72,7 @@ public CleartextFileChannel(FileChannel ciphertextFileChannel, ReadWriteLock rea
if (options.append()) {
position = fileSize.get();
}
headerWritten = !options.writable();
}

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

writeHeaderIfNeeded();

int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize();
long written = 0;
while (src.hasRemaining()) {
Expand Down Expand Up @@ -179,6 +186,14 @@ private long writeLockedInternal(ByteSource src, long position) throws IOExcepti
return written;
}

private void writeHeaderIfNeeded() throws IOException {
if (!headerWritten) {
LOG.trace("{} - Writing file header.", this);
ciphertextFileChannel.write(cryptor.fileHeaderCryptor().encryptHeader(fileHeaderLoader.get()), 0);
headerWritten = true;
}
}

@Override
protected void truncateLocked(long newSize) throws IOException {
Preconditions.checkArgument(newSize >= 0);
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/org/cryptomator/cryptofs/fh/ChunkLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@
import javax.inject.Inject;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

@OpenFileScoped
class ChunkLoader {

private final Cryptor cryptor;
private final ChunkIO ciphertext;
private final FileHeaderHandler headerHandler;
private final FileHeaderLoader headerLoader;
private final CryptoFileSystemStats stats;

@Inject
public ChunkLoader(Cryptor cryptor, ChunkIO ciphertext, FileHeaderHandler headerHandler, CryptoFileSystemStats stats) {
public ChunkLoader(Cryptor cryptor, ChunkIO ciphertext, FileHeaderLoader headerLoader, CryptoFileSystemStats stats) {
this.cryptor = cryptor;
this.ciphertext = ciphertext;
this.headerHandler = headerHandler;
this.headerLoader = headerLoader;
this.stats = stats;
}

Expand All @@ -36,7 +35,7 @@ public ChunkData load(Long chunkIndex) throws IOException {
return ChunkData.emptyWithSize(payloadSize);
} else {
ciphertextBuf.flip();
ByteBuffer cleartextBuf = cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, headerHandler.get(), true);
ByteBuffer cleartextBuf = cryptor.fileContentCryptor().decryptChunk(ciphertextBuf, chunkIndex, headerLoader.get(), true);
stats.addBytesDecrypted(cleartextBuf.remaining());
ByteBuffer cleartextBufWhichCanHoldFullChunk;
if (cleartextBuf.capacity() < payloadSize) {
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/org/cryptomator/cryptofs/fh/ChunkSaver.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ class ChunkSaver {

private final Cryptor cryptor;
private final ChunkIO ciphertext;
private final FileHeaderHandler headerHandler;
private final FileHeaderLoader headerLoader;
private final ExceptionsDuringWrite exceptionsDuringWrite;
private final CryptoFileSystemStats stats;

@Inject
public ChunkSaver(Cryptor cryptor, ChunkIO ciphertext, FileHeaderHandler headerHandler, ExceptionsDuringWrite exceptionsDuringWrite, CryptoFileSystemStats stats) {
public ChunkSaver(Cryptor cryptor, ChunkIO ciphertext, FileHeaderLoader headerLoader, ExceptionsDuringWrite exceptionsDuringWrite, CryptoFileSystemStats stats) {
this.cryptor = cryptor;
this.ciphertext = ciphertext;
this.headerHandler = headerHandler;
this.headerLoader = headerLoader;
this.exceptionsDuringWrite = exceptionsDuringWrite;
this.stats = stats;
}
Expand All @@ -30,9 +30,8 @@ public void save(long chunkIndex, ChunkData chunkData) throws IOException {
long ciphertextPos = chunkIndex * cryptor.fileContentCryptor().ciphertextChunkSize() + cryptor.fileHeaderCryptor().headerSize();
ByteBuffer cleartextBuf = chunkData.asReadOnlyBuffer();
stats.addBytesEncrypted(cleartextBuf.remaining());
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, headerHandler.get());
ByteBuffer ciphertextBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, chunkIndex, headerLoader.get());
try {
headerHandler.persistIfNeeded();
ciphertext.write(ciphertextBuf, ciphertextPos);
} catch (IOException e) {
exceptionsDuringWrite.add(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,17 @@
import java.util.concurrent.atomic.AtomicReference;

@OpenFileScoped
class FileHeaderHandler {
public class FileHeaderLoader {

private static final Logger LOG = LoggerFactory.getLogger(FileHeaderHandler.class);
private static final Logger LOG = LoggerFactory.getLogger(FileHeaderLoader.class);

private final ChunkIO ciphertext;
private final Cryptor cryptor;
private final AtomicReference<Path> path;
private final AtomicReference<FileHeader> header = new AtomicReference<>();
private boolean dirty;

@Inject
public FileHeaderHandler(ChunkIO ciphertext, Cryptor cryptor, @CurrentOpenFilePath AtomicReference<Path> path) {
public FileHeaderLoader(ChunkIO ciphertext, Cryptor cryptor, @CurrentOpenFilePath AtomicReference<Path> path) {
this.ciphertext = ciphertext;
this.cryptor = cryptor;
this.path = path;
Expand All @@ -43,12 +42,12 @@ private FileHeader load() throws UncheckedIOException {
try {
if (ciphertext.size() == 0) { // i.e. TRUNCATE_EXISTING, CREATE OR CREATE_NEW
LOG.trace("Generating file header for {}", path.get());
dirty = true;
return cryptor.fileHeaderCryptor().create();
} else {
LOG.trace("Reading file header from {}", path.get());
ByteBuffer existingHeaderBuf = ByteBuffer.allocate(cryptor.fileHeaderCryptor().headerSize());
ciphertext.read(existingHeaderBuf, 0);
int read = ciphertext.read(existingHeaderBuf, 0);
assert read == existingHeaderBuf.capacity();
existingHeaderBuf.flip();
try {
return cryptor.fileHeaderCryptor().decryptHeader(existingHeaderBuf);
Expand All @@ -61,13 +60,4 @@ private FileHeader load() throws UncheckedIOException {
}
}

public void persistIfNeeded() throws IOException {
FileHeader header = get(); // make sure to invoke get(), as this sets dirty as a side effect
if (dirty) {
LOG.trace("Writing file header to {}", path.get());
ciphertext.write(cryptor.fileHeaderCryptor().encryptHeader(header), 0);
dirty = false;
}
}

}
11 changes: 7 additions & 4 deletions src/main/java/org/cryptomator/cryptofs/fh/OpenCryptoFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,18 @@ public class OpenCryptoFile implements Closeable {

private final FileCloseListener listener;
private final AtomicReference<Instant> lastModified;
private final ChunkCache chunkCache;
private final ChunkIO chunkIO;
private final FileHeaderHandler headerHandler;
private final AtomicReference<Path> currentFilePath;
private final AtomicLong fileSize;
private final OpenCryptoFileComponent component;
private final ConcurrentMap<CleartextFileChannel, FileChannel> openChannels = new ConcurrentHashMap<>();

@Inject
public OpenCryptoFile(FileCloseListener listener, ChunkIO chunkIO, FileHeaderHandler headerHandler, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, ChunkIO chunkIO, @CurrentOpenFilePath AtomicReference<Path> currentFilePath, @OpenFileSize AtomicLong fileSize, @OpenFileModifiedDate AtomicReference<Instant> lastModified, OpenCryptoFileComponent component) {
this.listener = listener;
this.chunkCache = chunkCache;
this.chunkIO = chunkIO;
this.headerHandler = headerHandler;
this.currentFilePath = currentFilePath;
this.fileSize = fileSize;
this.component = component;
Expand All @@ -50,6 +50,10 @@ public OpenCryptoFile(FileCloseListener listener, ChunkIO chunkIO, FileHeaderHan
public synchronized FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException {
Path path = currentFilePath.get();

if (options.truncateExisting()) {
chunkCache.invalidateAll();
}

FileChannel ciphertextFileChannel = null;
CleartextFileChannel cleartextFileChannel = null;
try {
Expand Down Expand Up @@ -96,7 +100,6 @@ private synchronized void channelClosed(CleartextFileChannel cleartextFileChanne
try {
FileChannel ciphertextFileChannel = openChannels.remove(cleartextFileChannel);
if (ciphertextFileChannel != null) {
headerHandler.persistIfNeeded();
chunkIO.unregisterChannel(ciphertextFileChannel);
ciphertextFileChannel.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -28,7 +29,9 @@
import static java.lang.String.format;
import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties;
import static org.cryptomator.cryptofs.CryptoFileSystemUri.create;
Expand All @@ -55,6 +58,26 @@ public static void teardownClass() throws IOException {
inMemoryFs.close();
}

// tests https://github.com/cryptomator/cryptofs/issues/48
@Test
public void testTruncateExistingWhileStillOpen() throws IOException {
Path file = filePath(nextFileId());

try (FileChannel ch1 = FileChannel.open(file, CREATE_NEW, WRITE)) {
ch1.write(StandardCharsets.UTF_8.encode("goodbye world"), 0);
ch1.force(true); // will generate a file header
try (FileChannel ch2 = FileChannel.open(file, CREATE, WRITE, TRUNCATE_EXISTING)) { // reuse existing file header, but will not re-write it
ch2.write(StandardCharsets.UTF_8.encode("hello world"), 0);
}
}

try (FileChannel ch1 = FileChannel.open(file, READ)) {
ByteBuffer buf = ByteBuffer.allocate((int) ch1.size());
ch1.read(buf);
Assertions.assertArrayEquals("hello world".getBytes(), buf.array());
}
}

// tests https://github.com/cryptomator/cryptofs/issues/22
@Test
public void testFileSizeIsZeroAfterCreatingFileChannel() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.cryptomator.cryptofs.fh.ChunkCache;
import org.cryptomator.cryptofs.fh.ChunkData;
import org.cryptomator.cryptofs.fh.ExceptionsDuringWrite;
import org.cryptomator.cryptofs.fh.FileHeaderLoader;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.FileContentCryptor;
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
Expand Down Expand Up @@ -54,6 +55,7 @@ public class CleartextFileChannelTest {
private FileHeaderCryptor fileHeaderCryptor = mock(FileHeaderCryptor.class);
private FileContentCryptor fileContentCryptor = mock(FileContentCryptor.class);
private FileChannel ciphertextFileChannel = mock(FileChannel.class);
private FileHeaderLoader headerLoader = mock(FileHeaderLoader.class);
private EffectiveOpenOptions options = mock(EffectiveOpenOptions.class);
private AtomicLong fileSize = new AtomicLong(100);
private AtomicReference<Instant> lastModified = new AtomicReference(Instant.ofEpochMilli(0));
Expand All @@ -78,7 +80,7 @@ public void setUp() throws IOException {
when(readWriteLock.readLock()).thenReturn(readLock);
when(readWriteLock.writeLock()).thenReturn(writeLock);

inTest = new CleartextFileChannel(ciphertextFileChannel, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
inTest = new CleartextFileChannel(ciphertextFileChannel, headerLoader, readWriteLock, cryptor, chunkCache, options, fileSize, lastModified, attributeViewSupplier, exceptionsDuringWrite, closeListener, stats);
}

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

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

// A read from frist chunk:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ChunkLoaderTest {
private final Cryptor cryptor = mock(Cryptor.class);
private final CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
private final FileHeader header = mock(FileHeader.class);
private final FileHeaderHandler headerLoader = mock(FileHeaderHandler.class);
private final FileHeaderLoader headerLoader = mock(FileHeaderLoader.class);
private final ChunkLoader inTest = new ChunkLoader(cryptor, chunkIO, headerLoader, stats);

@BeforeEach
Expand Down
6 changes: 3 additions & 3 deletions src/test/java/org/cryptomator/cryptofs/fh/ChunkSaverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ public class ChunkSaverTest {
private final Cryptor cryptor = mock(Cryptor.class);
private final CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
private final FileHeader header = mock(FileHeader.class);
private final FileHeaderHandler headerHandler = mock(FileHeaderHandler.class);
private final FileHeaderLoader headerLoader = mock(FileHeaderLoader.class);
private final ExceptionsDuringWrite exceptionsDuringWrite = mock(ExceptionsDuringWrite.class);
private final ChunkSaver inTest = new ChunkSaver(cryptor, chunkIO, headerHandler, exceptionsDuringWrite, stats);
private final ChunkSaver inTest = new ChunkSaver(cryptor, chunkIO, headerLoader, exceptionsDuringWrite, stats);

@BeforeEach
public void setup() throws IOException {
when(cryptor.fileContentCryptor()).thenReturn(fileContentCryptor);
when(cryptor.fileHeaderCryptor()).thenReturn(fileHeaderCryptor);
when(headerHandler.get()).thenReturn(header);
when(headerLoader.get()).thenReturn(header);
when(fileContentCryptor.ciphertextChunkSize()).thenReturn(CIPHERTEXT_CHUNK_SIZE);
when(fileContentCryptor.cleartextChunkSize()).thenReturn(CLEARTEXT_CHUNK_SIZE);
when(fileHeaderCryptor.headerSize()).thenReturn(HEADER_SIZE);
Expand Down
Loading

0 comments on commit 345d924

Please sign in to comment.