From b96cc486392a9de7c2af92c75677dd6ea876e1d5 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Tue, 21 Apr 2015 23:18:50 +0200 Subject: [PATCH] Implemented chmod/chown and fixed some minor details --- src/CryDevice.cpp | 6 +- src/CryDir.cpp | 15 ++--- src/CryDir.h | 1 - src/CryFile.cpp | 8 --- src/CryFile.h | 1 - src/CryNode.cpp | 24 +++++++- src/CryNode.h | 3 + src/impl/DirBlob.cpp | 140 +++++++++++++++++++++++++++++++++---------- src/impl/DirBlob.h | 36 +++++++++-- 9 files changed, 173 insertions(+), 61 deletions(-) diff --git a/src/CryDevice.cpp b/src/CryDevice.cpp index 67253766..a915a971 100644 --- a/src/CryDevice.cpp +++ b/src/CryDevice.cpp @@ -47,7 +47,7 @@ Key CryDevice::GetOrCreateRootKey(CryConfig *config) { Key CryDevice::CreateRootBlobAndReturnKey() { auto rootBlob = _blobStore->create(); Key rootBlobKey = rootBlob->key(); - DirBlob::InitializeEmptyDir(std::move(rootBlob)); + DirBlob::InitializeEmptyDir(std::move(rootBlob), this); return rootBlobKey; } @@ -81,13 +81,13 @@ unique_ptr CryDevice::LoadDirBlob(const bf::path &path) { //TODO Check whether the next path component is a dir. // Right now, an assertion in DirBlob constructor will fail if it isn't. // But fuse should rather return the correct error code. - unique_ptr currentDir = make_unique(std::move(currentBlob)); + unique_ptr currentDir = make_unique(std::move(currentBlob), this); Key childKey = currentDir->GetChild(component.c_str()).key; currentBlob = _blobStore->load(childKey); } - return make_unique(std::move(currentBlob)); + return make_unique(std::move(currentBlob), this); } void CryDevice::statfs(const bf::path &path, struct statvfs *fsstat) { diff --git a/src/CryDir.cpp b/src/CryDir.cpp index 794460dc..4f3d2b1b 100644 --- a/src/CryDir.cpp +++ b/src/CryDir.cpp @@ -32,17 +32,11 @@ CryDir::CryDir(CryDevice *device, unique_ptr parent, const Key &key) CryDir::~CryDir() { } -void CryDir::stat(struct ::stat *result) const { - result->st_mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IWUSR; - return; - throw FuseErrnoException(ENOTSUP); -} - unique_ptr CryDir::createAndOpenFile(const string &name, mode_t mode) { auto blob = LoadBlob(); auto child = device()->CreateBlob(); Key childkey = child->key(); - blob->AddChildFile(name, childkey); + blob->AddChildFile(name, childkey, mode); //TODO Do we need a return value in createDir for fspp? If not, change fspp Dir interface! auto childblob = FileBlob::InitializeEmptyFile(std::move(child)); return make_unique(std::move(childblob)); @@ -52,12 +46,13 @@ void CryDir::createDir(const string &name, mode_t mode) { auto blob = LoadBlob(); auto child = device()->CreateBlob(); Key childkey = child->key(); - blob->AddChildDir(name, childkey); - DirBlob::InitializeEmptyDir(std::move(child)); + blob->AddChildDir(name, childkey, mode); + DirBlob::InitializeEmptyDir(std::move(child), device()); } unique_ptr CryDir::LoadBlob() const { - return make_unique(CryNode::LoadBlob()); + //TODO Without const_cast? + return make_unique(CryNode::LoadBlob(), const_cast(device())); } unique_ptr> CryDir::children() const { diff --git a/src/CryDir.h b/src/CryDir.h index d4ec35eb..1f8e7206 100644 --- a/src/CryDir.h +++ b/src/CryDir.h @@ -13,7 +13,6 @@ public: CryDir(CryDevice *device, std::unique_ptr parent, const blockstore::Key &key); virtual ~CryDir(); - void stat(struct ::stat *result) const override; //TODO return type variance to CryFile/CryDir? std::unique_ptr createAndOpenFile(const std::string &name, mode_t mode) override; void createDir(const std::string &name, mode_t mode) override; diff --git a/src/CryFile.cpp b/src/CryFile.cpp index 04913b54..f8c4b70f 100644 --- a/src/CryFile.cpp +++ b/src/CryFile.cpp @@ -31,14 +31,6 @@ unique_ptr CryFile::open(int flags) const { return make_unique(make_unique(std::move(blob))); } -void CryFile::stat(struct ::stat *result) const { - result->st_mode = S_IFREG | S_IRUSR | S_IXUSR | S_IWUSR; - //TODO Loading the blob for only getting the size is not very performant. - result->st_size = FileBlob(LoadBlob()).size(); - return; - throw FuseErrnoException(ENOTSUP); -} - void CryFile::truncate(off_t size) const { FileBlob(LoadBlob()).resize(size); } diff --git a/src/CryFile.h b/src/CryFile.h index 9b70216c..b25334c7 100644 --- a/src/CryFile.h +++ b/src/CryFile.h @@ -14,7 +14,6 @@ public: CryFile(CryDevice *device, std::unique_ptr parent, const blockstore::Key &key); virtual ~CryFile(); - void stat(struct ::stat *result) const override; std::unique_ptr open(int flags) const override; void truncate(off_t size) const override; fspp::Dir::EntryType getType() const override; diff --git a/src/CryNode.cpp b/src/CryNode.cpp index e7c06d8a..0be7a014 100644 --- a/src/CryNode.cpp +++ b/src/CryNode.cpp @@ -31,19 +31,23 @@ CryNode::~CryNode() { } void CryNode::access(int mask) const { + //TODO return; throw FuseErrnoException(ENOTSUP); } void CryNode::rename(const bf::path &to) { //TODO More efficient implementation possible: directly rename when it's actually not moved to a different directory + // It's also quite ugly code because in the parent==targetDir case, it depends on _parent not overriding the changes made by targetDir. + mode_t mode = _parent->GetChild(_key).mode; _parent->RemoveChild(_key); _parent->flush(); auto targetDir = _device->LoadDirBlob(to.parent_path()); - targetDir->AddChild(to.filename().native(), _key, getType()); + targetDir->AddChild(to.filename().native(), _key, getType(), mode); } void CryNode::utimens(const timespec times[2]) { + //TODO throw FuseErrnoException(ENOTSUP); } @@ -64,4 +68,22 @@ unique_ptr CryNode::LoadBlob() const { return _device->LoadBlob(_key); } +void CryNode::stat(struct ::stat *result) const { + if(_parent.get() == nullptr) { + //We arethe root directory. + //TODO What should we do? + result->st_mode = S_IFDIR; + } else { + _parent->statChild(_key, result); + } +} + +void CryNode::chmod(mode_t mode) { + _parent->chmodChild(_key, mode); +} + +void CryNode::chown(uid_t uid, gid_t gid) { + _parent->chownChild(_key, uid, gid); +} + } diff --git a/src/CryNode.h b/src/CryNode.h index 7d81ad85..56ceba8b 100644 --- a/src/CryNode.h +++ b/src/CryNode.h @@ -14,6 +14,9 @@ class CryNode: public virtual fspp::Node { public: CryNode(CryDevice *device, std::unique_ptr parent, const blockstore::Key &key); void access(int mask) const override; + void stat(struct ::stat *result) const override; + void chmod(mode_t mode) override; + void chown(uid_t uid, gid_t gid) override; void rename(const boost::filesystem::path &to) override; void utimens(const timespec times[2]) override; void remove() override; diff --git a/src/impl/DirBlob.cpp b/src/impl/DirBlob.cpp index 81671858..be6d89f1 100644 --- a/src/impl/DirBlob.cpp +++ b/src/impl/DirBlob.cpp @@ -5,7 +5,10 @@ #include "messmer/fspp/fuse/FuseErrnoException.h" #include +#include #include "MagicNumbers.h" +#include "../CryDevice.h" +#include "FileBlob.h" using std::unique_ptr; using std::make_unique; @@ -20,29 +23,26 @@ using blockstore::Data; namespace cryfs { -DirBlob::DirBlob(unique_ptr blob) : - _blob(std::move(blob)), _entries(), _changed(false) { +DirBlob::DirBlob(unique_ptr blob, CryDevice *device) : + _device(device), _blob(std::move(blob)), _entries(), _changed(false) { assert(magicNumber() == MagicNumbers::DIR); _readEntriesFromBlob(); } DirBlob::~DirBlob() { - flush(); + _writeEntriesToBlob(); } void DirBlob::flush() { - if (_changed) { - _writeEntriesToBlob(); - _changed = false; - } + _writeEntriesToBlob(); _blob->flush(); } -unique_ptr DirBlob::InitializeEmptyDir(unique_ptr blob) { +unique_ptr DirBlob::InitializeEmptyDir(unique_ptr blob, CryDevice *device) { blob->resize(1); unsigned char magicNumber = MagicNumbers::DIR; blob->write(&magicNumber, 0, 1); - return make_unique < DirBlob > (std::move(blob)); + return make_unique(std::move(blob), device); } unsigned char DirBlob::magicNumber() const { @@ -52,18 +52,28 @@ unsigned char DirBlob::magicNumber() const { } 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; + if (_changed) { + //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; + _blob->write(&entry.uid, offset, sizeof(uid_t)); + //TODO Writing them all in separate write calls is maybe imperformant. We could write the whole entry in one write call instead. + offset += sizeof(uid_t); + _blob->write(&entry.gid, offset, sizeof(gid_t)); + offset += sizeof(gid_t); + _blob->write(&entry.mode, offset, sizeof(mode_t)); + offset += sizeof(mode_t); + } + _changed = false; } } @@ -93,7 +103,18 @@ const char *DirBlob::readAndAddNextChild(const char *pos, std::string keystr(pos, keylength); pos += keylength + 1; - result->emplace_back(type, name, Key::FromString(keystr)); + //It might make sense to read all of them at once with a memcpy + + uid_t uid = *(uid_t*)pos; + pos += sizeof(uid_t); + + gid_t gid = *(gid_t*)pos; + pos += sizeof(gid_t); + + mode_t mode = *(mode_t*)pos; + pos += sizeof(mode_t); + + result->emplace_back(type, name, Key::FromString(keystr), mode, uid, gid); return pos; } @@ -104,21 +125,21 @@ bool DirBlob::hasChild(const string &name) const { return found != _entries.end(); } -void DirBlob::AddChildDir(const std::string &name, const Key &blobKey) { - AddChild(name, blobKey, fspp::Dir::EntryType::DIR); +void DirBlob::AddChildDir(const std::string &name, const Key &blobKey, mode_t mode) { + AddChild(name, blobKey, fspp::Dir::EntryType::DIR, mode); } -void DirBlob::AddChildFile(const std::string &name, const Key &blobKey) { - AddChild(name, blobKey, fspp::Dir::EntryType::FILE); +void DirBlob::AddChildFile(const std::string &name, const Key &blobKey, mode_t mode) { + AddChild(name, blobKey, fspp::Dir::EntryType::FILE, mode); } void DirBlob::AddChild(const std::string &name, const Key &blobKey, - fspp::Dir::EntryType entryType) { + fspp::Dir::EntryType entryType, mode_t mode) { if (hasChild(name)) { throw fspp::fuse::FuseErrnoException(EEXIST); } - _entries.emplace_back(entryType, name, blobKey); + _entries.emplace_back(entryType, name, blobKey, mode); _changed = true; } @@ -132,17 +153,33 @@ const DirBlob::Entry &DirBlob::GetChild(const string &name) const { return *found; } -void DirBlob::RemoveChild(const Key &key) { +const DirBlob::Entry &DirBlob::GetChild(const Key &key) const { auto found = std::find_if(_entries.begin(), _entries.end(), [key] (const Entry &entry) { - return entry.key == key; + return entry.key == key; }); if (found == _entries.end()) { - throw fspp::fuse::FuseErrnoException(ENOENT); + throw fspp::fuse::FuseErrnoException(ENOENT); } + return *found; +} + +void DirBlob::RemoveChild(const Key &key) { + auto found = _findChild(key); _entries.erase(found); _changed = true; } +std::vector::iterator DirBlob::_findChild(const Key &key) { + //TODO Code duplication with GetChild(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); + } + return found; +} + void DirBlob::AppendChildrenTo(vector *result) const { result->reserve(result->size() + _entries.size()); for (const auto &entry : _entries) { @@ -150,4 +187,45 @@ void DirBlob::AppendChildrenTo(vector *result) const { } } +void DirBlob::statChild(const Key &key, struct ::stat *result) const { + auto child = GetChild(key); + //TODO Loading the blob for only getting the size is not very performant. + // Furthermore, this is the only reason why DirBlob needs a pointer to CryDevice, which is ugly + result->st_mode = child.mode; + result->st_uid = child.uid; + result->st_gid = child.gid; + //TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..") + result->st_nlink = 1; + //TODO Handle file access times + clock_gettime(CLOCK_REALTIME, &result->st_atim); + result->st_mtim = result->st_ctim = result->st_atim; + if (child.type == fspp::Dir::EntryType::FILE) { + result->st_size = FileBlob(_device->LoadBlob(key)).size(); + } else { + result->st_size = 4096; + } + //TODO Move ceilDivision to general utils which can be used by cryfs as well + result->st_blocks = blobstore::onblocks::utils::ceilDivision(result->st_size, 512); + result->st_blksize = _device->BLOCKSIZE_BYTES; +} + +void DirBlob::chmodChild(const Key &key, mode_t mode) { + auto found = _findChild(key); + assert ((S_ISREG(mode) && S_ISREG(found->mode)) || (S_ISDIR(mode) && S_ISDIR(found->mode))); + found->mode = mode; + _changed = true; +} + +void DirBlob::chownChild(const Key &key, uid_t uid, gid_t gid) { + auto found = _findChild(key); + if (uid != (uid_t)-1) { + found->uid = uid; + _changed = true; + } + if (gid != (gid_t)-1) { + found->gid = gid; + _changed = true; + } +} + } diff --git a/src/impl/DirBlob.h b/src/impl/DirBlob.h index 632bb990..18c65e35 100644 --- a/src/impl/DirBlob.h +++ b/src/impl/DirBlob.h @@ -11,29 +11,50 @@ #include namespace cryfs{ +class CryDevice; class DirBlob { public: struct Entry { - Entry(fspp::Dir::EntryType type_, const std::string &name_, const blockstore::Key &key_): type(type_), name(name_), key(key_) {} + //TODO Remove default value for parameters uid_ and gid_ and instead pass them in + Entry(fspp::Dir::EntryType type_, const std::string &name_, const blockstore::Key &key_, mode_t mode_, uid_t uid_=0, gid_t gid_=0): type(type_), name(name_), key(key_), mode(mode_), uid(uid_), gid(gid_) { + switch(type) { + case fspp::Dir::EntryType::FILE: + mode |= S_IFREG; + break; + case fspp::Dir::EntryType::DIR: + mode |= S_IFDIR; + break; + } + assert((S_ISREG(mode) && type == fspp::Dir::EntryType::FILE) || (S_ISDIR(mode) && type == fspp::Dir::EntryType::DIR)); + } + fspp::Dir::EntryType type; std::string name; blockstore::Key key; + uid_t uid; + gid_t gid; + mode_t mode; }; - static std::unique_ptr InitializeEmptyDir(std::unique_ptr blob); + static std::unique_ptr InitializeEmptyDir(std::unique_ptr blob, CryDevice *device); - DirBlob(std::unique_ptr blob); + DirBlob(std::unique_ptr blob, CryDevice *device); virtual ~DirBlob(); void AppendChildrenTo(std::vector *result) 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 AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type); + const Entry &GetChild(const blockstore::Key &key) const; + void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode); + void AddChildFile(const std::string &name, const blockstore::Key &blobKey, mode_t mode); + void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type, mode_t mode); void RemoveChild(const blockstore::Key &key); void flush(); + void statChild(const blockstore::Key &key, struct ::stat *result) const; + void chmodChild(const blockstore::Key &key, mode_t mode); + void chownChild(const blockstore::Key &key, uid_t uid, gid_t gid); + private: unsigned char magicNumber() const; @@ -43,6 +64,9 @@ private: void _readEntriesFromBlob(); void _writeEntriesToBlob(); + std::vector::iterator _findChild(const blockstore::Key &key); + + CryDevice *_device; std::unique_ptr _blob; std::vector _entries; bool _changed;