From 9ecbe437abd82673f728c9ac3081db3a0a65b290 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Fri, 10 Apr 2015 00:52:00 +0200 Subject: [PATCH] - Refactor DirBlob: Keep an in-memory vector of the dir entries instead of regularly parsing it - Implement file deletion and dir deletion --- src/CryDevice.cpp | 16 +++-- src/CryDevice.h | 1 + src/CryDir.cpp | 7 +- src/CryDir.h | 3 +- src/CryFile.cpp | 3 +- src/impl/DirBlob.cpp | 153 +++++++++++++++++++++++++------------------ src/impl/DirBlob.h | 20 ++++-- 7 files changed, 124 insertions(+), 79 deletions(-) diff --git a/src/CryDevice.cpp b/src/CryDevice.cpp index 1089358c..0c82e1a8 100644 --- a/src/CryDevice.cpp +++ b/src/CryDevice.cpp @@ -58,15 +58,15 @@ unique_ptr CryDevice::Load(const bf::path &path) { if (path.parent_path().empty()) { //We are asked to load the root directory '/'. - return make_unique(this, _rootKey); + return make_unique(this, nullptr, _rootKey); } auto parent = LoadDirBlob(path.parent_path()); auto entry = parent->GetChild(path.filename().native()); - if (entry.first == fspp::Dir::EntryType::DIR) { - return make_unique(this, entry.second); - } else if (entry.first == fspp::Dir::EntryType::FILE) { - return make_unique(this, std::move(parent), entry.second); + if (entry.type == fspp::Dir::EntryType::DIR) { + return make_unique(this, std::move(parent), entry.key); + } else if (entry.type == fspp::Dir::EntryType::FILE) { + return make_unique(this, std::move(parent), entry.key); } else { throw FuseErrnoException(EIO); } @@ -81,7 +81,7 @@ unique_ptr CryDevice::LoadDirBlob(const bf::path &path) { // But fuse should rather return the correct error code. unique_ptr currentDir = make_unique(std::move(currentBlob)); - Key childKey = currentDir->GetChild(component.c_str()).second; + Key childKey = currentDir->GetChild(component.c_str()).key; currentBlob = _blobStore->load(childKey); } @@ -100,4 +100,8 @@ unique_ptr CryDevice::LoadBlob(const blockstore::Key &key) { return _blobStore->load(key); } +void CryDevice::RemoveBlob(const blockstore::Key &key) { + _blobStore->remove(_blobStore->load(key)); +} + } diff --git a/src/CryDevice.h b/src/CryDevice.h index 2e1b4941..dcdbc8f1 100644 --- a/src/CryDevice.h +++ b/src/CryDevice.h @@ -27,6 +27,7 @@ public: std::unique_ptr CreateBlob(); std::unique_ptr LoadBlob(const blockstore::Key &key); + void RemoveBlob(const blockstore::Key &key); private: blockstore::Key GetOrCreateRootKey(CryConfig *config); diff --git a/src/CryDir.cpp b/src/CryDir.cpp index 15faf63a..800feae9 100644 --- a/src/CryDir.cpp +++ b/src/CryDir.cpp @@ -25,8 +25,8 @@ using blockstore::Key; namespace cryfs { -CryDir::CryDir(CryDevice *device, const Key &key) -: _device(device), _key(key) { +CryDir::CryDir(CryDevice *device, unique_ptr parent, const Key &key) +: _device(device), _parent(std::move(parent)), _key(key) { } CryDir::~CryDir() { @@ -61,7 +61,8 @@ unique_ptr CryDir::LoadBlob() const { } void CryDir::rmdir() { - throw FuseErrnoException(ENOTSUP); + _parent->RemoveChild(_key); + _device->RemoveBlob(_key); } unique_ptr> CryDir::children() const { diff --git a/src/CryDir.h b/src/CryDir.h index 59145d12..9b5c1b7c 100644 --- a/src/CryDir.h +++ b/src/CryDir.h @@ -10,7 +10,7 @@ namespace cryfs { class CryDir: public fspp::Dir, CryNode { public: - CryDir(CryDevice *device, const blockstore::Key &key); + CryDir(CryDevice *device, std::unique_ptr parent, const blockstore::Key &key); virtual ~CryDir(); void stat(struct ::stat *result) const override; @@ -24,6 +24,7 @@ public: private: CryDevice *_device; + std::unique_ptr _parent; blockstore::Key _key; std::unique_ptr LoadBlob() const; diff --git a/src/CryFile.cpp b/src/CryFile.cpp index 2b202f44..a847d7f0 100644 --- a/src/CryFile.cpp +++ b/src/CryFile.cpp @@ -46,7 +46,8 @@ void CryFile::truncate(off_t size) const { } void CryFile::unlink() { - throw FuseErrnoException(ENOTSUP); + _parent->RemoveChild(_key); + _device->RemoveBlob(_key); } } diff --git a/src/impl/DirBlob.cpp b/src/impl/DirBlob.cpp index 01c3c272..81671858 100644 --- a/src/impl/DirBlob.cpp +++ b/src/impl/DirBlob.cpp @@ -18,23 +18,31 @@ using blobstore::Blob; using blockstore::Key; using blockstore::Data; -//TODO Refactor: Keep a parsed dir structure (list of entries and blob keys they're pointing to) in memory and serialize/deserialize it - namespace cryfs { -DirBlob::DirBlob(unique_ptr blob) -: _blob(std::move(blob)) { +DirBlob::DirBlob(unique_ptr blob) : + _blob(std::move(blob)), _entries(), _changed(false) { assert(magicNumber() == MagicNumbers::DIR); + _readEntriesFromBlob(); } DirBlob::~DirBlob() { + flush(); +} + +void DirBlob::flush() { + if (_changed) { + _writeEntriesToBlob(); + _changed = false; + } + _blob->flush(); } unique_ptr DirBlob::InitializeEmptyDir(unique_ptr blob) { blob->resize(1); unsigned char magicNumber = MagicNumbers::DIR; blob->write(&magicNumber, 0, 1); - return make_unique(std::move(blob)); + return make_unique < DirBlob > (std::move(blob)); } unsigned char DirBlob::magicNumber() const { @@ -43,39 +51,57 @@ unsigned char DirBlob::magicNumber() const { return number; } -void DirBlob::AppendChildrenTo(vector *result) const { - Data entries(_blob->size()-1); - _blob->read(entries.data(), 1, _blob->size()-1); - - const char *pos = (const char*)entries.data(); - while(pos < (const char*)entries.data()+entries.size()) { - pos = readAndAddNextChild(pos, result); +void DirBlob::_writeEntriesToBlob() { + //TODO Resizing is imperformant + _blob->resize(1); + unsigned int offset = 1; + for (const auto &entry : _entries) { + unsigned char entryTypeMagicNumber = static_cast(entry.type); + _blob->write(&entryTypeMagicNumber, offset, 1); + offset += 1; + _blob->write(entry.name.c_str(), offset, entry.name.size() + 1); + offset += entry.name.size() + 1; + string keystr = entry.key.ToString(); + _blob->write(keystr.c_str(), offset, keystr.size() + 1); + offset += keystr.size() + 1; } } +void DirBlob::_readEntriesFromBlob() { + _entries.clear(); + Data data(_blob->size() - 1); + _blob->read(data.data(), 1, _blob->size() - 1); + + const char *pos = (const char*) data.data(); + while (pos < (const char*) data.data() + data.size()) { + pos = readAndAddNextChild(pos, &_entries); + } +} + +const char *DirBlob::readAndAddNextChild(const char *pos, + vector *result) const { + // Read type magic number (whether it is a dir or a file) + fspp::Dir::EntryType type = + static_cast(*reinterpret_cast(pos)); + pos += 1; + + size_t namelength = strlen(pos); + std::string name(pos, namelength); + pos += namelength + 1; + + size_t keylength = strlen(pos); + std::string keystr(pos, keylength); + pos += keylength + 1; + + result->emplace_back(type, name, Key::FromString(keystr)); + return pos; +} + bool DirBlob::hasChild(const string &name) const { - //TODO Faster implementation without creating children array possible - vector children; - AppendChildrenTo(&children); - for (const auto &child : children) { - if (child.name == name) { - return true; - } - } - return false; -} - -const char *DirBlob::readAndAddNextChild(const char *pos, vector *result) const { - // Read type magic number (whether it is a dir or a file) - fspp::Dir::EntryType type = static_cast(*reinterpret_cast(pos)); - pos += 1; - - size_t length = strlen(pos); - std::string name(pos, length); - result->emplace_back(fspp::Dir::Entry(type, name)); - const char *posAfterName = pos + length + 1; - const char *posAfterKey = posAfterName + strlen(posAfterName) + 1; - return posAfterKey; + auto found = std::find_if(_entries.begin(), _entries.end(), [name] (const Entry &entry) { + return entry.name == name; + }); + return found != _entries.end(); } void DirBlob::AddChildDir(const std::string &name, const Key &blobKey) { @@ -86,43 +112,42 @@ void DirBlob::AddChildFile(const std::string &name, const Key &blobKey) { AddChild(name, blobKey, fspp::Dir::EntryType::FILE); } -void DirBlob::AddChild(const std::string &name, const Key &blobKey, fspp::Dir::EntryType entryType) { +void DirBlob::AddChild(const std::string &name, const Key &blobKey, + fspp::Dir::EntryType entryType) { if (hasChild(name)) { - throw fspp::fuse::FuseErrnoException(EEXIST); + throw fspp::fuse::FuseErrnoException(EEXIST); } - //TODO blob.resize(blob.size()+X) has to traverse tree twice. Better would be blob.addSize(X) which returns old size - uint64_t oldBlobSize = _blob->size(); - string blobKeyStr = blobKey.ToString(); - _blob->resize(oldBlobSize + name.size() + 1 + blobKeyStr.size() + 1); - - //Write entry type - unsigned char entryTypeMagicNumber = static_cast(entryType); - _blob->write(&entryTypeMagicNumber, oldBlobSize, 1); - //Write entry name inclusive null terminator - _blob->write(name.c_str(), oldBlobSize + 1, name.size()+1); - //Write blob key inclusive null terminator - _blob->write(blobKeyStr.c_str(), oldBlobSize + 1 + name.size() + 1, blobKeyStr.size()+1); + _entries.emplace_back(entryType, name, blobKey); + _changed = true; } -pair DirBlob::GetChild(const string &name) const { - Data entries(_blob->size()-1); - _blob->read(entries.data(), 1, _blob->size()-1); - - const char *pos = (const char*)entries.data(); - while(pos < (const char*)entries.data()+entries.size()) { - fspp::Dir::EntryType type = static_cast(*reinterpret_cast(pos)); - pos += 1; // Skip entry type magic number (whether it is a dir or a file) - size_t name_length = strlen(pos); - if (name_length == name.size() && 0==std::memcmp(pos, name.c_str(), name_length)) { - pos += strlen(pos) + 1; // Skip name - return make_pair(type, Key::FromString(pos)); // Return key - } - pos += strlen(pos) + 1; // Skip name - pos += strlen(pos) + 1; // Skip key +const DirBlob::Entry &DirBlob::GetChild(const string &name) const { + auto found = std::find_if(_entries.begin(), _entries.end(), [name] (const Entry &entry) { + return entry.name == name; + }); + if (found == _entries.end()) { + throw fspp::fuse::FuseErrnoException(ENOENT); } - throw fspp::fuse::FuseErrnoException(ENOENT); + return *found; } +void DirBlob::RemoveChild(const Key &key) { + auto found = std::find_if(_entries.begin(), _entries.end(), [key] (const Entry &entry) { + return entry.key == key; + }); + if (found == _entries.end()) { + throw fspp::fuse::FuseErrnoException(ENOENT); + } + _entries.erase(found); + _changed = true; +} + +void DirBlob::AppendChildrenTo(vector *result) const { + result->reserve(result->size() + _entries.size()); + for (const auto &entry : _entries) { + result->emplace_back(entry.type, entry.name); + } +} } diff --git a/src/impl/DirBlob.h b/src/impl/DirBlob.h index cb7c05a2..0b534d0d 100644 --- a/src/impl/DirBlob.h +++ b/src/impl/DirBlob.h @@ -14,27 +14,39 @@ namespace cryfs{ class DirBlob { public: + struct Entry { + Entry(fspp::Dir::EntryType type_, const std::string &name_, const blockstore::Key &key_): type(type_), name(name_), key(key_) {} + fspp::Dir::EntryType type; + std::string name; + blockstore::Key key; + }; + static std::unique_ptr InitializeEmptyDir(std::unique_ptr blob); DirBlob(std::unique_ptr blob); virtual ~DirBlob(); void AppendChildrenTo(std::vector *result) const; - //TODO Use struct instead of pair - std::pair GetChild(const std::string &name) const; + const Entry &GetChild(const std::string &name) const; void AddChildDir(const std::string &name, const blockstore::Key &blobKey); void AddChildFile(const std::string &name, const blockstore::Key &blobKey); + void RemoveChild(const blockstore::Key &key); + void flush(); private: unsigned char magicNumber() const; void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type); - const char *readAndAddNextChild(const char *pos, std::vector *result) const; - const char *getStartingPosOfEntry(const char *pos, const std::string &name) const; + const char *readAndAddNextChild(const char *pos, std::vector *result) const; bool hasChild(const std::string &name) const; + void _readEntriesFromBlob(); + void _writeEntriesToBlob(); + std::unique_ptr _blob; + std::vector _entries; + bool _changed; DISALLOW_COPY_AND_ASSIGN(DirBlob); };