From 8d316800bd347d067592dd692e574b0c2592c98c Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Thu, 23 Nov 2017 15:26:52 +0100 Subject: [PATCH 1/6] Support for vaults stored in locations that Windows gives us an UNC path for. --- .../cryptofs/CryptoFileSystemUri.java | 23 +++++++++++++++- .../cryptofs/CryptoFileSystemUriTest.java | 27 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemUri.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemUri.java index a7f2021a..bcdf11e9 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemUri.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemUri.java @@ -12,6 +12,8 @@ import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** *

@@ -33,16 +35,35 @@ public class CryptoFileSystemUri { public static final String URI_SCHEME = "cryptomator"; + private static final Pattern UNC_URI_PATTERN = Pattern.compile("^file://[^@/]+(@SSL)?(@[0-9]+)?.*"); private final Path pathToVault; private final String pathInsideVault; private CryptoFileSystemUri(URI uri) { validate(uri); - pathToVault = Paths.get(URI.create(uri.getAuthority())); + pathToVault = uncCompatibleUriToPath(URI.create(uri.getAuthority())); pathInsideVault = uri.getPath(); } + /** + * Creates Path from the given URI. On Windows this will recognize UNC paths. + * + * @param uri file:///file/path or file://server@SSL@123/actual/file/path + * @return /file/path or \\server@SSL@123\actual\file\path + */ + // visible for testing + static Path uncCompatibleUriToPath(URI uri) { + String in = uri.toString(); + Matcher m = UNC_URI_PATTERN.matcher(in); + if (m.find() && (m.group(1) != null || m.group(2) != null)) { // this is an UNC path! + String out = in.substring("file:".length()).replace('/', '\\'); + return Paths.get(out); + } else { + return Paths.get(uri); + } + } + /** * Constructs a CryptoFileSystem URI by using the given absolute path to a vault and constructing a path inside the vault from components. * diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemUriTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemUriTest.java index 942b3afd..a220a65d 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemUriTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemUriTest.java @@ -22,6 +22,33 @@ public class CryptoFileSystemUriTest { @Rule public ExpectedException thrown = ExpectedException.none(); + @Test + public void testUncCompatibleUriToPathWithUncSslUri() throws URISyntaxException { + URI uri = URI.create("file://webdavserver.com@SSL/DavWWWRoot/User7ff2b01/asd/"); + + Path path = CryptoFileSystemUri.uncCompatibleUriToPath(uri); + + assertThat(path, is(Paths.get("\\\\webdavserver.com@SSL\\DavWWWRoot\\User7ff2b01\\asd\\"))); + } + + @Test + public void testUncCompatibleUriToPathWithUncSslAndPortUri() throws URISyntaxException { + URI uri = URI.create("file://webdavserver.com@SSL@123/DavWWWRoot/User7ff2b01/asd/"); + + Path path = CryptoFileSystemUri.uncCompatibleUriToPath(uri); + + assertThat(path, is(Paths.get("\\\\webdavserver.com@SSL@123\\DavWWWRoot\\User7ff2b01\\asd\\"))); + } + + @Test + public void testUncCompatibleUriToPathWithNormalUri() throws URISyntaxException { + URI uri = URI.create("file:///normal/file/path/"); + + Path path = CryptoFileSystemUri.uncCompatibleUriToPath(uri); + + assertThat(path, is(Paths.get("/normal/file/path"))); + } + @Test public void testCreateWithoutPathComponents() { Path absolutePathToVault = Paths.get("a").toAbsolutePath(); From 8ff4322bb0ab6135d3cca210a8e90d01c473ad3d Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 24 Nov 2017 14:17:36 +0100 Subject: [PATCH 2/6] #17 Refactored folder deletion --- .../cryptofs/CiphertextDirectoryDeleter.java | 89 +++++-------------- 1 file changed, 21 insertions(+), 68 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CiphertextDirectoryDeleter.java b/src/main/java/org/cryptomator/cryptofs/CiphertextDirectoryDeleter.java index 7022641a..8ca73a16 100644 --- a/src/main/java/org/cryptomator/cryptofs/CiphertextDirectoryDeleter.java +++ b/src/main/java/org/cryptomator/cryptofs/CiphertextDirectoryDeleter.java @@ -1,17 +1,16 @@ package org.cryptomator.cryptofs; -import static java.nio.file.FileVisitResult.CONTINUE; +import static com.google.common.io.MoreFiles.deleteRecursively; +import static com.google.common.io.RecursiveDeleteOption.ALLOW_INSECURE; import static java.util.stream.Collectors.toSet; import static org.cryptomator.cryptofs.CiphertextDirectoryDeleter.DeleteResult.NO_FILES_DELETED; import static org.cryptomator.cryptofs.CiphertextDirectoryDeleter.DeleteResult.SOME_FILES_DELETED; import java.io.IOException; import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.FileVisitResult; -import java.nio.file.FileVisitor; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; import java.util.Set; import javax.inject.Inject; @@ -30,6 +29,11 @@ public void deleteCiphertextDirIncludingNonCiphertextFiles(Path ciphertextDir, C try { Files.deleteIfExists(ciphertextDir); } catch (DirectoryNotEmptyException e) { + /* + * The directory may not be empty due to two reasons: + * 1. + * 2. + */ switch (deleteNonCiphertextFiles(ciphertextDir, cleartextDir)) { case NO_FILES_DELETED: throw e; @@ -43,76 +47,25 @@ public void deleteCiphertextDirIncludingNonCiphertextFiles(Path ciphertextDir, C } private DeleteResult deleteNonCiphertextFiles(Path ciphertextDir, CryptoPath cleartextDir) throws IOException { - NonCiphertextFilesDeletingFileVisitor visitor; + DeleteResult result = NO_FILES_DELETED; + Set ciphertextFiles = ciphertextFiles(cleartextDir); + try (DirectoryStream stream = Files.newDirectoryStream(ciphertextDir, p -> !ciphertextFiles.contains(p))) { + for (Path path : stream) { + result = SOME_FILES_DELETED; + deleteRecursively(path, ALLOW_INSECURE); + } + } + return result; + } + + private Set ciphertextFiles(CryptoPath cleartextDir) throws IOException { try (CryptoDirectoryStream directoryStream = directoryStreamFactory.newDirectoryStream(cleartextDir, ignored -> true)) { - Set ciphertextFiles = directoryStream.ciphertextDirectoryListing().collect(toSet()); - visitor = new NonCiphertextFilesDeletingFileVisitor(ciphertextFiles); + return directoryStream.ciphertextDirectoryListing().collect(toSet()); } - Files.walkFileTree(ciphertextDir, visitor); - return visitor.getNumDeleted() == 0 // - ? NO_FILES_DELETED // - : SOME_FILES_DELETED; } static enum DeleteResult { NO_FILES_DELETED, SOME_FILES_DELETED } - private static class NonCiphertextFilesDeletingFileVisitor implements FileVisitor { - - private final Set ciphertextFiles; - - private int numDeleted = 0; - private int level = 0; - - public NonCiphertextFilesDeletingFileVisitor(Set ciphertextFiles) { - this.ciphertextFiles = ciphertextFiles; - } - - public int getNumDeleted() { - return numDeleted; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - level++; - return CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (!isOnRootLevel() || !isCiphertextFile(file)) { - Files.delete(file); - numDeleted++; - } - return CONTINUE; - } - - private boolean isOnRootLevel() { - return level == 1; - } - - private boolean isCiphertextFile(Path file) throws IOException { - return ciphertextFiles.contains(file); - } - - @Override - public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { - throw exc; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - if (exc != null) { - throw exc; - } - level--; - if (level > 0) { - Files.delete(dir); - numDeleted++; - } - return CONTINUE; - } - }; - } From 7c7566b5a2db490629c393e2b36068bc766e6847 Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 24 Nov 2017 14:53:04 +0100 Subject: [PATCH 3/6] Automatic migration of legacy longfiles with shorter threshold --- .../cryptofs/ConflictResolver.java | 6 +- .../org/cryptomator/cryptofs/Constants.java | 2 +- .../cryptofs/CryptoDirectoryStream.java | 22 +++- .../cryptofs/CryptoPathMapper.java | 2 +- .../cryptofs/LongFileNameProvider.java | 2 +- .../cryptofs/ConflictResolverTest.java | 2 +- .../CryptoDirectoryStreamIntegrationTest.java | 114 ++++++++++++++++++ ...ptyCiphertextDirectoryIntegrationTest.java | 4 +- .../cryptofs/LongFileNameProviderTest.java | 4 +- 9 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 src/test/java/org/cryptomator/cryptofs/CryptoDirectoryStreamIntegrationTest.java diff --git a/src/main/java/org/cryptomator/cryptofs/ConflictResolver.java b/src/main/java/org/cryptomator/cryptofs/ConflictResolver.java index 443283be..5bc41e70 100644 --- a/src/main/java/org/cryptomator/cryptofs/ConflictResolver.java +++ b/src/main/java/org/cryptomator/cryptofs/ConflictResolver.java @@ -1,7 +1,7 @@ package org.cryptomator.cryptofs; import static org.cryptomator.cryptofs.Constants.DIR_PREFIX; -import static org.cryptomator.cryptofs.Constants.NAME_SHORTENING_THRESHOLD; +import static org.cryptomator.cryptofs.Constants.SHORT_NAMES_MAX_LENGTH; import static org.cryptomator.cryptofs.LongFileNameProvider.LONG_NAME_FILE_EXT; import java.io.IOException; @@ -77,7 +77,7 @@ private Path resolveConflict(Path conflictingPath, String base32match, String di final boolean isDirectory; final String dirPrefix; final Path canonicalPath; - if (LongFileNameProvider.isDeflated(originalFileName)) { + if (longFileNameProvider.isDeflated(originalFileName)) { String inflated = longFileNameProvider.inflate(base32match + LONG_NAME_FILE_EXT); ciphertext = StringUtils.removeStart(inflated, DIR_PREFIX); isDirectory = inflated.startsWith(DIR_PREFIX); @@ -116,7 +116,7 @@ private Path renameConflictingFile(Path canonicalPath, Path conflictingPath, Str String alternativeCleartext = cleartext + " (Conflict " + i + ")"; String alternativeCiphertext = cryptor.fileNameCryptor().encryptFilename(alternativeCleartext, dirId.getBytes(StandardCharsets.UTF_8)); String alternativeCiphertextFileName = dirPrefix + alternativeCiphertext; - if (alternativeCiphertextFileName.length() >= NAME_SHORTENING_THRESHOLD) { + if (alternativeCiphertextFileName.length() > SHORT_NAMES_MAX_LENGTH) { alternativeCiphertextFileName = longFileNameProvider.deflate(alternativeCiphertextFileName); } alternativePath = canonicalPath.resolveSibling(alternativeCiphertextFileName); diff --git a/src/main/java/org/cryptomator/cryptofs/Constants.java b/src/main/java/org/cryptomator/cryptofs/Constants.java index aed2bb0b..d37f6bac 100644 --- a/src/main/java/org/cryptomator/cryptofs/Constants.java +++ b/src/main/java/org/cryptomator/cryptofs/Constants.java @@ -16,7 +16,7 @@ public final class Constants { static final String DATA_DIR_NAME = "d"; static final String METADATA_DIR_NAME = "m"; static final String DIR_PREFIX = "0"; - static final int NAME_SHORTENING_THRESHOLD = 129; + static final int SHORT_NAMES_MAX_LENGTH = 129; static final String ROOT_DIR_ID = ""; static final String SEPARATOR = "/"; diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java b/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java index 2343c893..be81e347 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java @@ -8,6 +8,8 @@ *******************************************************************************/ package org.cryptomator.cryptofs; +import static org.cryptomator.cryptofs.Constants.SHORT_NAMES_MAX_LENGTH; + import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; @@ -93,12 +95,18 @@ private ProcessedPaths resolveConflictingFileIfNeeded(ProcessedPaths paths) { } } - private ProcessedPaths inflateIfNeeded(ProcessedPaths paths) { + ProcessedPaths inflateIfNeeded(ProcessedPaths paths) { String fileName = paths.getCiphertextPath().getFileName().toString(); - if (LongFileNameProvider.isDeflated(fileName)) { + if (longFileNameProvider.isDeflated(fileName)) { try { String longFileName = longFileNameProvider.inflate(fileName); - return paths.withInflatedPath(paths.getCiphertextPath().resolveSibling(longFileName)); + if (longFileName.length() <= SHORT_NAMES_MAX_LENGTH) { + // "unshortify" filenames on the fly due to previously shorter threshold + paths = inflatePermanently(paths, longFileName); + return paths.withInflatedPath(paths.getCiphertextPath()); + } else { + return paths.withInflatedPath(paths.getCiphertextPath().resolveSibling(longFileName)); + } } catch (IOException e) { LOG.warn(paths.getCiphertextPath() + " could not be inflated."); return null; @@ -134,6 +142,12 @@ private boolean passesPlausibilityChecks(ProcessedPaths paths) { return !isBrokenDirectoryFile(paths); } + private ProcessedPaths inflatePermanently(ProcessedPaths paths, String longFileName) throws IOException { + Path newCiphertextPath = paths.getCiphertextPath().resolveSibling(longFileName); + Files.move(paths.getCiphertextPath(), newCiphertextPath); + return paths.withCiphertextPath(newCiphertextPath); + } + private boolean isBrokenDirectoryFile(ProcessedPaths paths) { Path potentialDirectoryFile = paths.getCiphertextPath(); if (paths.getInflatedPath().getFileName().toString().startsWith(Constants.DIR_PREFIX)) { @@ -169,7 +183,7 @@ public void close() throws IOException { () -> LOG.trace("CLOSE {}", directoryId)); } - private static class ProcessedPaths { + static class ProcessedPaths { private final Path ciphertextPath; private final Path inflatedPath; diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java b/src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java index 8dc31f99..64c2e735 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoPathMapper.java @@ -63,7 +63,7 @@ private String getCiphertextFileName(String dirId, String cleartextName, Ciphert // TODO overheadhunter: cache ciphertext names String prefix = (fileType == CiphertextFileType.DIRECTORY) ? DIR_PREFIX : ""; String ciphertextName = prefix + cryptor.fileNameCryptor().encryptFilename(cleartextName, dirId.getBytes(StandardCharsets.UTF_8)); - if (ciphertextName.length() >= Constants.NAME_SHORTENING_THRESHOLD) { + if (ciphertextName.length() > Constants.SHORT_NAMES_MAX_LENGTH) { return longFileNameProvider.deflate(ciphertextName); } else { return ciphertextName; diff --git a/src/main/java/org/cryptomator/cryptofs/LongFileNameProvider.java b/src/main/java/org/cryptomator/cryptofs/LongFileNameProvider.java index 5e763d46..ad37251f 100644 --- a/src/main/java/org/cryptomator/cryptofs/LongFileNameProvider.java +++ b/src/main/java/org/cryptomator/cryptofs/LongFileNameProvider.java @@ -53,7 +53,7 @@ public String load(String shortName) throws IOException { } - public static boolean isDeflated(String possiblyDeflatedFileName) { + public boolean isDeflated(String possiblyDeflatedFileName) { return possiblyDeflatedFileName.endsWith(LONG_NAME_FILE_EXT); } diff --git a/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java b/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java index da75c395..9b32e123 100644 --- a/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java +++ b/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java @@ -128,7 +128,7 @@ public void testRenameNormalFile() throws IOException { @Test public void testRenameLongFile() throws IOException { String longCiphertextName = "ABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGHABCDEFGH2345===="; - assert longCiphertextName.length() > Constants.NAME_SHORTENING_THRESHOLD; + assert longCiphertextName.length() > Constants.SHORT_NAMES_MAX_LENGTH; Mockito.when(testFileName.toString()).thenReturn("ABCDEF== (1).lng"); Mockito.when(longFileNameProvider.inflate("ABCDEF==.lng")).thenReturn("FEDCBA=="); Mockito.when(longFileNameProvider.deflate(longCiphertextName)).thenReturn("FEDCBA==.lng"); diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoDirectoryStreamIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoDirectoryStreamIntegrationTest.java new file mode 100644 index 00000000..f12b45e5 --- /dev/null +++ b/src/test/java/org/cryptomator/cryptofs/CryptoDirectoryStreamIntegrationTest.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2016 Sebastian Stenzel and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the accompanying LICENSE.txt. + * + * Contributors: + * Sebastian Stenzel - initial API and implementation + *******************************************************************************/ +package org.cryptomator.cryptofs; + +import static org.cryptomator.cryptofs.Constants.SHORT_NAMES_MAX_LENGTH; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.cryptomator.cryptofs.CryptoDirectoryStream.ProcessedPaths; +import org.cryptomator.cryptofs.CryptoPathMapper.Directory; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import com.google.common.jimfs.Jimfs; + +public class CryptoDirectoryStreamIntegrationTest { + + @Rule + public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private FileSystem fileSystem; + + private LongFileNameProvider longFileNameProvider = mock(LongFileNameProvider.class); + + private CryptoDirectoryStream inTest; + + @Before + public void setup() throws IOException { + fileSystem = Jimfs.newFileSystem(); + + Path dir = fileSystem.getPath("crapDirDoNotUse"); + Files.createDirectory(dir); + inTest = new CryptoDirectoryStream(new Directory("", dir), null, null, null, longFileNameProvider, null, null, null, null, null); + } + + @Test + public void testInflateIfNeededWithShortFilename() throws IOException { + String filename = "abc"; + Path ciphertextPath = fileSystem.getPath(filename); + Files.createFile(ciphertextPath); + when(longFileNameProvider.isDeflated(filename)).thenReturn(false); + + ProcessedPaths paths = new ProcessedPaths(ciphertextPath); + + ProcessedPaths result = inTest.inflateIfNeeded(paths); + + assertThat(result.getCiphertextPath(), is(ciphertextPath)); + assertThat(result.getInflatedPath(), is(ciphertextPath)); + assertThat(result.getCleartextPath(), is(nullValue())); + assertThat(Files.exists(ciphertextPath), is(true)); + } + + @Test + public void testInflateIfNeededWithRegularLongFilename() throws IOException { + String filename = "abc"; + String inflatedName = IntStream.range(0, SHORT_NAMES_MAX_LENGTH + 1).mapToObj(ignored -> "a").collect(Collectors.joining()); + Path ciphertextPath = fileSystem.getPath(filename); + Files.createFile(ciphertextPath); + Path inflatedPath = fileSystem.getPath(inflatedName); + when(longFileNameProvider.isDeflated(filename)).thenReturn(true); + when(longFileNameProvider.inflate(filename)).thenReturn(inflatedName); + + ProcessedPaths paths = new ProcessedPaths(ciphertextPath); + + ProcessedPaths result = inTest.inflateIfNeeded(paths); + + assertThat(result.getCiphertextPath(), is(ciphertextPath)); + assertThat(result.getInflatedPath(), is(inflatedPath)); + assertThat(result.getCleartextPath(), is(nullValue())); + assertThat(Files.exists(ciphertextPath), is(true)); + assertThat(Files.exists(inflatedPath), is(false)); + } + + @Test + public void testInflateIfNeededWithLongFilenameThatShouldActuallyBeShort() throws IOException { + String filename = "abc"; + String inflatedName = IntStream.range(0, SHORT_NAMES_MAX_LENGTH).mapToObj(ignored -> "a").collect(Collectors.joining()); + Path ciphertextPath = fileSystem.getPath(filename); + Files.createFile(ciphertextPath); + Path inflatedPath = fileSystem.getPath(inflatedName); + when(longFileNameProvider.isDeflated(filename)).thenReturn(true); + when(longFileNameProvider.inflate(filename)).thenReturn(inflatedName); + + ProcessedPaths paths = new ProcessedPaths(ciphertextPath); + + ProcessedPaths result = inTest.inflateIfNeeded(paths); + + assertThat(result.getCiphertextPath(), is(inflatedPath)); + assertThat(result.getInflatedPath(), is(inflatedPath)); + assertThat(result.getCleartextPath(), is(nullValue())); + assertThat(Files.exists(ciphertextPath), is(false)); + assertThat(Files.exists(inflatedPath), is(true)); + } + +} diff --git a/src/test/java/org/cryptomator/cryptofs/DeleteNonEmptyCiphertextDirectoryIntegrationTest.java b/src/test/java/org/cryptomator/cryptofs/DeleteNonEmptyCiphertextDirectoryIntegrationTest.java index 32f9cc2a..42fb5d6f 100644 --- a/src/test/java/org/cryptomator/cryptofs/DeleteNonEmptyCiphertextDirectoryIntegrationTest.java +++ b/src/test/java/org/cryptomator/cryptofs/DeleteNonEmptyCiphertextDirectoryIntegrationTest.java @@ -10,7 +10,7 @@ import static java.nio.file.Files.walkFileTree; import static java.nio.file.StandardOpenOption.CREATE_NEW; -import static org.cryptomator.cryptofs.Constants.NAME_SHORTENING_THRESHOLD; +import static org.cryptomator.cryptofs.Constants.SHORT_NAMES_MAX_LENGTH; import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties; import static org.cryptomator.cryptofs.CryptoFileSystemUri.create; @@ -133,7 +133,7 @@ public void testDeleteDirectoryContainingLongNamedDirectory() throws IOException // a // .. LongNameaaa... - String name = "LongName" + IntStream.range(0, NAME_SHORTENING_THRESHOLD) // + String name = "LongName" + IntStream.range(0, SHORT_NAMES_MAX_LENGTH) // .mapToObj(ignored -> "a") // .collect(Collectors.joining()); createFolder(cleartextDirectory, name); diff --git a/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java b/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java index 0a9ed94c..47b96256 100644 --- a/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java +++ b/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java @@ -52,8 +52,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO @Test public void testIsDeflated() throws IOException { - Assert.assertTrue(LongFileNameProvider.isDeflated("foo.lng")); - Assert.assertFalse(LongFileNameProvider.isDeflated("foo.txt")); + Assert.assertTrue(new LongFileNameProvider(null).isDeflated("foo.lng")); + Assert.assertFalse(new LongFileNameProvider(null).isDeflated("foo.txt")); } @Test From f8a233e0eb5248b799b140b84a4453db533700ea Mon Sep 17 00:00:00 2001 From: Markus Kreusch Date: Fri, 24 Nov 2017 15:24:20 +0100 Subject: [PATCH 4/6] Fixed unit tests and codacy issues --- .../java/org/cryptomator/cryptofs/CryptoDirectoryStream.java | 5 ++--- .../java/org/cryptomator/cryptofs/ConflictResolverTest.java | 3 +-- .../org/cryptomator/cryptofs/LongFileNameProviderTest.java | 5 +++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java b/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java index be81e347..90c7b49f 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoDirectoryStream.java @@ -102,8 +102,7 @@ ProcessedPaths inflateIfNeeded(ProcessedPaths paths) { String longFileName = longFileNameProvider.inflate(fileName); if (longFileName.length() <= SHORT_NAMES_MAX_LENGTH) { // "unshortify" filenames on the fly due to previously shorter threshold - paths = inflatePermanently(paths, longFileName); - return paths.withInflatedPath(paths.getCiphertextPath()); + return inflatePermanently(paths, longFileName); } else { return paths.withInflatedPath(paths.getCiphertextPath().resolveSibling(longFileName)); } @@ -145,7 +144,7 @@ private boolean passesPlausibilityChecks(ProcessedPaths paths) { private ProcessedPaths inflatePermanently(ProcessedPaths paths, String longFileName) throws IOException { Path newCiphertextPath = paths.getCiphertextPath().resolveSibling(longFileName); Files.move(paths.getCiphertextPath(), newCiphertextPath); - return paths.withCiphertextPath(newCiphertextPath); + return paths.withCiphertextPath(newCiphertextPath).withInflatedPath(newCiphertextPath); } private boolean isBrokenDirectoryFile(ProcessedPaths paths) { diff --git a/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java b/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java index 9b32e123..02914630 100644 --- a/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java +++ b/src/test/java/org/cryptomator/cryptofs/ConflictResolverTest.java @@ -120,7 +120,6 @@ public void testRenameNormalFile() throws IOException { Mockito.when(filenameCryptor.encryptFilename(Mockito.startsWith("abcdef ("), Mockito.any())).thenReturn(ciphertextName); Mockito.doThrow(new NoSuchFileException(ciphertextName)).when(testFileSystemProvider).checkAccess(Mockito.argThat(hasFileName(ciphertextName))); Path resolved = conflictResolver.resolveConflictsIfNecessary(testFile, dirId); - Mockito.verifyNoMoreInteractions(longFileNameProvider); Mockito.verify(testFileSystemProvider).move(Mockito.argThat(hasFileName("ABCDEF== (1)")), Mockito.argThat(hasFileName(ciphertextName)), Mockito.any()); Assert.assertEquals(ciphertextName, resolved.getFileName().toString()); } @@ -132,6 +131,7 @@ public void testRenameLongFile() throws IOException { Mockito.when(testFileName.toString()).thenReturn("ABCDEF== (1).lng"); Mockito.when(longFileNameProvider.inflate("ABCDEF==.lng")).thenReturn("FEDCBA=="); Mockito.when(longFileNameProvider.deflate(longCiphertextName)).thenReturn("FEDCBA==.lng"); + Mockito.when(longFileNameProvider.isDeflated("ABCDEF== (1).lng")).thenReturn(true); Mockito.when(filenameCryptor.decryptFilename(Mockito.eq("FEDCBA=="), Mockito.any())).thenReturn("fedcba"); Mockito.when(filenameCryptor.encryptFilename(Mockito.startsWith("fedcba ("), Mockito.any())).thenReturn(longCiphertextName); Mockito.doThrow(new NoSuchFileException("FEDCBA==.lng")).when(testFileSystemProvider).checkAccess(Mockito.argThat(hasFileName("FEDCBA==.lng"))); @@ -187,7 +187,6 @@ public void testRenameDirectoryFile() throws IOException, ReflectiveOperationExc Mockito.when(filenameCryptor.encryptFilename(Mockito.startsWith("abcdef ("), Mockito.any())).thenReturn(ciphertext); Mockito.doThrow(new NoSuchFileException(ciphertextName)).when(testFileSystemProvider).checkAccess(Mockito.argThat(hasFileName(ciphertextName))); Path resolved = conflictResolver.resolveConflictsIfNecessary(testFile, dirId); - Mockito.verifyNoMoreInteractions(longFileNameProvider); Mockito.verify(testFileSystemProvider).move(Mockito.argThat(hasFileName("0ABCDEF== (1)")), Mockito.argThat(hasFileName(ciphertextName)), Mockito.any()); Assert.assertEquals(ciphertextName, resolved.getFileName().toString()); } diff --git a/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java b/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java index 47b96256..d5936b01 100644 --- a/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java +++ b/src/test/java/org/cryptomator/cryptofs/LongFileNameProviderTest.java @@ -52,8 +52,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO @Test public void testIsDeflated() throws IOException { - Assert.assertTrue(new LongFileNameProvider(null).isDeflated("foo.lng")); - Assert.assertFalse(new LongFileNameProvider(null).isDeflated("foo.txt")); + Path aPath = tmpPath.resolve("foo"); + Assert.assertTrue(new LongFileNameProvider(aPath).isDeflated("foo.lng")); + Assert.assertFalse(new LongFileNameProvider(aPath).isDeflated("foo.txt")); } @Test From 62de6185cc05339072866d4b5fc58a2ac12dd61b Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 24 Nov 2017 19:15:07 +0100 Subject: [PATCH 5/6] fixed BasicFileAttributes.getSize() for non-regular files (e.g. OneDrive's Files On-Demand) --- .../cryptofs/CryptoBasicFileAttributes.java | 20 +++++++------------ .../CryptoBasicFileAttributesTest.java | 18 +++++++++++++---- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoBasicFileAttributes.java b/src/main/java/org/cryptomator/cryptofs/CryptoBasicFileAttributes.java index c017455a..1a3c1220 100644 --- a/src/main/java/org/cryptomator/cryptofs/CryptoBasicFileAttributes.java +++ b/src/main/java/org/cryptomator/cryptofs/CryptoBasicFileAttributes.java @@ -31,16 +31,6 @@ public BasicFileAttributes getDelegate() { return delegate; } - @Override - public boolean isRegularFile() { - return getDelegate().isRegularFile(); - } - - @Override - public boolean isDirectory() { - return getDelegate().isDirectory(); - } - @Override public boolean isOther() { return false; @@ -53,10 +43,14 @@ public boolean isSymbolicLink() { @Override public long size() { - if (isRegularFile()) { - return Cryptors.cleartextSize(getDelegate().size() - cryptor.fileHeaderCryptor().headerSize(), cryptor); - } else { + if (isDirectory()) { + return getDelegate().size(); + } else if (isOther()) { + return -1l; + } else if (isSymbolicLink()) { return -1l; + } else { + return Cryptors.cleartextSize(getDelegate().size() - cryptor.fileHeaderCryptor().headerSize(), cryptor); } } diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoBasicFileAttributesTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoBasicFileAttributesTest.java index 60680352..25027646 100644 --- a/src/test/java/org/cryptomator/cryptofs/CryptoBasicFileAttributesTest.java +++ b/src/test/java/org/cryptomator/cryptofs/CryptoBasicFileAttributesTest.java @@ -82,16 +82,26 @@ public void testIsSymbolicLink() { } @Test - public void testSize() throws IOException { + public void testSizeOfFile() throws IOException { Mockito.when(delegateAttr.size()).thenReturn(88l + 16 + 1337 + 32); - Mockito.when(delegateAttr.isRegularFile()).thenReturn(true); + Mockito.when(delegateAttr.isDirectory()).thenReturn(false); + Mockito.when(delegateAttr.isSymbolicLink()).thenReturn(false); + Mockito.when(delegateAttr.isOther()).thenReturn(false); Mockito.when(ciphertextFilePath.getFileName()).thenReturn(Paths.get("foo")); BasicFileAttributes attr = new CryptoBasicFileAttributes(delegateAttr, ciphertextFilePath, cryptor); + Assert.assertEquals(1337l, attr.size()); + } - Mockito.when(delegateAttr.isRegularFile()).thenReturn(false); - Assert.assertEquals(-1l, attr.size()); + @Test + public void testSizeOfDirectory() throws IOException { + Mockito.when(delegateAttr.size()).thenReturn(4096l); + Mockito.when(delegateAttr.isDirectory()).thenReturn(true); + + BasicFileAttributes attr = new CryptoBasicFileAttributes(delegateAttr, ciphertextFilePath, cryptor); + + Assert.assertEquals(4096l, attr.size()); } @Test(expected = IllegalArgumentException.class) From 8158249d36aa5b41d8145c4c20814e0e14156d53 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 24 Nov 2017 22:10:22 +0100 Subject: [PATCH 6/6] Preparing 1.4.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c213f505..65c17dc8 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.cryptomator cryptofs - 1.5.0-SNAPSHOT + 1.4.5 Cryptomator Crypto Filesystem This library provides the Java filesystem provider used by Cryptomator. https://github.com/cryptomator/cryptofs