Improved migration from vault version 3 to 4.

This commit is contained in:
Sebastian Stenzel
2016-07-11 18:07:55 +02:00
parent 0fdcdc816a
commit 69e133d561
14 changed files with 68 additions and 20 deletions

View File

@@ -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/");
}
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = 비밀번호 재입력

View File

@@ -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

View File

@@ -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 = Введите пароль ещё раз

View File

@@ -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

View File

@@ -7,7 +7,7 @@ main.addDirectory.contextMenu.new = Yeni bir kasa yarat
main.addDirectory.contextMenu.open = Var olan kasayı
# 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)