Skip to content

Commit

Permalink
Merge pull request #139 from cryptomator/feature/fix-missing-content-dir
Browse files Browse the repository at this point in the history
Implement fix for missing content dir
  • Loading branch information
infeo committed Jul 28, 2022
2 parents aa310aa + 14f5c96 commit 25e5dcf
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void check(Path pathToVault, VaultConfig config, Masterkey masterkey, Cry

// remaining dirIds (i.e. missing dirs):
dirVisitor.dirIds.forEach((dirId, dirIdFile) -> {
resultCollector.accept(new MissingDirectory(dirId, dirIdFile));
resultCollector.accept(new MissingContentDir(dirId, dirIdFile));
});

// remaining folders (i.e. missing dir.c9r files):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.cryptomator.cryptofs.health.dirid;

import org.cryptomator.cryptofs.CryptoPathMapper;
import org.cryptomator.cryptofs.DirectoryIdBackup;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.health.api.DiagnosticResult;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.Masterkey;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;

import static org.cryptomator.cryptofs.health.api.CommonDetailKeys.DIR_ID;
import static org.cryptomator.cryptofs.health.api.CommonDetailKeys.DIR_ID_FILE;

/**
* Valid dir.c9r file, nonexisting content dir
*/
public class MissingContentDir implements DiagnosticResult {

final String dirId;
final Path dirIdFile;

MissingContentDir(String dirId, Path dirIdFile) {
this.dirId = dirId;
this.dirIdFile = dirIdFile;
}

@Override
public Severity getSeverity() {
return Severity.WARN;
}

@Override
public String toString() {
return String.format("dir.c9r file (%s) points to non-existing directory.", dirIdFile);
}

@Override
public Map<String, String> details() {
return Map.of(DIR_ID, dirId, //
DIR_ID_FILE, dirIdFile.toString());
}

@Override
public void fix(Path pathToVault, VaultConfig config, Masterkey masterkey, Cryptor cryptor) throws IOException {
var dirIdHash = cryptor.fileNameCryptor().hashDirectoryId(dirId);
Path dirPath = pathToVault.resolve(Constants.DATA_DIR_NAME).resolve(dirIdHash.substring(0, 2)).resolve(dirIdHash.substring(2, 30));
Files.createDirectories(dirPath);
createDirIdBackupFile(cryptor, new CryptoPathMapper.CiphertextDirectory(dirId, dirPath));
}

//visible for testing
void createDirIdBackupFile(Cryptor cryptor, CryptoPathMapper.CiphertextDirectory cipherDirObj) throws IOException {
new DirectoryIdBackup(cryptor).execute(cipherDirObj);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.cryptomator.cryptofs.health.dirid;

import org.cryptomator.cryptofs.CryptoPathMapper;
import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.FileNameCryptor;
import org.cryptomator.cryptolib.api.Masterkey;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;

public class MissingContentDirTest {

@TempDir
public Path pathToVault;

private MissingContentDir result;
private String dirId;
private Cryptor cryptor;
private FileNameCryptor fileNameCryptor;

@BeforeEach
public void init() {
Path p = Mockito.mock(Path.class, "ignored");
dirId = "1234-456789-1234";
result = new MissingContentDir(dirId, p);

cryptor = Mockito.mock(Cryptor.class);
fileNameCryptor = Mockito.mock(FileNameCryptor.class);
Mockito.doReturn(fileNameCryptor).when(cryptor).fileNameCryptor();
Mockito.doReturn(fileNameCryptor).when(cryptor).fileNameCryptor();
}

@DisplayName("After fix the content dir including dirId file exists ")
@Test
public void testFix() throws IOException {
var dirIdHash = "ridiculous-30-char-pseudo-hash";
Mockito.doReturn(dirIdHash).when(fileNameCryptor).hashDirectoryId(dirId);
var resultSpy = Mockito.spy(result);
Mockito.doNothing().when(resultSpy).createDirIdBackupFile(Mockito.any(), Mockito.any());

resultSpy.fix(pathToVault, Mockito.mock(VaultConfig.class), Mockito.mock(Masterkey.class), cryptor);

var expectedPath = pathToVault.resolve("d/ri/diculous-30-char-pseudo-hash");
ArgumentMatcher<CryptoPathMapper.CiphertextDirectory> cipherDirMatcher = obj -> obj.dirId.equals(dirId) && obj.path.endsWith(expectedPath);
Mockito.verify(resultSpy, Mockito.times(1)).createDirIdBackupFile(Mockito.eq(cryptor), Mockito.argThat(cipherDirMatcher));
var attr = Assertions.assertDoesNotThrow(() -> Files.readAttributes(expectedPath, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS));
Assertions.assertTrue(attr.isDirectory());
}

@DisplayName("If dirId file creation fails, fix fails ")
@Test
public void testFixFailsOnFailingDirIdFile() throws IOException {
var dirIdHash = "ridiculous-30-char-pseudo-hash";
Mockito.doReturn(dirIdHash).when(fileNameCryptor).hashDirectoryId(dirId);
var resultSpy = Mockito.spy(result);
Mockito.doThrow(new IOException("Access denied")).when(resultSpy).createDirIdBackupFile(Mockito.any(), Mockito.any());

Assertions.assertThrows(IOException.class, () -> resultSpy.fix(pathToVault, Mockito.mock(VaultConfig.class), Mockito.mock(Masterkey.class), cryptor));
}
}

0 comments on commit 25e5dcf

Please sign in to comment.