diff --git a/src/cryfs/filesystem/CryNode.cpp b/src/cryfs/filesystem/CryNode.cpp index eeb77ef1..dd801631 100644 --- a/src/cryfs/filesystem/CryNode.cpp +++ b/src/cryfs/filesystem/CryNode.cpp @@ -62,14 +62,16 @@ void CryNode::rename(const bf::path &to) { if (old == boost::none) { throw FuseErrnoException(EIO); } - std::string oldName = old->name(); // Store, because if targetDir == *_parent, then the 'old' object will be invalidated after we add something to targetDir - if (targetDir->key() != (*_parent)->key() || oldName != to.filename().native()) { - auto onOverwritten = [this] (const blockstore::Key &key) { + fsblobstore::DirEntry oldEntry = *old; // Copying this and not only keeping the reference is necessary, because the operations below (i.e. RenameChild()) might make a reference invalid. + auto onOverwritten = [this] (const blockstore::Key &key) { device()->RemoveBlob(key); - }; - targetDir->AddOrOverwriteChild(to.filename().native(), old->key(), old->type(), old->mode(), old->uid(), old->gid(), - old->lastAccessTime(), old->lastModificationTime(), onOverwritten); - (*_parent)->RemoveChild(oldName); + }; + if (targetDir->key() == (*_parent)->key()) { + targetDir->RenameChild(oldEntry.key(), to.filename().native(), onOverwritten); + } else { + targetDir->AddOrOverwriteChild(to.filename().native(), oldEntry.key(), oldEntry.type(), oldEntry.mode(), oldEntry.uid(), oldEntry.gid(), + oldEntry.lastAccessTime(), oldEntry.lastModificationTime(), onOverwritten); + (*_parent)->RemoveChild(oldEntry.name()); } } diff --git a/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h b/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h index 15d0ab7f..7ef37f5f 100644 --- a/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h +++ b/src/cryfs/filesystem/cachingfsblobstore/DirBlobRef.h @@ -48,6 +48,10 @@ public: return _base->AddOrOverwriteChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime, onOverwritten); } + void RenameChild(const blockstore::Key &key, const std::string &newName, std::function onOverwritten) { + return _base->RenameChild(key, newName, onOverwritten); + } + void statChild(const blockstore::Key &key, struct ::stat *result) const { return _base->statChild(key, result); } diff --git a/src/cryfs/filesystem/fsblobstore/DirBlob.cpp b/src/cryfs/filesystem/fsblobstore/DirBlob.cpp index 72e0e192..b2915b0e 100644 --- a/src/cryfs/filesystem/fsblobstore/DirBlob.cpp +++ b/src/cryfs/filesystem/fsblobstore/DirBlob.cpp @@ -89,11 +89,16 @@ void DirBlob::AddOrOverwriteChild(const std::string &name, const Key &blobKey, f mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime, std::function onOverwritten) { std::unique_lock lock(_mutex); - _entries.addOrOverwrite(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime, onOverwritten); _changed = true; } +void DirBlob::RenameChild(const blockstore::Key &key, const std::string &newName, std::function onOverwritten) { + std::unique_lock lock(_mutex); + _entries.rename(key, newName, onOverwritten); + _changed = true; +} + boost::optional DirBlob::GetChild(const string &name) const { std::unique_lock lock(_mutex); return _entries.get(name); diff --git a/src/cryfs/filesystem/fsblobstore/DirBlob.h b/src/cryfs/filesystem/fsblobstore/DirBlob.h index 4234fd96..4875f197 100644 --- a/src/cryfs/filesystem/fsblobstore/DirBlob.h +++ b/src/cryfs/filesystem/fsblobstore/DirBlob.h @@ -47,6 +47,8 @@ namespace cryfs { mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime, std::function onOverwritten); + void RenameChild(const blockstore::Key &key, const std::string &newName, std::function onOverwritten); + void RemoveChild(const std::string &name); void RemoveChild(const blockstore::Key &key); diff --git a/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp index 13af750e..92aa8f49 100644 --- a/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.cpp @@ -19,9 +19,10 @@ DirEntryList::DirEntryList() : _entries() { Data DirEntryList::serialize() const { Data serialized(_serializedSize()); unsigned int offset = 0; - for (const auto &entry : _entries) { - entry.serialize(static_cast(serialized.dataOffset(offset))); - offset += entry.serializedSize(); + for (auto iter = _entries.begin(); iter != _entries.end(); ++iter) { + ASSERT(iter == _entries.begin() || std::less()((iter-1)->key(), iter->key()), "Invariant hurt: Directory entries should be ordered by key and not have duplicate keys."); + iter->serialize(static_cast(serialized.dataOffset(offset))); + offset += iter->serializedSize(); } return serialized; } @@ -39,6 +40,7 @@ void DirEntryList::deserializeFrom(const void *data, uint64_t size) { const char *pos = static_cast(data); while (pos < static_cast(data) + size) { pos = DirEntry::deserializeAndAddToVector(pos, &_entries); + ASSERT(_entries.size() == 1 || std::less()(_entries[_entries.size()-2].key(), _entries[_entries.size()-1].key()), "Invariant hurt: Directory entries should be ordered by key and not have duplicate keys."); } } @@ -72,6 +74,22 @@ void DirEntryList::addOrOverwrite(const string &name, const Key &blobKey, fspp:: } } +void DirEntryList::rename(const blockstore::Key &key, const std::string &name, std::function onOverwritten) { + auto foundSameName = _findByName(name); + if (foundSameName != _entries.end()) { + onOverwritten(foundSameName->key()); + _entries.erase(foundSameName); + } + + ASSERT(_findByName(name) == _entries.end(), "There is still an entry with this name. That means there was a duplicate."); + + auto elementToRename = _findByKey(key); + std::string oldName = elementToRename->name(); + ASSERT(elementToRename != _entries.end(), "Didn't find element to rename"); + elementToRename->setName(name); + ASSERT(_findByName(oldName) == _entries.end(), "There is an entry with the old name left. That means there was a duplicate."); +} + void DirEntryList::_overwrite(vector::iterator entry, const string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) { if (entry->type() != entryType) { diff --git a/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h index 0f0f1b79..7add8c44 100644 --- a/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntryList.h @@ -26,6 +26,7 @@ namespace cryfs { void addOrOverwrite(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime, std::function onOverwritten); + void rename(const blockstore::Key &key, const std::string &name, std::function onOverwritten); boost::optional get(const std::string &name) const; boost::optional get(const blockstore::Key &key) const; void remove(const std::string &name); diff --git a/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h b/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h index 1498900e..9e596f93 100644 --- a/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h +++ b/src/cryfs/filesystem/parallelaccessfsblobstore/DirBlobRef.h @@ -44,6 +44,10 @@ public: return _base->AddOrOverwriteChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime, onOverwritten); } + void RenameChild(const blockstore::Key &key, const std::string &newName, std::function onOverwritten) { + return _base->RenameChild(key, newName, onOverwritten); + } + void statChild(const blockstore::Key &key, struct ::stat *result) const { return _base->statChild(key, result); }