Directory timestamps are updated correctly. Also added test cases for this.

This commit is contained in:
Sebastian Messmer 2016-06-08 12:21:57 -07:00
parent 5a5037c992
commit 5aff394a16
14 changed files with 396 additions and 54 deletions

View File

@ -67,15 +67,20 @@ Key CryDevice::CreateRootBlobAndReturnKey() {
} }
optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) { optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) {
// TODO Split into smaller functions
ASSERT(path.is_absolute(), "Non absolute path given"); ASSERT(path.is_absolute(), "Non absolute path given");
callFsActionCallbacks(); callFsActionCallbacks();
if (path.parent_path().empty()) { if (path.parent_path().empty()) {
//We are asked to load the base directory '/'. //We are asked to load the base directory '/'.
return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(this, none, _rootKey)); return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(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()); auto optEntry = parent->GetChild(path.filename().native());
if (optEntry == boost::none) { if (optEntry == boost::none) {
return boost::none; return boost::none;
@ -84,33 +89,39 @@ optional<unique_ref<fspp::Node>> CryDevice::Load(const bf::path &path) {
switch(entry.type()) { switch(entry.type()) {
case fspp::Dir::EntryType::DIR: case fspp::Dir::EntryType::DIR:
return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(this, std::move(parent), entry.key())); return optional<unique_ref<fspp::Node>>(make_unique_ref<CryDir>(this, std::move(parent), std::move(grandparent), entry.key()));
case fspp::Dir::EntryType::FILE: case fspp::Dir::EntryType::FILE:
return optional<unique_ref<fspp::Node>>(make_unique_ref<CryFile>(this, std::move(parent), entry.key())); return optional<unique_ref<fspp::Node>>(make_unique_ref<CryFile>(this, std::move(parent), std::move(grandparent), entry.key()));
case fspp::Dir::EntryType::SYMLINK: case fspp::Dir::EntryType::SYMLINK:
return optional<unique_ref<fspp::Node>>(make_unique_ref<CrySymlink>(this, std::move(parent), entry.key())); return optional<unique_ref<fspp::Node>>(make_unique_ref<CrySymlink>(this, std::move(parent), std::move(grandparent), entry.key()));
} }
ASSERT(false, "Switch/case not exhaustive"); ASSERT(false, "Switch/case not exhaustive");
} }
unique_ref<DirBlobRef> CryDevice::LoadDirBlob(const bf::path &path) { CryDevice::DirBlobWithParent CryDevice::LoadDirBlobWithParent(const bf::path &path) {
auto blob = LoadBlob(path); auto blob = LoadBlobWithParent(path);
auto dir = dynamic_pointer_move<DirBlobRef>(blob); auto dir = dynamic_pointer_move<DirBlobRef>(blob.blob);
if (dir == none) { if (dir == none) {
throw FuseErrnoException(ENOTDIR); // Loaded blob is not a directory throw FuseErrnoException(ENOTDIR); // Loaded blob is not a directory
} }
return std::move(*dir); return DirBlobWithParent{std::move(*dir), std::move(blob.parent)};
} }
unique_ref<FsBlobRef> CryDevice::LoadBlob(const bf::path &path) { unique_ref<FsBlobRef> CryDevice::LoadBlob(const bf::path &path) {
auto currentBlob = _fsBlobStore->load(_rootKey); return LoadBlobWithParent(path).blob;
if (currentBlob == none) { }
CryDevice::BlobWithParent CryDevice::LoadBlobWithParent(const bf::path &path) {
optional<unique_ref<DirBlobRef>> parentBlob = none;
optional<unique_ref<FsBlobRef>> currentBlobOpt = _fsBlobStore->load(_rootKey);
if (currentBlobOpt == none) {
LOG(ERROR) << "Could not load root blob. Is the base directory accessible?"; LOG(ERROR) << "Could not load root blob. Is the base directory accessible?";
throw FuseErrnoException(EIO); throw FuseErrnoException(EIO);
} }
unique_ref<FsBlobRef> currentBlob = std::move(*currentBlobOpt);
for (const bf::path &component : path.relative_path()) { for (const bf::path &component : path.relative_path()) {
auto currentDir = dynamic_pointer_move<DirBlobRef>(*currentBlob); auto currentDir = dynamic_pointer_move<DirBlobRef>(currentBlob);
if (currentDir == none) { if (currentDir == none) {
throw FuseErrnoException(ENOTDIR); // Path component is not a dir throw FuseErrnoException(ENOTDIR); // Path component is not a dir
} }
@ -120,13 +131,15 @@ unique_ref<FsBlobRef> CryDevice::LoadBlob(const bf::path &path) {
throw FuseErrnoException(ENOENT); // Child entry in directory not found throw FuseErrnoException(ENOENT); // Child entry in directory not found
} }
Key childKey = childOpt->key(); Key childKey = childOpt->key();
currentBlob = _fsBlobStore->load(childKey); auto nextBlob = _fsBlobStore->load(childKey);
if (currentBlob == none) { if (nextBlob == none) {
throw FuseErrnoException(ENOENT); // Blob for directory entry not found 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) //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" // Running the python script, waiting for "Create files in sequential order...", then going into dir ~/tmp/cryfs-mount-.../Bonnie.../ and calling "ls"

View File

@ -26,7 +26,11 @@ public:
cpputils::unique_ref<parallelaccessfsblobstore::SymlinkBlobRef> CreateSymlinkBlob(const boost::filesystem::path &target); cpputils::unique_ref<parallelaccessfsblobstore::SymlinkBlobRef> CreateSymlinkBlob(const boost::filesystem::path &target);
cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob(const blockstore::Key &key); //TODO Do I still need this function? cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob(const blockstore::Key &key); //TODO Do I still need this function?
cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob(const boost::filesystem::path &path); cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob(const boost::filesystem::path &path);
cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> LoadDirBlob(const boost::filesystem::path &path); struct DirBlobWithParent {
cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> blob;
boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent;
};
DirBlobWithParent LoadDirBlobWithParent(const boost::filesystem::path &path);
void RemoveBlob(const blockstore::Key &key); void RemoveBlob(const blockstore::Key &key);
void onFsAction(std::function<void()> callback); void onFsAction(std::function<void()> callback);
@ -48,6 +52,12 @@ private:
blockstore::Key CreateRootBlobAndReturnKey(); blockstore::Key CreateRootBlobAndReturnKey();
static cpputils::unique_ref<blockstore::BlockStore> CreateEncryptedBlockStore(const CryConfig &config, cpputils::unique_ref<blockstore::BlockStore> baseBlockStore); static cpputils::unique_ref<blockstore::BlockStore> CreateEncryptedBlockStore(const CryConfig &config, cpputils::unique_ref<blockstore::BlockStore> baseBlockStore);
struct BlobWithParent {
cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> blob;
boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent;
};
BlobWithParent LoadBlobWithParent(const boost::filesystem::path &path);
DISALLOW_COPY_AND_ASSIGN(CryDevice); DISALLOW_COPY_AND_ASSIGN(CryDevice);
}; };

View File

@ -30,8 +30,8 @@ using cryfs::parallelaccessfsblobstore::DirBlobRef;
namespace cryfs { namespace cryfs {
CryDir::CryDir(CryDevice *device, boost::optional<unique_ref<DirBlobRef>> parent, const Key &key) CryDir::CryDir(CryDevice *device, optional<unique_ref<DirBlobRef>> parent, optional<unique_ref<DirBlobRef>> grandparent, const Key &key)
: CryNode(device, std::move(parent), key) { : CryNode(device, std::move(parent), std::move(grandparent), key) {
} }
CryDir::~CryDir() { CryDir::~CryDir() {
@ -39,6 +39,10 @@ CryDir::~CryDir() {
unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) { unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t mode, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks(); 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 child = device()->CreateFileBlob();
auto now = cpputils::time::now(); auto now = cpputils::time::now();
auto dirBlob = LoadBlob(); auto dirBlob = LoadBlob();
@ -48,6 +52,10 @@ unique_ref<fspp::OpenFile> CryDir::createAndOpenFile(const string &name, mode_t
void CryDir::createDir(const string &name, mode_t mode, uid_t uid, gid_t gid) { void CryDir::createDir(const string &name, mode_t mode, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks(); 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 blob = LoadBlob();
auto child = device()->CreateDirBlob(); auto child = device()->CreateDirBlob();
auto now = cpputils::time::now(); auto now = cpputils::time::now();
@ -61,8 +69,12 @@ unique_ref<DirBlobRef> CryDir::LoadBlob() const {
return std::move(*dir_blob); return std::move(*dir_blob);
} }
unique_ref<vector<fspp::Dir::Entry>> CryDir::children() const { unique_ref<vector<fspp::Dir::Entry>> CryDir::children() {
device()->callFsActionCallbacks(); 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<vector<fspp::Dir::Entry>>(); auto children = make_unique_ref<vector<fspp::Dir::Entry>>();
children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); children->push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));
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) { void CryDir::createSymlink(const string &name, const bf::path &target, uid_t uid, gid_t gid) {
device()->callFsActionCallbacks(); 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 blob = LoadBlob();
auto child = device()->CreateSymlinkBlob(target); auto child = device()->CreateSymlinkBlob(target);
auto now = cpputils::time::now(); 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() { void CryDir::remove() {
device()->callFsActionCallbacks(); 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(); auto blob = LoadBlob();
if (0 != blob->NumChildren()) { if (0 != blob->NumChildren()) {

View File

@ -10,7 +10,7 @@ namespace cryfs {
class CryDir final: public fspp::Dir, CryNode { class CryDir final: public fspp::Dir, CryNode {
public: public:
CryDir(CryDevice *device, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent, const blockstore::Key &key); CryDir(CryDevice *device, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::Key &key);
~CryDir(); ~CryDir();
//TODO return type variance to CryFile/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; 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) //TODO Make Entry a public class instead of hidden in DirBlob (which is not publicly visible)
cpputils::unique_ref<std::vector<fspp::Dir::Entry>> children() const override; cpputils::unique_ref<std::vector<fspp::Dir::Entry>> children() override;
fspp::Dir::EntryType getType() const override; fspp::Dir::EntryType getType() const override;

View File

@ -12,6 +12,7 @@ using fspp::fuse::FuseErrnoException;
using blockstore::Key; using blockstore::Key;
using boost::none; using boost::none;
using boost::optional;
using cpputils::unique_ref; using cpputils::unique_ref;
using cpputils::make_unique_ref; using cpputils::make_unique_ref;
using cpputils::dynamic_pointer_move; using cpputils::dynamic_pointer_move;
@ -20,8 +21,8 @@ using cryfs::parallelaccessfsblobstore::FileBlobRef;
namespace cryfs { namespace cryfs {
CryFile::CryFile(CryDevice *device, unique_ref<DirBlobRef> parent, const Key &key) CryFile::CryFile(CryDevice *device, unique_ref<DirBlobRef> parent, optional<unique_ref<DirBlobRef>> grandparent, const Key &key)
: CryNode(device, std::move(parent), key) { : CryNode(device, std::move(parent), std::move(grandparent), key) {
} }
CryFile::~CryFile() { CryFile::~CryFile() {
@ -56,6 +57,10 @@ fspp::Dir::EntryType CryFile::getType() const {
void CryFile::remove() { void CryFile::remove() {
device()->callFsActionCallbacks(); 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(); removeNode();
} }

View File

@ -11,7 +11,7 @@ namespace cryfs {
class CryFile final: public fspp::File, CryNode { class CryFile final: public fspp::File, CryNode {
public: public:
CryFile(CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> parent, const blockstore::Key &key); CryFile(CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::Key &key);
~CryFile(); ~CryFile();
cpputils::unique_ref<fspp::OpenFile> open(int flags) override; cpputils::unique_ref<fspp::OpenFile> open(int flags) override;

View File

@ -28,13 +28,18 @@ using fspp::fuse::FuseErrnoException;
namespace cryfs { namespace cryfs {
CryNode::CryNode(CryDevice *device, optional<unique_ref<DirBlobRef>> parent, const Key &key) CryNode::CryNode(CryDevice *device, optional<unique_ref<DirBlobRef>> parent, optional<unique_ref<DirBlobRef>> grandparent, const Key &key)
: _device(device), : _device(device),
_parent(none), _parent(none),
_grandparent(none),
_key(key) { _key(key) {
ASSERT(parent != none || grandparent == none, "Grandparent can only be set when parent is not none");
if (parent != none) { if (parent != none) {
_parent = cpputils::to_unique_ptr(std::move(*parent)); _parent = cpputils::to_unique_ptr(std::move(*parent));
} }
_grandparent = std::move(grandparent);
} }
CryNode::~CryNode() { CryNode::~CryNode() {
@ -47,6 +52,10 @@ void CryNode::access(int mask) const {
return; return;
} }
bool CryNode::isRootDir() const {
return _parent == none;
}
shared_ptr<const DirBlobRef> CryNode::parent() const { shared_ptr<const DirBlobRef> CryNode::parent() const {
ASSERT(_parent != none, "We are the root directory and can't get the parent of the root directory"); ASSERT(_parent != none, "We are the root directory and can't get the parent of the root directory");
return *_parent; return *_parent;
@ -57,24 +66,36 @@ shared_ptr<DirBlobRef> CryNode::parent() {
return *_parent; return *_parent;
} }
optional<DirBlobRef*> CryNode::grandparent() {
if (_grandparent == none) {
return none;
}
return _grandparent->get();
}
void CryNode::rename(const bf::path &to) { void CryNode::rename(const bf::path &to) {
device()->callFsActionCallbacks(); device()->callFsActionCallbacks();
if (_parent == none) { if (_parent == none) {
//We are the root direcory. //We are the root direcory.
throw FuseErrnoException(EBUSY); 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); auto old = (*_parent)->GetChild(_key);
if (old == boost::none) { if (old == boost::none) {
throw FuseErrnoException(EIO); 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) { auto onOverwritten = [this] (const blockstore::Key &key) {
device()->RemoveBlob(key); device()->RemoveBlob(key);
}; };
_updateParentModificationTimestamp();
if (targetDir->key() == (*_parent)->key()) { if (targetDir->key() == (*_parent)->key()) {
targetDir->RenameChild(oldEntry.key(), to.filename().native(), onOverwritten); targetDir->RenameChild(oldEntry.key(), to.filename().native(), onOverwritten);
} else { } else {
_updateTargetDirModificationTimestamp(*targetDir, std::move(targetDirParent));
targetDir->AddOrOverwriteChild(to.filename().native(), oldEntry.key(), oldEntry.type(), oldEntry.mode(), oldEntry.uid(), oldEntry.gid(), targetDir->AddOrOverwriteChild(to.filename().native(), oldEntry.key(), oldEntry.type(), oldEntry.mode(), oldEntry.uid(), oldEntry.gid(),
oldEntry.lastAccessTime(), oldEntry.lastModificationTime(), onOverwritten); oldEntry.lastAccessTime(), oldEntry.lastModificationTime(), onOverwritten);
(*_parent)->RemoveChild(oldEntry.name()); (*_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<unique_ref<DirBlobRef>> 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) { void CryNode::utimens(timespec lastAccessTime, timespec lastModificationTime) {
device()->callFsActionCallbacks(); device()->callFsActionCallbacks();
if (_parent == none) { if (_parent == none) {

View File

@ -14,7 +14,8 @@ class CryNode: public virtual fspp::Node {
public: public:
virtual ~CryNode(); virtual ~CryNode();
CryNode(CryDevice *device, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> 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<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::Key &key);
void access(int mask) const override; void access(int mask) const override;
void stat(struct ::stat *result) const override; void stat(struct ::stat *result) const override;
void chmod(mode_t mode) override; void chmod(mode_t mode) override;
@ -29,16 +30,22 @@ protected:
const CryDevice *device() const; const CryDevice *device() const;
const blockstore::Key &key() const; const blockstore::Key &key() const;
cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob() const; cpputils::unique_ref<parallelaccessfsblobstore::FsBlobRef> LoadBlob() const;
bool isRootDir() const;
std::shared_ptr<const parallelaccessfsblobstore::DirBlobRef> parent() const; std::shared_ptr<const parallelaccessfsblobstore::DirBlobRef> parent() const;
std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent(); std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent();
boost::optional<parallelaccessfsblobstore::DirBlobRef*> grandparent();
virtual fspp::Dir::EntryType getType() const = 0; virtual fspp::Dir::EntryType getType() const = 0;
void removeNode(); void removeNode();
private: private:
void _updateParentModificationTimestamp();
void _updateTargetDirModificationTimestamp(const parallelaccessfsblobstore::DirBlobRef &targetDir, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> targetDirParent);
CryDevice *_device; CryDevice *_device;
boost::optional<std::shared_ptr<parallelaccessfsblobstore::DirBlobRef>> _parent; boost::optional<std::shared_ptr<parallelaccessfsblobstore::DirBlobRef>> _parent;
boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> _grandparent;
blockstore::Key _key; blockstore::Key _key;
DISALLOW_COPY_AND_ASSIGN(CryNode); DISALLOW_COPY_AND_ASSIGN(CryNode);

View File

@ -25,8 +25,8 @@ using cryfs::parallelaccessfsblobstore::DirBlobRef;
namespace cryfs { namespace cryfs {
CrySymlink::CrySymlink(CryDevice *device, unique_ref<DirBlobRef> parent, const Key &key) CrySymlink::CrySymlink(CryDevice *device, unique_ref<DirBlobRef> parent, optional<unique_ref<DirBlobRef>> grandparent, const Key &key)
: CryNode(device, std::move(parent), key) { : CryNode(device, std::move(parent), std::move(grandparent), key) {
} }
CrySymlink::~CrySymlink() { CrySymlink::~CrySymlink() {
@ -53,6 +53,10 @@ bf::path CrySymlink::target() {
void CrySymlink::remove() { void CrySymlink::remove() {
device()->callFsActionCallbacks(); 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(); removeNode();
} }

View File

@ -11,7 +11,7 @@ namespace cryfs {
class CrySymlink final: public fspp::Symlink, CryNode { class CrySymlink final: public fspp::Symlink, CryNode {
public: public:
CrySymlink(CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> parent, const blockstore::Key &key); CrySymlink(CryDevice *device, cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef> parent, boost::optional<cpputils::unique_ref<parallelaccessfsblobstore::DirBlobRef>> grandparent, const blockstore::Key &key);
~CrySymlink(); ~CrySymlink();
boost::filesystem::path target() override; boost::filesystem::path target() override;

View File

@ -32,7 +32,7 @@ public:
//TODO Allow alternative implementation returning only children names without more information //TODO Allow alternative implementation returning only children names without more information
//virtual std::unique_ptr<std::vector<std::string>> children() const = 0; //virtual std::unique_ptr<std::vector<std::string>> children() const = 0;
virtual cpputils::unique_ref<std::vector<Entry>> children() const = 0; virtual cpputils::unique_ref<std::vector<Entry>> children() = 0;
}; };
} }

View File

@ -12,26 +12,29 @@
#include "FsppOpenFileTest.h" #include "FsppOpenFileTest.h"
#include "FsppDeviceTest_Timestamps.h" #include "FsppDeviceTest_Timestamps.h"
#include "FsppNodeTest_Timestamps.h" #include "FsppNodeTest_Timestamps.h"
#include "FsppDirTest_Timestamps.h"
#include "FsppSymlinkTest_Timestamps.h" #include "FsppSymlinkTest_Timestamps.h"
#include "FsppFileTest_Timestamps.h" #include "FsppFileTest_Timestamps.h"
#include "FsppOpenFileTest_Timestamps.h" #include "FsppOpenFileTest_Timestamps.h"
#define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \ #define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \
INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppDeviceTest_Timestamps, 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, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest_Timestamps, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest_Timestamps, FIXTURE); \ INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppDirTest_Timestamps_Entries, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest_Timestamps, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest_Timestamps, FIXTURE); \
INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \
INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Stat, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest_Timestamps, FIXTURE); \
INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Timestamps, FIXTURE); \ INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_FileOnly, FIXTURE); \ INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Stat, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_DirOnly, FIXTURE); \ INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Timestamps, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_SymlinkOnly, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_FileOnly, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_DirOnly, FIXTURE); \
INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest_Timestamps, 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 #endif

View File

@ -17,14 +17,14 @@ public:
} }
void EXPECT_CHILDREN_ARE(const boost::filesystem::path &path, const std::initializer_list<fspp::Dir::Entry> expected) { void EXPECT_CHILDREN_ARE(const boost::filesystem::path &path, const std::initializer_list<fspp::Dir::Entry> 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<fspp::Dir::Entry> expected) { void EXPECT_CHILDREN_ARE(fspp::Dir *dir, const std::initializer_list<fspp::Dir::Entry> expected) {
std::vector<fspp::Dir::Entry> expectedChildren = expected; std::vector<fspp::Dir::Entry> expectedChildren = expected;
expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, ".")); expectedChildren.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));
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<class Entry> template<class Entry>
@ -63,7 +63,7 @@ TYPED_TEST_P(FsppDirTest, Children_RootDir_Empty) {
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_Directly) { TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_Directly) {
auto rootdir = this->LoadDir("/"); auto rootdir = this->LoadDir("/");
rootdir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); rootdir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*rootdir, { this->EXPECT_CHILDREN_ARE(rootdir.get(), {
FileEntry("myfile") FileEntry("myfile")
}); });
} }
@ -78,7 +78,7 @@ TYPED_TEST_P(FsppDirTest, Children_RootDir_OneFile_AfterReloadingDir) {
TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_Directly) { TYPED_TEST_P(FsppDirTest, Children_RootDir_OneDir_Directly) {
auto rootdir = this->LoadDir("/"); auto rootdir = this->LoadDir("/");
rootdir->createDir("mydir", this->MODE_PUBLIC, 0, 0); rootdir->createDir("mydir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*rootdir, { this->EXPECT_CHILDREN_ARE(rootdir.get(), {
DirEntry("mydir") DirEntry("mydir")
}); });
} }
@ -108,7 +108,7 @@ TYPED_TEST_P(FsppDirTest, Children_Nested_OneFile_Directly) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
auto dir = this->LoadDir("/mydir"); auto dir = this->LoadDir("/mydir");
dir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0); dir->createAndOpenFile("myfile", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*dir, { this->EXPECT_CHILDREN_ARE(dir.get(), {
FileEntry("myfile") FileEntry("myfile")
}); });
} }
@ -125,7 +125,7 @@ TYPED_TEST_P(FsppDirTest, Children_Nested_OneDir_Directly) {
this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0); this->LoadDir("/")->createDir("mydir", this->MODE_PUBLIC, 0, 0);
auto dir = this->LoadDir("/mydir"); auto dir = this->LoadDir("/mydir");
dir->createDir("mysubdir", this->MODE_PUBLIC, 0, 0); dir->createDir("mysubdir", this->MODE_PUBLIC, 0, 0);
this->EXPECT_CHILDREN_ARE(*dir, { this->EXPECT_CHILDREN_ARE(dir.get(), {
DirEntry("mysubdir") DirEntry("mysubdir")
}); });
} }

View File

@ -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 ConcreteFileSystemTestFixture>
class FsppDirTest_Timestamps: public FileSystemTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<fspp::Node> {
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 ConcreteFileSystemTestFixture>
class FsppDirTest_Timestamps_Entries: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<fspp::Node> {
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