From 6c925d6a1fd53714f1f4fc7a47b7a9c45b1eae54 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 11 May 2026 12:12:51 +0200 Subject: [PATCH] Throw more specific error about vault directory --- .../common/vaults/VaultListManager.java | 42 ++++++++++- .../common/vaults/VaultListManagerTest.java | 72 +++++++++++++++++++ 2 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/cryptomator/common/vaults/VaultListManagerTest.java diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index e73075d0d..8cbafcd37 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -36,6 +36,7 @@ import java.util.ResourceBundle; import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; import static org.cryptomator.common.vaults.VaultState.Value.*; +import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME; @Singleton public class VaultListManager { @@ -74,9 +75,7 @@ public class VaultListManager { public Vault add(Path pathToVault) throws IOException { Path normalizedPathToVault = pathToVault.normalize().toAbsolutePath(); - if (CryptoFileSystemProvider.checkDirStructureForVault(normalizedPathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) { - throw new NoSuchFileException(normalizedPathToVault.toString(), null, "Not a vault directory"); - } + assertIsVaultDirectory(normalizedPathToVault); return get(normalizedPathToVault) // .orElseGet(() -> { @@ -86,6 +85,43 @@ public class VaultListManager { }); } + static void assertIsVaultDirectory(Path pathToVault) throws IOException { + if (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) == DirStructure.UNRELATED) { + throw new NoSuchFileException(pathToVault.toString(), null, "Not a vault directory: " + determineNotVaultDirectoryReason(pathToVault)); + } + } + + private static String determineNotVaultDirectoryReason(Path pathToVault) { + Path dataDir = pathToVault.resolve(DATA_DIR_NAME); + if (!Files.isDirectory(dataDir)) { + return describeNotDirectory(dataDir); + } + + Path vaultConfig = pathToVault.resolve(VAULTCONFIG_FILENAME); + if (!Files.isReadable(vaultConfig)) { + Path masterkey = pathToVault.resolve(MASTERKEY_FILENAME); + return describeNotReadable(vaultConfig) + "; " + describeNotReadable(masterkey) + " for legacy vault detection"; + } + + return "directory structure is unsupported"; + } + + private static String describeNotDirectory(Path path) { + if (Files.exists(path)) { + return path.getFileName() + " is not a directory"; + } else { + return path.getFileName() + " directory is missing"; + } + } + + private static String describeNotReadable(Path path) { + if (Files.exists(path)) { + return path.getFileName() + " is not readable"; + } else { + return path.getFileName() + " is missing"; + } + } + private VaultSettings newVaultSettings(Path path) { VaultSettings vaultSettings = VaultSettings.withRandomId(); vaultSettings.path.set(path); diff --git a/src/test/java/org/cryptomator/common/vaults/VaultListManagerTest.java b/src/test/java/org/cryptomator/common/vaults/VaultListManagerTest.java new file mode 100644 index 000000000..796bbff07 --- /dev/null +++ b/src/test/java/org/cryptomator/common/vaults/VaultListManagerTest.java @@ -0,0 +1,72 @@ +package org.cryptomator.common.vaults; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +import static org.cryptomator.common.Constants.MASTERKEY_FILENAME; +import static org.cryptomator.common.Constants.VAULTCONFIG_FILENAME; +import static org.cryptomator.cryptofs.common.Constants.DATA_DIR_NAME; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class VaultListManagerTest { + + @Test + void testAssertIsVaultDirectoryWhenDataDirIsMissing(@TempDir Path tmpDir) { + NoSuchFileException e = assertThrows(NoSuchFileException.class, () -> { + VaultListManager.assertIsVaultDirectory(tmpDir); + }); + + assertTrue(e.getReason().contains(DATA_DIR_NAME + " directory is missing")); + } + + @Test + void testAssertIsVaultDirectoryWhenDataDirIsFile(@TempDir Path tmpDir) throws IOException { + Files.createFile(tmpDir.resolve(DATA_DIR_NAME)); + + NoSuchFileException e = assertThrows(NoSuchFileException.class, () -> { + VaultListManager.assertIsVaultDirectory(tmpDir); + }); + + assertTrue(e.getReason().contains(DATA_DIR_NAME + " is not a directory")); + } + + @Test + void testAssertIsVaultDirectoryWhenVaultConfigAndMasterkeyAreMissing(@TempDir Path tmpDir) throws IOException { + Files.createDirectory(tmpDir.resolve(DATA_DIR_NAME)); + + NoSuchFileException e = assertThrows(NoSuchFileException.class, () -> { + VaultListManager.assertIsVaultDirectory(tmpDir); + }); + + assertTrue(e.getReason().contains(VAULTCONFIG_FILENAME + " is missing")); + assertTrue(e.getReason().contains(MASTERKEY_FILENAME + " is missing")); + } + + @Test + void testAssertIsVaultDirectoryAcceptsModernVault(@TempDir Path tmpDir) throws IOException { + Files.createDirectory(tmpDir.resolve(DATA_DIR_NAME)); + Files.createFile(tmpDir.resolve(VAULTCONFIG_FILENAME)); + + assertDoesNotThrow(() -> { + VaultListManager.assertIsVaultDirectory(tmpDir); + }); + } + + @Test + void testAssertIsVaultDirectoryAcceptsLegacyVaultCandidate(@TempDir Path tmpDir) throws IOException { + Files.createDirectory(tmpDir.resolve(DATA_DIR_NAME)); + Files.createFile(tmpDir.resolve(MASTERKEY_FILENAME)); + + assertDoesNotThrow(() -> { + VaultListManager.assertIsVaultDirectory(tmpDir); + }); + } + +}