diff --git a/main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/ContainsMatcher.java b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/ContainsMatcher.java similarity index 94% rename from main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/ContainsMatcher.java rename to main/commons-test/src/main/java/org/cryptomator/common/test/matcher/ContainsMatcher.java index b72289783..48d54130a 100644 --- a/main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/ContainsMatcher.java +++ b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/ContainsMatcher.java @@ -1,4 +1,4 @@ -package org.cryptomator.commons.test.matcher; +package org.cryptomator.common.test.matcher; import org.hamcrest.Matcher; import org.hamcrest.Matchers; diff --git a/main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/PropertyMatcher.java b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/PropertyMatcher.java similarity index 97% rename from main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/PropertyMatcher.java rename to main/commons-test/src/main/java/org/cryptomator/common/test/matcher/PropertyMatcher.java index 2ab70c62f..42a44faf2 100644 --- a/main/commons-test/src/main/java/org/cryptomator/commons/test/matcher/PropertyMatcher.java +++ b/main/commons-test/src/main/java/org/cryptomator/common/test/matcher/PropertyMatcher.java @@ -3,7 +3,7 @@ * This file is licensed under the terms of the MIT license. * See the LICENSE.txt file for more info. ******************************************************************************/ -package org.cryptomator.commons.test.matcher; +package org.cryptomator.common.test.matcher; import java.util.function.Function; diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/OpenFiles.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/OpenFiles.java index 21bac4e7f..09712e57d 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/OpenFiles.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/OpenFiles.java @@ -39,7 +39,7 @@ public class OpenFiles implements AutoCloseable { } static void cleanup(Collection readableFiles, Collection writableFiles) { - Iterator iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator(); + Iterator iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator(); UncheckedIOException firstException = null; while (iterator.hasNext()) { AutoCloseable openFile = iterator.next(); diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableBytes.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableBytes.java deleted file mode 100644 index bc38a81a2..000000000 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableBytes.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Markus Kreusch - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - ******************************************************************************/ -package org.cryptomator.filesystem; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteBuffer; - -public interface ReadableBytes { - - /** - *

- * Tries to fill the remaining space in the given byte buffer with data from - * this readable bytes from the current position. - *

- * May read less bytes if the end of this readable bytes has been reached. - * - * @param target - * the byte buffer to fill - * @throws UncheckedIOException - * if an {@link IOException} occurs while reading from this - * {@code ReadableBytes} - */ - void read(ByteBuffer target) throws UncheckedIOException; - - /** - *

- * Tries to fill the remaining space in the given byte buffer with data from - * this readable bytes from the given position. - *

- * May read less bytes if the end of this readable bytes has been reached. - * - * @param target - * the byte buffer to fill - * @param position - * the position to read bytes from - * @throws UncheckedIOException - * if an {@link IOException} occurs while reading from this - * {@code ReadableBytes} - */ - void read(ByteBuffer target, long position) throws UncheckedIOException; - -} diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableFile.java index c8ec7e28d..a7e64cadb 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/ReadableFile.java @@ -5,12 +5,46 @@ ******************************************************************************/ package org.cryptomator.filesystem; +import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; -public interface ReadableFile extends ReadableBytes, AutoCloseable { +public interface ReadableFile extends ReadableByteChannel { void copyTo(WritableFile other) throws UncheckedIOException; + /** + *

+ * Tries to fill the remaining space in the given byte buffer with data from + * this readable bytes from the current position. + *

+ * May read less bytes if the end of this readable bytes has been reached. + * + * @param target + * the byte buffer to fill + * @return the number of bytes actually read, or {@code -1} if the end of + * file has been reached + * @throws UncheckedIOException + * if an {@link IOException} occurs while reading from this + * {@code ReadableBytes} + */ + int read(ByteBuffer target) throws UncheckedIOException; + + /** + *

+ * Fast-forwards or rewinds the file to the specified position. + *

+ * Consecutive reads on the file will begin at the new position. + * + * @param position + * the position to set the file to + * @throws UncheckedIOException + * if an {@link IOException} occurs + * + */ + void position(long position) throws UncheckedIOException; + @Override void close() throws UncheckedIOException; diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableBytes.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableBytes.java deleted file mode 100644 index 698a7c62c..000000000 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableBytes.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Markus Kreusch - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - ******************************************************************************/ -package org.cryptomator.filesystem; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteBuffer; - -public interface WritableBytes { - - /** - * Writes the data in the given byte buffer to this readable bytes at the - * current position. - * - * @param target - * the byte buffer to use - * @throws UncheckedIOException - * if an {@link IOException} occurs while writing - */ - void write(ByteBuffer source) throws UncheckedIOException; - - /** - * Writes the data in the given byte buffer to this readable bytes at the - * given position, overwriting existing content (not inserting). - * - * @param target - * the byte buffer to use - * @param position - * the position to write the data to - * @throws UncheckedIOException - * if an {@link IOException} occurs while writing - */ - void write(ByteBuffer source, int position) throws UncheckedIOException; - -} diff --git a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java index edba72be2..1d2282093 100644 --- a/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java +++ b/main/filesystem-api/src/main/java/org/cryptomator/filesystem/WritableFile.java @@ -5,10 +5,13 @@ ******************************************************************************/ package org.cryptomator.filesystem; +import java.io.IOException; import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.WritableByteChannel; import java.time.Instant; -public interface WritableFile extends WritableBytes, AutoCloseable { +public interface WritableFile extends WritableByteChannel { void moveTo(WritableFile other) throws UncheckedIOException; @@ -24,6 +27,37 @@ public interface WritableFile extends WritableBytes, AutoCloseable { void truncate() throws UncheckedIOException; + /** + * Writes the data in the given byte buffer to this readable bytes at the + * current position. + * + * @param source + * the byte buffer to use + * @return the number of bytes written, always equal to + * {@code source.remaining()} + * @throws UncheckedIOException + * if an {@link IOException} occurs while writing + */ + int write(ByteBuffer source) throws UncheckedIOException; + + /** + *

+ * Fast-forwards or rewinds the file to the specified position. + *

+ * Consecutive writes on the file will begin at the new position. + *

+ * If the position is set to a value greater than the current end of file + * consecutive writes will write data to the given position. The value of + * all bytes between this position and the previous end of file will be + * unspecified. + * + * @param position + * the position to set the file to + * @throws UncheckedIOException + * if an {@link IOException} occurs + */ + void position(long position) throws UncheckedIOException; + /** *

* Closes this {@code WritableFile} which finally commits all operations diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoReadableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoReadableFile.java index 0fc4d37e8..c297a73dc 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoReadableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoReadableFile.java @@ -1,5 +1,6 @@ package org.cryptomator.crypto.fs; +import java.io.UncheckedIOException; import java.nio.ByteBuffer; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -14,7 +15,10 @@ import org.cryptomator.io.ByteBuffers; class CryptoReadableFile implements ReadableFile { - private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with encrypted chunk size + MAC size + private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with + // encrypted + // chunk size + + // MAC size private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); private final ExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); @@ -26,7 +30,8 @@ class CryptoReadableFile implements ReadableFile { public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file) { final int headerSize = cryptor.getHeaderSize(); final ByteBuffer header = ByteBuffer.allocate(headerSize); - file.read(header, 0); + file.position(0); + file.read(header); header.flip(); this.decryptor = cryptor.createFileContentDecryptor(header); this.file = file; @@ -42,31 +47,37 @@ class CryptoReadableFile implements ReadableFile { } @Override - public void read(ByteBuffer target) { + public int read(ByteBuffer target) { try { + if (bufferedCleartext == FileContentCryptor.EOF) { + return -1; + } + int bytesRead = 0; while (target.remaining() > 0 && bufferedCleartext != FileContentCryptor.EOF) { bufferCleartext(); - readFromBufferedCleartext(target); + bytesRead += readFromBufferedCleartext(target); } + return bytesRead; } catch (InterruptedException e) { Thread.currentThread().interrupt(); + throw new RuntimeException(e); } } + @Override + public void position(long position) throws UncheckedIOException { + throw new UnsupportedOperationException("Partial read unsupported"); + } + private void bufferCleartext() throws InterruptedException { if (!bufferedCleartext.hasRemaining()) { bufferedCleartext = decryptor.cleartext(); } } - private void readFromBufferedCleartext(ByteBuffer target) { + private int readFromBufferedCleartext(ByteBuffer target) { assert bufferedCleartext != null; - ByteBuffers.copy(bufferedCleartext, target); - } - - @Override - public void read(ByteBuffer target, long position) { - throw new UnsupportedOperationException("Partial read not implemented yet."); + return ByteBuffers.copy(bufferedCleartext, target); } @Override @@ -79,6 +90,11 @@ class CryptoReadableFile implements ReadableFile { } } + @Override + public boolean isOpen() { + return file.isOpen(); + } + @Override public void close() { executorService.shutdownNow(); @@ -95,7 +111,7 @@ class CryptoReadableFile implements ReadableFile { @Override public Void call() { - file.read(EMPTY_BUFFER, startpos); + file.position(startpos); int bytesRead = -1; try { do { diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoWritableFile.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoWritableFile.java index 0694749a1..59fc3def3 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoWritableFile.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/fs/CryptoWritableFile.java @@ -32,24 +32,27 @@ class CryptoWritableFile implements WritableFile { private void writeHeader() { ByteBuffer header = encryptor.getHeader(); header.rewind(); - file.write(header, 0); + file.position(0); + file.write(header); } @Override - public void write(ByteBuffer source) { - final ByteBuffer cleartextCopy = ByteBuffer.allocate(source.remaining()); + public int write(ByteBuffer source) { + final int size = source.remaining(); + final ByteBuffer cleartextCopy = ByteBuffer.allocate(size); ByteBuffers.copy(source, cleartextCopy); cleartextCopy.flip(); try { encryptor.append(cleartextCopy); - file.write(source); + return size; } catch (InterruptedException e) { Thread.currentThread().interrupt(); + throw new RuntimeException(e); } } @Override - public void write(ByteBuffer source, int position) { + public void position(long position) { throw new UnsupportedOperationException("Partial write not implemented yet."); } @@ -75,7 +78,16 @@ class CryptoWritableFile implements WritableFile { @Override public void truncate() { - this.write(ByteBuffer.allocate(0), 0); + /* + * TODO kill writer thread (EOF) and reinitialize CryptoWritableFile + * after truncating the file + */ + throw new UnsupportedOperationException("Truncate not supported yet"); + } + + @Override + public boolean isOpen() { + return file.isOpen(); } @Override diff --git a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java index 02f92084a..531b5468a 100644 --- a/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java +++ b/main/filesystem-inmemory/src/main/java/org/cryptomator/filesystem/inmem/InMemoryFile.java @@ -52,31 +52,24 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF } @Override - public void read(ByteBuffer target) { - ByteBuffers.copy(content, target); - } - - @Override - public void read(ByteBuffer target, long position) { - if (position > Integer.MAX_VALUE) { - throw new IllegalArgumentException("Can not keep files of virtually unlimited size in memory."); - } + public void position(long position) throws UncheckedIOException { content.position((int) position); - ByteBuffers.copy(content, target); } @Override - public void write(ByteBuffer source) { - this.write(source, content.position()); + public int read(ByteBuffer target) { + return ByteBuffers.copy(content, target); } @Override - public void write(ByteBuffer source, int position) { + public int write(ByteBuffer source) { assert content != null; - expandContentCapacityIfRequired(position + source.remaining()); - content.position(position); + final int initialContentPosition = content.position(); + expandContentCapacityIfRequired(initialContentPosition + source.remaining()); + content.position(initialContentPosition); assert content.remaining() >= source.remaining(); content.put(source); + return content.position() - initialContentPosition; } private void expandContentCapacityIfRequired(int requiredCapacity) { @@ -121,7 +114,12 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF // returning null removes the entry. return null; }); - assert!this.exists(); + assert !this.exists(); + } + + @Override + public boolean isOpen() { + return lock.isWriteLockedByCurrentThread() || lock.getReadHoldCount() > 0; } @Override diff --git a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java index 5e32c0289..11b614b86 100644 --- a/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java +++ b/main/filesystem-inmemory/src/test/java/org/cryptomator/filesystem/inmem/InMemoryFileSystemTest.java @@ -136,7 +136,8 @@ public class InMemoryFileSystemTest { } final ByteBuffer readBuf = ByteBuffer.allocate(5); try (ReadableFile readable = bazFile.openReadable()) { - readable.read(readBuf, 6); + readable.position(6); + readable.read(readBuf); } Assert.assertEquals("world", new String(readBuf.array())); } @@ -159,7 +160,8 @@ public class InMemoryFileSystemTest { Assert.assertTrue(test1File.exists()); Assert.assertTrue(test2File.exists()); - // copy foo/bar/ to qwe/asd/ (result is qwe/asd/file1.txt & qwe/asd/file2.txt) + // copy foo/bar/ to qwe/asd/ (result is qwe/asd/file1.txt & + // qwe/asd/file2.txt) fooBarFolder.copyTo(qweAsdFolder); Assert.assertTrue(qweAsdFolder.exists()); Assert.assertEquals(2, qweAsdFolder.files().count()); diff --git a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java index ce520c782..9b1a1d00c 100644 --- a/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java +++ b/main/filesystem-nio/src/main/java/org/cryptomator/filesystem/nio/NioFile.java @@ -42,11 +42,17 @@ class NioFile extends NioNode implements File { private class ReadableView implements ReadableFile { @Override - public void read(ByteBuffer target) throws UncheckedIOException { + public int read(ByteBuffer target) throws UncheckedIOException { + return -1; } @Override - public void read(ByteBuffer target, long position) throws UncheckedIOException { + public boolean isOpen() { + return false; + } + + @Override + public void position(long position) throws UncheckedIOException { } @Override @@ -62,11 +68,17 @@ class NioFile extends NioNode implements File { private class WritableView implements WritableFile { @Override - public void write(ByteBuffer source) throws UncheckedIOException { + public int write(ByteBuffer source) throws UncheckedIOException { + return -1; } @Override - public void write(ByteBuffer source, int position) throws UncheckedIOException { + public boolean isOpen() { + return false; + } + + @Override + public void position(long position) throws UncheckedIOException { } @Override diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java index 4234887bf..8c16236d9 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioFolderTest.java @@ -1,7 +1,7 @@ package org.cryptomator.filesystem.nio; import static java.util.stream.Collectors.toList; -import static org.cryptomator.commons.test.matcher.ContainsMatcher.containsInAnyOrder; +import static org.cryptomator.common.test.matcher.ContainsMatcher.containsInAnyOrder; import static org.cryptomator.filesystem.FolderCreateMode.FAIL_IF_PARENT_IS_MISSING; import static org.cryptomator.filesystem.FolderCreateMode.INCLUDING_PARENTS; import static org.cryptomator.filesystem.nio.FilesystemSetupUtils.emptyFilesystem; diff --git a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioNodeMatcher.java b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioNodeMatcher.java index 01cb4253d..8ce118294 100644 --- a/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioNodeMatcher.java +++ b/main/filesystem-nio/src/test/java/org/cryptomator/filesystem/nio/NioNodeMatcher.java @@ -2,7 +2,7 @@ package org.cryptomator.filesystem.nio; import static org.hamcrest.CoreMatchers.equalTo; -import org.cryptomator.commons.test.matcher.PropertyMatcher; +import org.cryptomator.common.test.matcher.PropertyMatcher; import org.cryptomator.filesystem.File; import org.cryptomator.filesystem.Folder; import org.hamcrest.Matcher;