Skip to content

Commit a01a2b2

Browse files
committed
Merge branch 'develop' into feature/cipher-to-clear
2 parents 0915cb8 + 38062dd commit a01a2b2

File tree

10 files changed

+74
-116
lines changed

10 files changed

+74
-116
lines changed

src/main/java/org/cryptomator/cryptofs/CryptoFileSystems.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ class CryptoFileSystems {
3434

3535
private final ConcurrentMap<Path, CryptoFileSystemImpl> fileSystems = new ConcurrentHashMap<>();
3636
private final CryptoFileSystemComponent.Factory cryptoFileSystemComponentFactory;
37-
private final FileSystemCapabilityChecker capabilityChecker;
3837
private final SecureRandom csprng;
3938

4039
@Inject
41-
public CryptoFileSystems(CryptoFileSystemComponent.Factory cryptoFileSystemComponentFactory, FileSystemCapabilityChecker capabilityChecker, SecureRandom csprng) {
40+
public CryptoFileSystems(CryptoFileSystemComponent.Factory cryptoFileSystemComponentFactory, SecureRandom csprng) {
4241
this.cryptoFileSystemComponentFactory = cryptoFileSystemComponentFactory;
43-
this.capabilityChecker = capabilityChecker;
4442
this.csprng = csprng;
4543
}
4644

@@ -53,13 +51,12 @@ public CryptoFileSystemImpl create(CryptoFileSystemProvider provider, Path pathT
5351
try (Masterkey key = properties.keyLoader().loadKey(keyId)) {
5452
var config = configLoader.verify(key.getEncoded(), Constants.VAULT_VERSION);
5553
backupVaultConfigFile(normalizedPathToVault, properties);
56-
var adjustedProperties = adjustForCapabilities(pathToVault, properties);
5754
var cryptor = CryptorProvider.forScheme(config.getCipherCombo()).provide(key.copy(), csprng);
5855
try {
5956
checkVaultRootExistence(pathToVault, cryptor);
6057
return fileSystems.compute(normalizedPathToVault, (path, fs) -> {
6158
if (fs == null) {
62-
return cryptoFileSystemComponentFactory.create(cryptor, config, provider, normalizedPathToVault, adjustedProperties).cryptoFileSystem();
59+
return cryptoFileSystemComponentFactory.create(cryptor, config, provider, normalizedPathToVault, properties).cryptoFileSystem();
6360
} else {
6461
throw new FileSystemAlreadyExistsException();
6562
}
@@ -123,23 +120,6 @@ private void backupVaultConfigFile(Path pathToVault, CryptoFileSystemProperties
123120
BackupHelper.attemptBackup(vaultConfigFile);
124121
}
125122

126-
private CryptoFileSystemProperties adjustForCapabilities(Path pathToVault, CryptoFileSystemProperties originalProperties) throws FileSystemCapabilityChecker.MissingCapabilityException {
127-
if (!originalProperties.readonly()) {
128-
try {
129-
capabilityChecker.assertWriteAccess(pathToVault);
130-
return originalProperties;
131-
} catch (FileSystemCapabilityChecker.MissingCapabilityException e) {
132-
capabilityChecker.assertReadAccess(pathToVault);
133-
LOG.warn("No write access to vault. Fallback to read-only access.");
134-
Set<CryptoFileSystemProperties.FileSystemFlags> flags = EnumSet.copyOf(originalProperties.flags());
135-
flags.add(CryptoFileSystemProperties.FileSystemFlags.READONLY);
136-
return CryptoFileSystemProperties.cryptoFileSystemPropertiesFrom(originalProperties).withFlags(flags).build();
137-
}
138-
} else {
139-
return originalProperties;
140-
}
141-
}
142-
143123
public void remove(CryptoFileSystemImpl cryptoFileSystem) {
144124
fileSystems.values().remove(cryptoFileSystem);
145125
}

src/main/java/org/cryptomator/cryptofs/common/Constants.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ private Constants() {
2929

3030
public static final int MAX_SYMLINK_LENGTH = 32767; // max path length on NTFS and FAT32: 32k-1
3131
public static final int MAX_DIR_ID_LENGTH = 36; // UUIDv4: hex-encoded 16 byte int + 4 hyphens = 36 ASCII chars
32-
public static final int MIN_CIPHER_NAME_LENGTH = 26; //rounded up base64url encoded (16 bytes IV + 0 bytes empty string) + file suffix = 26 ASCII chars
32+
public static final int MAX_CIPHER_NAME_LENGTH = 220; // calculations done in https://github.com/cryptomator/cryptofs/issues/60#issuecomment-523238303
33+
public static final int MIN_CIPHER_NAME_LENGTH = 28; //rounded up base64url encoded (16 bytes IV + 0 bytes empty string) + file suffix = 28 ASCII chars
34+
public static final int MAX_ADDITIONAL_PATH_LENGTH = 48; // beginning at d/... see https://github.com/cryptomator/cryptofs/issues/77
3335

3436
public static final String SEPARATOR = "/";
3537
public static final String RECOVERY_DIR_NAME = "LOST+FOUND";

src/main/java/org/cryptomator/cryptofs/common/FileSystemCapabilityChecker.java

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import org.slf4j.Logger;
88
import org.slf4j.LoggerFactory;
99

10-
import javax.inject.Inject;
11-
import javax.inject.Singleton;
1210
import java.io.IOException;
1311
import java.nio.file.DirectoryIteratorException;
1412
import java.nio.file.DirectoryStream;
@@ -17,13 +15,9 @@
1715
import java.nio.file.Files;
1816
import java.nio.file.Path;
1917

20-
@Singleton
21-
public class FileSystemCapabilityChecker {
18+
public final class FileSystemCapabilityChecker {
2219

2320
private static final Logger LOG = LoggerFactory.getLogger(FileSystemCapabilityChecker.class);
24-
private static final int MAX_CIPHERTEXT_NAME_LENGTH = 220; // inclusive. calculations done in https://github.com/cryptomator/cryptofs/issues/60#issuecomment-523238303
25-
private static final int MIN_CIPHERTEXT_NAME_LENGTH = 28; // base64(iv).c9r
26-
private static final int MAX_ADDITIONAL_PATH_LENGTH = 48; // beginning at d/... see https://github.com/cryptomator/cryptofs/issues/77
2721

2822
public enum Capability {
2923
/**
@@ -41,8 +35,8 @@ public enum Capability {
4135
WRITE_ACCESS,
4236
}
4337

44-
@Inject
45-
public FileSystemCapabilityChecker() {
38+
private FileSystemCapabilityChecker() {
39+
4640
}
4741

4842
/**
@@ -53,7 +47,7 @@ public FileSystemCapabilityChecker() {
5347
* @implNote Only short-running tests with constant time are performed
5448
* @since 1.9.2
5549
*/
56-
public void assertAllCapabilities(Path pathToVault) throws MissingCapabilityException {
50+
public static void assertAllCapabilities(Path pathToVault) throws MissingCapabilityException {
5751
assertReadAccess(pathToVault);
5852
assertWriteAccess(pathToVault);
5953
}
@@ -65,7 +59,7 @@ public void assertAllCapabilities(Path pathToVault) throws MissingCapabilityExce
6559
* @throws MissingCapabilityException if the check fails
6660
* @since 1.9.3
6761
*/
68-
public void assertReadAccess(Path pathToVault) throws MissingCapabilityException {
62+
public static void assertReadAccess(Path pathToVault) throws MissingCapabilityException {
6963
try (DirectoryStream<Path> ds = Files.newDirectoryStream(pathToVault)) {
7064
assert ds != null;
7165
} catch (IOException e) {
@@ -80,7 +74,7 @@ public void assertReadAccess(Path pathToVault) throws MissingCapabilityException
8074
* @throws MissingCapabilityException if the check fails
8175
* @since 1.9.3
8276
*/
83-
public void assertWriteAccess(Path pathToVault) throws MissingCapabilityException {
77+
public static void assertWriteAccess(Path pathToVault) throws MissingCapabilityException {
8478
Path checkDir = pathToVault.resolve("c");
8579
try {
8680
Files.createDirectories(checkDir);
@@ -93,9 +87,9 @@ public void assertWriteAccess(Path pathToVault) throws MissingCapabilityExceptio
9387
}
9488
}
9589

96-
public int determineSupportedCleartextFileNameLength(Path pathToVault) throws IOException {
90+
public static int determineSupportedCleartextFileNameLength(Path pathToVault) throws IOException {
9791
int maxCiphertextLen = determineSupportedCiphertextFileNameLength(pathToVault);
98-
assert maxCiphertextLen >= MIN_CIPHERTEXT_NAME_LENGTH;
92+
assert maxCiphertextLen >= Constants.MIN_CIPHER_NAME_LENGTH;
9993
// math explained in https://github.com/cryptomator/cryptofs/issues/60#issuecomment-523238303;
10094
// subtract 4 for file extension, base64-decode, subtract 16 for IV
10195
return (maxCiphertextLen - 4) / 4 * 3 - 16;
@@ -108,22 +102,22 @@ public int determineSupportedCleartextFileNameLength(Path pathToVault) throws IO
108102
* @return Number of chars a .c9r file is allowed to have
109103
* @throws IOException If unable to perform this check
110104
*/
111-
public int determineSupportedCiphertextFileNameLength(Path pathToVault) throws IOException {
112-
int subPathLength = MAX_ADDITIONAL_PATH_LENGTH - 2; // subtract "c/"
113-
return determineSupportedCiphertextFileNameLength(pathToVault.resolve("c"), subPathLength, MIN_CIPHERTEXT_NAME_LENGTH, MAX_CIPHERTEXT_NAME_LENGTH);
105+
public static int determineSupportedCiphertextFileNameLength(Path pathToVault) throws IOException {
106+
int subPathLength = Constants.MAX_ADDITIONAL_PATH_LENGTH - 2; // subtract "c/"
107+
return determineSupportedCiphertextFileNameLength(pathToVault.resolve("c"), subPathLength, Constants.MIN_CIPHER_NAME_LENGTH, Constants.MAX_CIPHER_NAME_LENGTH);
114108
}
115109

116110
/**
117111
* Determines the number of chars a filename is allowed to have inside of subdirectories of <code>dir</code> by running an experiment.
118112
*
119-
* @param dir Path to a directory where to conduct the experiment (e.g. <code>/path/to/vault/c</code>)
120-
* @param subPathLength Defines the combined number of chars of the subdirectories inside <code>dir</code>, including slashes but excluding the leading slash. Must be a minimum of 6
113+
* @param dir Path to a directory where to conduct the experiment (e.g. <code>/path/to/vault/c</code>)
114+
* @param subPathLength Defines the combined number of chars of the subdirectories inside <code>dir</code>, including slashes but excluding the leading slash. Must be a minimum of 6
121115
* @param minFileNameLength The minimum filename length to check
122116
* @param maxFileNameLength The maximum filename length to check
123117
* @return The supported filename length inside a subdirectory of <code>dir</code> with <code>subPathLength</code> chars
124118
* @throws IOException If unable to perform this check
125119
*/
126-
public int determineSupportedCiphertextFileNameLength(Path dir, int subPathLength, int minFileNameLength, int maxFileNameLength) throws IOException {
120+
public static int determineSupportedCiphertextFileNameLength(Path dir, int subPathLength, int minFileNameLength, int maxFileNameLength) throws IOException {
127121
Preconditions.checkArgument(subPathLength >= 6, "subPathLength must be larger than charcount(a/nnn/)");
128122
Preconditions.checkArgument(minFileNameLength > 0);
129123
Preconditions.checkArgument(maxFileNameLength <= 999);
@@ -144,7 +138,7 @@ public int determineSupportedCiphertextFileNameLength(Path dir, int subPathLengt
144138
}
145139
}
146140

147-
private int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundIncl, int upperBoundExcl) {
141+
private static int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundIncl, int upperBoundExcl) {
148142
assert lowerBoundIncl < upperBoundExcl;
149143
int mid = (lowerBoundIncl + upperBoundExcl) / 2;
150144
assert mid < upperBoundExcl;
@@ -159,7 +153,7 @@ private int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundInc
159153
}
160154
}
161155

162-
private boolean canHandleFileNameLength(Path parent, int nameLength) {
156+
private static boolean canHandleFileNameLength(Path parent, int nameLength) {
163157
Path checkDir = parent.resolve(String.format("%03d", nameLength));
164158
Path checkFile = checkDir.resolve(Strings.repeat("a", nameLength));
165159
try {
@@ -178,7 +172,7 @@ private boolean canHandleFileNameLength(Path parent, int nameLength) {
178172
}
179173
}
180174

181-
private boolean canListDir(Path dir) {
175+
private static boolean canListDir(Path dir) {
182176
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
183177
ds.iterator().hasNext(); // throws DirectoryIteratorException on Windows if child path too long
184178
return true;
@@ -187,15 +181,15 @@ private boolean canListDir(Path dir) {
187181
}
188182
}
189183

190-
private void deleteSilently(Path path) {
184+
private static void deleteSilently(Path path) {
191185
try {
192186
Files.delete(path);
193187
} catch (IOException e) {
194188
LOG.trace("Failed to delete " + path, e);
195189
}
196190
}
197191

198-
private void deleteRecursivelySilently(Path dir) {
192+
private static void deleteRecursivelySilently(Path dir) {
199193
try {
200194
if (Files.exists(dir)) {
201195
MoreFiles.deleteRecursively(dir, RecursiveDeleteOption.ALLOW_INSECURE);

src/main/java/org/cryptomator/cryptofs/migration/MigrationModule.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@
2525
@Module
2626
class MigrationModule {
2727

28-
@Provides
29-
FileSystemCapabilityChecker provideFileSystemCapabilityChecker() {
30-
return new FileSystemCapabilityChecker();
31-
}
32-
3328
@Provides
3429
@IntoMap
3530
@MigratorKey(Migration.FIVE_TO_SIX)

src/main/java/org/cryptomator/cryptofs/migration/Migrators.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,10 @@ public class Migrators {
4646
private static final MigrationComponent COMPONENT = DaggerMigrationComponent.builder().csprng(strongSecureRandom()).build();
4747

4848
private final Map<Migration, Migrator> migrators;
49-
private final FileSystemCapabilityChecker fsCapabilityChecker;
5049

5150
@Inject
52-
Migrators(Map<Migration, Migrator> migrators, FileSystemCapabilityChecker fsCapabilityChecker) {
51+
Migrators(Map<Migration, Migrator> migrators) {
5352
this.migrators = migrators;
54-
this.fsCapabilityChecker = fsCapabilityChecker;
5553
}
5654

5755
private static SecureRandom strongSecureRandom() {
@@ -69,9 +67,9 @@ public static Migrators get() {
6967
/**
7068
* Inspects the vault and checks if it is supported by this library.
7169
*
72-
* @param pathToVault Path to the vault's root
70+
* @param pathToVault Path to the vault's root
7371
* @param vaultConfigFilename Name of the vault config file located in the vault
74-
* @param masterkeyFilename Name of the masterkey file optionally located in the vault
72+
* @param masterkeyFilename Name of the masterkey file optionally located in the vault
7573
* @return <code>true</code> if the vault at the given path is of an older format than supported by this library
7674
* @throws IOException if an I/O error occurs parsing the masterkey file
7775
*/
@@ -83,19 +81,19 @@ public boolean needsMigration(Path pathToVault, String vaultConfigFilename, Stri
8381
/**
8482
* Performs the actual migration. This task may take a while and this method will block.
8583
*
86-
* @param pathToVault Path to the vault's root
87-
* @param vaultConfigFilename Name of the vault config file located inside <code>pathToVault</code>
88-
* @param masterkeyFilename Name of the masterkey file located inside <code>pathToVault</code>
89-
* @param passphrase The passphrase needed to unlock the vault
90-
* @param progressListener Listener that will get notified of progress updates
84+
* @param pathToVault Path to the vault's root
85+
* @param vaultConfigFilename Name of the vault config file located inside <code>pathToVault</code>
86+
* @param masterkeyFilename Name of the masterkey file located inside <code>pathToVault</code>
87+
* @param passphrase The passphrase needed to unlock the vault
88+
* @param progressListener Listener that will get notified of progress updates
9189
* @param continuationListener Listener that will get asked if there are events that require feedback
92-
* @throws NoApplicableMigratorException If the vault can not be migrated, because no migrator could be found
93-
* @throws InvalidPassphraseException If the passphrase could not be used to unlock the vault
90+
* @throws NoApplicableMigratorException If the vault can not be migrated, because no migrator could be found
91+
* @throws InvalidPassphraseException If the passphrase could not be used to unlock the vault
9492
* @throws FileSystemCapabilityChecker.MissingCapabilityException If the underlying filesystem lacks features required to store a vault
95-
* @throws IOException if an I/O error occurs migrating the vault
93+
* @throws IOException if an I/O error occurs migrating the vault
9694
*/
9795
public void migrate(Path pathToVault, String vaultConfigFilename, String masterkeyFilename, CharSequence passphrase, MigrationProgressListener progressListener, MigrationContinuationListener continuationListener) throws NoApplicableMigratorException, CryptoException, IOException {
98-
fsCapabilityChecker.assertAllCapabilities(pathToVault);
96+
FileSystemCapabilityChecker.assertAllCapabilities(pathToVault);
9997
int vaultVersion = determineVaultVersion(pathToVault, vaultConfigFilename, masterkeyFilename);
10098
try {
10199
Migrator migrator = findApplicableMigrator(vaultVersion).orElseThrow(NoApplicableMigratorException::new);

src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void migrate(Path vaultRoot, String vaultConfigFilename, String masterkey
6666
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
6767

6868
// check file system capabilities:
69-
int filenameLengthLimit = new FileSystemCapabilityChecker().determineSupportedCiphertextFileNameLength(vaultRoot.resolve("c"), 46, 28, 220);
69+
int filenameLengthLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(vaultRoot.resolve("c"), 46, 28, 220);
7070
int pathLengthLimit = filenameLengthLimit + 48; // TODO
7171
PreMigrationVisitor preMigrationVisitor;
7272
if (filenameLengthLimit >= 220) {

src/test/java/org/cryptomator/cryptofs/CryptoFileSystemsTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public class CryptoFileSystemsTest {
4141
private final Path dataDirPath = mock(Path.class, "normalizedVaultPath/d");
4242
private final Path preContenRootPath = mock(Path.class, "normalizedVaultPath/d/AB");
4343
private final Path contenRootPath = mock(Path.class, "normalizedVaultPath/d/AB/CDEFGHIJKLMNOP");
44-
private final FileSystemCapabilityChecker capabilityChecker = mock(FileSystemCapabilityChecker.class);
4544
private final CryptoFileSystemProvider provider = mock(CryptoFileSystemProvider.class);
4645
private final CryptoFileSystemProperties properties = mock(CryptoFileSystemProperties.class);
4746
private final CryptoFileSystemComponent cryptoFileSystemComponent = mock(CryptoFileSystemComponent.class);
@@ -65,7 +64,7 @@ public class CryptoFileSystemsTest {
6564
private MockedStatic<CryptorProvider> cryptorProviderClass;
6665
private MockedStatic<BackupHelper> backupHelperClass;
6766

68-
private final CryptoFileSystems inTest = new CryptoFileSystems(cryptoFileSystemComponentFactory, capabilityChecker, csprng);
67+
private final CryptoFileSystems inTest = new CryptoFileSystems(cryptoFileSystemComponentFactory, csprng);
6968

7069
@BeforeEach
7170
public void setup() throws IOException, MasterkeyLoadingFailedException {

0 commit comments

Comments
 (0)