diff --git a/pom.xml b/pom.xml
index 55bcaa7e..3536385b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.cryptomator
cryptofs
- 1.9.12
+ 1.9.13
Cryptomator Crypto Filesystem
This library provides the Java filesystem provider used by Cryptomator.
https://github.com/cryptomator/cryptofs
@@ -16,7 +16,7 @@
1.3.2
2.27
- 29.0-jre
+ 30.0-jre
1.7.30
5.6.2
diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java
index f17b8dc8..fb10f3ef 100644
--- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java
+++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemImpl.java
@@ -372,6 +372,10 @@ private FileChannel newFileChannel(CryptoPath cleartextFilePath, EffectiveOpenOp
FileChannel ch = openCryptoFiles.getOrCreate(ciphertextFilePath).newFileChannel(options); // might throw FileAlreadyExists
if (options.writable()) {
ciphertextPath.persistLongFileName();
+ stats.incrementAccessesWritten();
+ }
+ if (options.readable()) {
+ stats.incrementAccessesRead();
}
return ch;
}
diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java
index e7412e64..2ccb7f48 100644
--- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java
+++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemModule.java
@@ -40,7 +40,7 @@ public Cryptor provideCryptor(CryptorProvider cryptorProvider, @PathToVault Path
byte[] keyFileContents = Files.readAllBytes(masterKeyPath);
Cryptor cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(keyFileContents), properties.passphrase(), properties.pepper(), Constants.VAULT_VERSION);
if (!readonlyFlag.isSet()) {
- MasterkeyBackupHelper.backupMasterKey(masterKeyPath);
+ MasterkeyBackupHelper.attemptMasterKeyBackup(masterKeyPath);
}
return cryptor;
} catch (IOException e) {
diff --git a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemStats.java b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemStats.java
index b83c4776..9c76f5d2 100644
--- a/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemStats.java
+++ b/src/main/java/org/cryptomator/cryptofs/CryptoFileSystemStats.java
@@ -18,9 +18,15 @@ public class CryptoFileSystemStats {
private final LongAdder bytesWritten = new LongAdder();
private final LongAdder bytesDecrypted = new LongAdder();
private final LongAdder bytesEncrypted = new LongAdder();
+ private final LongAdder totalBytesRead = new LongAdder();
+ private final LongAdder totalBytesWritten = new LongAdder();
+ private final LongAdder totalBytesDecrypted = new LongAdder();
+ private final LongAdder totalBytesEncrypted = new LongAdder();
private final LongAdder chunkCacheAccesses = new LongAdder();
private final LongAdder chunkCacheMisses = new LongAdder();
private final LongAdder chunkCacheHits = new LongAdder();
+ private final LongAdder amountOfAccessesRead = new LongAdder();
+ private final LongAdder amountOfAccessesWritten = new LongAdder();
@Inject
CryptoFileSystemStats() {
@@ -30,32 +36,52 @@ public long pollBytesRead() {
return bytesRead.sumThenReset();
}
+ public long pollTotalBytesRead() {
+ return totalBytesRead.sum();
+ }
+
public void addBytesRead(long numBytes) {
bytesRead.add(numBytes);
+ totalBytesRead.add(numBytes);
}
public long pollBytesWritten() {
return bytesWritten.sumThenReset();
}
+ public long pollTotalBytesWritten() {
+ return totalBytesWritten.sum();
+ }
+
public void addBytesWritten(long numBytes) {
bytesWritten.add(numBytes);
+ totalBytesWritten.add(numBytes);
}
public long pollBytesDecrypted() {
return bytesDecrypted.sumThenReset();
}
+ public long pollTotalBytesDecrypted() {
+ return totalBytesDecrypted.sum();
+ }
+
public void addBytesDecrypted(long numBytes) {
bytesDecrypted.add(numBytes);
+ totalBytesDecrypted.add(numBytes);
}
public long pollBytesEncrypted() {
return bytesEncrypted.sumThenReset();
}
+ public long pollTotalBytesEncrypted() {
+ return totalBytesEncrypted.sum();
+ }
+
public void addBytesEncrypted(long numBytes) {
bytesEncrypted.add(numBytes);
+ totalBytesEncrypted.add(numBytes);
}
public long pollChunkCacheAccesses() {
@@ -80,4 +106,20 @@ public void addChunkCacheMiss() {
chunkCacheHits.decrement();
}
-}
+ public long pollAmountOfAccessesRead() {
+ return amountOfAccessesRead.sum();
+ }
+
+ public void incrementAccessesRead() {
+ amountOfAccessesRead.increment();
+ }
+
+ public long pollAmountOfAccessesWritten() {
+ return amountOfAccessesWritten.sum();
+ }
+
+ public void incrementAccessesWritten() {
+ amountOfAccessesWritten.increment();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java b/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java
index d96283fb..97a1097a 100644
--- a/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java
+++ b/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java
@@ -5,28 +5,22 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SeekableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.AccessDeniedException;
+import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
/**
* Utility class for generating a suffix for the backup file to make it unique to its original master key file.
*/
public final class MasterkeyBackupHelper {
-
+
private static final Logger LOG = LoggerFactory.getLogger(MasterkeyBackupHelper.class);
/**
@@ -46,25 +40,27 @@ public static String generateFileIdSuffix(byte[] fileBytes) {
}
/**
- * Do a best-effort attempt to backup the masterkey at the given path. Fail silently if a valid backup already exists.
- *
+ * Do a best-effort attempt to backup the masterkey at the given path.
+ * Fails silently if a _valid_ backup already exists and fails with a log entry, if any IO error occurs while creating or reading the backup file.
+ *
* @param masterKeyPath The masterkey file to backup
- * @throws IOException Any non-recoverable I/O exception that occurs during this attempt
+ * @throws IOException If the masterkey cannot be read.
*/
- public static Path backupMasterKey(Path masterKeyPath) throws IOException {
+ public static Path attemptMasterKeyBackup(Path masterKeyPath) throws IOException {
byte[] keyFileContents = Files.readAllBytes(masterKeyPath);
String backupFileName = masterKeyPath.getFileName().toString() + generateFileIdSuffix(keyFileContents) + Constants.MASTERKEY_BACKUP_SUFFIX;
Path backupFilePath = masterKeyPath.resolveSibling(backupFileName);
- try (WritableByteChannel ch = Files.newByteChannel(backupFilePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
+ try (WritableByteChannel ch = Files.newByteChannel(backupFilePath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
ch.write(ByteBuffer.wrap(keyFileContents));
- } catch (AccessDeniedException e) {
- LOG.info("Storage device does not allow writing backup file. Comparing masterkey with backup directly.");
+ } catch (AccessDeniedException | FileAlreadyExistsException e) {
assertExistingBackupMatchesContent(backupFilePath, ByteBuffer.wrap(keyFileContents));
+ } catch (IOException e) {
+ LOG.warn("Failed to backup valid masterkey file.");
}
return backupFilePath;
}
-
- private static void assertExistingBackupMatchesContent(Path backupFilePath, ByteBuffer expectedContent) throws IOException {
+
+ private static void assertExistingBackupMatchesContent(Path backupFilePath, ByteBuffer expectedContent) {
if (Files.exists(backupFilePath)) {
// TODO replace by Files.mismatch() when using JDK > 12
ByteBuffer buf = ByteBuffer.allocateDirect(expectedContent.remaining() + 1);
@@ -72,12 +68,14 @@ private static void assertExistingBackupMatchesContent(Path backupFilePath, Byte
ch.read(buf);
buf.flip();
if (buf.compareTo(expectedContent) != 0) {
- throw new IllegalStateException("Corrupt masterkey backup: " + backupFilePath);
+ LOG.warn("Corrupt masterkey backup {}. Please replace it manually or unlock the vault on a writable storage device.", backupFilePath);
+ } else {
+ LOG.debug("Verified backup file: {}", backupFilePath);
}
- LOG.debug("Verified backup file: {}", backupFilePath);
- } catch (NoSuchFileException e) {
- LOG.warn("Did not find backup file: {}", backupFilePath);
+ } catch (IOException e) {
+ LOG.warn("Failed to compare valid masterkey with backup file.", e);
}
}
}
+
}
diff --git a/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java b/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
index 69956dc8..8c39a1c0 100644
--- a/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
+++ b/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
@@ -8,7 +8,6 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -16,7 +15,6 @@
import javax.inject.Inject;
import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
-import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener;
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
import org.cryptomator.cryptofs.migration.api.Migrator;
@@ -48,7 +46,7 @@ public void migrate(Path vaultRoot, String masterkeyFilename, CharSequence passp
KeyFile keyFile = KeyFile.parse(fileContentsBeforeUpgrade);
try (Cryptor cryptor = cryptorProvider.createFromKeyFile(keyFile, passphrase, 5)) {
// create backup, as soon as we know the password was correct:
- Path masterkeyBackupFile = MasterkeyBackupHelper.backupMasterKey(masterkeyFile);
+ Path masterkeyBackupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(masterkeyFile);
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
progressListener.update(MigrationProgressListener.ProgressState.FINALIZING, 0.0);
diff --git a/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java b/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
index e681a67f..b4f5bc5b 100644
--- a/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
+++ b/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
@@ -6,7 +6,6 @@
package org.cryptomator.cryptofs.migration.v7;
import org.cryptomator.cryptofs.FileNameTooLongException;
-import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.common.DeletingFileVisitor;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
@@ -28,7 +27,6 @@
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.EnumSet;
@@ -52,7 +50,7 @@ public void migrate(Path vaultRoot, String masterkeyFilename, CharSequence passp
KeyFile keyFile = KeyFile.parse(fileContentsBeforeUpgrade);
try (Cryptor cryptor = cryptorProvider.createFromKeyFile(keyFile, passphrase, 6)) {
// create backup, as soon as we know the password was correct:
- Path masterkeyBackupFile = MasterkeyBackupHelper.backupMasterKey(masterkeyFile);
+ Path masterkeyBackupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(masterkeyFile);
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
// check file system capabilities:
diff --git a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemStatsTest.java b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemStatsTest.java
index 96fbcda5..b3db77aa 100644
--- a/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemStatsTest.java
+++ b/src/test/java/org/cryptomator/cryptofs/CryptoFileSystemStatsTest.java
@@ -97,4 +97,54 @@ public void testPollChunkCacheMisses() {
Assertions.assertEquals(2l, inTest.pollChunkCacheMisses());
}
+ @Test
+ public void testPollTotalBytesRead() {
+ Assertions.assertEquals(0l, inTest.pollTotalBytesRead());
+ inTest.addBytesRead(1l);
+ Assertions.assertEquals(1l, inTest.pollTotalBytesRead());
+ inTest.addBytesRead(5l);
+ Assertions.assertEquals(6l, inTest.pollTotalBytesRead());
+ }
+
+ @Test
+ public void testPollTotalBytesWritten() {
+ Assertions.assertEquals(0l, inTest.pollTotalBytesWritten());
+ inTest.addBytesWritten(1l);
+ Assertions.assertEquals(1l, inTest.pollTotalBytesWritten());
+ inTest.addBytesWritten(5l);
+ Assertions.assertEquals(6l, inTest.pollTotalBytesWritten());
+ }
+
+ @Test
+ public void testPollTotalBytesDecrypted() {
+ Assertions.assertEquals(0l, inTest.pollTotalBytesDecrypted());
+ inTest.addBytesDecrypted(1l);
+ Assertions.assertEquals(1l, inTest.pollTotalBytesDecrypted());
+ inTest.addBytesDecrypted(5l);
+ Assertions.assertEquals(6l, inTest.pollTotalBytesDecrypted());
+ }
+
+ @Test
+ public void testPollTotalBytesEncrypted() {
+ Assertions.assertEquals(0l, inTest.pollTotalBytesEncrypted());
+ inTest.addBytesEncrypted(1l);
+ Assertions.assertEquals(1l, inTest.pollTotalBytesEncrypted());
+ inTest.addBytesEncrypted(5l);
+ Assertions.assertEquals(6l, inTest.pollTotalBytesEncrypted());
+ }
+
+ @Test
+ public void testPollAmountOfFilesRead() {
+ Assertions.assertEquals(0l, inTest.pollAmountOfAccessesRead());
+ inTest.incrementAccessesRead();
+ Assertions.assertEquals(1l, inTest.pollAmountOfAccessesRead());
+ }
+
+ @Test
+ public void testPollAmountOfFilesWritten() {
+ Assertions.assertEquals(0l, inTest.pollAmountOfAccessesWritten());
+ inTest.incrementAccessesWritten();
+ Assertions.assertEquals(1l, inTest.pollAmountOfAccessesWritten());
+ }
+
}
diff --git a/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java b/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java
index da51a340..f4b4c15a 100644
--- a/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java
+++ b/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java
@@ -24,11 +24,11 @@ public void testBackupFilePosix(byte[] contents, @TempDir Path tmp) throws IOExc
Path originalFile = tmp.resolve("original");
Files.write(originalFile, contents);
- Path backupFile = MasterkeyBackupHelper.backupMasterKey(originalFile);
+ Path backupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
Assertions.assertArrayEquals(contents, Files.readAllBytes(backupFile));
Files.setPosixFilePermissions(backupFile, PosixFilePermissions.fromString("r--r--r--"));
- Path backupFile2 = MasterkeyBackupHelper.backupMasterKey(originalFile);
+ Path backupFile2 = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
Assertions.assertEquals(backupFile, backupFile2);
}
@@ -39,11 +39,11 @@ public void testBackupFileWin(byte[] contents, @TempDir Path tmp) throws IOExcep
Path originalFile = tmp.resolve("original");
Files.write(originalFile, contents);
- Path backupFile = MasterkeyBackupHelper.backupMasterKey(originalFile);
+ Path backupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
Assertions.assertArrayEquals(contents, Files.readAllBytes(backupFile));
Files.getFileAttributeView(backupFile, DosFileAttributeView.class).setReadOnly(true);
- Path backupFile2 = MasterkeyBackupHelper.backupMasterKey(originalFile);
+ Path backupFile2 = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
Assertions.assertEquals(backupFile, backupFile2);
}