From 69e133d5611aa3a96251ca9670530dbc2edcae4e Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 11 Jul 2016 18:07:55 +0200 Subject: [PATCH] Improved migration from vault version 3 to 4. --- .../ui/controllers/WelcomeController.java | 2 +- .../ui/model/UpgradeVersion3to4.java | 56 +++++++++++++++++-- main/ui/src/main/resources/fxml/welcome.fxml | 8 +-- .../ui/src/main/resources/localization/de.txt | 2 +- .../ui/src/main/resources/localization/en.txt | 2 +- .../ui/src/main/resources/localization/es.txt | 2 +- .../ui/src/main/resources/localization/fr.txt | 2 +- .../ui/src/main/resources/localization/hu.txt | 2 +- .../ui/src/main/resources/localization/it.txt | 2 +- .../ui/src/main/resources/localization/kr.txt | 2 +- .../ui/src/main/resources/localization/nl.txt | 2 +- .../ui/src/main/resources/localization/ru.txt | 2 +- .../ui/src/main/resources/localization/sk.txt | 2 +- .../ui/src/main/resources/localization/tr.txt | 2 +- 14 files changed, 68 insertions(+), 20 deletions(-) diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java index 0ac30e0b2..b7c41776a 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ b/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/"); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java index 45c3a9ff6..51a75b263 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/UpgradeVersion3to4.java +++ b/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 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); diff --git a/main/ui/src/main/resources/fxml/welcome.fxml b/main/ui/src/main/resources/fxml/welcome.fxml index aa6af8885..a6bd238c2 100644 --- a/main/ui/src/main/resources/fxml/welcome.fxml +++ b/main/ui/src/main/resources/fxml/welcome.fxml @@ -20,18 +20,18 @@ - + - + - + - + \ No newline at end of file diff --git a/main/ui/src/main/resources/localization/de.txt b/main/ui/src/main/resources/localization/de.txt index f1143ea72..aab0f325b 100644 --- a/main/ui/src/main/resources/localization/de.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/en.txt b/main/ui/src/main/resources/localization/en.txt index 6f8dc24a7..f7bae8e29 100644 --- a/main/ui/src/main/resources/localization/en.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/es.txt b/main/ui/src/main/resources/localization/es.txt index aa60affa9..8a7efa64b 100644 --- a/main/ui/src/main/resources/localization/es.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/fr.txt b/main/ui/src/main/resources/localization/fr.txt index ceebcb110..36ac3046a 100644 --- a/main/ui/src/main/resources/localization/fr.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/hu.txt b/main/ui/src/main/resources/localization/hu.txt index 3539b25cb..f332a172f 100644 --- a/main/ui/src/main/resources/localization/hu.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/it.txt b/main/ui/src/main/resources/localization/it.txt index e11ead4f3..12ddb3da6 100644 --- a/main/ui/src/main/resources/localization/it.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/kr.txt b/main/ui/src/main/resources/localization/kr.txt index 45a0c8d0c..5ae12cfc0 100644 --- a/main/ui/src/main/resources/localization/kr.txt +++ b/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 = 비밀번호 재입력 diff --git a/main/ui/src/main/resources/localization/nl.txt b/main/ui/src/main/resources/localization/nl.txt index b86476fe5..473534744 100644 --- a/main/ui/src/main/resources/localization/nl.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/ru.txt b/main/ui/src/main/resources/localization/ru.txt index 112688c82..64754cb8d 100644 --- a/main/ui/src/main/resources/localization/ru.txt +++ b/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 = Введите пароль ещё раз diff --git a/main/ui/src/main/resources/localization/sk.txt b/main/ui/src/main/resources/localization/sk.txt index b31a62a5b..9c05a37d7 100644 --- a/main/ui/src/main/resources/localization/sk.txt +++ b/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 diff --git a/main/ui/src/main/resources/localization/tr.txt b/main/ui/src/main/resources/localization/tr.txt index 4e5105457..44fcda4ff 100644 --- a/main/ui/src/main/resources/localization/tr.txt +++ b/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)