From 5aff394a163690f6fdd760210de0c98192ce8a24 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Wed, 8 Jun 2016 12:21:57 -0700 Subject: [PATCH] Directory timestamps are updated correctly. Also added test cases for this. --- src/cryfs/filesystem/CryDevice.cpp | 43 ++-- src/cryfs/filesystem/CryDevice.h | 12 +- src/cryfs/filesystem/CryDir.cpp | 26 ++- src/cryfs/filesystem/CryDir.h | 4 +- src/cryfs/filesystem/CryFile.cpp | 9 +- src/cryfs/filesystem/CryFile.h | 2 +- src/cryfs/filesystem/CryNode.cpp | 42 +++- src/cryfs/filesystem/CryNode.h | 9 +- src/cryfs/filesystem/CrySymlink.cpp | 8 +- src/cryfs/filesystem/CrySymlink.h | 2 +- src/fspp/fs_interface/Dir.h | 2 +- src/fspp/fstest/FsTest.h | 33 +-- src/fspp/fstest/FsppDirTest.h | 14 +- src/fspp/fstest/FsppDirTest_Timestamps.h | 244 +++++++++++++++++++++++ 14 files changed, 396 insertions(+), 54 deletions(-) create mode 100644 src/fspp/fstest/FsppDirTest_Timestamps.h diff --git a/src/cryfs/filesystem/CryDevice.cpp b/src/cryfs/filesystem/CryDevice.cpp index 080b2ec1..e3263abe 100644 --- a/src/cryfs/filesystem/CryDevice.cpp +++ b/src/cryfs/filesystem/CryDevice.cpp @@ -67,15 +67,20 @@ Key CryDevice::CreateRootBlobAndReturnKey() { } optional> CryDevice::Load(const bf::path &path) { + // TODO Split into smaller functions ASSERT(path.is_absolute(), "Non absolute path given"); callFsActionCallbacks(); if (path.parent_path().empty()) { //We are asked to load the base directory '/'. - return optional>(make_unique_ref(this, none, _rootKey)); + return optional>(make_unique_ref(this, none, none, _rootKey)); } - auto parent = LoadDirBlob(path.parent_path()); + + auto parentWithGrandparent = LoadDirBlobWithParent(path.parent_path()); + auto parent = std::move(parentWithGrandparent.blob); + auto grandparent = std::move(parentWithGrandparent.parent); + auto optEntry = parent->GetChild(path.filename().native()); if (optEntry == boost::none) { return boost::none; @@ -84,33 +89,39 @@ optional> CryDevice::Load(const bf::path &path) { switch(entry.type()) { case fspp::Dir::EntryType::DIR: - return optional>(make_unique_ref(this, std::move(parent), entry.key())); + return optional>(make_unique_ref(this, std::move(parent), std::move(grandparent), entry.key())); case fspp::Dir::EntryType::FILE: - return optional>(make_unique_ref(this, std::move(parent), entry.key())); + return optional>(make_unique_ref(this, std::move(parent), std::move(grandparent), entry.key())); case fspp::Dir::EntryType::SYMLINK: - return optional>(make_unique_ref(this, std::move(parent), entry.key())); + return optional>(make_unique_ref(this, std::move(parent), std::move(grandparent), entry.key())); } ASSERT(false, "Switch/case not exhaustive"); } -unique_ref CryDevice::LoadDirBlob(const bf::path &path) { - auto blob = LoadBlob(path); - auto dir = dynamic_pointer_move(blob); +CryDevice::DirBlobWithParent CryDevice::LoadDirBlobWithParent(const bf::path &path) { + auto blob = LoadBlobWithParent(path); + auto dir = dynamic_pointer_move(blob.blob); if (dir == none) { throw FuseErrnoException(ENOTDIR); // Loaded blob is not a directory } - return std::move(*dir); + return DirBlobWithParent{std::move(*dir), std::move(blob.parent)}; } unique_ref CryDevice::LoadBlob(const bf::path &path) { - auto currentBlob = _fsBlobStore->load(_rootKey); - if (currentBlob == none) { + return LoadBlobWithParent(path).blob; +} + +CryDevice::BlobWithParent CryDevice::LoadBlobWithParent(const bf::path &path) { + optional> parentBlob = none; + optional> currentBlobOpt = _fsBlobStore->load(_rootKey); + if (currentBlobOpt == none) { LOG(ERROR) << "Could not load root blob. Is the base directory accessible?"; throw FuseErrnoException(EIO); } + unique_ref currentBlob = std::move(*currentBlobOpt); for (const bf::path &component : path.relative_path()) { - auto currentDir = dynamic_pointer_move(*currentBlob); + auto currentDir = dynamic_pointer_move(currentBlob); if (currentDir == none) { throw FuseErrnoException(ENOTDIR); // Path component is not a dir } @@ -120,13 +131,15 @@ unique_ref CryDevice::LoadBlob(const bf::path &path) { throw FuseErrnoException(ENOENT); // Child entry in directory not found } Key childKey = childOpt->key(); - currentBlob = _fsBlobStore->load(childKey); - if (currentBlob == none) { + auto nextBlob = _fsBlobStore->load(childKey); + if (nextBlob == none) { throw FuseErrnoException(ENOENT); // Blob for directory entry not found } + parentBlob = std::move(*currentDir); + currentBlob = std::move(*nextBlob); } - return std::move(*currentBlob); + return BlobWithParent{std::move(currentBlob), std::move(parentBlob)}; //TODO (I think this is resolved, but I should test it) // Running the python script, waiting for "Create files in sequential order...", then going into dir ~/tmp/cryfs-mount-.../Bonnie.../ and calling "ls" diff --git a/src/cryfs/filesystem/CryDevice.h b/src/cryfs/filesystem/CryDevice.h index ebb02b1c..8c76affb 100644 --- a/src/cryfs/filesystem/CryDevice.h +++ b/src/cryfs/filesystem/CryDevice.h @@ -26,7 +26,11 @@ public: cpputils::unique_ref CreateSymlinkBlob(const boost::filesystem::path &target); cpputils::unique_ref LoadBlob(const blockstore::Key &key); //TODO Do I still need this function? cpputils::unique_ref LoadBlob(const boost::filesystem::path &path); - cpputils::unique_ref LoadDirBlob(const boost::filesystem::path &path); + struct DirBlobWithParent { + cpputils::unique_ref blob; + boost::optional> parent; + }; + DirBlobWithParent LoadDirBlobWithParent(const boost::filesystem::path &path); void RemoveBlob(const blockstore::Key &key); void onFsAction(std::function callback); @@ -48,6 +52,12 @@ private: blockstore::Key CreateRootBlobAndReturnKey(); static cpputils::unique_ref CreateEncryptedBlockStore(const CryConfig &config, cpputils::unique_ref baseBlockStore); + struct BlobWithParent { + cpputils::unique_ref blob; + boost::optional> parent; + }; + BlobWithParent LoadBlobWithParent(const boost::filesystem::path &path); + DISALLOW_COPY_AND_ASSIGN(CryDevice); }; diff --git a/src/cryfs/filesystem/CryDir.cpp b/src/cryfs/filesystem/CryDir.cpp index b7480397..f6911775 100644 --- a/src/cryfs/filesystem/CryDir.cpp +++ b/src/cryfs/filesystem/CryDir.cpp @@ -30,8 +30,8 @@ using cryfs::parallelaccessfsblobstore::DirBlobRef; namespace cryfs { -CryDir::CryDir(CryDevice *device, boost::optional> parent, const Key &key) -: CryNode(device, std::move(parent), key) { +CryDir::CryDir(CryDevice *device, optional> parent, optional> grandparent, const Key &key) +: CryNode(device, std::move(parent), std::move(grandparent), key) { } CryDir::~CryDir() { @@ -39,6 +39,10 @@ CryDir::~CryDir() { unique_ref CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) { device()->callFsActionCallbacks(); + if (!isRootDir()) { + //TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function) + parent()->updateModificationTimestampForChild(key()); + } auto child = device()->CreateFileBlob(); auto now = cpputils::time::now(); auto dirBlob = LoadBlob(); @@ -48,6 +52,10 @@ unique_ref CryDir::createAndOpenFile(const string &name, mode_t void CryDir::createDir(const string &name, mode_t mode, uid_t uid, gid_t gid) { device()->callFsActionCallbacks(); + if (!isRootDir()) { + //TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function) + parent()->updateModificationTimestampForChild(key()); + } auto blob = LoadBlob(); auto child = device()->CreateDirBlob(); auto now = cpputils::time::now(); @@ -61,8 +69,12 @@ unique_ref CryDir::LoadBlob() const { return std::move(*dir_blob); } -unique_ref> CryDir::children() const { +unique_ref> CryDir::children() { device()->callFsActionCallbacks(); + if (!isRootDir()) { + //TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function) + parent()->updateAccessTimestampForChild(key()); + } auto children = make_unique_ref>(); children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "..")); @@ -78,6 +90,10 @@ fspp::Dir::EntryType CryDir::getType() const { void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) { device()->callFsActionCallbacks(); + if (!isRootDir()) { + //TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function) + parent()->updateModificationTimestampForChild(key()); + } auto blob = LoadBlob(); auto child = device()->CreateSymlinkBlob(target); auto now = cpputils::time::now(); @@ -86,6 +102,10 @@ void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid void CryDir::remove() { device()->callFsActionCallbacks(); + if (grandparent() != none) { + //TODO Instead of doing nothing when we're in the root directory, handle timestamps in the root dir correctly + (*grandparent())->updateModificationTimestampForChild(parent()->key()); + } { auto blob = LoadBlob(); if (0 != blob->NumChildren()) { diff --git a/src/cryfs/filesystem/CryDir.h b/src/cryfs/filesystem/CryDir.h index 4239c61f..ecbdd49e 100644 --- a/src/cryfs/filesystem/CryDir.h +++ b/src/cryfs/filesystem/CryDir.h @@ -10,7 +10,7 @@ namespace cryfs { class CryDir final: public fspp::Dir, CryNode { public: - CryDir(CryDevice *device, boost::optional> parent, const blockstore::Key &key); + CryDir(CryDevice *device, boost::optional> parent, boost::optional> grandparent, const blockstore::Key &key); ~CryDir(); //TODO return type variance to CryFile/CryDir? @@ -19,7 +19,7 @@ public: void createSymlink(const std::string &name, const boost::filesystem::path &target, uid_t uid, gid_t gid) override; //TODO Make Entry a public class instead of hidden in DirBlob (which is not publicly visible) - cpputils::unique_ref> children() const override; + cpputils::unique_ref> children() override; fspp::Dir::EntryType getType() const override; diff --git a/src/cryfs/filesystem/CryFile.cpp b/src/cryfs/filesystem/CryFile.cpp index 9d2e0c88..e9bfde30 100644 --- a/src/cryfs/filesystem/CryFile.cpp +++ b/src/cryfs/filesystem/CryFile.cpp @@ -12,6 +12,7 @@ using fspp::fuse::FuseErrnoException; using blockstore::Key; using boost::none; +using boost::optional; using cpputils::unique_ref; using cpputils::make_unique_ref; using cpputils::dynamic_pointer_move; @@ -20,8 +21,8 @@ using cryfs::parallelaccessfsblobstore::FileBlobRef; namespace cryfs { -CryFile::CryFile(CryDevice *device, unique_ref parent, const Key &key) -: CryNode(device, std::move(parent), key) { +CryFile::CryFile(CryDevice *device, unique_ref parent, optional> grandparent, const Key &key) +: CryNode(device, std::move(parent), std::move(grandparent), key) { } CryFile::~CryFile() { @@ -56,6 +57,10 @@ fspp::Dir::EntryType CryFile::getType() const { void CryFile::remove() { device()->callFsActionCallbacks(); + if (grandparent() != none) { + //TODO Instead of doing nothing when we're in the root directory, handle timestamps in the root dir correctly + (*grandparent())->updateModificationTimestampForChild(parent()->key()); + } removeNode(); } diff --git a/src/cryfs/filesystem/CryFile.h b/src/cryfs/filesystem/CryFile.h index 270578ba..e10db0c3 100644 --- a/src/cryfs/filesystem/CryFile.h +++ b/src/cryfs/filesystem/CryFile.h @@ -11,7 +11,7 @@ namespace cryfs { class CryFile final: public fspp::File, CryNode { public: - CryFile(CryDevice *device, cpputils::unique_ref parent, const blockstore::Key &key); + CryFile(CryDevice *device, cpputils::unique_ref parent, boost::optional> grandparent, const blockstore::Key &key); ~CryFile(); cpputils::unique_ref open(int flags) override; diff --git a/src/cryfs/filesystem/CryNode.cpp b/src/cryfs/filesystem/CryNode.cpp index 76dd4713..0524b47c 100644 --- a/src/cryfs/filesystem/CryNode.cpp +++ b/src/cryfs/filesystem/CryNode.cpp @@ -28,13 +28,18 @@ using fspp::fuse::FuseErrnoException; namespace cryfs { -CryNode::CryNode(CryDevice *device, optional> parent, const Key &key) +CryNode::CryNode(CryDevice *device, optional> parent, optional> grandparent, const Key &key) : _device(device), _parent(none), + _grandparent(none), _key(key) { + + ASSERT(parent != none || grandparent == none, "Grandparent can only be set when parent is not none"); + if (parent != none) { _parent = cpputils::to_unique_ptr(std::move(*parent)); } + _grandparent = std::move(grandparent); } CryNode::~CryNode() { @@ -47,6 +52,10 @@ void CryNode::access(int mask) const { return; } +bool CryNode::isRootDir() const { + return _parent == none; +} + shared_ptr CryNode::parent() const { ASSERT(_parent != none, "We are the root directory and can't get the parent of the root directory"); return *_parent; @@ -57,24 +66,36 @@ shared_ptr CryNode::parent() { return *_parent; } +optional CryNode::grandparent() { + if (_grandparent == none) { + return none; + } + return _grandparent->get(); +} + void CryNode::rename(const bf::path &to) { device()->callFsActionCallbacks(); if (_parent == none) { //We are the root direcory. throw FuseErrnoException(EBUSY); } - auto targetDir = _device->LoadDirBlob(to.parent_path()); + auto targetDirWithParent = _device->LoadDirBlobWithParent(to.parent_path()); + auto targetDir = std::move(targetDirWithParent.blob); + auto targetDirParent = std::move(targetDirWithParent.parent); + auto old = (*_parent)->GetChild(_key); if (old == boost::none) { throw FuseErrnoException(EIO); } - 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. + fsblobstore::DirEntry oldEntry = *old; // Copying this (instead of 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); }; + _updateParentModificationTimestamp(); if (targetDir->key() == (*_parent)->key()) { targetDir->RenameChild(oldEntry.key(), to.filename().native(), onOverwritten); } else { + _updateTargetDirModificationTimestamp(*targetDir, std::move(targetDirParent)); targetDir->AddOrOverwriteChild(to.filename().native(), oldEntry.key(), oldEntry.type(), oldEntry.mode(), oldEntry.uid(), oldEntry.gid(), oldEntry.lastAccessTime(), oldEntry.lastModificationTime(), onOverwritten); (*_parent)->RemoveChild(oldEntry.name()); @@ -83,6 +104,21 @@ void CryNode::rename(const bf::path &to) { } } +void CryNode::_updateParentModificationTimestamp() { + if (_grandparent != none) { + // TODO Handle timestamps of the root directory (_grandparent == none) correctly. + ASSERT(_parent != none, "Grandparent is set, so also parent has to be set"); + (*_grandparent)->updateModificationTimestampForChild((*_parent)->key()); + } +} + +void CryNode::_updateTargetDirModificationTimestamp(const DirBlobRef &targetDir, optional> targetDirParent) { + if (targetDirParent != none) { + // TODO Handle timestamps of the root directory (targetDirParent == none) correctly. + (*targetDirParent)->updateModificationTimestampForChild(targetDir.key()); + } +} + void CryNode::utimens(timespec lastAccessTime, timespec lastModificationTime) { device()->callFsActionCallbacks(); if (_parent == none) { diff --git a/src/cryfs/filesystem/CryNode.h b/src/cryfs/filesystem/CryNode.h index 5f892091..5cab5476 100644 --- a/src/cryfs/filesystem/CryNode.h +++ b/src/cryfs/filesystem/CryNode.h @@ -14,7 +14,8 @@ class CryNode: public virtual fspp::Node { public: virtual ~CryNode(); - CryNode(CryDevice *device, boost::optional> parent, const blockstore::Key &key); + // TODO grandparent is only needed to set the timestamps of the parent directory on rename and remove. Delete grandparent parameter once we store timestamps in the blob itself instead of in the directory listing. + CryNode(CryDevice *device, boost::optional> parent, boost::optional> grandparent, const blockstore::Key &key); void access(int mask) const override; void stat(struct ::stat *result) const override; void chmod(mode_t mode) override; @@ -29,16 +30,22 @@ protected: const CryDevice *device() const; const blockstore::Key &key() const; cpputils::unique_ref LoadBlob() const; + bool isRootDir() const; std::shared_ptr parent() const; std::shared_ptr parent(); + boost::optional grandparent(); virtual fspp::Dir::EntryType getType() const = 0; void removeNode(); private: + void _updateParentModificationTimestamp(); + void _updateTargetDirModificationTimestamp(const parallelaccessfsblobstore::DirBlobRef &targetDir, boost::optional> targetDirParent); + CryDevice *_device; boost::optional> _parent; + boost::optional> _grandparent; blockstore::Key _key; DISALLOW_COPY_AND_ASSIGN(CryNode); diff --git a/src/cryfs/filesystem/CrySymlink.cpp b/src/cryfs/filesystem/CrySymlink.cpp index b48a0445..531870fc 100644 --- a/src/cryfs/filesystem/CrySymlink.cpp +++ b/src/cryfs/filesystem/CrySymlink.cpp @@ -25,8 +25,8 @@ using cryfs::parallelaccessfsblobstore::DirBlobRef; namespace cryfs { -CrySymlink::CrySymlink(CryDevice *device, unique_ref parent, const Key &key) -: CryNode(device, std::move(parent), key) { +CrySymlink::CrySymlink(CryDevice *device, unique_ref parent, optional> grandparent, const Key &key) +: CryNode(device, std::move(parent), std::move(grandparent), key) { } CrySymlink::~CrySymlink() { @@ -53,6 +53,10 @@ bf::path CrySymlink::target() { void CrySymlink::remove() { device()->callFsActionCallbacks(); + if (grandparent() != none) { + //TODO Instead of doing nothing when we're in the root directory, handle timestamps in the root dir correctly + (*grandparent())->updateModificationTimestampForChild(parent()->key()); + } removeNode(); } diff --git a/src/cryfs/filesystem/CrySymlink.h b/src/cryfs/filesystem/CrySymlink.h index 6506c034..3e641eef 100644 --- a/src/cryfs/filesystem/CrySymlink.h +++ b/src/cryfs/filesystem/CrySymlink.h @@ -11,7 +11,7 @@ namespace cryfs { class CrySymlink final: public fspp::Symlink, CryNode { public: - CrySymlink(CryDevice *device, cpputils::unique_ref parent, const blockstore::Key &key); + CrySymlink(CryDevice *device, cpputils::unique_ref parent, boost::optional> grandparent, const blockstore::Key &key); ~CrySymlink(); boost::filesystem::path target() override; diff --git a/src/fspp/fs_interface/Dir.h b/src/fspp/fs_interface/Dir.h index 338dcf2b..06b8ae62 100644 --- a/src/fspp/fs_interface/Dir.h +++ b/src/fspp/fs_interface/Dir.h @@ -32,7 +32,7 @@ public: //TODO Allow alternative implementation returning only children names without more information //virtual std::unique_ptr> children() const = 0; - virtual cpputils::unique_ref> children() const = 0; + virtual cpputils::unique_ref> children() = 0; }; } diff --git a/src/fspp/fstest/FsTest.h b/src/fspp/fstest/FsTest.h index b392e75d..36389460 100644 --- a/src/fspp/fstest/FsTest.h +++ b/src/fspp/fstest/FsTest.h @@ -12,26 +12,29 @@ #include "FsppOpenFileTest.h" #include "FsppDeviceTest_Timestamps.h" #include "FsppNodeTest_Timestamps.h" +#include "FsppDirTest_Timestamps.h" #include "FsppSymlinkTest_Timestamps.h" #include "FsppFileTest_Timestamps.h" #include "FsppOpenFileTest_Timestamps.h" #define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ - INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppDeviceTest_Timestamps, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest_Timestamps, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest_Timestamps, FIXTURE); \ - INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \ - INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Stat, FIXTURE); \ - INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Timestamps, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_FileOnly, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_DirOnly, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_SymlinkOnly, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest_Timestamps, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppDeviceTest_Timestamps, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest_Timestamps, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppDirTest_Timestamps_Entries, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest_Timestamps, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest_Timestamps, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Stat, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Timestamps, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_FileOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_DirOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_SymlinkOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest_Timestamps, FIXTURE); \ #endif diff --git a/src/fspp/fstest/FsppDirTest.h b/src/fspp/fstest/FsppDirTest.h index 29818d4d..401228df 100644 --- a/src/fspp/fstest/FsppDirTest.h +++ b/src/fspp/fstest/FsppDirTest.h @@ -17,14 +17,14 @@ public: } void EXPECT_CHILDREN_ARE(const boost::filesystem::path &path, const std::initializer_list expected) { - EXPECT_CHILDREN_ARE(*this->LoadDir(path), expected); + EXPECT_CHILDREN_ARE(this->LoadDir(path).get(), expected); } - void EXPECT_CHILDREN_ARE(const fspp::Dir &dir, const std::initializer_list expected) { + void EXPECT_CHILDREN_ARE(fspp::Dir *dir, const std::initializer_list expected) { std::vector expectedChildren = expected; expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "..")); - EXPECT_UNORDERED_EQ(expectedChildren, *dir.children()); + EXPECT_UNORDERED_EQ(expectedChildren, *dir->children()); } template @@ -63,7 +63,7 @@ TYPED_TEST_P(FsppDirTest, Children_RootDir_Empty) { TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_Directly) { auto rootdir = this->LoadDir("/"); rootdir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); - this->EXPECT_CHILDREN_ARE(*rootdir, { + this->EXPECT_CHILDREN_ARE(rootdir.get(), { FileEntry("myfile") }); } @@ -78,7 +78,7 @@ TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_AfterReloadingDir) { TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_Directly) { auto rootdir = this->LoadDir("/"); rootdir->createDir("mydir", this->MODE_PUBLIC, 0, 0); - this->EXPECT_CHILDREN_ARE(*rootdir, { + this->EXPECT_CHILDREN_ARE(rootdir.get(), { DirEntry("mydir") }); } @@ -108,7 +108,7 @@ TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_Directly) { this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); auto dir = this->LoadDir("/mydir"); dir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); - this->EXPECT_CHILDREN_ARE(*dir, { + this->EXPECT_CHILDREN_ARE(dir.get(), { FileEntry("myfile") }); } @@ -125,7 +125,7 @@ TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_Directly) { this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); auto dir = this->LoadDir("/mydir"); dir->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); - this->EXPECT_CHILDREN_ARE(*dir, { + this->EXPECT_CHILDREN_ARE(dir.get(), { DirEntry("mysubdir") }); } diff --git a/src/fspp/fstest/FsppDirTest_Timestamps.h b/src/fspp/fstest/FsppDirTest_Timestamps.h new file mode 100644 index 00000000..eada85d3 --- /dev/null +++ b/src/fspp/fstest/FsppDirTest_Timestamps.h @@ -0,0 +1,244 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPDIRTEST_TIMESTAMPS_H_ +#define MESSMER_FSPP_FSTEST_FSPPDIRTEST_TIMESTAMPS_H_ + +#include "testutils/TimestampTestUtils.h" + +template +class FsppDirTest_Timestamps: public FileSystemTest, public TimestampTestUtils { +public: +}; +TYPED_TEST_CASE_P(FsppDirTest_Timestamps); + +TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile) { + auto dir = this->CreateDir("/mydir"); + auto operation = [&dir] () { + dir->createAndOpenFile("childname", S_IFREG, 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +} + +/* TODO Re-enable this test once the root dir handles timestamps correctly +TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_inRootDir) { + auto dir = this->LoadDir("/"); + auto operation = [&dir] () { + dir->createAndOpenFile("childname", S_IFREG, 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +}*/ + +TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile) { + auto dir = this->CreateDir("/mydir"); + timespec lowerBound = now(); + dir->createAndOpenFile("childname", S_IFREG, 1000, 1000); + timespec upperBound = now(); + auto child = this->LoadFile("/mydir/childname"); + this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child); +} + +TYPED_TEST_P(FsppDirTest_Timestamps, createDir) { + auto dir = this->CreateDir("/mydir"); + auto operation = [&dir] () { + dir->createDir("childname", S_IFDIR, 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +} + +/* TODO Re-enable this test once the root dir handles timestamps correctly +TYPED_TEST_P(FsppDirTest_Timestamps, createDir_inRootDir) { + auto dir = this->LoadDir("/"); + auto operation = [&dir] () { + dir->createDir("childname", S_IFDIR, 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +}*/ + +TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) { + auto dir = this->CreateDir("/mydir"); + timespec lowerBound = now(); + dir->createDir("childname", S_IFDIR, 1000, 1000); + timespec upperBound = now(); + auto child = this->LoadDir("/mydir/childname"); + this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child); +} + +TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink) { + auto dir = this->CreateDir("/mydir"); + auto operation = [&dir] () { + dir->createSymlink("childname", "/target", 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +} + +/* TODO Re-enable this test once the root dir handles timestamps correctly +TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_inRootDir) { + auto dir = this->LoadDir("/"); + auto operation = [&dir] () { + dir->createSymlink("childname", "/target", 1000, 1000); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); +}*/ + +TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) { + auto dir = this->CreateDir("/mydir"); + timespec lowerBound = now(); + dir->createSymlink("childname", "/target", 1000, 1000); + timespec upperBound = now(); + auto child = this->LoadSymlink("/mydir/childname"); + this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child); + this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *child); +} + +TYPED_TEST_P(FsppDirTest_Timestamps, children_empty) { + auto dir = this->CreateDir("/mydir"); + auto operation = [&dir] () { + dir->children(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp}); +} + +/* TODO Re-enable this test once the root dir handles timestamps correctly +TYPED_TEST_P(FsppDirTest_Timestamps, children_empty_inRootDir) { + auto dir = this->LoadDir("/"); + auto operation = [&dir] () { + dir->children(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp}); +}*/ + +TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty) { + auto dir = this->CreateDir("/mydir"); + dir->createAndOpenFile("filename", S_IFREG, 1000, 1000); + auto operation = [&dir] () { + dir->children(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp}); +} + +/* TODO Re-enable this test once the root dir handles timestamps correctly +TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty_inRootDir) { + auto dir = this->LoadDir("/"); + dir->createAndOpenFile("filename", S_IFREG, 1000, 1000); + auto operation = [&dir] () { + dir->children(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp}); +}*/ + +template +class FsppDirTest_Timestamps_Entries: public FsppNodeTest, public TimestampTestUtils { +public: + + void Test_deleteChild() { + auto dir = this->CreateDir("/mydir"); + auto child = this->CreateNode("/mydir/childname"); + auto operation = [&child]() { + child->remove(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, + this->ExpectUpdatesModificationTimestamp, + this->ExpectUpdatesMetadataTimestamp}); + } + + /* TODO Re-enable this test once the root dir handles timestamps correctly + void Test_deleteChild_inRootDir() { + auto dir = this->LoadDir("/"); + auto child = this->CreateNode("/childname"); + auto operation = [&child] () { + child->remove(); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); + }*/ + + void Test_renameChild() { + auto dir = this->CreateDir("/mydir"); + auto child = this->CreateNode("/mydir/childname"); + auto operation = [&child]() { + child->rename("/mydir/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, + this->ExpectUpdatesModificationTimestamp, + this->ExpectUpdatesMetadataTimestamp}); + } + + /* TODO Re-enable this test once the root dir handles timestamps correctly + void Test_renameChild_inRootDir() { + auto dir = this->LoadDir("/"); + auto child = this->CreateNode("/childname"); + auto operation = [&child] () { + child->rename("/mydir/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); + }*/ + + void Test_moveChildIn() { + auto sourceDir = this->CreateDir("/sourcedir"); + auto child = this->CreateNode("/sourcedir/childname"); + auto dir = this->CreateDir("/mydir"); + auto operation = [&child]() { + child->rename("/mydir/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, + this->ExpectUpdatesModificationTimestamp, + this->ExpectUpdatesMetadataTimestamp}); + } + + /* TODO Re-enable this test once the root dir handles timestamps correctly + void Test_moveChildIn_inRootDir() { + auto sourceDir = this->CreateDir("/sourcedir"); + auto child = this->CreateNode("/sourcedir/childname"); + auto dir = this->LoadDir("/"); + auto operation = [&child] () { + child->rename("/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); + }*/ + + void Test_moveChildOut() { + auto dir = this->CreateDir("/mydir"); + auto child = this->CreateNode("/mydir/childname"); + this->CreateDir("/targetdir"); + auto operation = [&child]() { + child->rename("/targetdir/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, + this->ExpectUpdatesModificationTimestamp, + this->ExpectUpdatesMetadataTimestamp}); + } + + /* TODO Re-enable this test once the root dir handles timestamps correctly + void Test_moveChildOut_inRootDir() { + auto dir = this->LoadDir("/"); + auto child = this->CreateNode("/childname"); + this->CreateDir("/targetdir"); + auto operation = [&child] () { + child->rename("/targetdir/mychild"); + }; + this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*dir, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp}); + }*/ +}; + +REGISTER_TYPED_TEST_CASE_P(FsppDirTest_Timestamps, + createAndOpenFile, + createAndOpenFile_TimestampsOfCreatedFile, + createDir, + createDir_TimestampsOfCreatedDir, + createSymlink, + createSymlink_TimestampsOfCreatedSymlink, + children_empty, + children_nonempty +); + +REGISTER_NODE_TEST_CASE(FsppDirTest_Timestamps_Entries, + deleteChild, + renameChild, + moveChildIn, + moveChildOut +); + +#endif