Skip to content

Commit ae19036

Browse files
committed
Merge branch 'release/2.5.3'
2 parents e1e07f3 + 4b9c122 commit ae19036

4 files changed

Lines changed: 65 additions & 10 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<modelVersion>4.0.0</modelVersion>
33
<groupId>org.cryptomator</groupId>
44
<artifactId>cryptofs</artifactId>
5-
<version>2.5.2</version>
5+
<version>2.5.3</version>
66
<name>Cryptomator Crypto Filesystem</name>
77
<description>This library provides the Java filesystem provider used by Cryptomator.</description>
88
<url>https://github.com/cryptomator/cryptofs</url>

src/main/java/org/cryptomator/cryptofs/dir/DirectoryStreamFactory.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public synchronized CryptoDirectoryStream newDirectoryStream(CryptoPath cleartex
3636
throw new ClosedFileSystemException();
3737
}
3838
CiphertextDirectory ciphertextDir = cryptoPathMapper.getCiphertextDir(cleartextDir);
39-
//TODO: use HealthCheck with warning and suggest fix to create one
4039
DirectoryStream<Path> ciphertextDirStream = Files.newDirectoryStream(ciphertextDir.path, this::matchesEncryptedContentPattern);
4140
CryptoDirectoryStream cleartextDirStream = directoryStreamComponentBuilder //
4241
.dirId(ciphertextDir.dirId) //

src/main/java/org/cryptomator/cryptofs/health/dirid/OrphanContentDir.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private void fix(Path pathToVault, VaultConfig config, Cryptor cryptor) throws I
9191
String longNameSuffix = createClearnameToBeShortened(config.getShorteningThreshold());
9292
Optional<String> dirId = retrieveDirId(orphanedDir, cryptor);
9393

94-
try (var orphanedContentStream = Files.newDirectoryStream(orphanedDir, p -> !Constants.DIR_BACKUP_FILE_NAME.equals(p.getFileName().toString()))) {
94+
try (var orphanedContentStream = Files.newDirectoryStream(orphanedDir, this::matchesEncryptedContentPattern)) {
9595
for (Path orphanedResource : orphanedContentStream) {
9696
boolean isShortened = orphanedResource.toString().endsWith(Constants.DEFLATED_FILE_SUFFIX);
9797
//@formatter:off
@@ -112,11 +112,22 @@ private void fix(Path pathToVault, VaultConfig config, Cryptor cryptor) throws I
112112
adoptOrphanedResource(orphanedResource, newClearName, isShortened, stepParentDir, cryptor.fileNameCryptor(), sha1);
113113
}
114114
}
115-
116115
Files.deleteIfExists(orphanedDir.resolve(Constants.DIR_BACKUP_FILE_NAME));
116+
try (var nonCryptomatorFiles = Files.newDirectoryStream(orphanedDir)) {
117+
for (Path p : nonCryptomatorFiles) {
118+
Files.move(p, stepParentDir.path.resolve(p.getFileName()), LinkOption.NOFOLLOW_LINKS);
119+
}
120+
}
117121
Files.delete(orphanedDir);
118122
}
119123

124+
//see also DirectoryStreamFactory
125+
private boolean matchesEncryptedContentPattern(Path path) {
126+
var tmp = path.getFileName().toString();
127+
return tmp.length() >= Constants.MIN_CIPHER_NAME_LENGTH //
128+
&& (tmp.endsWith(Constants.CRYPTOMATOR_FILE_SUFFIX) || tmp.endsWith(Constants.DEFLATED_FILE_SUFFIX));
129+
}
130+
120131
//visible for testing
121132
Path prepareRecoveryDir(Path pathToVault, FileNameCryptor cryptor) throws IOException {
122133
Path dataDir = pathToVault.resolve(Constants.DATA_DIR_NAME);

src/test/java/org/cryptomator/cryptofs/health/dirid/OrphanDirTest.java

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,8 @@ public void testFixNoDirId() throws IOException {
405405
result = new OrphanContentDir(dataDir.relativize(cipherOrphan));
406406
var resultSpy = Mockito.spy(result);
407407

408-
Path orphan1 = cipherOrphan.resolve("orphan1.c9r");
409-
Path orphan2 = cipherOrphan.resolve("orphan2.c9s");
408+
Path orphan1 = cipherOrphan.resolve("orphan1_with_at_least_26chars.c9r");
409+
Path orphan2 = cipherOrphan.resolve("orphan2_with_at_least_26chars.c9s");
410410
Files.createFile(orphan1);
411411
Files.createDirectories(orphan2);
412412

@@ -435,8 +435,8 @@ public void testFixContinuesOnNotRecoverableFilename() throws IOException {
435435
result = new OrphanContentDir(dataDir.relativize(cipherOrphan));
436436
var resultSpy = Mockito.spy(result);
437437

438-
Path orphan1 = cipherOrphan.resolve("orphan1.c9r");
439-
Path orphan2 = cipherOrphan.resolve("orphan2.c9s");
438+
Path orphan1 = cipherOrphan.resolve("orphan1_with_at_least_26chars.c9r");
439+
Path orphan2 = cipherOrphan.resolve("orphan2_with_at_least_26chars.c9s");
440440
Files.createFile(orphan1);
441441
Files.createDirectories(orphan2);
442442
Files.createFile(cipherOrphan.resolve(Constants.DIR_BACKUP_FILE_NAME));
@@ -473,9 +473,9 @@ public void testFixWithDirId() throws IOException {
473473
var resultSpy = Mockito.spy(result);
474474

475475
var lostName1 = "Brother.sibling";
476-
Path orphan1 = cipherOrphan.resolve("orphan1.c9r");
476+
Path orphan1 = cipherOrphan.resolve("orphan1_with_at_least_26chars.c9r");
477477
var lostName2 = "Sister.sibling";
478-
Path orphan2 = cipherOrphan.resolve("orphan2.c9s");
478+
Path orphan2 = cipherOrphan.resolve("orphan2_with_at_least_26chars.c9s");
479479
Files.createFile(orphan1);
480480
Files.createDirectories(orphan2);
481481
Files.createFile(cipherOrphan.resolve(Constants.DIR_BACKUP_FILE_NAME));
@@ -507,6 +507,51 @@ public void testFixWithDirId() throws IOException {
507507
Assertions.assertTrue(Files.notExists(cipherOrphan));
508508
}
509509

510+
@Test
511+
@DisplayName("fix() prepares vault, process every Cryptomator resource in orphanDir, moves non-Cryptomator resources and deletes orphanDir")
512+
public void testFixWithNonCryptomatorFiles() throws IOException {
513+
result = new OrphanContentDir(dataDir.relativize(cipherOrphan));
514+
var resultSpy = Mockito.spy(result);
515+
516+
var lostName1 = "Brother.sibling";
517+
Path orphan1 = cipherOrphan.resolve("orphan1_with_at_least_26chars.c9r");
518+
var lostName2 = "Sister.sibling";
519+
Path orphan2 = cipherOrphan.resolve("orphan2_with_at_least_26chars.c9s");
520+
Path unrelated = cipherOrphan.resolve("unrelated.file");
521+
Files.createFile(orphan1);
522+
Files.createDirectories(orphan2);
523+
Files.createFile(unrelated);
524+
Files.createFile(cipherOrphan.resolve(Constants.DIR_BACKUP_FILE_NAME));
525+
526+
var dirId = Optional.of("trololo-id");
527+
528+
CryptoPathMapper.CiphertextDirectory stepParentDir = new CryptoPathMapper.CiphertextDirectory("aaaaaa", dataDir.resolve("22/2222"));
529+
Files.createDirectories(stepParentDir.path); //needs to be created here, otherwise the Files.move(non-crypto-resource, stepparent) will fail
530+
531+
VaultConfig config = Mockito.mock(VaultConfig.class);
532+
Mockito.doReturn(170).when(config).getShorteningThreshold();
533+
Masterkey masterkey = Mockito.mock(Masterkey.class);
534+
535+
Mockito.doReturn(cipherRecovery).when(resultSpy).prepareRecoveryDir(pathToVault, fileNameCryptor);
536+
Mockito.doReturn(stepParentDir).when(resultSpy).prepareStepParent(Mockito.eq(dataDir), Mockito.eq(cipherRecovery), Mockito.eq(cryptor), Mockito.any());
537+
Mockito.doReturn(dirId).when(resultSpy).retrieveDirId(cipherOrphan, cryptor);
538+
Mockito.doReturn(lostName1).when(resultSpy).decryptFileName(Mockito.eq(orphan1), Mockito.anyBoolean(), Mockito.eq(dirId.get()), Mockito.eq(fileNameCryptor));
539+
Mockito.doReturn(lostName2).when(resultSpy).decryptFileName(Mockito.eq(orphan2), Mockito.anyBoolean(), Mockito.eq(dirId.get()), Mockito.eq(fileNameCryptor));
540+
Mockito.doAnswer(invocationOnMock -> {
541+
Path orphanedResource = invocationOnMock.getArgument(0);
542+
Files.delete(orphanedResource);
543+
return null;
544+
}).when(resultSpy).adoptOrphanedResource(Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.eq(stepParentDir), Mockito.eq(fileNameCryptor), Mockito.any());
545+
546+
resultSpy.fix(pathToVault, config, masterkey, cryptor);
547+
548+
Mockito.verify(resultSpy, Mockito.never()).adoptOrphanedResource(Mockito.eq(unrelated), Mockito.any(), Mockito.anyBoolean(), Mockito.eq(stepParentDir), Mockito.eq(fileNameCryptor), Mockito.any());
549+
Mockito.verify(resultSpy, Mockito.times(1)).adoptOrphanedResource(Mockito.eq(orphan1), Mockito.eq(lostName1), Mockito.anyBoolean(), Mockito.eq(stepParentDir), Mockito.eq(fileNameCryptor), Mockito.any());
550+
Mockito.verify(resultSpy, Mockito.times(1)).adoptOrphanedResource(Mockito.eq(orphan2), Mockito.eq(lostName2), Mockito.anyBoolean(), Mockito.eq(stepParentDir), Mockito.eq(fileNameCryptor), Mockito.any());
551+
Assertions.assertTrue(Files.exists(stepParentDir.path.resolve("unrelated.file")));
552+
Assertions.assertTrue(Files.notExists(cipherOrphan));
553+
}
554+
510555

511556
@Test
512557
@DisplayName("results with same orphan write to same cleartext stepparent")

0 commit comments

Comments
 (0)