Browse Source

Improved migration from vault version 3 to 4.

Sebastian Stenzel 8 years ago
parent
commit
69e133d561

+ 1 - 1
main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java

@@ -161,7 +161,7 @@ public class WelcomeController extends LocalizedFXMLViewController {
 
 	@FXML
 	public void didClickUpdateLink(ActionEvent event) {
-		app.getHostServices().showDocument("https://cryptomator.org/#download");
+		app.getHostServices().showDocument("https://cryptomator.org/");
 	}
 
 }

+ 52 - 4
main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java

@@ -1,12 +1,15 @@
 package org.cryptomator.ui.model;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.nio.file.FileVisitResult;
 import java.nio.file.FileVisitor;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -14,22 +17,40 @@ import javax.inject.Inject;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
+import org.apache.commons.codec.binary.Base32;
+import org.apache.commons.codec.binary.BaseNCodec;
+import org.apache.commons.lang3.StringUtils;
 import org.cryptomator.crypto.engine.Cryptor;
 import org.cryptomator.filesystem.crypto.Constants;
 import org.cryptomator.ui.settings.Localization;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * Contains the collective knowledge of all creatures who were alive during the development of vault format 3.
+ * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these.
+ */
 @Singleton
 class UpgradeVersion3to4 extends UpgradeStrategy {
 
 	private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3to4.class);
 	private static final Pattern BASE32_FOLLOWED_BY_UNDERSCORE_PATTERN = Pattern.compile("^(([A-Z2-7]{8})*[A-Z2-7=]{8})_");
 	private static final int FILE_MIN_SIZE = 88; // vault version 3 files have a header of 88 bytes (assuming no chunks at all)
+	private static final String LONG_FILENAME_SUFFIX = ".lng";
+	private static final String OLD_FOLDER_SUFFIX = "_";
+	private static final String NEW_FOLDER_PREFIX = "0";
+
+	private final MessageDigest sha1;
+	private final BaseNCodec base32 = new Base32();
 
 	@Inject
 	public UpgradeVersion3to4(Provider<Cryptor> cryptorProvider, Localization localization) {
 		super(cryptorProvider, localization);
+		try {
+			sha1 = MessageDigest.getInstance("SHA-1");
+		} catch (NoSuchAlgorithmException e) {
+			throw new AssertionError("SHA-1 exists in every JVM");
+		}
 	}
 
 	@Override
@@ -40,6 +61,7 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
 	@Override
 	protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException {
 		Path dataDir = vault.path().get().resolve("d");
+		Path metadataDir = vault.path().get().resolve("m");
 		if (!Files.isDirectory(dataDir)) {
 			return; // empty vault. no migration needed.
 		}
@@ -53,7 +75,12 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
 
 				@Override
 				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-					migrate(file, attrs);
+					String name = file.getFileName().toString();
+					if (name.endsWith(LONG_FILENAME_SUFFIX)) {
+						migrateLong(metadataDir, file);
+					} else {
+						migrate(file, attrs);
+					}
 					return FileVisitResult.CONTINUE;
 				}
 
@@ -82,7 +109,7 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
 		if (m.find(0) && size < FILE_MIN_SIZE) {
 			String base32 = m.group(1);
 			String suffix = name.substring(m.end());
-			String renamed = "0" + base32 + (suffix.isEmpty() ? "" : " " + suffix);
+			String renamed = NEW_FOLDER_PREFIX + base32 + (suffix.isEmpty() ? "" : " " + suffix);
 			renameWithoutOverwriting(file, renamed);
 		}
 	}
@@ -96,12 +123,33 @@ class UpgradeVersion3to4 extends UpgradeStrategy {
 		LOG.info("Renaming {} to {}", path, newPath.getFileName());
 	}
 
+	private void migrateLong(Path metadataDir, Path path) throws IOException {
+		String oldName = path.getFileName().toString();
+		Path oldMetadataFile = metadataDir.resolve(oldName.substring(0, 2)).resolve(oldName.substring(2, 4)).resolve(oldName);
+		if (Files.isRegularFile(oldMetadataFile)) {
+			String oldContent = new String(Files.readAllBytes(oldMetadataFile), UTF_8);
+			if (oldContent.endsWith(OLD_FOLDER_SUFFIX)) {
+				String newContent = NEW_FOLDER_PREFIX + StringUtils.removeEnd(oldContent, OLD_FOLDER_SUFFIX);
+				String newName = base32.encodeAsString(sha1.digest(newContent.getBytes(UTF_8))) + LONG_FILENAME_SUFFIX;
+				Path newPath = path.resolveSibling(newName);
+				Path newMetadataFile = metadataDir.resolve(newName.substring(0, 2)).resolve(newName.substring(2, 4)).resolve(newName);
+				Files.move(path, newPath);
+				Files.createDirectories(newMetadataFile.getParent());
+				Files.write(newMetadataFile, newContent.getBytes(UTF_8));
+				Files.delete(oldMetadataFile);
+				LOG.info("Renaming {} to {}\nDeleting {}\nCreating {}", path, newName, oldMetadataFile, newMetadataFile);
+			}
+		} else {
+			LOG.warn("Found uninflatable long file name. Expected: {}", oldMetadataFile);
+		}
+	}
+
 	@Override
 	public boolean isApplicable(Vault vault) {
 		final Path masterkeyFile = vault.path().getValue().resolve(Constants.MASTERKEY_FILENAME);
 		try {
 			if (Files.isRegularFile(masterkeyFile)) {
-				final String keyContents = new String(Files.readAllBytes(masterkeyFile), StandardCharsets.UTF_8);
+				final String keyContents = new String(Files.readAllBytes(masterkeyFile), UTF_8);
 				return keyContents.contains("\"version\":3") || keyContents.contains("\"version\": 3");
 			} else {
 				LOG.warn("Not a file: {}", masterkeyFile);

+ 4 - 4
main/ui/src/main/resources/fxml/welcome.fxml

@@ -20,18 +20,18 @@
 
 <VBox prefWidth="400.0" prefHeight="400.0" spacing="24.0" alignment="CENTER" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
 	
-	<VBox fx:id="checkForUpdatesContainer" spacing="6.0" alignment="CENTER" cacheShape="true" cache="true" prefHeight="50.0">
+	<VBox fx:id="checkForUpdatesContainer" spacing="6.0" alignment="CENTER" cacheShape="true" cache="true" prefHeight="64.0">
 		<HBox alignment="CENTER" spacing="5.0" cacheShape="true" cache="true">
 			<Label fx:id="checkForUpdatesStatus" cacheShape="true" cache="true" />
 			<ProgressIndicator fx:id="checkForUpdatesIndicator" progress="-1" prefWidth="15.0" prefHeight="15.0" cacheShape="true" cache="true" cacheHint="SPEED" />
 		</HBox>
-		<Hyperlink alignment="CENTER" fx:id="updateLink" onAction="#didClickUpdateLink" cacheShape="true" cache="true" disable="true" />
+		<Hyperlink wrapText="true" textAlignment="CENTER" fx:id="updateLink" onAction="#didClickUpdateLink" cacheShape="true" cache="true" disable="true" />
 	</VBox>
 	
-	<ImageView fitHeight="200.0" preserveRatio="true" smooth="false" cache="true" style="-fx-background-color: green;">
+	<ImageView fitHeight="200.0" preserveRatio="true" smooth="true" cache="true" style="-fx-background-color: green;">
 		<Image url="/bot_welcome.png"/>
 	</ImageView>
 	
-	<VBox prefHeight="50.0"/>
+	<VBox prefHeight="64.0"/>
 
 </VBox>

+ 1 - 1
main/ui/src/main/resources/localization/de.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Tresor erstellen
 main.addDirectory.contextMenu.open = Tresor öffnen
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Prüfe auf Updates...
-welcome.newVersionMessage = Version %1$s kann heruntergeladen werden. Momentane Version %2$s.
+welcome.newVersionMessage = Version %1$s kann heruntergeladen werden.\nMomentane Version %2$s.
 # initialize.fxml
 initialize.label.password = Passwort
 initialize.label.retypePassword = Passwort bestätigen

+ 1 - 1
main/ui/src/main/resources/localization/en.txt

@@ -19,7 +19,7 @@ main.directoryList.remove.confirmation.content=The vault will only be removed fr
 
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking=Checking for Updates...
-welcome.newVersionMessage=Version %1$s can be downloaded. This is %2$s.
+welcome.newVersionMessage=Version %1$s can be downloaded.\nThis is %2$s.
 
 # initialize.fxml
 initialize.label.password=Password

+ 1 - 1
main/ui/src/main/resources/localization/es.txt

@@ -8,7 +8,7 @@ main.addDirectory.contextMenu.new = Crear una nueva caja fuerte
 main.addDirectory.contextMenu.open = Abrir una caja fuerte existente
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Chequando por actualizaciónes...
-welcome.newVersionMessage = Se puede bajar version %1$s. Este es %2$s.
+welcome.newVersionMessage = Se puede bajar version %1$s.\nEste es %2$s.
 # initialize.fxml
 initialize.label.password = Contraseña
 initialize.label.retypePassword = Reintroduzca contraseña

+ 1 - 1
main/ui/src/main/resources/localization/fr.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Créer un nouveau coffre
 main.addDirectory.contextMenu.open = Ouvrir un coffre existant
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Recherche de mise à jour...
-welcome.newVersionMessage = La version %1$s peut-être téléchargée. Il s'agit de %2$s.
+welcome.newVersionMessage = La version %1$s peut-être téléchargée.\nIl s'agit de %2$s.
 # initialize.fxml
 initialize.label.password = Mot de passe
 initialize.label.retypePassword = Confirmation

+ 1 - 1
main/ui/src/main/resources/localization/hu.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Új széf létrehozása
 main.addDirectory.contextMenu.open = Létező széf megnyitása
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Frissítések keresése...
-welcome.newVersionMessage = Új verzió érhető el\: %1$s. Jelenlegi verzió\: %2$s.
+welcome.newVersionMessage = Új verzió érhető el\: %1$s.\nJelenlegi verzió\: %2$s.
 # initialize.fxml
 initialize.label.password = Jelszó
 initialize.label.retypePassword = Jelszó ismét

+ 1 - 1
main/ui/src/main/resources/localization/it.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Crea un nuovo vault
 main.addDirectory.contextMenu.open = Apri un vault
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Verifica aggiornamenti...
-welcome.newVersionMessage = La versione %1$s può essere scaricata. Questa è %2$s
+welcome.newVersionMessage = La versione %1$s può essere scaricata.\nQuesta è %2$s
 # initialize.fxml
 initialize.label.password = Password
 initialize.label.retypePassword = Conferma password

+ 1 - 1
main/ui/src/main/resources/localization/kr.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = 새 보관함 만들기
 main.addDirectory.contextMenu.open = 기존 보관함 열기
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = 업데이트 확인 중...
-welcome.newVersionMessage = %1$s 버전이 새로 다운로드 가능합니다. 지금 버전은 %2$s 입니다.
+welcome.newVersionMessage = %1$s 버전이 새로 다운로드 가능합니다.\n지금 버전은 %2$s 입니다.
 # initialize.fxml
 initialize.label.password = 비밀번호
 initialize.label.retypePassword = 비밀번호 재입력

+ 1 - 1
main/ui/src/main/resources/localization/nl.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Creeer nieuwe kluis
 main.addDirectory.contextMenu.open = Open bestaande kluis
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Controleren op Updates...
-welcome.newVersionMessage = Versie %1$s kan worden gedownload. Dit is %2$s.
+welcome.newVersionMessage = Versie %1$s kan worden gedownload.\nDit is %2$s.
 # initialize.fxml
 initialize.label.password = Wachtwoord
 initialize.label.retypePassword = Voer wachtwoord opnieuw in

+ 1 - 1
main/ui/src/main/resources/localization/ru.txt

@@ -8,7 +8,7 @@ main.addDirectory.contextMenu.open = Открыть имеющееся хран
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Проверка обновлений...
 # Does the first %s mean the new version number, and the second %s - the current version user has?
-welcome.newVersionMessage = Доступна версия %1$s. У вас версия %2$s.
+welcome.newVersionMessage = Доступна версия %1$s.\nУ вас версия %2$s.
 # initialize.fxml
 initialize.label.password = Пароль
 initialize.label.retypePassword = Введите пароль ещё раз

+ 1 - 1
main/ui/src/main/resources/localization/sk.txt

@@ -10,7 +10,7 @@ main.addDirectory.contextMenu.new = Vytvoriť nový trezor
 main.addDirectory.contextMenu.open = Otvoriť existujúci trezor
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Kontrolujú sa aktualizácie...
-welcome.newVersionMessage = Verzia %1$s je pripravená na stiahnutie. Toto je verzia %2$s.
+welcome.newVersionMessage = Verzia %1$s je pripravená na stiahnutie.\nToto je verzia %2$s.
 # initialize.fxml
 initialize.label.password = Heslo
 initialize.label.retypePassword = Zadajte heslo znova

+ 1 - 1
main/ui/src/main/resources/localization/tr.txt

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Yeni bir kasa yarat
 main.addDirectory.contextMenu.open = Var olan kasayı aç
 # welcome.fxml
 welcome.checkForUpdates.label.currentlyChecking = Güncellemeler kontrol ediliyor...
-welcome.newVersionMessage = Sürüm %1$s indirilebilir. Şu anki sürüm\: %2$s
+welcome.newVersionMessage = Sürüm %1$s indirilebilir.\nŞu anki sürüm\: %2$s
 # initialize.fxml
 initialize.label.password = Şifre
 initialize.label.retypePassword = Şifre (tekrar)