Skip to content

Commit f279fcc

Browse files
committed
make FileSystemCapabilityChecker a static-only class
1 parent cfef709 commit f279fcc

File tree

8 files changed

+62
-86
lines changed

8 files changed

+62
-86
lines changed

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

Lines changed: 1 addition & 3 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

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 {

src/test/java/org/cryptomator/cryptofs/common/FileSystemCapabilityCheckerTest.java

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@
2020
import java.util.Collections;
2121

2222
public class FileSystemCapabilityCheckerTest {
23-
23+
2424
@Nested
2525
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
2626
public class PathLengthLimits {
27-
27+
2828
private Path pathToVault = Mockito.mock(Path.class);
2929
private Path cDir = Mockito.mock(Path.class);
3030
private Path fillerDir = Mockito.mock(Path.class);
3131
private Path nnnDir = Mockito.mock(Path.class);
3232
private FileSystem fileSystem = Mockito.mock(FileSystem.class);
3333
private FileSystemProvider fileSystemProvider = Mockito.mock(FileSystemProvider.class);
34-
34+
3535
@BeforeEach
3636
public void setup() throws IOException {
3737
Mockito.when(pathToVault.getFileSystem()).thenReturn(fileSystem);
@@ -55,15 +55,14 @@ public void testUnlimitedLength() throws IOException {
5555
String checkFileStr = invocation.getArgument(0);
5656
Path checkFileMock = Mockito.mock(Path.class, checkFileStr);
5757
Mockito.when(checkFileMock.getFileSystem()).thenReturn(fileSystem);
58-
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any()))
59-
.thenReturn(new SeekableByteChannelMock(0));
58+
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any())).thenReturn(new SeekableByteChannelMock(0));
6059
return checkFileMock;
6160
});
6261
Mockito.when(fileSystemProvider.newDirectoryStream(Mockito.eq(checkDirMock), Mockito.any())).thenReturn(DirectoryStreamMock.empty());
6362
return checkDirMock;
6463
});
6564

66-
int determinedLimit = new FileSystemCapabilityChecker().determineSupportedCiphertextFileNameLength(pathToVault);
65+
int determinedLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(pathToVault);
6766

6867
Assertions.assertEquals(220, determinedLimit);
6968
}
@@ -80,13 +79,12 @@ public void testLimitedLengthDuringDirListing() throws IOException {
8079
String checkFileStr = invocation.getArgument(0);
8180
Path checkFileMock = Mockito.mock(Path.class, checkFileStr);
8281
Mockito.when(checkFileMock.getFileSystem()).thenReturn(fileSystem);
83-
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any()))
84-
.thenReturn(new SeekableByteChannelMock(0));
82+
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any())).thenReturn(new SeekableByteChannelMock(0));
8583
return checkFileMock;
8684
});
8785
Mockito.when(fileSystemProvider.newDirectoryStream(Mockito.eq(checkDirMock), Mockito.any())).then(invocation3 -> {
8886
Iterable<Path> iterable = Mockito.mock(Iterable.class);
89-
if (Integer.valueOf(checkDirStr) > limit) {
87+
if (Integer.parseInt(checkDirStr) > limit) {
9088
Mockito.when(iterable.iterator()).thenThrow(new DirectoryIteratorException(new IOException("path too long")));
9189
} else {
9290
Mockito.when(iterable.iterator()).thenReturn(Collections.emptyIterator());
@@ -96,8 +94,8 @@ public void testLimitedLengthDuringDirListing() throws IOException {
9694
return checkDirMock;
9795
});
9896

99-
int determinedLimit = new FileSystemCapabilityChecker().determineSupportedCiphertextFileNameLength(pathToVault);
100-
97+
int determinedLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(pathToVault);
98+
10199
Assertions.assertEquals(limit, determinedLimit);
102100
}
103101

@@ -113,20 +111,18 @@ public void testLimitedLengthDuringFileCreation() throws IOException {
113111
String checkFileStr = invocation.getArgument(0);
114112
Path checkFileMock = Mockito.mock(Path.class, checkFileStr);
115113
Mockito.when(checkFileMock.getFileSystem()).thenReturn(fileSystem);
116-
if (Integer.valueOf(checkDirStr) > limit) {
117-
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any()))
118-
.thenThrow(new IOException("name too long"));
114+
if (Integer.parseInt(checkDirStr) > limit) {
115+
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any())).thenThrow(new IOException("name too long"));
119116
} else {
120-
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any()))
121-
.thenReturn(new SeekableByteChannelMock(0));
117+
Mockito.when(fileSystemProvider.newByteChannel(Mockito.eq(checkFileMock), Mockito.any())).thenReturn(new SeekableByteChannelMock(0));
122118
}
123119
return checkFileMock;
124120
});
125121
Mockito.when(fileSystemProvider.newDirectoryStream(Mockito.eq(checkDirMock), Mockito.any())).thenReturn(DirectoryStreamMock.empty());
126122
return checkDirMock;
127123
});
128124

129-
int determinedLimit = new FileSystemCapabilityChecker().determineSupportedCiphertextFileNameLength(pathToVault);
125+
int determinedLimit = FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(pathToVault);
130126

131127
Assertions.assertEquals(limit, determinedLimit);
132128
}
@@ -136,14 +132,14 @@ public void testLimitedLengthDuringFileCreation() throws IOException {
136132
@CsvSource({"220, 146", "219, 143", "218, 143", "217, 143", "216, 143", "215, 140"})
137133
public void testDetermineSupportedCleartextFileNameLength(int ciphertextLimit, int expectedCleartextLimit) throws IOException {
138134
Path path = Mockito.mock(Path.class);
139-
FileSystemCapabilityChecker checker = Mockito.spy(new FileSystemCapabilityChecker());
140-
Mockito.doReturn(ciphertextLimit).when(checker).determineSupportedCiphertextFileNameLength(path);
141-
142-
int result = checker.determineSupportedCleartextFileNameLength(path);
143-
144-
Assertions.assertEquals(expectedCleartextLimit, result);
135+
try (var staticCheckerMock = Mockito.mockStatic(FileSystemCapabilityChecker.class)) {
136+
staticCheckerMock.when(() -> FileSystemCapabilityChecker.determineSupportedCiphertextFileNameLength(path)).thenReturn(ciphertextLimit);
137+
staticCheckerMock.when(() -> FileSystemCapabilityChecker.determineSupportedCleartextFileNameLength(path)).thenCallRealMethod();
138+
int result = FileSystemCapabilityChecker.determineSupportedCleartextFileNameLength(path);
139+
Assertions.assertEquals(expectedCleartextLimit, result);
140+
}
145141
}
146-
142+
147143
}
148144

149145
}

0 commit comments

Comments
 (0)