Skip to content

Commit 88608cc

Browse files
Merge branch 'release/2.4.3'
2 parents c5432f8 + 4137bef commit 88608cc

29 files changed

+441
-123
lines changed

pom.xml

Lines changed: 21 additions & 17 deletions
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.4.2</version>
5+
<version>2.4.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>
@@ -18,21 +18,21 @@
1818
<maven.compiler.release>17</maven.compiler.release>
1919

2020
<!-- dependencies -->
21-
<cryptolib.version>2.0.3</cryptolib.version>
22-
<jwt.version>3.19.1</jwt.version>
23-
<dagger.version>2.41</dagger.version>
21+
<cryptolib.version>2.1.0-rc1</cryptolib.version>
22+
<jwt.version>4.0.0</jwt.version>
23+
<dagger.version>2.44</dagger.version>
2424
<guava.version>31.1-jre</guava.version>
25-
<slf4j.version>1.7.36</slf4j.version>
25+
<slf4j.version>2.0.3</slf4j.version>
2626

2727
<!-- test dependencies -->
28-
<junit.jupiter.version>5.8.2</junit.jupiter.version>
29-
<mockito.version>4.4.0</mockito.version>
28+
<junit.jupiter.version>5.9.1</junit.jupiter.version>
29+
<mockito.version>4.8.0</mockito.version>
3030
<hamcrest.version>2.2</hamcrest.version>
3131

3232
<!-- build plugin dependencies -->
33-
<dependency-check.version>7.0.3</dependency-check.version>
34-
<jacoco.version>0.8.7</jacoco.version>
35-
<nexus-staging.version>1.6.12</nexus-staging.version>
33+
<dependency-check.version>7.2.1</dependency-check.version>
34+
<jacoco.version>0.8.8</jacoco.version>
35+
<nexus-staging.version>1.6.13</nexus-staging.version>
3636
</properties>
3737

3838
<licenses>
@@ -71,6 +71,12 @@
7171
<artifactId>java-jwt</artifactId>
7272
<version>${jwt.version}</version>
7373
</dependency>
74+
<dependency>
75+
<!-- fix for CVE-2022-42003, CVE-2022-42004 in java-jwt - can be removed when we update java-jwt: -->
76+
<groupId>com.fasterxml.jackson.core</groupId>
77+
<artifactId>jackson-databind</artifactId>
78+
<version>2.14.0-rc1</version>
79+
</dependency>
7480
<dependency>
7581
<groupId>com.google.dagger</groupId>
7682
<artifactId>dagger</artifactId>
@@ -96,7 +102,7 @@
96102
</dependency>
97103
<dependency>
98104
<groupId>org.mockito</groupId>
99-
<artifactId>mockito-core</artifactId>
105+
<artifactId>mockito-inline</artifactId>
100106
<version>${mockito.version}</version>
101107
<scope>test</scope>
102108
</dependency>
@@ -125,7 +131,7 @@
125131
<plugin>
126132
<groupId>org.apache.maven.plugins</groupId>
127133
<artifactId>maven-compiler-plugin</artifactId>
128-
<version>3.8.1</version>
134+
<version>3.10.1</version>
129135
<configuration>
130136
<showWarnings>true</showWarnings>
131137
<annotationProcessorPaths>
@@ -140,19 +146,17 @@
140146
<plugin>
141147
<groupId>org.apache.maven.plugins</groupId>
142148
<artifactId>maven-surefire-plugin</artifactId>
143-
<version>3.0.0-M5</version>
149+
<version>3.0.0-M7</version>
144150
<configuration>
145151
<systemPropertyVariables>
146152
<org.slf4j.simpleLogger.defaultLogLevel>ERROR</org.slf4j.simpleLogger.defaultLogLevel>
147153
</systemPropertyVariables>
148-
<!-- JPMS blocks Mockito from deep reflection unless opening the module... -->
149-
<useModulePath>false</useModulePath>
150154
</configuration>
151155
</plugin>
152156
<plugin>
153157
<groupId>org.apache.maven.plugins</groupId>
154158
<artifactId>maven-jar-plugin</artifactId>
155-
<version>3.2.0</version>
159+
<version>3.3.0</version>
156160
</plugin>
157161
<plugin>
158162
<artifactId>maven-source-plugin</artifactId>
@@ -168,7 +172,7 @@
168172
</plugin>
169173
<plugin>
170174
<artifactId>maven-javadoc-plugin</artifactId>
171-
<version>3.3.0</version>
175+
<version>3.4.1</version>
172176
<executions>
173177
<execution>
174178
<id>attach-javadocs</id>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ private FileChannel newFileChannelFromFile(CryptoPath cleartextFilePath, Effecti
378378
Files.createDirectories(ciphertextPath.getRawPath()); // suppresses FileAlreadyExists
379379
}
380380

381-
FileChannel ch = openCryptoFiles.getOrCreate(ciphertextFilePath).newFileChannel(options); // might throw FileAlreadyExists
381+
FileChannel ch = openCryptoFiles.getOrCreate(ciphertextFilePath).newFileChannel(options, attrs); // might throw FileAlreadyExists
382382
try {
383383
if (options.writable()) {
384384
ciphertextPath.persistLongFileName();

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ public class CryptoFileSystemProperties extends AbstractMap<String, Object> {
4444

4545
static final int DEFAULT_MAX_CLEARTEXT_NAME_LENGTH = LongFileNameProvider.MAX_FILENAME_BUFFER_SIZE;
4646

47+
/**
48+
* Shortening threshold for ciphertext filenames.
49+
*
50+
* @since 2.5.0
51+
*/
52+
public static final String PROPERTY_SHORTENING_THRESHOLD = "shorteningThreshold";
53+
54+
static final int DEFAULT_SHORTENING_THRESHOLD = 220;
55+
4756
/**
4857
* Key identifying the key loader used during initialization.
4958
*
@@ -105,6 +114,7 @@ private CryptoFileSystemProperties(Builder builder) {
105114
Map.entry(PROPERTY_VAULTCONFIG_FILENAME, builder.vaultConfigFilename), //
106115
Map.entry(PROPERTY_MASTERKEY_FILENAME, builder.masterkeyFilename), //
107116
Map.entry(PROPERTY_MAX_CLEARTEXT_NAME_LENGTH, builder.maxCleartextNameLength), //
117+
Map.entry(PROPERTY_SHORTENING_THRESHOLD, builder.shorteningThreshold), //
108118
Map.entry(PROPERTY_CIPHER_COMBO, builder.cipherCombo) //
109119
);
110120
}
@@ -139,6 +149,10 @@ int maxCleartextNameLength() {
139149
return (int) get(PROPERTY_MAX_CLEARTEXT_NAME_LENGTH);
140150
}
141151

152+
int shorteningThreshold() {
153+
return (int) get(PROPERTY_SHORTENING_THRESHOLD);
154+
}
155+
142156
@Override
143157
public Set<Entry<String, Object>> entrySet() {
144158
return entries;
@@ -193,6 +207,7 @@ public static class Builder {
193207
private String vaultConfigFilename = DEFAULT_VAULTCONFIG_FILENAME;
194208
private String masterkeyFilename = DEFAULT_MASTERKEY_FILENAME;
195209
private int maxCleartextNameLength = DEFAULT_MAX_CLEARTEXT_NAME_LENGTH;
210+
private int shorteningThreshold = DEFAULT_SHORTENING_THRESHOLD;
196211

197212
private Builder() {
198213
}
@@ -203,6 +218,7 @@ private Builder(Map<String, ?> properties) {
203218
checkedSet(String.class, PROPERTY_MASTERKEY_FILENAME, properties, this::withMasterkeyFilename);
204219
checkedSet(Set.class, PROPERTY_FILESYSTEM_FLAGS, properties, this::withFlags);
205220
checkedSet(Integer.class, PROPERTY_MAX_CLEARTEXT_NAME_LENGTH, properties, this::withMaxCleartextNameLength);
221+
checkedSet(Integer.class, PROPERTY_SHORTENING_THRESHOLD, properties, this::withShorteningThreshold);
206222
checkedSet(CryptorProvider.Scheme.class, PROPERTY_CIPHER_COMBO, properties, this::withCipherCombo);
207223
}
208224

@@ -231,6 +247,18 @@ public Builder withMaxCleartextNameLength(int maxCleartextNameLength) {
231247
return this;
232248
}
233249

250+
/**
251+
* Sets the shortening threshold used during vault initialization.
252+
*
253+
* @param shorteningThreshold The maximum ciphertext filename length not to be shortened
254+
* @return this
255+
* @since 2.5.0
256+
*/
257+
public Builder withShorteningThreshold(int shorteningThreshold) {
258+
this.shorteningThreshold = shorteningThreshold;
259+
return this;
260+
}
261+
234262

235263
/**
236264
* Sets the cipher combo used during vault initialization.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public static void initialize(Path pathToVault, CryptoFileSystemProperties prope
142142
throw new NotDirectoryException(pathToVault.toString());
143143
}
144144
byte[] rawKey = new byte[0];
145-
var config = VaultConfig.createNew().cipherCombo(properties.cipherCombo()).shorteningThreshold(Constants.DEFAULT_SHORTENING_THRESHOLD).build();
145+
var config = VaultConfig.createNew().cipherCombo(properties.cipherCombo()).shorteningThreshold(properties.shorteningThreshold()).build();
146146
try (Masterkey key = properties.keyLoader().loadKey(keyId);
147147
Cryptor cryptor = CryptorProvider.forScheme(config.getCipherCombo()).provide(key, strongSecureRandom())) {
148148
rawKey = key.getEncoded();

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,13 @@ public String inflate(Path c9sPath) throws IOException {
8282
public DeflatedFileName deflate(Path c9rPath) {
8383
String longFileName = c9rPath.getFileName().toString();
8484
byte[] longFileNameBytes = longFileName.getBytes(UTF_8);
85-
byte[] hash = MessageDigestSupplier.SHA1.get().digest(longFileNameBytes);
86-
String shortName = BASE64.encode(hash) + DEFLATED_FILE_SUFFIX;
87-
Path c9sPath = c9rPath.resolveSibling(shortName);
88-
longNames.put(c9sPath, longFileName);
89-
return new DeflatedFileName(c9sPath, longFileName, readonlyFlag);
85+
try (var sha1 = MessageDigestSupplier.SHA1.instance()) {
86+
byte[] hash = sha1.get().digest(longFileNameBytes);
87+
String shortName = BASE64.encode(hash) + DEFLATED_FILE_SUFFIX;
88+
Path c9sPath = c9rPath.resolveSibling(shortName);
89+
longNames.put(c9sPath, longFileName);
90+
return new DeflatedFileName(c9sPath, longFileName, readonlyFlag);
91+
}
9092
}
9193

9294
public static class DeflatedFileName {

src/main/java/org/cryptomator/cryptofs/attr/CryptoBasicFileAttributes.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ sealed class CryptoBasicFileAttributes implements BasicFileAttributes
3535
public CryptoBasicFileAttributes(BasicFileAttributes delegate, CiphertextFileType ciphertextFileType, Path ciphertextPath, Cryptor cryptor, Optional<OpenCryptoFile> openCryptoFile) {
3636
this.ciphertextFileType = ciphertextFileType;
3737
this.size = switch (ciphertextFileType) {
38-
case SYMLINK, DIRECTORY -> delegate.size();
39-
case FILE -> getPlaintextFileSize(ciphertextPath, delegate.size(), openCryptoFile, cryptor);
38+
case DIRECTORY -> delegate.size();
39+
case SYMLINK, FILE -> getPlaintextFileSize(ciphertextPath, delegate.size(), openCryptoFile, cryptor);
4040
};
4141
this.lastModifiedTime = openCryptoFile.map(OpenCryptoFile::getLastModifiedTime).orElseGet(delegate::lastModifiedTime);
4242
this.lastAccessTime = openCryptoFile.map(openFile -> FileTime.from(Instant.now())).orElseGet(delegate::lastAccessTime);

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ private Constants() {
2626
public static final String CONTENTS_FILE_NAME = "contents.c9r";
2727
public static final String INFLATED_FILE_NAME = "name.c9s";
2828

29-
public static final int DEFAULT_SHORTENING_THRESHOLD = 220;
3029
public static final int MAX_SYMLINK_LENGTH = 32767; // max path length on NTFS and FAT32: 32k-1
3130
public static final int MAX_DIR_FILE_LENGTH = 36; // UUIDv4: hex-encoded 16 byte int + 4 hyphens = 36 ASCII chars
3231
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

src/main/java/org/cryptomator/cryptofs/fh/OpenCryptoFile.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.IOException;
2222
import java.nio.channels.FileChannel;
2323
import java.nio.file.Path;
24+
import java.nio.file.attribute.FileAttribute;
2425
import java.nio.file.attribute.FileTime;
2526
import java.time.Instant;
2627
import java.util.Optional;
@@ -65,7 +66,7 @@ public OpenCryptoFile(FileCloseListener listener, ChunkCache chunkCache, Cryptor
6566
* @return A new file channel. Ideally used in a try-with-resource statement. If the channel is not properly closed, this OpenCryptoFile will stay open indefinite.
6667
* @throws IOException
6768
*/
68-
public synchronized FileChannel newFileChannel(EffectiveOpenOptions options) throws IOException {
69+
public synchronized FileChannel newFileChannel(EffectiveOpenOptions options, FileAttribute<?>... attrs) throws IOException {
6970
Path path = currentFilePath.get();
7071

7172
if (options.truncateExisting()) {
@@ -75,7 +76,7 @@ public synchronized FileChannel newFileChannel(EffectiveOpenOptions options) thr
7576
FileChannel ciphertextFileChannel = null;
7677
CleartextFileChannel cleartextFileChannel = null;
7778
try {
78-
ciphertextFileChannel = path.getFileSystem().provider().newFileChannel(path, options.createOpenOptionsForEncryptedFile());
79+
ciphertextFileChannel = path.getFileSystem().provider().newFileChannel(path, options.createOpenOptionsForEncryptedFile(), attrs);
7980
final FileHeader header;
8081
final boolean isNewHeader;
8182
if (ciphertextFileChannel.size() == 0l) {

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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,22 @@ public void check(Path pathToVault, VaultConfig config, Masterkey masterkey, Cry
6161
boolean foundDir = dirVisitor.secondLevelDirs.remove(expectedDir);
6262
if (foundDir) {
6363
iter.remove();
64-
resultCollector.accept(new HealthyDir(dirId, dirIdFile, expectedDir));
64+
if (Files.exists(expectedDir.resolve(Constants.DIR_ID_FILE))) {
65+
resultCollector.accept(new HealthyDir(dirId, dirIdFile, expectedDir));
66+
} else {
67+
resultCollector.accept(new MissingDirIdBackup(dirId, expectedDir));
68+
}
6569
}
6670
}
6771

6872
// remaining dirIds (i.e. missing dirs):
6973
dirVisitor.dirIds.forEach((dirId, dirIdFile) -> {
70-
resultCollector.accept(new MissingDirectory(dirId, dirIdFile));
74+
resultCollector.accept(new MissingContentDir(dirId, dirIdFile));
7175
});
7276

7377
// remaining folders (i.e. missing dir.c9r files):
7478
dirVisitor.secondLevelDirs.forEach(dir -> {
75-
resultCollector.accept(new OrphanDir(dir));
79+
resultCollector.accept(new OrphanContentDir(dir));
7680
});
7781
}
7882

@@ -83,6 +87,8 @@ static class DirVisitor extends SimpleFileVisitor<Path> {
8387
private final Consumer<DiagnosticResult> resultCollector;
8488
public final Map<String, Path> dirIds = new HashMap<>(); // contents of all found dir.c9r files
8589
public final Set<Path> secondLevelDirs = new HashSet<>(); // all d/2/30 dirs
90+
public final Set<Path> c9rDirsWithDirId = new HashSet<>(); // all d/2/30/abcd=.c9r dirs containing a dirId file
91+
8692

8793
public DirVisitor(Path dataDirPath, Consumer<DiagnosticResult> resultCollector) {
8894
this.dataDirPath = dataDirPath;
@@ -93,19 +99,28 @@ public DirVisitor(Path dataDirPath, Consumer<DiagnosticResult> resultCollector)
9399
@Override
94100
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
95101
if (Constants.DIR_FILE_NAME.equals(file.getFileName().toString())) {
102+
c9rDirsWithDirId.add(file.getParent());
96103
return visitDirFile(file, attrs);
97104
}
98105
return FileVisitResult.CONTINUE;
99106
}
100107

101108
private FileVisitResult visitDirFile(Path file, BasicFileAttributes attrs) throws IOException {
102109
assert Constants.DIR_FILE_NAME.equals(file.getFileName().toString());
110+
var parentDirName = file.getParent().getFileName().toString();
111+
112+
if (!(parentDirName.endsWith(Constants.CRYPTOMATOR_FILE_SUFFIX) || parentDirName.endsWith(Constants.DEFLATED_FILE_SUFFIX))) {
113+
LOG.warn("Encountered loose dir.c9r file.");
114+
resultCollector.accept(new LooseDirIdFile(file));
115+
return FileVisitResult.CONTINUE;
116+
}
117+
103118
if (attrs.size() > Constants.MAX_DIR_FILE_LENGTH) {
104119
LOG.warn("Encountered dir.c9r file of size {}", attrs.size());
105-
resultCollector.accept(new ObeseDirFile(file, attrs.size()));
120+
resultCollector.accept(new ObeseDirIdFile(file, attrs.size()));
106121
} else if (attrs.size() == 0) {
107122
LOG.warn("Empty dir.c9r file at {}.", file);
108-
resultCollector.accept(new EmptyDirFile(file));
123+
resultCollector.accept(new EmptyDirIdFile(file));
109124
} else {
110125
byte[] bytes = Files.readAllBytes(file);
111126
String dirId = new String(bytes, StandardCharsets.UTF_8);
@@ -115,6 +130,7 @@ private FileVisitResult visitDirFile(Path file, BasicFileAttributes attrs) throw
115130
resultCollector.accept(new DirIdCollision(dirId, file, otherFile));
116131
} else {
117132
dirIds.put(dirId, file);
133+
c9rDirsWithDirId.add(file);
118134
}
119135
}
120136
return FileVisitResult.SKIP_SIBLINGS;
@@ -128,6 +144,16 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
128144
}
129145
return FileVisitResult.CONTINUE;
130146
}
147+
148+
@Override
149+
public FileVisitResult postVisitDirectory(Path dir, IOException e) {
150+
var dirName = dir.getFileName().toString();
151+
if (dirName.endsWith(Constants.CRYPTOMATOR_FILE_SUFFIX) && !c9rDirsWithDirId.contains(dir)) {
152+
LOG.warn("Missing dirId file for c9r directory {}.", dir);
153+
resultCollector.accept(new MissingDirIdFile(dir));
154+
}
155+
return FileVisitResult.CONTINUE;
156+
}
131157
}
132158

133159
}

src/main/java/org/cryptomator/cryptofs/health/dirid/EmptyDirFile.java renamed to src/main/java/org/cryptomator/cryptofs/health/dirid/EmptyDirIdFile.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
*
1717
* @see org.cryptomator.cryptofs.common.Constants#ROOT_DIR_ID
1818
*/
19-
public class EmptyDirFile implements DiagnosticResult {
19+
public class EmptyDirIdFile implements DiagnosticResult {
2020

21-
final Path dirFile;
21+
final Path dirIdFile;
2222

23-
public EmptyDirFile(Path dirFile) {this.dirFile = dirFile;}
23+
public EmptyDirIdFile(Path dirIdFile) {this.dirIdFile = dirIdFile;}
2424

2525
@Override
2626
public Severity getSeverity() {
@@ -29,7 +29,7 @@ public Severity getSeverity() {
2929

3030
@Override
3131
public String toString() {
32-
return String.format("File %s is empty, expected content", dirFile);
32+
return String.format("File %s is empty, expected content", dirIdFile);
3333
}
3434

3535
/*
@@ -44,6 +44,6 @@ public void fix(Path pathToVault, VaultConfig config, Masterkey masterkey, Crypt
4444

4545
@Override
4646
public Map<String, String> details() {
47-
return Map.of(DIR_ID_FILE, dirFile.toString());
47+
return Map.of(DIR_ID_FILE, dirIdFile.toString());
4848
}
4949
}

0 commit comments

Comments
 (0)