Integrity data from KnownBlockVersions is persisted to a file

This commit is contained in:
Sebastian Messmer 2016-06-21 15:56:29 -07:00
parent d7f547dd47
commit 13411c4e59
5 changed files with 150 additions and 7 deletions

View File

@ -1,13 +1,36 @@
#include <fstream>
#include "KnownBlockVersions.h"
namespace bf = boost::filesystem;
using std::unordered_map;
using std::pair;
using std::string;
using boost::optional;
using boost::none;
namespace blockstore {
namespace versioncounting {
KnownBlockVersions::KnownBlockVersions()
:_knownVersions() {
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;0\0";
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath)
:_knownVersions(_loadStateFile(stateFilePath)), _stateFilePath(stateFilePath), _valid(true) {
}
KnownBlockVersions::KnownBlockVersions(KnownBlockVersions &&rhs)
: _knownVersions(std::move(rhs._knownVersions)), _stateFilePath(std::move(rhs._stateFilePath)), _valid(true) {
rhs._valid = false;
}
KnownBlockVersions::~KnownBlockVersions() {
if (_valid) {
_saveStateFile();
}
}
bool KnownBlockVersions::checkAndUpdateVersion(const Key &key, uint64_t version) {
ASSERT(_valid, "Object not valid due to a std::move");
auto found = _knownVersions.find(key);
if (found == _knownVersions.end()) {
_knownVersions.emplace(key, version);
@ -28,5 +51,55 @@ void KnownBlockVersions::updateVersion(const Key &key, uint64_t version) {
}
}
unordered_map<Key, uint64_t> KnownBlockVersions::_loadStateFile(const bf::path &stateFilePath) {
std::ifstream file(stateFilePath.native().c_str());
if (!file.good()) {
return unordered_map<Key, uint64_t>();
}
_checkHeader(&file);
unordered_map<Key, uint64_t> result;
optional<pair<Key, uint64_t>> entry = _readEntry(&file);
while(none != entry) {
result.insert(*entry);
entry = _readEntry(&file);
}
ASSERT(file.eof(), "Didn't read until end of file");
return result;
};
void KnownBlockVersions::_checkHeader(std::ifstream *file) {
char actualHeader[HEADER.size()];
file->read(actualHeader, HEADER.size());
if (HEADER != string(actualHeader, HEADER.size())) {
throw std::runtime_error("Invalid local state: Invalid integrity file header.");
}
}
optional<pair<Key, uint64_t>> KnownBlockVersions::_readEntry(std::ifstream *file) {
ASSERT(file->good(), "Error reading file");
pair<Key, uint64_t> result(Key::Null(), 0);
file->read((char*)result.first.data(), result.first.BINARY_LENGTH);
if (file->eof()) {
// Couldn't read another entry. File end.
return none;
}
ASSERT(file->good(), "Error reading file");
file->read((char*)&result.second, sizeof(result.second));
ASSERT(file->good(), "Error reading file");
return result;
};
void KnownBlockVersions::_saveStateFile() const {
std::ofstream file(_stateFilePath.native().c_str());
file.write(HEADER.c_str(), HEADER.size());
for (const auto &entry : _knownVersions) {
file.write((char*)entry.first.data(), entry.first.BINARY_LENGTH);
file.write((char*)&entry.second, sizeof(entry.second));
}
}
}
}

View File

@ -4,14 +4,17 @@
#include <cpp-utils/macros.h>
#include <blockstore/utils/Key.h>
#include <boost/filesystem/path.hpp>
#include <boost/optional.hpp>
namespace blockstore {
namespace versioncounting {
class KnownBlockVersions final {
public:
KnownBlockVersions();
KnownBlockVersions(KnownBlockVersions &&rhs) = default;
KnownBlockVersions(const boost::filesystem::path &stateFilePath);
KnownBlockVersions(KnownBlockVersions &&rhs);
~KnownBlockVersions();
__attribute__((warn_unused_result))
bool checkAndUpdateVersion(const Key &key, uint64_t version);
@ -20,6 +23,16 @@ namespace blockstore {
private:
std::unordered_map<Key, uint64_t> _knownVersions;
boost::filesystem::path _stateFilePath;
bool _valid;
static const std::string HEADER;
static std::unordered_map<Key, uint64_t> _loadStateFile(const boost::filesystem::path &stateFilePath);
static void _checkHeader(std::ifstream *file);
static boost::optional<std::pair<Key, uint64_t>> _readEntry(std::ifstream *file);
void _saveStateFile() const;
DISALLOW_COPY_AND_ASSIGN(KnownBlockVersions);
};

View File

@ -1,12 +1,18 @@
#include <gtest/gtest.h>
#include <blockstore/implementations/versioncounting/KnownBlockVersions.h>
#include <cpp-utils/tempfile/TempFile.h>
using blockstore::versioncounting::KnownBlockVersions;
using cpputils::TempFile;
class KnownBlockVersionsTest : public ::testing::Test {
public:
blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
KnownBlockVersionsTest() :stateFile(false), testobj(stateFile.path()) {}
blockstore::Key key = blockstore::Key::FromString("1491BB4932A389EE14BC7090AC772972");
blockstore::Key key2 = blockstore::Key::FromString("C772972491BB4932A1389EE14BC7090A");
TempFile stateFile;
KnownBlockVersions testobj;
};
@ -75,3 +81,45 @@ TEST_F(KnownBlockVersionsTest, checkAndUpdate_existingEntry_invalidDoesntModifyE
EXPECT_FALSE(testobj.checkAndUpdateVersion(key, 99));
EXPECT_TRUE(testobj.checkAndUpdateVersion(key, 100));
}
TEST_F(KnownBlockVersionsTest, checkAndUpdate_twoEntriesDontInfluenceEachOther) {
testobj.updateVersion(key, 100);
testobj.updateVersion(key2, 100);
testobj.updateVersion(key, 150);
EXPECT_FALSE(testobj.checkAndUpdateVersion(key, 149));
EXPECT_TRUE(testobj.checkAndUpdateVersion(key, 150));
EXPECT_FALSE(testobj.checkAndUpdateVersion(key2, 99));
EXPECT_TRUE(testobj.checkAndUpdateVersion(key2, 100));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_empty) {
TempFile stateFile(false);
KnownBlockVersions(stateFile.path());
EXPECT_TRUE(KnownBlockVersions(stateFile.path()).checkAndUpdateVersion(key, 0));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_oneentry) {
TempFile stateFile(false);
KnownBlockVersions(stateFile.path()).updateVersion(key, 100);
EXPECT_FALSE(KnownBlockVersions(stateFile.path()).checkAndUpdateVersion(key, 99));
EXPECT_TRUE(KnownBlockVersions(stateFile.path()).checkAndUpdateVersion(key, 100));
}
TEST_F(KnownBlockVersionsTest, saveAndLoad_twoentries) {
TempFile stateFile(false);
{
KnownBlockVersions obj(stateFile.path());
obj.updateVersion(key, 100);
obj.updateVersion(key2, 50);
}
KnownBlockVersions obj(stateFile.path());
EXPECT_FALSE(obj.checkAndUpdateVersion(key, 99));
EXPECT_TRUE(obj.checkAndUpdateVersion(key, 100));
EXPECT_FALSE(obj.checkAndUpdateVersion(key2, 49));
EXPECT_TRUE(obj.checkAndUpdateVersion(key2, 50));
}

View File

@ -2,6 +2,7 @@
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "../../testutils/BlockStoreTest.h"
#include <gtest/gtest.h>
#include <cpp-utils/tempfile/TempFile.h>
using ::testing::Test;
@ -14,11 +15,15 @@ using cpputils::Data;
using cpputils::DataFixture;
using cpputils::make_unique_ref;
using cpputils::unique_ref;
using cpputils::TempFile;
class VersionCountingBlockStoreTestFixture: public BlockStoreTestFixture {
public:
VersionCountingBlockStoreTestFixture() :stateFile(false) {}
TempFile stateFile;
unique_ref<BlockStore> createBlockStore() override {
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), KnownBlockVersions());
return make_unique_ref<VersionCountingBlockStore>(make_unique_ref<FakeBlockStore>(), KnownBlockVersions(stateFile.path()));
}
};

View File

@ -3,6 +3,7 @@
#include "blockstore/implementations/testfake/FakeBlockStore.h"
#include "blockstore/utils/BlockStoreUtils.h"
#include <cpp-utils/data/DataFixture.h>
#include <cpp-utils/tempfile/TempFile.h>
using ::testing::Test;
@ -10,6 +11,7 @@ using cpputils::DataFixture;
using cpputils::Data;
using cpputils::unique_ref;
using cpputils::make_unique_ref;
using cpputils::TempFile;
using blockstore::testfake::FakeBlockStore;
@ -19,10 +21,12 @@ class VersionCountingBlockStoreTest: public Test {
public:
static constexpr unsigned int BLOCKSIZE = 1024;
VersionCountingBlockStoreTest():
stateFile(false),
baseBlockStore(new FakeBlockStore),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), KnownBlockVersions())),
blockStore(make_unique_ref<VersionCountingBlockStore>(std::move(cpputils::nullcheck(std::unique_ptr<FakeBlockStore>(baseBlockStore)).value()), KnownBlockVersions(stateFile.path()))),
data(DataFixture::generate(BLOCKSIZE)) {
}
TempFile stateFile;
FakeBlockStore *baseBlockStore;
unique_ref<VersionCountingBlockStore> blockStore;
Data data;