Fix rename when overwriting an existing file in the same directory
This commit is contained in:
parent
c403ec6b48
commit
a03ab91aba
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<void (const blockstore::Key &key)> onOverwritten) {
|
||||
return _base->RenameChild(key, newName, onOverwritten);
|
||||
}
|
||||
|
||||
void statChild(const blockstore::Key &key, struct ::stat *result) const {
|
||||
return _base->statChild(key, result);
|
||||
}
|
||||
|
@ -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<void (const blockstore::Key &key)> onOverwritten) {
|
||||
std::unique_lock<std::mutex> 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<void (const blockstore::Key &key)> onOverwritten) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_entries.rename(key, newName, onOverwritten);
|
||||
_changed = true;
|
||||
}
|
||||
|
||||
boost::optional<const DirEntry&> DirBlob::GetChild(const string &name) const {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
return _entries.get(name);
|
||||
|
@ -47,6 +47,8 @@ namespace cryfs {
|
||||
mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime,
|
||||
std::function<void (const blockstore::Key &key)> onOverwritten);
|
||||
|
||||
void RenameChild(const blockstore::Key &key, const std::string &newName, std::function<void (const blockstore::Key &key)> onOverwritten);
|
||||
|
||||
void RemoveChild(const std::string &name);
|
||||
|
||||
void RemoveChild(const blockstore::Key &key);
|
||||
|
@ -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<uint8_t*>(serialized.dataOffset(offset)));
|
||||
offset += entry.serializedSize();
|
||||
for (auto iter = _entries.begin(); iter != _entries.end(); ++iter) {
|
||||
ASSERT(iter == _entries.begin() || std::less<Key>()((iter-1)->key(), iter->key()), "Invariant hurt: Directory entries should be ordered by key and not have duplicate keys.");
|
||||
iter->serialize(static_cast<uint8_t*>(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<const char*>(data);
|
||||
while (pos < static_cast<const char*>(data) + size) {
|
||||
pos = DirEntry::deserializeAndAddToVector(pos, &_entries);
|
||||
ASSERT(_entries.size() == 1 || std::less<Key>()(_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<void (const blockstore::Key &key)> 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<DirEntry>::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) {
|
||||
|
@ -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<void (const blockstore::Key &key)> onOverwritten);
|
||||
void rename(const blockstore::Key &key, const std::string &name, std::function<void (const blockstore::Key &key)> onOverwritten);
|
||||
boost::optional<const DirEntry&> get(const std::string &name) const;
|
||||
boost::optional<const DirEntry&> get(const blockstore::Key &key) const;
|
||||
void remove(const std::string &name);
|
||||
|
@ -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<void (const blockstore::Key &key)> onOverwritten) {
|
||||
return _base->RenameChild(key, newName, onOverwritten);
|
||||
}
|
||||
|
||||
void statChild(const blockstore::Key &key, struct ::stat *result) const {
|
||||
return _base->statChild(key, result);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user