diff --git a/src/main/java/org/cryptomator/common/vaults/Vault.java b/src/main/java/org/cryptomator/common/vaults/Vault.java index 74ac7dc40..96bf3b252 100644 --- a/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -17,9 +17,6 @@ import org.cryptomator.cryptofs.CryptoFileSystem; import org.cryptomator.cryptofs.CryptoFileSystemProperties; import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags; import org.cryptomator.cryptofs.CryptoFileSystemProvider; -import org.cryptomator.cryptofs.VaultConfig; -import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig; -import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker; import org.cryptomator.cryptolib.api.CryptoException; import org.cryptomator.cryptolib.api.MasterkeyLoader; @@ -38,8 +35,6 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; @@ -62,6 +57,7 @@ public class Vault { private final AtomicReference cryptoFileSystem; private final VaultState state; private final ObjectProperty lastKnownException; + private final VaultConfigCache configCache; private final VaultStats stats; private final StringBinding displayName; private final StringBinding displayablePath; @@ -78,8 +74,9 @@ public class Vault { private volatile Volume volume; @Inject - Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) { + Vault(VaultSettings vaultSettings, VaultConfigCache configCache, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) { this.vaultSettings = vaultSettings; + this.configCache = configCache; this.volumeProvider = volumeProvider; this.defaultMountFlags = defaultMountFlags; this.cryptoFileSystem = cryptoFileSystem; @@ -107,10 +104,10 @@ public class Vault { Set flags = EnumSet.noneOf(FileSystemFlags.class); if (vaultSettings.usesReadOnlyMode().get()) { flags.add(FileSystemFlags.READONLY); - } else if(vaultSettings.maxCleartextFilenameLength().get() == -1) { + } else if (vaultSettings.maxCleartextFilenameLength().get() == -1) { LOG.debug("Determining cleartext filename length limitations..."); var checker = new FileSystemCapabilityChecker(); - int shorteningThreshold = getUnverifiedVaultConfig().allegedShorteningThreshold(); + int shorteningThreshold = configCache.get().allegedShorteningThreshold(); int ciphertextLimit = checker.determineSupportedCiphertextFileNameLength(getPath()); if (ciphertextLimit < shorteningThreshold) { int cleartextLimit = checker.determineSupportedCleartextFileNameLength(getPath()); @@ -328,19 +325,6 @@ public class Vault { return stats; } - /** - * Attempts to read the vault config file and parse it without verifying its integrity. - * - * @return an unverified vault config - * @throws VaultConfigLoadException if the read file cannot be properly parsed - * @throws IOException if reading the file fails - * - */ - public UnverifiedVaultConfig getUnverifiedVaultConfig() throws IOException { - Path configPath = getPath().resolve(org.cryptomator.common.Constants.VAULTCONFIG_FILENAME); - String token = Files.readString(configPath, StandardCharsets.US_ASCII); - return VaultConfig.decode(token); - } public Observable[] observables() { return new Observable[]{state}; @@ -375,6 +359,10 @@ public class Vault { } } + public VaultConfigCache getVaultConfigCache() { + return configCache; + } + public void setCustomMountFlags(String mountFlags) { vaultSettings.mountFlags().set(mountFlags); } diff --git a/src/main/java/org/cryptomator/common/vaults/VaultComponent.java b/src/main/java/org/cryptomator/common/vaults/VaultComponent.java index 588ff64cd..be844f510 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultComponent.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultComponent.java @@ -25,6 +25,9 @@ public interface VaultComponent { @BindsInstance Builder vaultSettings(VaultSettings vaultSettings); + @BindsInstance + Builder vaultConfigCache(VaultConfigCache configCache); + @BindsInstance Builder initialVaultState(VaultState.Value vaultState); diff --git a/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java b/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java new file mode 100644 index 000000000..80d70ffbf --- /dev/null +++ b/src/main/java/org/cryptomator/common/vaults/VaultConfigCache.java @@ -0,0 +1,65 @@ +package org.cryptomator.common.vaults; + +import org.cryptomator.common.Constants; +import org.cryptomator.common.settings.VaultSettings; +import org.cryptomator.cryptofs.VaultConfig; +import org.cryptomator.cryptofs.VaultConfigLoadException; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Wrapper for lazy loading and on-demand reloading of the vault configuration. + */ +public class VaultConfigCache { + + private final VaultSettings settings; + private final AtomicReference config; + + VaultConfigCache(VaultSettings settings) { + this.settings = settings; + this.config = new AtomicReference<>(null); + } + + void reloadConfig() throws IOException { + try { + config.set(readConfigFromStorage(this.settings.path().get())); + } catch (IOException e) { + config.set(null); + throw e; + } + } + + public VaultConfig.UnverifiedVaultConfig get() throws IOException { + if (config.get() == null) { + reloadConfig(); + } + return config.get(); + } + + public VaultConfig.UnverifiedVaultConfig getUnchecked() { + try { + return get(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + + /** + * Attempts to read the vault config file and parse it without verifying its integrity. + * + * @throws VaultConfigLoadException if the read file cannot be properly parsed + * @throws IOException if reading the file fails + */ + static VaultConfig.UnverifiedVaultConfig readConfigFromStorage(Path vaultPath) throws IOException { + Path configPath = vaultPath.resolve(Constants.VAULTCONFIG_FILENAME); + String token = Files.readString(configPath, StandardCharsets.US_ASCII); + return VaultConfig.decode(token); + } + +} diff --git a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java index 05ea8ce07..6a8c31d4f 100644 --- a/src/main/java/org/cryptomator/common/vaults/VaultListManager.java +++ b/src/main/java/org/cryptomator/common/vaults/VaultListManager.java @@ -18,7 +18,6 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Singleton; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.io.IOException; import java.nio.file.Files; @@ -31,6 +30,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.ERROR; +import static org.cryptomator.common.vaults.VaultState.Value.LOCKED; @Singleton public class VaultListManager { @@ -96,6 +96,11 @@ public class VaultListManager { VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings); try { VaultState.Value vaultState = determineVaultState(vaultSettings.path().get()); + VaultConfigCache wrapper = new VaultConfigCache(vaultSettings); + compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config + if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state + wrapper.reloadConfig(); + } compBuilder.initialVaultState(vaultState); } catch (IOException e) { LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e); @@ -112,6 +117,9 @@ public class VaultListManager { case LOCKED, NEEDS_MIGRATION, MISSING -> { try { var determinedState = determineVaultState(vault.getPath()); + if (determinedState == LOCKED) { + vault.getVaultConfigCache().reloadConfig(); + } state.set(determinedState); yield determinedState; } catch (IOException e) { @@ -132,7 +140,9 @@ public class VaultListManager { return switch (CryptoFileSystemProvider.checkDirStructureForVault(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME)) { case VAULT -> VaultState.Value.LOCKED; case UNRELATED -> VaultState.Value.MISSING; - case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? VaultState.Value.NEEDS_MIGRATION : VaultState.Value.MISSING; + case MAYBE_LEGACY -> Migrators.get().needsMigration(pathToVault, VAULTCONFIG_FILENAME, MASTERKEY_FILENAME) ? // + VaultState.Value.NEEDS_MIGRATION // + : VaultState.Value.MISSING; }; } diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index ea0c1ed38..b8d5bbff0 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -12,7 +12,6 @@ public enum FxmlFile { ERROR("/fxml/error.fxml"), // FORGET_PASSWORD("/fxml/forget_password.fxml"), // HEALTH_START("/fxml/health_start.fxml"), // - HEALTH_START_FAIL("/fxml/health_start_fail.fxml"), // HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), // LOCK_FORCED("/fxml/lock_forced.fxml"), // LOCK_FAILED("/fxml/lock_failed.fxml"), // diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java index f78e815c6..aa69e0828 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckComponent.java @@ -16,26 +16,15 @@ import javafx.stage.Stage; @Subcomponent(modules = {HealthCheckModule.class}) public interface HealthCheckComponent { - LoadUnverifiedConfigResult loadConfig(); - @HealthCheckWindow Stage window(); @FxmlScene(FxmlFile.HEALTH_START) Lazy startScene(); - @FxmlScene(FxmlFile.HEALTH_START_FAIL) - Lazy failScene(); - default Stage showHealthCheckWindow() { Stage stage = window(); - // TODO reevaluate config loading, as soon as we have the new generic error screen - var unverifiedConf = loadConfig(); - if (unverifiedConf.config() != null) { - stage.setScene(startScene().get()); - } else { - stage.setScene(failScene().get()); - } + stage.setScene(startScene().get()); stage.show(); return stage; } @@ -52,5 +41,4 @@ public interface HealthCheckComponent { HealthCheckComponent build(); } - record LoadUnverifiedConfigResult(VaultConfig.UnverifiedVaultConfig config, Throwable error) {} } diff --git a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java index ad5ac6156..c36f486e0 100644 --- a/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java +++ b/src/main/java/org/cryptomator/ui/health/HealthCheckModule.java @@ -27,7 +27,6 @@ import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -37,18 +36,6 @@ import java.util.concurrent.atomic.AtomicReference; @Module(subcomponents = {KeyLoadingComponent.class}) abstract class HealthCheckModule { - // TODO reevaluate config loading, as soon as we have the new generic error screen - @Provides - @HealthCheckScoped - static HealthCheckComponent.LoadUnverifiedConfigResult provideLoadConfigResult(@HealthCheckWindow Vault vault) { - try { - return new HealthCheckComponent.LoadUnverifiedConfigResult(vault.getUnverifiedVaultConfig(), null); - } catch (IOException e) { - return new HealthCheckComponent.LoadUnverifiedConfigResult(null, e); - } - } - - @Provides @HealthCheckScoped static AtomicReference provideMasterkeyRef() { @@ -129,13 +116,6 @@ abstract class HealthCheckModule { return fxmlLoaders.createScene(FxmlFile.HEALTH_START); } - @Provides - @FxmlScene(FxmlFile.HEALTH_START_FAIL) - @HealthCheckScoped - static Scene provideHealthStartFailScene(@HealthCheckWindow FxmlLoaderFactory fxmlLoaders) { - return fxmlLoaders.createScene(FxmlFile.HEALTH_START_FAIL); - } - @Provides @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) @HealthCheckScoped @@ -148,11 +128,6 @@ abstract class HealthCheckModule { @FxControllerKey(StartController.class) abstract FxController bindStartController(StartController controller); - @Binds - @IntoMap - @FxControllerKey(StartFailController.class) - abstract FxController bindStartFailController(StartFailController controller); - @Binds @IntoMap @FxControllerKey(CheckListController.class) diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java index ebba001b5..0f2b1e6d3 100644 --- a/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/src/main/java/org/cryptomator/ui/health/StartController.java @@ -1,7 +1,8 @@ package org.cryptomator.ui.health; -import com.google.common.base.Preconditions; import dagger.Lazy; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultConfigCache; import org.cryptomator.cryptofs.VaultConfig; import org.cryptomator.cryptofs.VaultConfigLoadException; import org.cryptomator.cryptofs.VaultKeyInvalidException; @@ -18,8 +19,6 @@ import org.slf4j.LoggerFactory; import javax.inject.Inject; import javax.inject.Named; import javafx.application.Platform; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; import javafx.fxml.FXML; import javafx.scene.Scene; import javafx.stage.Stage; @@ -35,7 +34,7 @@ public class StartController implements FxController { private final Stage window; private final Stage unlockWindow; - private final ObjectProperty unverifiedVaultConfig; + private final VaultConfigCache vaultConfig; private final KeyLoadingStrategy keyLoadingStrategy; private final ExecutorService executor; private final AtomicReference masterkeyRef; @@ -44,11 +43,10 @@ public class StartController implements FxController { private final Lazy errorComponent; @Inject - public StartController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent, @Named("unlockWindow") Stage unlockWindow) { - Preconditions.checkNotNull(configLoadResult.config()); + public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference masterkeyRef, AtomicReference vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy checkScene, Lazy errorComponent, @Named("unlockWindow") Stage unlockWindow) { this.window = window; this.unlockWindow = unlockWindow; - this.unverifiedVaultConfig = new SimpleObjectProperty<>(configLoadResult.config()); + this.vaultConfig = vault.getVaultConfigCache(); this.keyLoadingStrategy = keyLoadingStrategy; this.executor = executor; this.masterkeyRef = masterkeyRef; @@ -71,7 +69,6 @@ public class StartController implements FxController { private void loadKey() { assert !Platform.isFxApplicationThread(); - assert unverifiedVaultConfig.get() != null; try { keyLoadingStrategy.use(this::verifyVaultConfig); } catch (VaultConfigLoadException | UnlockCancelledException e) { @@ -80,7 +77,7 @@ public class StartController implements FxController { } private void verifyVaultConfig(KeyLoadingStrategy keyLoadingStrategy) throws VaultConfigLoadException { - var unverifiedCfg = unverifiedVaultConfig.get(); + var unverifiedCfg = vaultConfig.getUnchecked(); try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) { var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); vaultConfigRef.set(verifiedCfg); diff --git a/src/main/java/org/cryptomator/ui/health/StartFailController.java b/src/main/java/org/cryptomator/ui/health/StartFailController.java deleted file mode 100644 index 826766026..000000000 --- a/src/main/java/org/cryptomator/ui/health/StartFailController.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.cryptomator.ui.health; - -import com.google.common.base.Preconditions; -import org.cryptomator.cryptofs.VaultConfigLoadException; -import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.controls.FontAwesome5Icon; - -import javax.inject.Inject; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.value.ObservableValue; -import javafx.fxml.FXML; -import javafx.scene.control.TitledPane; -import javafx.stage.Stage; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; - -// TODO reevaluate config loading, as soon as we have the new generic error screen -@HealthCheckScoped -public class StartFailController implements FxController { - - private final Stage window; - private final ObjectProperty loadError; - private final ObjectProperty moreInfoIcon; - - /* FXML */ - public TitledPane moreInfoPane; - - @Inject - public StartFailController(@HealthCheckWindow Stage window, HealthCheckComponent.LoadUnverifiedConfigResult configLoadResult) { - Preconditions.checkNotNull(configLoadResult.error()); - this.window = window; - this.loadError = new SimpleObjectProperty<>(configLoadResult.error()); - this.moreInfoIcon = new SimpleObjectProperty<>(FontAwesome5Icon.CARET_RIGHT); - } - - public void initialize() { - moreInfoPane.expandedProperty().addListener(this::setMoreInfoIcon); - } - - private void setMoreInfoIcon(ObservableValue observable, boolean wasExpanded, boolean willExpand) { - moreInfoIcon.set(willExpand ? FontAwesome5Icon.CARET_DOWN : FontAwesome5Icon.CARET_RIGHT); - } - - @FXML - public void close() { - window.close(); - } - - /* Getter & Setter */ - - public ObjectProperty moreInfoIconProperty() { - return moreInfoIcon; - } - - public FontAwesome5Icon getMoreInfoIcon() { - return moreInfoIcon.getValue(); - } - - public String getStackTrace() { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - loadError.get().printStackTrace(new PrintStream(baos)); - return baos.toString(StandardCharsets.UTF_8); - } - - public String getLocalizedErrorMessage() { - return loadError.get().getLocalizedMessage(); - } - - public boolean isParseException() { - return loadError.get() instanceof VaultConfigLoadException; - } - - public boolean isIoException() { - return !isParseException(); - } - -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java index f7eb8922f..bff757b1a 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/KeyLoadingModule.java @@ -3,7 +3,6 @@ package org.cryptomator.ui.keyloading; import dagger.Module; import dagger.Provides; import org.cryptomator.common.vaults.Vault; -import org.cryptomator.cryptofs.VaultConfig.UnverifiedVaultConfig; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.FxmlLoaderFactory; @@ -11,9 +10,7 @@ import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingModule; import javax.inject.Provider; import java.io.IOException; -import java.net.URI; import java.util.Map; -import java.util.Optional; import java.util.ResourceBundle; @Module(includes = {MasterkeyFileLoadingModule.class}) @@ -31,7 +28,7 @@ abstract class KeyLoadingModule { @KeyLoadingScoped static KeyLoadingStrategy provideKeyLoaderProvider(@KeyLoading Vault vault, Map> strategies) { try { - String scheme = vault.getUnverifiedVaultConfig().getKeyId().getScheme(); + String scheme = vault.getVaultConfigCache().get().getKeyId().getScheme(); var fallback = KeyLoadingStrategy.failed(new IllegalArgumentException("Unsupported key id " + scheme)); return strategies.getOrDefault(scheme, () -> fallback).get(); } catch (IOException e) { diff --git a/src/main/resources/fxml/health_start_fail.fxml b/src/main/resources/fxml/health_start_fail.fxml deleted file mode 100644 index 9a35e4788..000000000 --- a/src/main/resources/fxml/health_start_fail.fxml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - -