Store own client id into the block next to the version number. This will be used to fix synchronization conflicts where the version number on one clients progresses slower than on another client, but synchronizes later.
This commit is contained in:
parent
a5391a854d
commit
57af168cfd
@ -1,4 +1,5 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <cpp-utils/random/Random.h>
|
||||||
#include "KnownBlockVersions.h"
|
#include "KnownBlockVersions.h"
|
||||||
|
|
||||||
namespace bf = boost::filesystem;
|
namespace bf = boost::filesystem;
|
||||||
@ -7,6 +8,7 @@ using std::pair;
|
|||||||
using std::string;
|
using std::string;
|
||||||
using boost::optional;
|
using boost::optional;
|
||||||
using boost::none;
|
using boost::none;
|
||||||
|
using cpputils::Random;
|
||||||
|
|
||||||
namespace blockstore {
|
namespace blockstore {
|
||||||
namespace versioncounting {
|
namespace versioncounting {
|
||||||
@ -14,11 +16,12 @@ namespace versioncounting {
|
|||||||
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;0\0";
|
const string KnownBlockVersions::HEADER = "cryfs.integritydata.knownblockversions;0\0";
|
||||||
|
|
||||||
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath)
|
KnownBlockVersions::KnownBlockVersions(const bf::path &stateFilePath)
|
||||||
:_knownVersions(_loadStateFile(stateFilePath)), _stateFilePath(stateFilePath), _valid(true) {
|
:_knownVersions(), _stateFilePath(stateFilePath), _myClientId(0), _valid(true) {
|
||||||
|
_loadStateFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
KnownBlockVersions::KnownBlockVersions(KnownBlockVersions &&rhs)
|
KnownBlockVersions::KnownBlockVersions(KnownBlockVersions &&rhs)
|
||||||
: _knownVersions(std::move(rhs._knownVersions)), _stateFilePath(std::move(rhs._stateFilePath)), _valid(true) {
|
: _knownVersions(std::move(rhs._knownVersions)), _stateFilePath(std::move(rhs._stateFilePath)), _myClientId(rhs._myClientId), _valid(true) {
|
||||||
rhs._valid = false;
|
rhs._valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,21 +54,24 @@ void KnownBlockVersions::updateVersion(const Key &key, uint64_t version) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unordered_map<Key, uint64_t> KnownBlockVersions::_loadStateFile(const bf::path &stateFilePath) {
|
void KnownBlockVersions::_loadStateFile() {
|
||||||
std::ifstream file(stateFilePath.native().c_str());
|
std::ifstream file(_stateFilePath.native().c_str());
|
||||||
if (!file.good()) {
|
if (!file.good()) {
|
||||||
return unordered_map<Key, uint64_t>();
|
// File doesn't exist means we loaded empty state. Assign a random client id.
|
||||||
|
_myClientId = *reinterpret_cast<uint32_t*>(Random::PseudoRandom().getFixedSize<sizeof(uint32_t)>().data());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
_checkHeader(&file);
|
_checkHeader(&file);
|
||||||
|
file.read((char*)&_myClientId, sizeof(_myClientId));
|
||||||
|
ASSERT(file.good(), "Error reading file");
|
||||||
|
|
||||||
unordered_map<Key, uint64_t> result;
|
_knownVersions.clear();
|
||||||
optional<pair<Key, uint64_t>> entry = _readEntry(&file);
|
optional<pair<Key, uint64_t>> entry = _readEntry(&file);
|
||||||
while(none != entry) {
|
while(none != entry) {
|
||||||
result.insert(*entry);
|
_knownVersions.insert(*entry);
|
||||||
entry = _readEntry(&file);
|
entry = _readEntry(&file);
|
||||||
}
|
}
|
||||||
ASSERT(file.eof(), "Didn't read until end of file");
|
ASSERT(file.eof(), "Didn't read until end of file");
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void KnownBlockVersions::_checkHeader(std::ifstream *file) {
|
void KnownBlockVersions::_checkHeader(std::ifstream *file) {
|
||||||
@ -95,11 +101,16 @@ optional<pair<Key, uint64_t>> KnownBlockVersions::_readEntry(std::ifstream *file
|
|||||||
void KnownBlockVersions::_saveStateFile() const {
|
void KnownBlockVersions::_saveStateFile() const {
|
||||||
std::ofstream file(_stateFilePath.native().c_str());
|
std::ofstream file(_stateFilePath.native().c_str());
|
||||||
file.write(HEADER.c_str(), HEADER.size());
|
file.write(HEADER.c_str(), HEADER.size());
|
||||||
|
file.write((char*)&_myClientId, sizeof(_myClientId));
|
||||||
for (const auto &entry : _knownVersions) {
|
for (const auto &entry : _knownVersions) {
|
||||||
file.write((char*)entry.first.data(), entry.first.BINARY_LENGTH);
|
file.write((char*)entry.first.data(), entry.first.BINARY_LENGTH);
|
||||||
file.write((char*)&entry.second, sizeof(entry.second));
|
file.write((char*)&entry.second, sizeof(entry.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t KnownBlockVersions::myClientId() const {
|
||||||
|
return _myClientId;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,17 @@ namespace blockstore {
|
|||||||
|
|
||||||
void updateVersion(const Key &key, uint64_t version);
|
void updateVersion(const Key &key, uint64_t version);
|
||||||
|
|
||||||
|
uint32_t myClientId() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<Key, uint64_t> _knownVersions;
|
std::unordered_map<Key, uint64_t> _knownVersions;
|
||||||
boost::filesystem::path _stateFilePath;
|
boost::filesystem::path _stateFilePath;
|
||||||
|
uint32_t _myClientId;
|
||||||
bool _valid;
|
bool _valid;
|
||||||
|
|
||||||
static const std::string HEADER;
|
static const std::string HEADER;
|
||||||
|
|
||||||
static std::unordered_map<Key, uint64_t> _loadStateFile(const boost::filesystem::path &stateFilePath);
|
void _loadStateFile();
|
||||||
static void _checkHeader(std::ifstream *file);
|
static void _checkHeader(std::ifstream *file);
|
||||||
static boost::optional<std::pair<Key, uint64_t>> _readEntry(std::ifstream *file);
|
static boost::optional<std::pair<Key, uint64_t>> _readEntry(std::ifstream *file);
|
||||||
void _saveStateFile() const;
|
void _saveStateFile() const;
|
||||||
|
@ -51,7 +51,7 @@ private:
|
|||||||
bool _dataChanged;
|
bool _dataChanged;
|
||||||
|
|
||||||
void _storeToBaseBlock();
|
void _storeToBaseBlock();
|
||||||
static cpputils::Data _prependHeaderToData(uint64_t version, cpputils::Data data);
|
static cpputils::Data _prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data);
|
||||||
static void _checkFormatHeader(const cpputils::Data &data);
|
static void _checkFormatHeader(const cpputils::Data &data);
|
||||||
static uint64_t _readVersion(const cpputils::Data &data);
|
static uint64_t _readVersion(const cpputils::Data &data);
|
||||||
static bool _versionIsNondecreasing(const Key &key, uint64_t version, KnownBlockVersions *knownBlockVersions);
|
static bool _versionIsNondecreasing(const Key &key, uint64_t version, KnownBlockVersions *knownBlockVersions);
|
||||||
@ -59,7 +59,7 @@ private:
|
|||||||
// This header is prepended to blocks to allow future versions to have compatibility.
|
// This header is prepended to blocks to allow future versions to have compatibility.
|
||||||
static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
|
static constexpr uint16_t FORMAT_VERSION_HEADER = 0;
|
||||||
static constexpr uint64_t VERSION_ZERO = 0;
|
static constexpr uint64_t VERSION_ZERO = 0;
|
||||||
static constexpr unsigned int HEADER_LENGTH = sizeof(FORMAT_VERSION_HEADER) + sizeof(VERSION_ZERO);
|
static constexpr unsigned int HEADER_LENGTH = sizeof(FORMAT_VERSION_HEADER) + sizeof(uint32_t) + sizeof(VERSION_ZERO);
|
||||||
|
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions) {
|
inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> VersionCountingBlock::TryCreateNew(BlockStore *baseBlockStore, const Key &key, cpputils::Data data, KnownBlockVersions *knownBlockVersions) {
|
||||||
cpputils::Data dataWithHeader = _prependHeaderToData(VERSION_ZERO, std::move(data));
|
cpputils::Data dataWithHeader = _prependHeaderToData(knownBlockVersions->myClientId(), VERSION_ZERO, std::move(data));
|
||||||
auto baseBlock = baseBlockStore->tryCreate(key, dataWithHeader.copy()); // TODO Copy necessary?
|
auto baseBlock = baseBlockStore->tryCreate(key, dataWithHeader.copy()); // TODO Copy necessary?
|
||||||
if (baseBlock == boost::none) {
|
if (baseBlock == boost::none) {
|
||||||
//TODO Test this code branch
|
//TODO Test this code branch
|
||||||
@ -79,11 +79,12 @@ inline boost::optional<cpputils::unique_ref<VersionCountingBlock>> VersionCounti
|
|||||||
return cpputils::make_unique_ref<VersionCountingBlock>(std::move(*baseBlock), std::move(dataWithHeader), VERSION_ZERO, knownBlockVersions);
|
return cpputils::make_unique_ref<VersionCountingBlock>(std::move(*baseBlock), std::move(dataWithHeader), VERSION_ZERO, knownBlockVersions);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline cpputils::Data VersionCountingBlock::_prependHeaderToData(const uint64_t version, cpputils::Data data) {
|
inline cpputils::Data VersionCountingBlock::_prependHeaderToData(uint32_t myClientId, uint64_t version, cpputils::Data data) {
|
||||||
static_assert(HEADER_LENGTH == sizeof(FORMAT_VERSION_HEADER) + sizeof(version), "Wrong header length");
|
static_assert(HEADER_LENGTH == sizeof(FORMAT_VERSION_HEADER) + sizeof(myClientId) + sizeof(version), "Wrong header length");
|
||||||
cpputils::Data result(data.size() + HEADER_LENGTH);
|
cpputils::Data result(data.size() + HEADER_LENGTH);
|
||||||
std::memcpy(result.dataOffset(0), &FORMAT_VERSION_HEADER, sizeof(FORMAT_VERSION_HEADER));
|
std::memcpy(result.dataOffset(0), &FORMAT_VERSION_HEADER, sizeof(FORMAT_VERSION_HEADER));
|
||||||
std::memcpy(result.dataOffset(sizeof(FORMAT_VERSION_HEADER)), &version, sizeof(version));
|
std::memcpy(result.dataOffset(sizeof(FORMAT_VERSION_HEADER)), &myClientId, sizeof(myClientId));
|
||||||
|
std::memcpy(result.dataOffset(sizeof(FORMAT_VERSION_HEADER)+sizeof(myClientId)), &version, sizeof(version));
|
||||||
std::memcpy((uint8_t*)result.dataOffset(HEADER_LENGTH), data.data(), data.size());
|
std::memcpy((uint8_t*)result.dataOffset(HEADER_LENGTH), data.data(), data.size());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -109,7 +110,7 @@ inline void VersionCountingBlock::_checkFormatHeader(const cpputils::Data &data)
|
|||||||
|
|
||||||
inline uint64_t VersionCountingBlock::_readVersion(const cpputils::Data &data) {
|
inline uint64_t VersionCountingBlock::_readVersion(const cpputils::Data &data) {
|
||||||
uint64_t version;
|
uint64_t version;
|
||||||
std::memcpy(&version, data.dataOffset(sizeof(FORMAT_VERSION_HEADER)), sizeof(version));
|
std::memcpy(&version, data.dataOffset(sizeof(FORMAT_VERSION_HEADER) + sizeof(uint32_t)), sizeof(version));
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +161,9 @@ inline void VersionCountingBlock::resize(size_t newSize) {
|
|||||||
inline void VersionCountingBlock::_storeToBaseBlock() {
|
inline void VersionCountingBlock::_storeToBaseBlock() {
|
||||||
if (_dataChanged) {
|
if (_dataChanged) {
|
||||||
++_version;
|
++_version;
|
||||||
std::memcpy(_dataWithHeader.dataOffset(sizeof(FORMAT_VERSION_HEADER)), &_version, sizeof(_version));
|
uint32_t myClientId = _knownBlockVersions->myClientId();
|
||||||
|
std::memcpy(_dataWithHeader.dataOffset(sizeof(FORMAT_VERSION_HEADER)), &myClientId, sizeof(myClientId));
|
||||||
|
std::memcpy(_dataWithHeader.dataOffset(sizeof(FORMAT_VERSION_HEADER) + sizeof(myClientId)), &_version, sizeof(_version));
|
||||||
_baseBlock->write(_dataWithHeader.data(), 0, _dataWithHeader.size());
|
_baseBlock->write(_dataWithHeader.data(), 0, _dataWithHeader.size());
|
||||||
_knownBlockVersions->updateVersion(key(), _version);
|
_knownBlockVersions->updateVersion(key(), _version);
|
||||||
_dataChanged = false;
|
_dataChanged = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user