Skip to content

Commit 3aac52a

Browse files
Armin Schrenkoverheadhunter
andauthored
Feature/type check (#109)
adds new health check to the health service: CiphertextFileTypeCheck. * checks directories ending with c9r or c9s * tries to determine their cleartext type by checking for existence of type files (dir.c9r, symlink.c9r and contents.c9r) * produces three results: KnownType, AmbiguousType and UnknownType. * exists only one, valid type file, a KnownType is returned * exist at least two valid type files, an AmbiguousType is returned * is no valid type file present, an UnknownType is returned. * valid type files for c9r dirs are dir.c9r and symlink.c9r. * valid type files for c9s are the same as c9r with additionally contents.c9r. Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com> Co-authored-by: Sebastian Stenzel <sebastian.stenzel@gmail.com>
1 parent d41b4ac commit 3aac52a

File tree

8 files changed

+465
-3
lines changed

8 files changed

+465
-3
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143
<version>3.0.0-M5</version>
144144
<configuration>
145145
<!-- Allow reflection for Mockito, so it can properly mock non-public classes -->
146-
<argLine>--add-opens=org.cryptomator.cryptofs/org.cryptomator.cryptofs.health.dirid=ALL-UNNAMED</argLine>
146+
<argLine>--add-opens=org.cryptomator.cryptofs/org.cryptomator.cryptofs.health.dirid=ALL-UNNAMED --add-opens=org.cryptomator.cryptofs/org.cryptomator.cryptofs.health.type=ALL-UNNAMED</argLine>
147147
</configuration>
148148
</plugin>
149149
<plugin>

src/main/java/module-info.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
2+
import org.cryptomator.cryptofs.common.CiphertextFileType;
23
import org.cryptomator.cryptofs.health.api.HealthCheck;
34
import org.cryptomator.cryptofs.health.dirid.DirIdCheck;
5+
import org.cryptomator.cryptofs.health.type.CiphertextFileTypeCheck;
46

57
import java.nio.file.spi.FileSystemProvider;
68

@@ -25,6 +27,6 @@
2527

2628
uses HealthCheck;
2729

28-
provides HealthCheck with DirIdCheck;
30+
provides HealthCheck with DirIdCheck, CiphertextFileTypeCheck;
2931
provides FileSystemProvider with CryptoFileSystemProvider;
3032
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.cryptomator.cryptofs.health.type;
2+
3+
import org.cryptomator.cryptofs.common.CiphertextFileType;
4+
import org.cryptomator.cryptofs.health.api.CommonDetailKeys;
5+
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
6+
7+
import java.nio.file.Path;
8+
import java.util.Map;
9+
import java.util.Set;
10+
11+
/**
12+
* A ciphertext dir ending with c9r or c9s but does contain more than one valid type file.
13+
*/
14+
public class AmbiguousType implements DiagnosticResult {
15+
16+
final Path cipherDir;
17+
final Set<CiphertextFileType> possibleTypes;
18+
19+
AmbiguousType(Path cipherDir, Set<CiphertextFileType> possibleTypes) {
20+
this.cipherDir = cipherDir;
21+
this.possibleTypes = possibleTypes;
22+
}
23+
24+
@Override
25+
public Severity getSeverity() {
26+
return Severity.CRITICAL;
27+
}
28+
29+
@Override
30+
public String toString() {
31+
return String.format("Node %s of ambiguous type. Possible types are: %s", cipherDir, possibleTypes);
32+
}
33+
34+
@Override
35+
public Map<String, String> details() {
36+
return Map.of(CommonDetailKeys.ENCRYPTED_PATH, cipherDir.toString(),
37+
"Possible Types", possibleTypes.toString());
38+
}
39+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package org.cryptomator.cryptofs.health.type;
2+
3+
import org.cryptomator.cryptofs.VaultConfig;
4+
import org.cryptomator.cryptofs.common.CiphertextFileType;
5+
import org.cryptomator.cryptofs.common.Constants;
6+
import org.cryptomator.cryptofs.health.api.CheckFailed;
7+
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
8+
import org.cryptomator.cryptofs.health.api.HealthCheck;
9+
import org.cryptomator.cryptolib.api.Cryptor;
10+
import org.cryptomator.cryptolib.api.Masterkey;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
13+
14+
import java.io.IOException;
15+
import java.nio.file.FileVisitResult;
16+
import java.nio.file.Files;
17+
import java.nio.file.LinkOption;
18+
import java.nio.file.Path;
19+
import java.nio.file.SimpleFileVisitor;
20+
import java.nio.file.attribute.BasicFileAttributes;
21+
import java.util.EnumSet;
22+
import java.util.Set;
23+
import java.util.function.Consumer;
24+
25+
/**
26+
* Checks for each c9r or c9s dir in the vault structure if its {@link org.cryptomator.cryptofs.common.CiphertextFileType} can be determined.
27+
* <p>
28+
* The type is based on the presence of a valid type file.
29+
* Valid type files for a c9r dir are {@value org.cryptomator.cryptofs.common.Constants#DIR_FILE_NAME} and {@value org.cryptomator.cryptofs.common.Constants#SYMLINK_FILE_NAME}.
30+
* Valid type files for a c9s dir are the ones for c9r and {@value org.cryptomator.cryptofs.common.Constants#CONTENTS_FILE_NAME}.
31+
*/
32+
public class CiphertextFileTypeCheck implements HealthCheck {
33+
34+
private static final Logger LOG = LoggerFactory.getLogger(CiphertextFileTypeCheck.class);
35+
private static final int MAX_TRAVERSAL_DEPTH = 3;
36+
37+
@Override
38+
public String name() {
39+
return "Resource Type Check";
40+
}
41+
42+
@Override
43+
public void check(Path pathToVault, VaultConfig config, Masterkey masterkey, Cryptor cryptor, Consumer<DiagnosticResult> resultCollector) {
44+
45+
// scan vault structure:
46+
var dataDirPath = pathToVault.resolve(Constants.DATA_DIR_NAME);
47+
var dirVisitor = new CiphertextFileTypeCheck.DirVisitor(resultCollector);
48+
try {
49+
Files.walkFileTree(dataDirPath, Set.of(), MAX_TRAVERSAL_DEPTH, dirVisitor);
50+
} catch (IOException e) {
51+
LOG.error("Traversal of data dir failed.", e);
52+
resultCollector.accept(new CheckFailed("Traversal of data dir failed. See log for details."));
53+
}
54+
}
55+
56+
// visible for testing
57+
static class DirVisitor extends SimpleFileVisitor<Path> {
58+
59+
private final Consumer<DiagnosticResult> resultCollector;
60+
61+
public DirVisitor(Consumer<DiagnosticResult> resultCollector) {
62+
this.resultCollector = resultCollector;
63+
}
64+
65+
@Override
66+
public FileVisitResult visitFile(Path dir, BasicFileAttributes attrs) {
67+
var name = dir.getFileName().toString();
68+
if (attrs.isDirectory() && name.endsWith(Constants.CRYPTOMATOR_FILE_SUFFIX)) {
69+
checkCiphertextType(dir, false);
70+
} else if (attrs.isDirectory() && name.endsWith(Constants.DEFLATED_FILE_SUFFIX)) {
71+
checkCiphertextType(dir, true);
72+
}
73+
return FileVisitResult.CONTINUE;
74+
}
75+
76+
// visible for testing
77+
void checkCiphertextType(Path dir, boolean checkForContentsC9r) {
78+
var types = containedCiphertextFileTypes(dir, checkForContentsC9r);
79+
resultCollector.accept(switch (types.size()) {
80+
case 0 -> new UnknownType(dir);
81+
case 1 -> new KnownType(dir, types.iterator().next());
82+
default -> new AmbiguousType(dir, types);
83+
});
84+
}
85+
86+
private Set<CiphertextFileType> containedCiphertextFileTypes(Path dir, boolean checkForContentsC9r) {
87+
var result = EnumSet.noneOf(CiphertextFileType.class);
88+
if (containsDirFile(dir)) {
89+
result.add(CiphertextFileType.DIRECTORY);
90+
}
91+
if (containsSymlinkFile(dir)) {
92+
result.add(CiphertextFileType.SYMLINK);
93+
}
94+
if (checkForContentsC9r && containsContentsFile(dir)) {
95+
result.add(CiphertextFileType.FILE);
96+
}
97+
return result;
98+
}
99+
100+
private boolean containsDirFile(Path path) {
101+
var dirc9r = path.resolve(Constants.DIR_FILE_NAME);
102+
return Files.isRegularFile(dirc9r, LinkOption.NOFOLLOW_LINKS);
103+
}
104+
105+
private boolean containsSymlinkFile(Path path) {
106+
var symlinkc9r = path.resolve(Constants.SYMLINK_FILE_NAME);
107+
return Files.isRegularFile(symlinkc9r, LinkOption.NOFOLLOW_LINKS);
108+
}
109+
110+
private boolean containsContentsFile(Path path) {
111+
var contentsc9r = path.resolve(Constants.CONTENTS_FILE_NAME);
112+
return Files.isRegularFile(contentsc9r, LinkOption.NOFOLLOW_LINKS);
113+
}
114+
115+
}
116+
117+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.cryptomator.cryptofs.health.type;
2+
3+
import org.cryptomator.cryptofs.common.CiphertextFileType;
4+
import org.cryptomator.cryptofs.health.api.CommonDetailKeys;
5+
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
6+
7+
import java.nio.file.Path;
8+
import java.util.Map;
9+
10+
/**
11+
* A c9r or c9s dir containing exactly one, valid type file.
12+
*/
13+
public class KnownType implements DiagnosticResult {
14+
15+
final Path cipherDir;
16+
final CiphertextFileType type;
17+
18+
KnownType(Path ctfDirectory, CiphertextFileType type) {
19+
this.cipherDir = ctfDirectory;
20+
this.type = type;
21+
}
22+
23+
@Override
24+
public Severity getSeverity() {
25+
return Severity.GOOD;
26+
}
27+
28+
@Override
29+
public String toString() {
30+
return String.format("Node %s with determined type %s.", cipherDir, type);
31+
}
32+
33+
@Override
34+
public Map<String, String> details() {
35+
return Map.of(CommonDetailKeys.ENCRYPTED_PATH, cipherDir.toString(),
36+
"Type", type.name());
37+
}
38+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.cryptomator.cryptofs.health.type;
2+
3+
import org.cryptomator.cryptofs.health.api.CommonDetailKeys;
4+
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
5+
6+
import java.nio.file.Path;
7+
import java.util.Map;
8+
9+
/**
10+
* A ciphertext dir ending with c9r or c9s but does not contain a valid type file.
11+
*/
12+
public class UnknownType implements DiagnosticResult {
13+
14+
final Path cipherDir;
15+
16+
UnknownType(Path c9rDir) {
17+
this.cipherDir = c9rDir;
18+
}
19+
20+
@Override
21+
public Severity getSeverity() {
22+
return Severity.CRITICAL;
23+
}
24+
25+
/*
26+
TODO: remove directory? might cause data loss (for shortened files)
27+
@Override
28+
public void fix(Path pathToVault, VaultConfig config, Masterkey masterkey, Cryptor cryptor) throws IOException {
29+
DiagnosticResult.super.fix(pathToVault, config, masterkey, cryptor);
30+
}
31+
*/
32+
33+
@Override
34+
public String toString() {
35+
return String.format("Node %s of unknown type.", cipherDir);
36+
}
37+
38+
@Override
39+
public Map<String, String> details() {
40+
return Map.of(CommonDetailKeys.ENCRYPTED_PATH, cipherDir.toString());
41+
}
42+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
org.cryptomator.cryptofs.health.dirid.DirIdCheck
1+
org.cryptomator.cryptofs.health.dirid.DirIdCheck
2+
org.cryptomator.cryptofs.health.type.CiphertextFileTypeCheck

0 commit comments

Comments
 (0)