Integrity data from KnownBlockVersions is persisted to a file
This commit is contained in:
parent
d7f547dd47
commit
13411c4e59
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user