Rename operation allows overwriting existing files.

This commit is contained in:
Sebastian Messmer 2016-03-24 02:47:50 +00:00
parent 03ccead85b
commit 6e7004d485
9 changed files with 160 additions and 34 deletions

View File

@ -8,6 +8,7 @@ Version 0.9.3 (unreleased)
* The ciphertext block size is configurable. You can use the "--blocksize-bytes" command line argument. If not specified, CryFS will ask you for a block size when creating a file system.
* Fix fstat (a bug in the fstat implementation caused problems with some text editors (e.g. nano) falsely thinking a file changed since they opened it).
* Fix rename (when trying to rename a file to an already existing file name, a bug deleted it instead).
* Rename operation allows overwriting existing files.
Version 0.9.2
---------------

View File

@ -63,8 +63,8 @@ void CryNode::rename(const bf::path &to) {
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 (oldName != to.filename().native()) {
targetDir->AddChild(to.filename().native(), old->key(), old->type(), old->mode(), old->uid(), old->gid(),
if (targetDir->key() != (*_parent)->key() || oldName != to.filename().native()) {
targetDir->AddOrOverwriteChild(to.filename().native(), old->key(), old->type(), old->mode(), old->uid(), old->gid(),
old->lastAccessTime(), old->lastModificationTime());
(*_parent)->RemoveChild(oldName);
}

View File

@ -42,9 +42,9 @@ public:
return _base->flush();
}
void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
void AddOrOverwriteChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
return _base->AddChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime);
return _base->AddOrOverwriteChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime);
}
void statChild(const blockstore::Key &key, struct ::stat *result) const {

View File

@ -65,24 +65,31 @@ void DirBlob::_readEntriesFromBlob() {
}
void DirBlob::AddChildDir(const std::string &name, const Key &blobKey, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
AddChild(name, blobKey, fspp::Dir::EntryType::DIR, mode, uid, gid, lastAccessTime, lastModificationTime);
_addChild(name, blobKey, fspp::Dir::EntryType::DIR, mode, uid, gid, lastAccessTime, lastModificationTime);
}
void DirBlob::AddChildFile(const std::string &name, const Key &blobKey, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
AddChild(name, blobKey, fspp::Dir::EntryType::FILE, mode, uid, gid, lastAccessTime, lastModificationTime);
_addChild(name, blobKey, fspp::Dir::EntryType::FILE, mode, uid, gid, lastAccessTime, lastModificationTime);
}
void DirBlob::AddChildSymlink(const std::string &name, const blockstore::Key &blobKey, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
AddChild(name, blobKey, fspp::Dir::EntryType::SYMLINK, S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH, uid, gid, lastAccessTime, lastModificationTime);
_addChild(name, blobKey, fspp::Dir::EntryType::SYMLINK, S_IFLNK | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH, uid, gid, lastAccessTime, lastModificationTime);
}
void DirBlob::AddChild(const std::string &name, const Key &blobKey,
void DirBlob::_addChild(const std::string &name, const Key &blobKey,
fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
std::unique_lock<std::mutex> lock(_mutex);
_entries.add(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime);
_changed = true;
}
void DirBlob::AddOrOverwriteChild(const std::string &name, const Key &blobKey,
fspp::Dir::EntryType entryType, mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
std::unique_lock<std::mutex> lock(_mutex);
_entries.addOrOverwrite(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime);
_changed = true;
}
boost::optional<const DirEntry&> DirBlob::GetChild(const string &name) const {
std::unique_lock<std::mutex> lock(_mutex);
return _entries.get(name);

View File

@ -43,7 +43,7 @@ namespace cryfs {
void AddChildSymlink(const std::string &name, const blockstore::Key &blobKey, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime);
void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
void AddOrOverwriteChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime);
void RemoveChild(const std::string &name);
@ -66,6 +66,8 @@ namespace cryfs {
private:
void _addChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime);
void _readEntriesFromBlob();
void _writeEntriesToBlob();

View File

@ -43,10 +43,7 @@ void DirEntryList::deserializeFrom(const void *data, uint64_t size) {
}
bool DirEntryList::_hasChild(const string &name) const {
auto found = std::find_if(_entries.begin(), _entries.end(), [&name] (const DirEntry &entry) {
return entry.name() == name;
});
return found != _entries.end();
return _entries.end() != _findByName(name);
}
void DirEntryList::add(const string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode,
@ -54,10 +51,40 @@ void DirEntryList::add(const string &name, const Key &blobKey, fspp::Dir::EntryT
if (_hasChild(name)) {
throw fspp::fuse::FuseErrnoException(EEXIST);
}
_add(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime);
}
void DirEntryList::_add(const string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode,
uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
auto insert_pos = _findUpperBound(blobKey);
_entries.emplace(insert_pos, entryType, name, blobKey, mode, uid, gid, lastAccessTime, lastModificationTime, time::now());
}
void DirEntryList::addOrOverwrite(const string &name, const Key &blobKey, fspp::Dir::EntryType entryType, mode_t mode,
uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
auto found = _findByName(name);
if (found != _entries.end()) {
_overwrite(&*found, name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime);
} else {
_add(name, blobKey, entryType, mode, uid, gid, lastAccessTime, lastModificationTime);
}
}
void DirEntryList::_overwrite(DirEntry *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) {
if (entry->type() == fspp::Dir::EntryType::DIR) {
// new path is an existing directory, but old path is not a directory
throw fspp::fuse::FuseErrnoException(EISDIR);
}
if (entryType == fspp::Dir::EntryType::DIR) {
// oldpath is a directory, and newpath exists but is not a directory.
throw fspp::fuse::FuseErrnoException(ENOTDIR);
}
}
*entry = DirEntry(entryType, name, blobKey, mode, uid, gid, lastAccessTime, lastModificationTime, time::now());
}
boost::optional<const DirEntry&> DirEntryList::get(const string &name) const {
auto found = _findByName(name);
if (found == _entries.end()) {
@ -90,12 +117,16 @@ void DirEntryList::remove(const Key &key) {
_entries.erase(lowerBound, upperBound);
}
vector<DirEntry>::const_iterator DirEntryList::_findByName(const string &name) const {
vector<DirEntry>::iterator DirEntryList::_findByName(const string &name) {
return std::find_if(_entries.begin(), _entries.end(), [&name] (const DirEntry &entry) {
return entry.name() == name;
});
}
vector<DirEntry>::const_iterator DirEntryList::_findByName(const string &name) const {
return const_cast<DirEntryList*>(this)->_findByName(name);
}
vector<DirEntry>::iterator DirEntryList::_findByKey(const Key &key) {
auto found = _findLowerBound(key);
if (found == _entries.end() || found->key() != key) {

View File

@ -21,6 +21,8 @@ namespace cryfs {
void add(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);
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);
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);
@ -37,12 +39,17 @@ namespace cryfs {
private:
uint64_t _serializedSize() const;
bool _hasChild(const std::string &name) const;
std::vector<DirEntry>::iterator _findByName(const std::string &name);
std::vector<DirEntry>::const_iterator _findByName(const std::string &name) const;
std::vector<DirEntry>::iterator _findByKey(const blockstore::Key &key);
std::vector<DirEntry>::const_iterator _findByKey(const blockstore::Key &key) const;
std::vector<DirEntry>::iterator _findUpperBound(const blockstore::Key &key);
std::vector<DirEntry>::iterator _findLowerBound(const blockstore::Key &key);
std::vector<DirEntry>::iterator _findFirst(const blockstore::Key &hint, std::function<bool (const DirEntry&)> pred);
void _add(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);
void _overwrite(DirEntry *entry, 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::vector<DirEntry> _entries;

View File

@ -38,9 +38,9 @@ public:
return _base->flush();
}
void AddChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
void AddOrOverwriteChild(const std::string &name, const blockstore::Key &blobKey, fspp::Dir::EntryType type,
mode_t mode, uid_t uid, gid_t gid, timespec lastAccessTime, timespec lastModificationTime) {
return _base->AddChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime);
return _base->AddOrOverwriteChild(name, blobKey, type, mode, uid, gid, lastAccessTime, lastModificationTime);
}
void statChild(const blockstore::Key &key, struct ::stat *result) const {

View File

@ -54,7 +54,15 @@ public:
EXPECT_NE(boost::none, this->device->Load("/mydir/newname"));
}
void Test_Rename_RootToNested() {
void Test_Rename_RootToNested_SameName() {
CreateDir("/mydir");
auto node = CreateNode("/oldname");
node->rename("/mydir/oldname");
EXPECT_EQ(boost::none, this->device->Load("/oldname"));
EXPECT_NE(boost::none, this->device->Load("/mydir/oldname"));
}
void Test_Rename_RootToNested_NewName() {
CreateDir("/mydir");
auto node = CreateNode("/oldname");
node->rename("/mydir/newname");
@ -62,7 +70,15 @@ public:
EXPECT_NE(boost::none, this->device->Load("/mydir/newname"));
}
void Test_Rename_NestedToRoot() {
void Test_Rename_NestedToRoot_SameName() {
CreateDir("/mydir");
auto node = CreateNode("/mydir/oldname");
node->rename("/oldname");
EXPECT_EQ(boost::none, this->device->Load("/mydir/oldname"));
EXPECT_NE(boost::none, this->device->Load("/oldname"));
}
void Test_Rename_NestedToRoot_NewName() {
CreateDir("/mydir");
auto node = CreateNode("/mydir/oldname");
node->rename("/newname");
@ -70,7 +86,16 @@ public:
EXPECT_NE(boost::none, this->device->Load("/newname"));
}
void Test_Rename_NestedToNested() {
void Test_Rename_NestedToNested_SameName() {
CreateDir("/mydir");
CreateDir("/mydir2");
auto node = CreateNode("/mydir/oldname");
node->rename("/mydir2/oldname");
EXPECT_EQ(boost::none, this->device->Load("/mydir/oldname"));
EXPECT_NE(boost::none, this->device->Load("/mydir2/oldname"));
}
void Test_Rename_NestedToNested_NewName() {
CreateDir("/mydir");
CreateDir("/mydir2");
auto node = CreateNode("/mydir/oldname");
@ -95,22 +120,70 @@ public:
}
}
private:
void CreateDir(const boost::filesystem::path &path) {
this->LoadDir(path.parent_path())->createDir(path.filename().native(), this->MODE_PUBLIC, 0, 0);
void Test_Rename_Overwrite() {
auto node = CreateNode("/oldname");
CreateNode("/newname");
node->rename("/newname");
EXPECT_EQ(boost::none, this->device->Load("/oldname"));
EXPECT_NE(boost::none, this->device->Load("/newname"));
}
void CreateFile(const boost::filesystem::path &path) {
this->LoadDir(path.parent_path())->createAndOpenFile(path.filename().native(), this->MODE_PUBLIC, 0, 0);
void Test_Rename_Overwrite_DoesntHaveSameEntryTwice() {
auto node = CreateNode("/oldname");
CreateNode("/newname");
EXPECT_EQ(4u, this->LoadDir("/")->children()->size()); // 4, because of '.' and '..'
node->rename("/newname");
EXPECT_EQ(3u, this->LoadDir("/")->children()->size()); // 3, because of '.' and '..'
}
void Test_Rename_Overwrite_DirWithFile() {
auto file = CreateFile("/oldname");
CreateDir("/newname");
try {
file->rename("/newname");
EXPECT_TRUE(false); // expect throw
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EISDIR, e.getErrno());
}
EXPECT_NE(boost::none, this->device->Load("/oldname"));
EXPECT_NE(boost::none, this->device->Load("/newname"));
}
void Test_Rename_Overwrite_FileWithDir() {
auto dir = CreateDir("/oldname");
CreateFile("/newname");
try {
dir->rename("/newname");
EXPECT_TRUE(false); // expect throw
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno());
}
EXPECT_NE(boost::none, this->device->Load("/oldname"));
EXPECT_NE(boost::none, this->device->Load("/newname"));
}
protected:
cpputils::unique_ref<fspp::Dir> CreateDir(const boost::filesystem::path &path) {
this->LoadDir(path.parent_path())->createDir(path.filename().native(), this->MODE_PUBLIC, 0, 0);
return this->LoadDir(path);
}
cpputils::unique_ref<fspp::File> CreateFile(const boost::filesystem::path &path) {
this->LoadDir(path.parent_path())->createAndOpenFile(path.filename().native(), this->MODE_PUBLIC, 0, 0);
return this->LoadFile(path);
}
cpputils::unique_ref<fspp::Symlink> CreateSymlink(const boost::filesystem::path &path) {
this->LoadDir(path.parent_path())->createSymlink(path.filename().native(), "/my/symlink/target", 0, 0);
return this->LoadSymlink(path);
};
};
template<class ConcreteFileSystemTestFixture>
class FsppNodeTest_File: public FsppNodeTest<ConcreteFileSystemTestFixture> {
public:
cpputils::unique_ref<fspp::Node> CreateNode(const boost::filesystem::path &path) override {
this->LoadDir(path.parent_path())->createAndOpenFile(path.filename().native(), this->MODE_PUBLIC, 0, 0);
return this->LoadFile(path);
return this->CreateFile(path);
}
};
@ -118,8 +191,7 @@ template<class ConcreteFileSystemTestFixture>
class FsppNodeTest_Dir: public FsppNodeTest<ConcreteFileSystemTestFixture> {
public:
cpputils::unique_ref<fspp::Node> CreateNode(const boost::filesystem::path &path) override {
this->LoadDir(path.parent_path())->createDir(path.filename().native(), this->MODE_PUBLIC, 0, 0);
return this->LoadDir(path);
return this->CreateDir(path);
}
};
@ -127,8 +199,7 @@ template<class ConcreteFileSystemTestFixture>
class FsppNodeTest_Symlink: public FsppNodeTest<ConcreteFileSystemTestFixture> {
public:
cpputils::unique_ref<fspp::Node> CreateNode(const boost::filesystem::path &path) override {
this->LoadDir(path.parent_path())->createSymlink(path.filename().native(), "/my/symlink/target", 0, 0);
return this->LoadSymlink(path);
return this->CreateSymlink(path);
}
};
@ -155,11 +226,18 @@ REGISTER_NODE_TEST_CASES(
Rename_TargetParentDirIsFile,
Rename_InRoot,
Rename_InNested,
Rename_RootToNested,
Rename_NestedToRoot,
Rename_NestedToNested,
Rename_RootToNested_SameName,
Rename_RootToNested_NewName,
Rename_NestedToRoot_SameName,
Rename_NestedToRoot_NewName,
Rename_NestedToNested_SameName,
Rename_NestedToNested_NewName,
Rename_ToItself,
Rename_RootDir
Rename_RootDir,
Rename_Overwrite,
Rename_Overwrite_DoesntHaveSameEntryTwice,
Rename_Overwrite_DirWithFile,
Rename_Overwrite_FileWithDir
);
#endif