Filesystems have a blocksizeBytes configuration option

This commit is contained in:
Sebastian Messmer 2016-03-01 17:45:48 +01:00
parent 35ae3b9c24
commit 3254220987
10 changed files with 312 additions and 12 deletions

View File

@ -15,11 +15,11 @@ using cpputils::Data;
namespace cryfs {
CryConfig::CryConfig()
: _rootBlob(""), _encKey(""), _cipher(""), _version("") {
: _rootBlob(""), _encKey(""), _cipher(""), _version(""), _blocksizeBytes(0) {
}
CryConfig::CryConfig(CryConfig &&rhs)
: _rootBlob(std::move(rhs._rootBlob)), _encKey(std::move(rhs._encKey)), _cipher(std::move(rhs._cipher)), _version(std::move(rhs._version)) {
: _rootBlob(std::move(rhs._rootBlob)), _encKey(std::move(rhs._encKey)), _cipher(std::move(rhs._cipher)), _version(std::move(rhs._version)), _blocksizeBytes(rhs._blocksizeBytes) {
}
CryConfig CryConfig::load(const Data &data) {
@ -33,6 +33,7 @@ CryConfig CryConfig::load(const Data &data) {
cfg._encKey = pt.get("cryfs.key", "");
cfg._cipher = pt.get("cryfs.cipher", "");
cfg._version = pt.get("cryfs.version", "0.8"); // CryFS 0.8 didn't specify this field, so if the field doesn't exist, it's 0.8.
cfg._blocksizeBytes = pt.get<uint32_t>("cryfs.blocksizeBytes", 32 * 1024); // TODO Put here the actual block size value of earlier CryFS versions
return cfg;
}
@ -43,6 +44,7 @@ Data CryConfig::save() const {
pt.put("cryfs.key", _encKey);
pt.put("cryfs.cipher", _cipher);
pt.put("cryfs.version", _version);
pt.put<uint32_t>("cryfs.blocksizeBytes", _blocksizeBytes);
stringstream stream;
write_json(stream, pt);
@ -81,4 +83,12 @@ void CryConfig::SetVersion(const std::string &value) {
_version = value;
}
uint32_t CryConfig::BlocksizeBytes() const {
return _blocksizeBytes;
}
void CryConfig::SetBlocksizeBytes(uint32_t value) {
_blocksizeBytes = value;
}
}

View File

@ -27,6 +27,9 @@ public:
const std::string &Version() const;
void SetVersion(const std::string &value);
uint32_t BlocksizeBytes() const;
void SetBlocksizeBytes(uint32_t value);
static CryConfig load(const cpputils::Data &data);
cpputils::Data save() const;
@ -35,6 +38,7 @@ private:
std::string _encKey;
std::string _cipher;
std::string _version;
uint32_t _blocksizeBytes;
DISALLOW_COPY_AND_ASSIGN(CryConfig);
};

View File

@ -11,6 +11,7 @@ using std::shared_ptr;
namespace cryfs {
constexpr const char *CryConfigConsole::DEFAULT_CIPHER;
constexpr uint32_t CryConfigConsole::DEFAULT_BLOCKSIZE_BYTES;
CryConfigConsole::CryConfigConsole(shared_ptr<Console> console, bool noninteractive)
: _console(std::move(console)), _useDefaultSettings(noninteractive ? optional<bool>(true) : none) {
@ -45,6 +46,28 @@ namespace cryfs {
return _console->askYesNo(string() + (*warning) + " Do you want to take this cipher nevertheless?");
}
uint32_t CryConfigConsole::askBlocksizeBytes() {
if (_checkUseDefaultSettings()) {
return DEFAULT_BLOCKSIZE_BYTES;
} else {
return _askBlocksizeBytes();
}
}
uint32_t CryConfigConsole::_askBlocksizeBytes() const {
vector<string> sizes = {"8KB", "32KB", "64KB", "512KB", "1MB", "4MB"};
int index = _console->ask("Which block size do you want to use?", sizes);
switch(index) {
case 0: return 8*1024;
case 1: return 32*1024;
case 2: return 64*1024;
case 3: return 512*1024;
case 4: return 1024*1024;
case 5: return 4*1024*1024;
default: ASSERT(false, "Unhandled case");
}
}
bool CryConfigConsole::_checkUseDefaultSettings() {
if (_useDefaultSettings == none) {
_useDefaultSettings = _console->askYesNo("Use default settings?");

View File

@ -13,8 +13,10 @@ namespace cryfs {
CryConfigConsole(CryConfigConsole &&rhs) = default;
std::string askCipher();
uint32_t askBlocksizeBytes();
static constexpr const char *DEFAULT_CIPHER = "aes-256-gcm";
static constexpr uint32_t DEFAULT_BLOCKSIZE_BYTES = 32 * 1024; // 32KB
private:
@ -22,6 +24,7 @@ namespace cryfs {
std::string _askCipher() const;
bool _showWarningForCipherAndReturnIfOk(const std::string &cipherName) const;
uint32_t _askBlocksizeBytes() const;
std::shared_ptr<cpputils::Console> _console;
boost::optional<bool> _useDefaultSettings;

View File

@ -20,12 +20,17 @@ namespace cryfs {
CryConfig CryConfigCreator::create(const optional<string> &cipherFromCommandLine) {
CryConfig config;
config.SetCipher(_generateCipher(cipherFromCommandLine));
config.SetEncryptionKey(_generateEncKey(config.Cipher()));
config.SetRootBlob(_generateRootBlobKey());
config.SetVersion(version::VERSION_STRING);
config.SetBlocksizeBytes(_generateBlocksizeBytes());
config.SetRootBlob(_generateRootBlobKey());
config.SetEncryptionKey(_generateEncKey(config.Cipher()));
return config;
}
uint32_t CryConfigCreator::_generateBlocksizeBytes() {
return _configConsole.askBlocksizeBytes();
}
string CryConfigCreator::_generateCipher(const optional<string> &cipherFromCommandLine) {
if (cipherFromCommandLine != none) {
ASSERT(std::find(CryCiphers::supportedCipherNames().begin(), CryCiphers::supportedCipherNames().end(), *cipherFromCommandLine) != CryCiphers::supportedCipherNames().end(), "Invalid cipher");

View File

@ -19,6 +19,7 @@ namespace cryfs {
std::string _generateCipher(const boost::optional<std::string> &cipherFromCommandLine);
std::string _generateEncKey(const std::string &cipher);
std::string _generateRootBlobKey();
uint32_t _generateBlocksizeBytes();
std::shared_ptr<cpputils::Console> _console;
CryConfigConsole _configConsole;

146
src/stats/main.cpp Normal file
View File

@ -0,0 +1,146 @@
#include <iostream>
#include <boost/filesystem.hpp>
#include "../impl/config/CryConfigFile.h"
#include <blockstore/implementations/ondisk/OnDiskBlockStore.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNodeStore.h>
#include <blobstore/implementations/onblocks/datanodestore/DataNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataInnerNode.h>
#include <blobstore/implementations/onblocks/datanodestore/DataLeafNode.h>
#include <blobstore/implementations/onblocks/BlobStoreOnBlocks.h>
#include <cryfs/impl/filesystem/fsblobstore/FsBlobStore.h>
#include <cryfs/impl/filesystem/fsblobstore/DirBlob.h>
#include <cryfs/impl/filesystem/CryDevice.h>
using namespace boost;
using namespace boost::filesystem;
using namespace std;
using namespace cryfs;
using namespace cpputils;
using namespace blockstore;
using namespace blockstore::ondisk;
using namespace blobstore::onblocks;
using namespace blobstore::onblocks::datanodestore;
using namespace cryfs::fsblobstore;
void printNode(unique_ref<DataNode> node) {
std::cout << "Key: " << node->key().ToString() << ", Depth: " << node->depth() << " ";
auto innerNode = dynamic_pointer_move<DataInnerNode>(node);
if (innerNode != none) {
std::cout << "Type: inner\n";
return;
}
auto leafNode = dynamic_pointer_move<DataLeafNode>(node);
if (leafNode != none) {
std::cout << "Type: leaf\n";
return;
}
}
set<Key> _getBlockstoreUnaccountedBlocks(const CryConfig &config) {
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config.EncryptionKey());
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(encryptedBlockStore), CryDevice::BLOCKSIZE_BYTES);
std::set<Key> unaccountedBlocks;
uint32_t numBlocks = nodeStore->numNodes();
uint32_t i = 0;
cout << "There are " << nodeStore->numNodes() << " blocks." << std::endl;
// Add all blocks to unaccountedBlocks
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto key = Key::FromString(file->path().filename().c_str());
unaccountedBlocks.insert(key);
}
}
i = 0;
cout << "\nRemove blocks that have a parent" << endl;
//Remove root block from unaccountedBlocks
unaccountedBlocks.erase(Key::FromString(config.RootBlob()));
//Remove all blocks that have a parent node from unaccountedBlocks
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto key = Key::FromString(file->path().filename().c_str());
auto node = nodeStore->load(key);
auto innerNode = dynamic_pointer_move<DataInnerNode>(*node);
if (innerNode != none) {
for (uint32_t childIndex = 0; childIndex < (*innerNode)->numChildren(); ++childIndex) {
auto child = (*innerNode)->getChild(childIndex)->key();
unaccountedBlocks.erase(child);
}
}
}
}
return unaccountedBlocks;
}
set<Key> _getBlocksReferencedByDirEntries(const CryConfig &config) {
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config.Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config.EncryptionKey());
auto fsBlobStore = make_unique_ref<FsBlobStore>(make_unique_ref<BlobStoreOnBlocks>(std::move(encryptedBlockStore), CryDevice::BLOCKSIZE_BYTES));
set<Key> blocksReferencedByDirEntries;
uint32_t numBlocks = fsBlobStore->numBlocks();
uint32_t i = 0;
cout << "\nRemove blocks referenced by dir entries" << endl;
for (auto file = directory_iterator("/home/heinzi/basedir"); file != directory_iterator(); ++file) {
cout << "\r" << (++i) << "/" << numBlocks << flush;
if (file->path().filename() != "cryfs.config") {
auto key = Key::FromString(file->path().filename().c_str());
try {
auto blob = fsBlobStore->load(key);
if (blob != none) {
auto dir = dynamic_pointer_move<DirBlob>(*blob);
if (dir != none) {
vector<fspp::Dir::Entry> children;
(*dir)->AppendChildrenTo(&children);
for (const auto &child : children) {
blocksReferencedByDirEntries.insert((*dir)->GetChild(child.name)->key);
}
}
}
} catch (...) {}
}
}
return blocksReferencedByDirEntries;
}
int main() {
cout << "Password: ";
string password;
getline(cin, password);
cout << "Loading config" << endl;
auto config = CryConfigFile::load("/home/heinzi/basedir/cryfs.config", password);
set<Key> unaccountedBlocks = _getBlockstoreUnaccountedBlocks(*config->config());
//Remove all blocks that are referenced by a directory entry from unaccountedBlocks
set<Key> blocksReferencedByDirEntries = _getBlocksReferencedByDirEntries(*config->config());
for (const auto &key : blocksReferencedByDirEntries) {
unaccountedBlocks.erase(key);
}
cout << "\nCalculate statistics" << endl;
auto onDiskBlockStore = make_unique_ref<OnDiskBlockStore>("/home/heinzi/basedir");
auto encryptedBlockStore = CryCiphers::find(config->config()->Cipher()).createEncryptedBlockstore(std::move(onDiskBlockStore), config->config()->EncryptionKey());
auto nodeStore = make_unique_ref<DataNodeStore>(std::move(encryptedBlockStore), CryDevice::BLOCKSIZE_BYTES);
uint32_t numUnaccountedBlocks = unaccountedBlocks.size();
uint32_t numLeaves = 0;
uint32_t numInner = 0;
cout << "\nUnaccounted blocks: " << unaccountedBlocks.size() << endl;
for (const auto &key : unaccountedBlocks) {
std::cout << "\r" << (numLeaves+numInner) << "/" << numUnaccountedBlocks << flush;
auto node = nodeStore->load(key);
auto innerNode = dynamic_pointer_move<DataInnerNode>(*node);
if (innerNode != none) {
++numInner;
printNode(std::move(*innerNode));
}
auto leafNode = dynamic_pointer_move<DataLeafNode>(*node);
if (leafNode != none) {
++numLeaves;
printNode(std::move(*leafNode));
}
}
cout << "\n" << numLeaves << " leaves and " << numInner << " inner nodes" << endl;
}

View File

@ -42,6 +42,10 @@ class CryConfigConsoleTest_Cipher: public CryConfigConsoleTest {};
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(1).WillOnce(Return(false)); \
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), UnorderedElementsAreArray(CryCiphers::supportedCipherNames()))).Times(1)
#define EXPECT_ASK_FOR_BLOCKSIZE() \
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(1).WillOnce(Return(false)); \
EXPECT_CALL(*console, ask(HasSubstr("block size"), _)).Times(1)
TEST_F(CryConfigConsoleTest_Cipher, AsksForCipher) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher());
cryconsole.askCipher();
@ -61,6 +65,18 @@ TEST_F(CryConfigConsoleTest_Cipher, ChooseDefaultCipherWhenNoninteractiveEnviron
EXPECT_EQ(CryConfigConsole::DEFAULT_CIPHER, cipher);
}
TEST_F(CryConfigConsoleTest_Cipher, AsksForBlocksize) {
EXPECT_ASK_FOR_BLOCKSIZE().WillOnce(Return(0));
cryconsole.askBlocksizeBytes();
}
TEST_F(CryConfigConsoleTest_Cipher, ChooseDefaultBlocksizeWhenNoninteractiveEnvironment) {
EXPECT_CALL(*console, askYesNo(HasSubstr("default"))).Times(0);
EXPECT_CALL(*console, ask(HasSubstr("block size"), _)).Times(0);
uint32_t blocksize = noninteractiveCryconsole.askBlocksizeBytes();
EXPECT_EQ(CryConfigConsole::DEFAULT_BLOCKSIZE_BYTES, blocksize);
}
class CryConfigConsoleTest_Cipher_Choose: public CryConfigConsoleTest_Cipher, public ::testing::WithParamInterface<string> {
public:
string cipherName = GetParam();

View File

@ -24,60 +24,110 @@ using ::testing::HasSubstr;
using ::testing::UnorderedElementsAreArray;
using ::testing::WithParamInterface;
#define EXPECT_ASK_TO_USE_DEFAULT_SETTINGS() \
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(1)
#define EXPECT_DOES_NOT_ASK_TO_USE_DEFAULT_SETTINGS() \
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(0)
#define EXPECT_ASK_FOR_CIPHER() \
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), UnorderedElementsAreArray(CryCiphers::supportedCipherNames()))).Times(1)
#define EXPECT_DOES_NOT_ASK_FOR_CIPHER() \
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), _)).Times(0)
#define EXPECT_ASK_FOR_BLOCKSIZE() \
EXPECT_CALL(*console, ask(HasSubstr("block size"), _)).Times(1)
#define EXPECT_DOES_NOT_ASK_FOR_BLOCKSIZE() \
EXPECT_CALL(*console, ask(HasSubstr("block size"), _)).Times(0)
class CryConfigCreatorTest: public ::testing::Test {
public:
CryConfigCreatorTest()
: console(make_shared<MockConsole>()),
creator(console, cpputils::Random::PseudoRandom(), false),
noninteractiveCreator(console, cpputils::Random::PseudoRandom(), true) {
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), _)).WillRepeatedly(ChooseAnyCipher());
EXPECT_CALL(*console, ask(HasSubstr("block size"), _)).WillRepeatedly(Return(0));
}
shared_ptr<MockConsole> console;
CryConfigCreator creator;
CryConfigCreator noninteractiveCreator;
void AnswerNoToDefaultSettings() {
EXPECT_ASK_TO_USE_DEFAULT_SETTINGS().WillOnce(Return(false));
}
void AnswerYesToDefaultSettings() {
EXPECT_ASK_TO_USE_DEFAULT_SETTINGS().WillOnce(Return(true));
}
};
#define EXPECT_ASK_FOR_CIPHER() \
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(1).WillOnce(Return(false)); \
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), UnorderedElementsAreArray(CryCiphers::supportedCipherNames()))).Times(1)
#define EXPECT_DOES_NOT_ASK_FOR_CIPHER() \
EXPECT_CALL(*console, askYesNo("Use default settings?")).Times(0); \
EXPECT_CALL(*console, ask(HasSubstr("block cipher"), _)).Times(0);
TEST_F(CryConfigCreatorTest, DoesAskForCipherIfNotSpecified) {
AnswerNoToDefaultSettings();
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher());
CryConfig config = creator.create(none);
}
TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfSpecified) {
AnswerNoToDefaultSettings();
EXPECT_DOES_NOT_ASK_FOR_CIPHER();
CryConfig config = creator.create(string("aes-256-gcm"));
}
TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfUsingDefaultSettings) {
AnswerYesToDefaultSettings();
EXPECT_DOES_NOT_ASK_FOR_CIPHER();
CryConfig config = creator.create(none);
}
TEST_F(CryConfigCreatorTest, DoesNotAskForCipherIfNoninteractive) {
EXPECT_DOES_NOT_ASK_TO_USE_DEFAULT_SETTINGS();
EXPECT_DOES_NOT_ASK_FOR_CIPHER();
CryConfig config = noninteractiveCreator.create(none);
}
TEST_F(CryConfigCreatorTest, DoesAskForBlocksizeIfNotSpecified) {
AnswerNoToDefaultSettings();
EXPECT_ASK_FOR_BLOCKSIZE().WillOnce(Return(1));
CryConfig config = creator.create(none);
}
//TODO DoesNotAskForCipherIfSpecified
TEST_F(CryConfigCreatorTest, DoesNotAskForBlocksizeIfNoninteractive) {
EXPECT_DOES_NOT_ASK_TO_USE_DEFAULT_SETTINGS();
EXPECT_DOES_NOT_ASK_FOR_BLOCKSIZE();
CryConfig config = noninteractiveCreator.create(none);
}
TEST_F(CryConfigCreatorTest, DoesNotAskForBlocksizeIfUsingDefaultSettings) {
AnswerYesToDefaultSettings();
EXPECT_DOES_NOT_ASK_FOR_BLOCKSIZE();
CryConfig config = creator.create(none);
}
TEST_F(CryConfigCreatorTest, ChoosesEmptyRootBlobId) {
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseAnyCipher());
AnswerNoToDefaultSettings();
CryConfig config = creator.create(none);
EXPECT_EQ("", config.RootBlob()); // This tells CryFS to create a new root blob
}
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_448) {
AnswerNoToDefaultSettings();
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("mars-448-gcm"));
CryConfig config = creator.create(none);
cpputils::Mars448_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
}
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_256) {
AnswerNoToDefaultSettings();
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-256-gcm"));
CryConfig config = creator.create(none);
cpputils::AES256_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
}
TEST_F(CryConfigCreatorTest, ChoosesValidEncryptionKey_128) {
AnswerNoToDefaultSettings();
EXPECT_ASK_FOR_CIPHER().WillOnce(ChooseCipher("aes-128-gcm"));
CryConfig config = creator.create(none);
cpputils::AES128_GCM::EncryptionKey::FromString(config.EncryptionKey()); // This crashes if invalid
}
//TODO Add test cases ensuring that the values entered are correctly taken

View File

@ -76,3 +76,45 @@ TEST_F(CryConfigTest, Cipher_AfterSaveAndLoad) {
CryConfig loaded = SaveAndLoad(std::move(cfg));
EXPECT_EQ("mycipher", loaded.Cipher());
}
TEST_F(CryConfigTest, Version_Init) {
EXPECT_EQ("", cfg.Version());
}
TEST_F(CryConfigTest, Version) {
cfg.SetVersion("0.9.1");
EXPECT_EQ("0.9.1", cfg.Version());
}
TEST_F(CryConfigTest, Version_AfterMove) {
cfg.SetCipher("0.9.1");
CryConfig moved = std::move(cfg);
EXPECT_EQ("0.9.1", moved.Cipher());
}
TEST_F(CryConfigTest, Version_AfterSaveAndLoad) {
cfg.SetCipher("0.9.2");
CryConfig loaded = SaveAndLoad(std::move(cfg));
EXPECT_EQ("0.9.2", loaded.Cipher());
}
TEST_F(CryConfigTest, BlocksizeBytes_Init) {
EXPECT_EQ(0u, cfg.BlocksizeBytes());
}
TEST_F(CryConfigTest, BlocksizeBytes) {
cfg.SetBlocksizeBytes(4*1024*1024);
EXPECT_EQ(4*1024*1024u, cfg.BlocksizeBytes());
}
TEST_F(CryConfigTest, BlocksizeBytes_AfterMove) {
cfg.SetBlocksizeBytes(32*1024);
CryConfig moved = std::move(cfg);
EXPECT_EQ(32*1024u, moved.BlocksizeBytes());
}
TEST_F(CryConfigTest, BlocksizeBytes_AfterSaveAndLoad) {
cfg.SetBlocksizeBytes(10*1024);
CryConfig loaded = SaveAndLoad(std::move(cfg));
EXPECT_EQ(10*1024u, loaded.BlocksizeBytes());
}