Skip to content

Commit 38062dd

Browse files
authored
Merge pull request #268 from cryptomator/feature/refactor-filesystem-checker
Feature: Refactor filesystem checker
2 parents c905804 + f279fcc commit 38062dd

File tree

8 files changed

+63
-105
lines changed

8 files changed

+63
-105
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/FileSystemCapabilityChecker.java

Lines changed: 16 additions & 19 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,8 +15,7 @@
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);
2421

@@ -38,8 +35,8 @@ public enum Capability {
3835
WRITE_ACCESS,
3936
}
4037

41-
@Inject
42-
public FileSystemCapabilityChecker() {
38+
private FileSystemCapabilityChecker() {
39+
4340
}
4441

4542
/**
@@ -50,7 +47,7 @@ public FileSystemCapabilityChecker() {
5047
* @implNote Only short-running tests with constant time are performed
5148
* @since 1.9.2
5249
*/
53-
public void assertAllCapabilities(Path pathToVault) throws MissingCapabilityException {
50+
public static void assertAllCapabilities(Path pathToVault) throws MissingCapabilityException {
5451
assertReadAccess(pathToVault);
5552
assertWriteAccess(pathToVault);
5653
}
@@ -62,7 +59,7 @@ public void assertAllCapabilities(Path pathToVault) throws MissingCapabilityExce
6259
* @throws MissingCapabilityException if the check fails
6360
* @since 1.9.3
6461
*/
65-
public void assertReadAccess(Path pathToVault) throws MissingCapabilityException {
62+
public static void assertReadAccess(Path pathToVault) throws MissingCapabilityException {
6663
try (DirectoryStream<Path> ds = Files.newDirectoryStream(pathToVault)) {
6764
assert ds != null;
6865
} catch (IOException e) {
@@ -77,7 +74,7 @@ public void assertReadAccess(Path pathToVault) throws MissingCapabilityException
7774
* @throws MissingCapabilityException if the check fails
7875
* @since 1.9.3
7976
*/
80-
public void assertWriteAccess(Path pathToVault) throws MissingCapabilityException {
77+
public static void assertWriteAccess(Path pathToVault) throws MissingCapabilityException {
8178
Path checkDir = pathToVault.resolve("c");
8279
try {
8380
Files.createDirectories(checkDir);
@@ -90,7 +87,7 @@ public void assertWriteAccess(Path pathToVault) throws MissingCapabilityExceptio
9087
}
9188
}
9289

93-
public int determineSupportedCleartextFileNameLength(Path pathToVault) throws IOException {
90+
public static int determineSupportedCleartextFileNameLength(Path pathToVault) throws IOException {
9491
int maxCiphertextLen = determineSupportedCiphertextFileNameLength(pathToVault);
9592
assert maxCiphertextLen >= Constants.MIN_CIPHER_NAME_LENGTH;
9693
// math explained in https://github.com/cryptomator/cryptofs/issues/60#issuecomment-523238303;
@@ -105,22 +102,22 @@ public int determineSupportedCleartextFileNameLength(Path pathToVault) throws IO
105102
* @return Number of chars a .c9r file is allowed to have
106103
* @throws IOException If unable to perform this check
107104
*/
108-
public int determineSupportedCiphertextFileNameLength(Path pathToVault) throws IOException {
105+
public static int determineSupportedCiphertextFileNameLength(Path pathToVault) throws IOException {
109106
int subPathLength = Constants.MAX_ADDITIONAL_PATH_LENGTH - 2; // subtract "c/"
110107
return determineSupportedCiphertextFileNameLength(pathToVault.resolve("c"), subPathLength, Constants.MIN_CIPHER_NAME_LENGTH, Constants.MAX_CIPHER_NAME_LENGTH);
111108
}
112109

113110
/**
114111
* Determines the number of chars a filename is allowed to have inside of subdirectories of <code>dir</code> by running an experiment.
115112
*
116-
* @param dir Path to a directory where to conduct the experiment (e.g. <code>/path/to/vault/c</code>)
117-
* @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
118115
* @param minFileNameLength The minimum filename length to check
119116
* @param maxFileNameLength The maximum filename length to check
120117
* @return The supported filename length inside a subdirectory of <code>dir</code> with <code>subPathLength</code> chars
121118
* @throws IOException If unable to perform this check
122119
*/
123-
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 {
124121
Preconditions.checkArgument(subPathLength >= 6, "subPathLength must be larger than charcount(a/nnn/)");
125122
Preconditions.checkArgument(minFileNameLength > 0);
126123
Preconditions.checkArgument(maxFileNameLength <= 999);
@@ -141,7 +138,7 @@ public int determineSupportedCiphertextFileNameLength(Path dir, int subPathLengt
141138
}
142139
}
143140

144-
private int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundIncl, int upperBoundExcl) {
141+
private static int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundIncl, int upperBoundExcl) {
145142
assert lowerBoundIncl < upperBoundExcl;
146143
int mid = (lowerBoundIncl + upperBoundExcl) / 2;
147144
assert mid < upperBoundExcl;
@@ -156,7 +153,7 @@ private int determineSupportedCiphertextFileNameLength(Path p, int lowerBoundInc
156153
}
157154
}
158155

159-
private boolean canHandleFileNameLength(Path parent, int nameLength) {
156+
private static boolean canHandleFileNameLength(Path parent, int nameLength) {
160157
Path checkDir = parent.resolve(String.format("%03d", nameLength));
161158
Path checkFile = checkDir.resolve(Strings.repeat("a", nameLength));
162159
try {
@@ -175,7 +172,7 @@ private boolean canHandleFileNameLength(Path parent, int nameLength) {
175172
}
176173
}
177174

178-
private boolean canListDir(Path dir) {
175+
private static boolean canListDir(Path dir) {
179176
try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
180177
ds.iterator().hasNext(); // throws DirectoryIteratorException on Windows if child path too long
181178
return true;
@@ -184,15 +181,15 @@ private boolean canListDir(Path dir) {
184181
}
185182
}
186183

187-
private void deleteSilently(Path path) {
184+
private static void deleteSilently(Path path) {
188185
try {
189186
Files.delete(path);
190187
} catch (IOException e) {
191188
LOG.trace("Failed to delete " + path, e);
192189
}
193190
}
194191

195-
private void deleteRecursivelySilently(Path dir) {
192+
private static void deleteRecursivelySilently(Path dir) {
196193
try {
197194
if (Files.exists(dir)) {
198195
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)