Skip to content

Commit

Permalink
further refactor inner classes of CryptoPathMapper to own class files
Browse files Browse the repository at this point in the history
  • Loading branch information
infeo committed Oct 12, 2024
1 parent 3515794 commit aeaea9f
Show file tree
Hide file tree
Showing 23 changed files with 358 additions and 367 deletions.
14 changes: 14 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/CipherDir.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.cryptomator.cryptofs;

import java.nio.file.Path;
import java.util.Objects;

//own file due to dagger
public record CipherDir(String dirId, Path contentDirPath) {

public CipherDir(String dirId, Path contentDirPath) {
this.dirId = Objects.requireNonNull(dirId);
this.contentDirPath = Objects.requireNonNull(contentDirPath);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cryptomator.cryptofs;

import java.util.Objects;

//own file due to dagger
public record CipherNodeNameParameters(String dirId, String clearNodeName) {

public CipherNodeNameParameters(String dirId, String clearNodeName) {
this.dirId = Objects.requireNonNull(dirId);
this.clearNodeName = Objects.requireNonNull(clearNodeName);
}

}
64 changes: 64 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/ClearToCipherDirCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.cryptomator.cryptofs;

import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public class ClearToCipherDirCache {

private static final int MAX_CACHED_PATHS = 5000;
private static final Duration MAX_CACHE_AGE = Duration.ofSeconds(20);

//TODO: not testable!
private final AsyncCache<CryptoPath, CipherDir> ciphertextDirectories = Caffeine.newBuilder() //
.maximumSize(MAX_CACHED_PATHS) //
.expireAfterWrite(MAX_CACHE_AGE) //
.buildAsync();

//TODO: this a expensive operation
// with a cachesize of _n_ and comparsion cost of _x_
// runtime is n*x
void removeAllKeysWithPrefix(CryptoPath basePrefix) {
ciphertextDirectories.asMap().keySet().removeIf(p -> p.startsWith(basePrefix));
}

//TODO: this is a very expensive operation
// with a cache size of _n_ and comparsion cost of _x_
// runtime is n*(1+x)
void recomputeAllKeysWithPrefix(CryptoPath oldPrefix, CryptoPath newPrefix) {
var remappedEntries = new ArrayList<Map.Entry<CryptoPath, CompletableFuture<CipherDir>>>();
ciphertextDirectories.asMap().entrySet().removeIf(e -> {
if (e.getKey().startsWith(oldPrefix)) {
var remappedPath = newPrefix.resolve(oldPrefix.relativize(e.getKey()));
return remappedEntries.add(Map.entry(remappedPath, e.getValue()));
} else {
return false;
}
});
remappedEntries.forEach(e -> ciphertextDirectories.put(e.getKey(), e.getValue()));
}

//cheap operation: log(n)
CipherDir putIfAbsent(CryptoPath cleartextPath, CipherDirLoader ifAbsent) throws IOException {
var futureMapping = new CompletableFuture<CipherDir>();
var currentMapping = ciphertextDirectories.asMap().putIfAbsent(cleartextPath, futureMapping);
if (currentMapping != null) {
return currentMapping.join();
} else {
futureMapping.complete(ifAbsent.load());
return futureMapping.join();
}
}

@FunctionalInterface
interface CipherDirLoader {

CipherDir load() throws IOException;
}

}
21 changes: 10 additions & 11 deletions src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*******************************************************************************/
package org.cryptomator.cryptofs;

import org.cryptomator.cryptofs.CryptoPathMapper.CiphertextDirectory;
import org.cryptomator.cryptofs.attr.AttributeByNameProvider;
import org.cryptomator.cryptofs.attr.AttributeProvider;
import org.cryptomator.cryptofs.attr.AttributeViewProvider;
Expand Down Expand Up @@ -142,7 +141,7 @@ public Path getCiphertextPath(Path cleartextPath) throws IOException {
var p = CryptoPath.castAndAssertAbsolute(cleartextPath);
var nodeType = cryptoPathMapper.getCiphertextFileType(p);
if (nodeType == CiphertextFileType.DIRECTORY) {
return cryptoPathMapper.getCiphertextDir(p).path;
return cryptoPathMapper.getCiphertextDir(p).contentDirPath();
}
var cipherFile = cryptoPathMapper.getCiphertextFilePath(p);
if (nodeType == CiphertextFileType.SYMLINK) {
Expand Down Expand Up @@ -316,22 +315,22 @@ void createDirectory(CryptoPath cleartextDir, FileAttribute<?>... attrs) throws
if (cleartextParentDir == null) {
return;
}
Path ciphertextParentDir = cryptoPathMapper.getCiphertextDir(cleartextParentDir).path;
Path ciphertextParentDir = cryptoPathMapper.getCiphertextDir(cleartextParentDir).contentDirPath();
if (!Files.exists(ciphertextParentDir)) {
throw new NoSuchFileException(cleartextParentDir.toString());
}
cryptoPathMapper.assertNonExisting(cleartextDir);
CiphertextFilePath ciphertextPath = cryptoPathMapper.getCiphertextFilePath(cleartextDir);
Path ciphertextDirFile = ciphertextPath.getDirFilePath();
CiphertextDirectory ciphertextDir = cryptoPathMapper.getCiphertextDir(cleartextDir);
var ciphertextDir = cryptoPathMapper.getCiphertextDir(cleartextDir);
// atomically check for FileAlreadyExists and create otherwise:
Files.createDirectory(ciphertextPath.getRawPath());
try (FileChannel channel = FileChannel.open(ciphertextDirFile, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE), attrs)) {
channel.write(UTF_8.encode(ciphertextDir.dirId));
channel.write(UTF_8.encode(ciphertextDir.dirId()));
}
// create dir if and only if the dirFile has been created right now (not if it has been created before):
try {
Files.createDirectories(ciphertextDir.path);
Files.createDirectories(ciphertextDir.contentDirPath());
dirIdBackup.execute(ciphertextDir);
ciphertextPath.persistLongFileName();
} catch (IOException e) {
Expand Down Expand Up @@ -432,7 +431,7 @@ private void deleteFileOrSymlink(CiphertextFilePath ciphertextPath) throws IOExc
}

private void deleteDirectory(CryptoPath cleartextPath, CiphertextFilePath ciphertextPath) throws IOException {
Path ciphertextDir = cryptoPathMapper.getCiphertextDir(cleartextPath).path;
Path ciphertextDir = cryptoPathMapper.getCiphertextDir(cleartextPath).contentDirPath();
Path ciphertextDirFile = ciphertextPath.getDirFilePath();
try {
ciphertextDirDeleter.deleteCiphertextDirIncludingNonCiphertextFiles(ciphertextDir, cleartextPath);
Expand Down Expand Up @@ -505,7 +504,7 @@ private void copyDirectory(CryptoPath cleartextSource, CryptoPath cleartextTarge
ciphertextTarget.persistLongFileName();
} else if (ArrayUtils.contains(options, StandardCopyOption.REPLACE_EXISTING)) {
// keep existing (if empty):
Path ciphertextTargetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).path;
Path ciphertextTargetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).contentDirPath();
try (DirectoryStream<Path> ds = Files.newDirectoryStream(ciphertextTargetDir)) {
if (ds.iterator().hasNext()) {
throw new DirectoryNotEmptyException(cleartextTarget.toString());
Expand All @@ -515,8 +514,8 @@ private void copyDirectory(CryptoPath cleartextSource, CryptoPath cleartextTarge
throw new FileAlreadyExistsException(cleartextTarget.toString(), null, "Ciphertext file already exists: " + ciphertextTarget);
}
if (ArrayUtils.contains(options, StandardCopyOption.COPY_ATTRIBUTES)) {
Path ciphertextSourceDir = cryptoPathMapper.getCiphertextDir(cleartextSource).path;
Path ciphertextTargetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).path;
Path ciphertextSourceDir = cryptoPathMapper.getCiphertextDir(cleartextSource).contentDirPath();
Path ciphertextTargetDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).contentDirPath();
copyAttributes(ciphertextSourceDir, ciphertextTargetDir);
}
}
Expand Down Expand Up @@ -622,7 +621,7 @@ private void moveDirectory(CryptoPath cleartextSource, CryptoPath cleartextTarge
throw new AtomicMoveNotSupportedException(cleartextSource.toString(), cleartextTarget.toString(), "Replacing directories during move requires non-atomic status checks.");
}
// check if dir is empty:
Path targetCiphertextDirContentDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).path;
Path targetCiphertextDirContentDir = cryptoPathMapper.getCiphertextDir(cleartextTarget).contentDirPath();
boolean targetCiphertextDirExists = true;
try (DirectoryStream<Path> ds = Files.newDirectoryStream(targetCiphertextDirContentDir, DirectoryStreamFilters.EXCLUDE_DIR_ID_BACKUP)) {
if (ds.iterator().hasNext()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public static void initialize(Path pathToVault, CryptoFileSystemProperties prope
Path vaultCipherRootPath = pathToVault.resolve(Constants.DATA_DIR_NAME).resolve(dirHash.substring(0, 2)).resolve(dirHash.substring(2));
Files.createDirectories(vaultCipherRootPath);
// create dirId backup:
DirectoryIdBackup.backupManually(cryptor, new CryptoPathMapper.CiphertextDirectory(Constants.ROOT_DIR_ID, vaultCipherRootPath));
DirectoryIdBackup.backupManually(cryptor, new CipherDir(Constants.ROOT_DIR_ID, vaultCipherRootPath));
} finally {
Arrays.fill(rawKey, (byte) 0x00);
}
Expand Down
Loading

0 comments on commit aeaea9f

Please sign in to comment.