mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-20 06:54:16 +00:00
adjusted to updated API, restored Folder.copy and Folder.move
This commit is contained in:
@@ -8,7 +8,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -39,15 +38,20 @@ public class CryptoFile extends CryptoNode implements File {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadableFile openReadable(long timeout, TimeUnit unit) throws IOException, TimeoutException {
|
||||
public ReadableFile openReadable(long timeout, TimeUnit unit) throws TimeoutException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WritableFile openWritable(long timeout, TimeUnit unit) throws IOException, TimeoutException {
|
||||
public WritableFile openWritable(long timeout, TimeUnit unit) throws TimeoutException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return parent.toString() + name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.cryptomator.crypto.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -37,7 +38,7 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
File physicalFile() throws IOException {
|
||||
File physicalFile() {
|
||||
return physicalDataRoot().file(ROOT_DIR_FILE);
|
||||
}
|
||||
|
||||
@@ -67,12 +68,7 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(FolderCreateMode mode) throws IOException {
|
||||
public void create(FolderCreateMode mode) {
|
||||
physicalDataRoot().create(mode);
|
||||
physicalMetadataRoot().create(mode);
|
||||
final File dirFile = physicalFile();
|
||||
@@ -81,9 +77,14 @@ public class CryptoFileSystem extends CryptoFolder implements FileSystem {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(directoryId.getBytes());
|
||||
writable.write(buf);
|
||||
} catch (TimeoutException e) {
|
||||
throw new IOException("Failed to lock directory file in time." + dirFile, e);
|
||||
throw new UncheckedIOException(new IOException("Failed to lock directory file in time." + dirFile, e));
|
||||
}
|
||||
physicalFolder().create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return physicalRoot + ":::/";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.nio.ByteBuffer;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -46,7 +47,7 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
return name() + FILE_EXT;
|
||||
}
|
||||
|
||||
protected String getDirectoryId() throws IOException {
|
||||
protected String getDirectoryId() {
|
||||
if (directoryId.get() == null) {
|
||||
File dirFile = physicalFile();
|
||||
if (dirFile.exists()) {
|
||||
@@ -58,9 +59,7 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
buf.get(bytes);
|
||||
directoryId.set(new String(bytes));
|
||||
} catch (TimeoutException e) {
|
||||
throw new IOException("Failed to lock directory file in time." + dirFile, e);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
throw new UncheckedIOException(new IOException("Failed to lock directory file in time." + dirFile, e));
|
||||
}
|
||||
} else {
|
||||
directoryId.compareAndSet(null, UUID.randomUUID().toString());
|
||||
@@ -69,11 +68,11 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
return directoryId.get();
|
||||
}
|
||||
|
||||
File physicalFile() throws IOException {
|
||||
File physicalFile() {
|
||||
return parent.physicalFolder().file(encryptedName());
|
||||
}
|
||||
|
||||
Folder physicalFolder() throws IOException {
|
||||
Folder physicalFolder() {
|
||||
final String encryptedThenHashedDirId;
|
||||
try {
|
||||
final byte[] hash = MessageDigest.getInstance("SHA-1").digest(getDirectoryId().getBytes());
|
||||
@@ -87,20 +86,16 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
|
||||
@Override
|
||||
public Instant lastModified() {
|
||||
try {
|
||||
return physicalFile().lastModified();
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
return physicalFile().lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<? extends Node> children() throws IOException {
|
||||
public Stream<? extends Node> children() {
|
||||
return Stream.concat(files(), folders());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<CryptoFile> files() throws IOException {
|
||||
public Stream<CryptoFile> files() {
|
||||
return physicalFolder().files().map(File::name).filter(s -> s.endsWith(CryptoFile.FILE_EXT)).map(this::decryptFileName).map(this::file);
|
||||
}
|
||||
|
||||
@@ -115,7 +110,7 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<CryptoFolder> folders() throws IOException {
|
||||
public Stream<CryptoFolder> folders() {
|
||||
return physicalFolder().files().map(File::name).filter(s -> s.endsWith(CryptoFolder.FILE_EXT)).map(this::decryptFolderName).map(this::folder);
|
||||
}
|
||||
|
||||
@@ -130,13 +125,13 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(FolderCreateMode mode) throws IOException {
|
||||
public void create(FolderCreateMode mode) {
|
||||
final File dirFile = physicalFile();
|
||||
if (dirFile.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!parent.exists() && FolderCreateMode.FAIL_IF_PARENT_IS_MISSING.equals(mode)) {
|
||||
throw new FileNotFoundException(parent.name);
|
||||
throw new UncheckedIOException(new FileNotFoundException(parent.name));
|
||||
} else if (!parent.exists() && FolderCreateMode.INCLUDING_PARENTS.equals(mode)) {
|
||||
parent.create(mode);
|
||||
}
|
||||
@@ -146,15 +141,65 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
final ByteBuffer buf = ByteBuffer.wrap(directoryId.getBytes());
|
||||
writable.write(buf);
|
||||
} catch (TimeoutException e) {
|
||||
throw new IOException("Failed to lock directory file in time." + dirFile, e);
|
||||
throw new UncheckedIOException(new IOException("Failed to lock directory file in time." + dirFile, e));
|
||||
}
|
||||
physicalFolder().create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() throws IOException {
|
||||
public void copyTo(Folder target) {
|
||||
if (this.contains(target)) {
|
||||
throw new IllegalArgumentException("Can not copy parent to child directory (src: " + this + ", dst: " + target + ")");
|
||||
}
|
||||
|
||||
Folder.super.copyTo(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveTo(Folder target) {
|
||||
if (target instanceof CryptoFolder) {
|
||||
moveToInternal((CryptoFolder) target);
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Can not move CryptoFolder to conventional folder.");
|
||||
}
|
||||
}
|
||||
|
||||
private void moveToInternal(CryptoFolder target) {
|
||||
if (this.contains(target) || target.contains(this)) {
|
||||
throw new IllegalArgumentException("Can not move directories containing one another (src: " + this + ", dst: " + target + ")");
|
||||
}
|
||||
|
||||
target.physicalFile().parent().get().create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
assert target.physicalFile().parent().get().exists();
|
||||
try (WritableFile src = this.physicalFile().openWritable(1, TimeUnit.SECONDS); WritableFile dst = target.physicalFile().openWritable(1, TimeUnit.SECONDS)) {
|
||||
src.moveTo(dst);
|
||||
} catch (TimeoutException e) {
|
||||
throw new UncheckedIOException(new IOException("Failed to lock file for moving (src: " + this + ", dst: " + target + ")", e));
|
||||
}
|
||||
// directoryId is now used by target, we must no longer use the same id (we'll generate a new one when needed)
|
||||
directoryId.set(null);
|
||||
}
|
||||
|
||||
private boolean contains(Node node) {
|
||||
Optional<? extends Folder> nodeParent = node.parent();
|
||||
while (nodeParent.isPresent()) {
|
||||
if (this.equals(nodeParent.get())) {
|
||||
return true;
|
||||
}
|
||||
nodeParent = nodeParent.get().parent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return parent.toString() + name + "/";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
@@ -52,11 +50,7 @@ abstract class CryptoNode implements Node {
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
try {
|
||||
return parent.children().anyMatch(node -> node.equals(this));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
return parent.children().anyMatch(node -> node.equals(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+61
-12
@@ -28,35 +28,84 @@ public class CryptoFileSystemTest {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CryptoFileSystemTest.class);
|
||||
|
||||
@Test
|
||||
public void testFilenameEncryption() throws UncheckedIOException, IOException {
|
||||
public void testVaultStructureInitialization() throws UncheckedIOException, IOException {
|
||||
// mock cryptor:
|
||||
Cryptor cryptor = new NoCryptor();
|
||||
final Cryptor cryptor = new NoCryptor();
|
||||
|
||||
// some mock fs:
|
||||
FileSystem physicalFs = new InMemoryFileSystem();
|
||||
Folder physicalDataRoot = physicalFs.folder("d");
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final Folder physicalDataRoot = physicalFs.folder("d");
|
||||
Assert.assertFalse(physicalDataRoot.exists());
|
||||
|
||||
// init crypto fs:
|
||||
FileSystem fs = new CryptoFileSystem(physicalFs, cryptor);
|
||||
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor);
|
||||
fs.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
Assert.assertTrue(physicalDataRoot.exists());
|
||||
Assert.assertEquals(physicalFs.children().count(), 2);
|
||||
Assert.assertEquals(1, physicalDataRoot.files().count()); // ROOT file
|
||||
Assert.assertEquals(1, physicalDataRoot.folders().count()); // ROOT directory
|
||||
|
||||
LOG.debug(DirectoryPrinter.print(physicalFs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectoryCreation() throws UncheckedIOException, IOException {
|
||||
// mock stuff and prepare crypto FS:
|
||||
final Cryptor cryptor = new NoCryptor();
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final Folder physicalDataRoot = physicalFs.folder("d");
|
||||
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor);
|
||||
fs.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
|
||||
// add another encrypted folder:
|
||||
Folder fooFolder = fs.folder("foo");
|
||||
Folder barFolder = fooFolder.folder("bar");
|
||||
final Folder fooFolder = fs.folder("foo");
|
||||
final Folder fooBarFolder = fooFolder.folder("bar");
|
||||
Assert.assertFalse(fooFolder.exists());
|
||||
Assert.assertFalse(barFolder.exists());
|
||||
barFolder.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
Assert.assertFalse(fooBarFolder.exists());
|
||||
fooBarFolder.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
Assert.assertTrue(fooFolder.exists());
|
||||
Assert.assertTrue(barFolder.exists());
|
||||
Assert.assertTrue(fooBarFolder.exists());
|
||||
Assert.assertEquals(3, countDataFolders(physicalDataRoot)); // parent + foo + bar
|
||||
|
||||
LOG.info(DirectoryPrinter.print(fs));
|
||||
LOG.info(DirectoryPrinter.print(physicalFs));
|
||||
LOG.debug(DirectoryPrinter.print(fs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDirectoryMoving() throws UncheckedIOException, IOException {
|
||||
// mock stuff and prepare crypto FS:
|
||||
final Cryptor cryptor = new NoCryptor();
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor);
|
||||
fs.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
|
||||
// create foo/bar/ and then move foo/ to baz/:
|
||||
final Folder fooFolder = fs.folder("foo");
|
||||
final Folder fooBarFolder = fooFolder.folder("bar");
|
||||
final Folder bazFolder = fs.folder("baz");
|
||||
final Folder bazBarFolder = bazFolder.folder("bar");
|
||||
fooBarFolder.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
Assert.assertTrue(fooBarFolder.exists());
|
||||
Assert.assertFalse(bazFolder.exists());
|
||||
fooFolder.moveTo(bazFolder);
|
||||
// foo/bar/ should no longer exist, but baz/bar/ should:
|
||||
Assert.assertFalse(fooBarFolder.exists());
|
||||
Assert.assertTrue(bazFolder.exists());
|
||||
Assert.assertTrue(bazBarFolder.exists());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testDirectoryMovingWithinBloodline() throws UncheckedIOException, IOException {
|
||||
// mock stuff and prepare crypto FS:
|
||||
final Cryptor cryptor = new NoCryptor();
|
||||
final FileSystem physicalFs = new InMemoryFileSystem();
|
||||
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor);
|
||||
fs.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
|
||||
// create foo/bar/ and then try to move foo/bar/ to foo/
|
||||
final Folder fooFolder = fs.folder("foo");
|
||||
final Folder fooBarFolder = fooFolder.folder("bar");
|
||||
fooBarFolder.create(FolderCreateMode.INCLUDING_PARENTS);
|
||||
fooBarFolder.moveTo(fooFolder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
@@ -25,19 +23,16 @@ final class DirectoryWalker {
|
||||
}
|
||||
|
||||
public static void walk(Folder folder, int depth, int maxDepth, Consumer<Node> visitor) {
|
||||
try {
|
||||
folder.files().forEach(visitor);
|
||||
if (depth == maxDepth) {
|
||||
return;
|
||||
} else {
|
||||
folder.folders().forEach(childFolder -> {
|
||||
visitor.accept(childFolder);
|
||||
walk(childFolder, depth + 1, maxDepth, visitor);
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
folder.files().forEach(visitor);
|
||||
if (depth == maxDepth) {
|
||||
return;
|
||||
} else {
|
||||
folder.folders().forEach(childFolder -> {
|
||||
visitor.accept(childFolder);
|
||||
walk(childFolder, depth + 1, maxDepth, visitor);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user