mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-04-19 09:06:54 -04:00
Refactored filesystem api
* Removed Readable- and WritableBytes ** Replaced with Readable-/WritableByteChannel ** Methods now integrated in Readable- and WritableFile ** Replaced positioned read/write by method to set the position
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package org.cryptomator.commons.test.matcher;
|
||||
package org.cryptomator.common.test.matcher;
|
||||
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
@@ -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;
|
||||
|
||||
@@ -39,7 +39,7 @@ public class OpenFiles implements AutoCloseable {
|
||||
}
|
||||
|
||||
static void cleanup(Collection<ReadableFile> readableFiles, Collection<WritableFile> writableFiles) {
|
||||
Iterator<AutoCloseable> iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator();
|
||||
Iterator<? extends AutoCloseable> iterator = Stream.concat(readableFiles.stream(), writableFiles.stream()).iterator();
|
||||
UncheckedIOException firstException = null;
|
||||
while (iterator.hasNext()) {
|
||||
AutoCloseable openFile = iterator.next();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Tries to fill the remaining space in the given byte buffer with data from
|
||||
* this readable bytes from the current position.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Tries to fill the remaining space in the given byte buffer with data from
|
||||
* this readable bytes from the given position.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Tries to fill the remaining space in the given byte buffer with data from
|
||||
* this readable bytes from the current position.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Fast-forwards or rewinds the file to the specified position.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Fast-forwards or rewinds the file to the specified position.
|
||||
* <p>
|
||||
* Consecutive writes on the file will begin at the new position.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Closes this {@code WritableFile} which finally commits all operations
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user