Add support for atime mount options (noatime, strictatime, relatime, atime, nodiratime). As before, relatime is the default.

This commit is contained in:
Sebastian Messmer 2019-10-13 18:29:59 +07:00
parent ae09ff98aa
commit 8e617b1342
45 changed files with 1903 additions and 576 deletions

View File

@ -3,6 +3,9 @@ Version 0.11.0 (unreleased)
Build changes:
* Switch to Conan package manager
New features:
* Add support for atime mount options (noatime, strictatime, relatime, atime, nodiratime). As before, relatime is the default.
Version 0.10.2
---------------

View File

@ -244,8 +244,7 @@ namespace cryfs_cli {
}
};
const bool missingBlockIsIntegrityViolation = config.configFile->config()->missingBlockIsIntegrityViolation();
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId,
options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
_device = optional<unique_ref<CryDevice>>(make_unique_ref<CryDevice>(std::move(config.configFile), std::move(blockStore), std::move(localStateDir), config.myClientId, options.allowIntegrityViolations(), missingBlockIsIntegrityViolation, std::move(onIntegrityViolation)));
_sanityCheckFilesystem(_device->get());
auto initFilesystem = [&] (fspp::fuse::Fuse *fs){

View File

@ -81,12 +81,10 @@ ProgramOptions Parser::parse(const vector<string> &supportedCiphers) const {
if (vm.count("missing-block-is-integrity-violation")) {
missingBlockIsIntegrityViolation = vm["missing-block-is-integrity-violation"].as<bool>();
}
if (vm.count("fuse-option")) {
auto options = vm["fuse-option"].as<vector<string>>();
for (const auto& option: options) {
if (option == "noatime" || option == "atime") {
LOG(WARN, "CryFS currently doesn't support noatime/atime flags. Using relatime behavior.");
}
fuseOptions.push_back("-o");
fuseOptions.push_back(option);
}

View File

@ -21,7 +21,8 @@ ProgramOptions::ProgramOptions(bf::path baseDir, bf::path mountDir, optional<bf:
_foreground(foreground),
_allowFilesystemUpgrade(allowFilesystemUpgrade), _allowReplacedFilesystem(allowReplacedFilesystem), _allowIntegrityViolations(allowIntegrityViolations),
_cipher(std::move(cipher)), _blocksizeBytes(std::move(blocksizeBytes)), _unmountAfterIdleMinutes(std::move(unmountAfterIdleMinutes)),
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)), _fuseOptions(std::move(fuseOptions)) {
_missingBlockIsIntegrityViolation(std::move(missingBlockIsIntegrityViolation)), _logFile(std::move(logFile)),
_fuseOptions(std::move(fuseOptions)) {
if (!_mountDirIsDriveLetter) {
_mountDir = bf::absolute(std::move(_mountDir));
}

View File

@ -22,8 +22,10 @@
#include "cryfs/impl/localstate/LocalStateDir.h"
#include <cryfs/impl/CryfsException.h>
using std::string;
//TODO Get rid of this in favor of exception hierarchy
using fspp::fuse::FuseErrnoException;

View File

@ -15,6 +15,7 @@
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/FileBlobRef.h"
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
namespace cryfs {
class CryDevice final: public fspp::Device {

View File

@ -9,7 +9,6 @@
#include "CryFile.h"
#include "CryOpenFile.h"
#include <cpp-utils/system/time.h>
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
//TODO Get rid of this in favor of exception hierarchy
using fspp::fuse::FuseErrnoException;
@ -72,7 +71,7 @@ vector<fspp::Dir::Entry> CryDir::children() {
device()->callFsActionCallbacks();
if (!isRootDir()) { // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
//TODO Instead of doing nothing when we're the root directory, handle timestamps in the root dir correctly (and delete isRootDir() function)
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
}
vector<fspp::Dir::Entry> children;
children.push_back(fspp::Dir::Entry(fspp::Dir::EntryType::DIR, "."));

View File

@ -70,6 +70,10 @@ optional<DirBlobRef*> CryNode::grandparent() {
return _grandparent->get();
}
fspp::TimestampUpdateBehavior CryNode::timestampUpdateBehavior() const {
return _device->getContext().timestampUpdateBehavior();
}
void CryNode::rename(const bf::path &to) {
device()->callFsActionCallbacks();
if (_parent == none) {

View File

@ -16,6 +16,7 @@ public:
// 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::BlockId &blockId);
void access(int mask) const override;
stat_info stat() const override;
void chmod(fspp::mode_t mode) override;
@ -37,6 +38,7 @@ protected:
std::shared_ptr<const parallelaccessfsblobstore::DirBlobRef> parent() const;
std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent();
boost::optional<parallelaccessfsblobstore::DirBlobRef*> grandparent();
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
virtual fspp::Dir::EntryType getType() const = 0;

View File

@ -43,7 +43,7 @@ void CryOpenFile::truncate(fspp::num_bytes_t size) const {
fspp::num_bytes_t CryOpenFile::read(void *buf, fspp::num_bytes_t count, fspp::num_bytes_t offset) const {
_device->callFsActionCallbacks();
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
_parent->updateAccessTimestampForChild(_fileBlob->blockId(), timestampUpdateBehavior());
return _fileBlob->read(buf, offset, count);
}
@ -64,4 +64,8 @@ void CryOpenFile::fdatasync() {
_fileBlob->flush();
}
fspp::TimestampUpdateBehavior CryOpenFile::timestampUpdateBehavior() const {
return _device->getContext().timestampUpdateBehavior();
}
}

View File

@ -21,6 +21,7 @@ public:
void flush() override;
void fsync() override;
void fdatasync() override;
fspp::TimestampUpdateBehavior timestampUpdateBehavior() const;
private:
const CryDevice *_device;

View File

@ -4,7 +4,6 @@
#include "CryDevice.h"
#include "CrySymlink.h"
#include "cryfs/impl/filesystem/parallelaccessfsblobstore/SymlinkBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
//TODO Get rid of this in favor of exception hierarchy
@ -43,7 +42,7 @@ fspp::Dir::EntryType CrySymlink::getType() const {
bf::path CrySymlink::target() {
device()->callFsActionCallbacks();
parent()->updateAccessTimestampForChild(blockId(), fsblobstore::TimestampUpdateBehavior::RELATIME);
parent()->updateAccessTimestampForChild(blockId(), timestampUpdateBehavior());
auto blob = LoadBlob(); // NOLINT (workaround https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82481 )
return blob->target();
}

View File

@ -4,8 +4,8 @@
#include "FsBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/DirBlob.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
#include <fspp/fs_interface/Context.h>
namespace cryfs {
namespace cachingfsblobstore {
@ -62,7 +62,7 @@ public:
return _base->statChildWithKnownSize(blockId, size);
}
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
}

View File

@ -173,7 +173,7 @@ fspp::Node::stat_info DirBlob::statChildWithKnownSize(const BlockId &blockId, fs
return result;
}
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
std::unique_lock<std::mutex> lock(_entriesAndChangedMutex);
if (_entries.updateAccessTimestampForChild(blockId, timestampUpdateBehavior)) {
_changed = true;

View File

@ -61,7 +61,7 @@ namespace cryfs {
fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, fspp::num_bytes_t size) const;
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);

View File

@ -228,22 +228,30 @@ void DirEntryList::setAccessTimes(const blockstore::BlockId &blockId, timespec l
found->setLastModificationTime(lastModificationTime);
}
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {
ASSERT(timestampUpdateBehavior == TimestampUpdateBehavior::RELATIME, "Currently only relatime supported");
bool DirEntryList::updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
auto found = _findById(blockId);
const timespec lastAccessTime = found->lastAccessTime();
const timespec lastModificationTime = found->lastModificationTime();
const timespec now = cpputils::time::now();
const timespec yesterday {
/*.tv_sec = */ now.tv_sec - 60*60*24,
/*.tv_nsec = */ now.tv_nsec
};
bool changed = false;
if (lastAccessTime < lastModificationTime || lastAccessTime < yesterday) {
found->setLastAccessTime(now);
changed = true;
switch (found->type()) {
case fspp::Dir::EntryType::FILE:
// fallthrough
case fspp::Dir::EntryType::SYMLINK:
if (timestampUpdateBehavior->shouldUpdateATimeOnFileRead(lastAccessTime, lastModificationTime, now)) {
found->setLastAccessTime(now);
return true;
}
return false;
case fspp::Dir::EntryType::DIR:
if (timestampUpdateBehavior->shouldUpdateATimeOnDirectoryRead(lastAccessTime, lastModificationTime, now)) {
found->setLastAccessTime(now);
return true;
}
return false;
}
return changed;
throw std::logic_error("Unhandled case");
}
void DirEntryList::updateModificationTimestampForChild(const blockstore::BlockId &blockId) {

View File

@ -3,10 +3,10 @@
#define MESSMER_CRYFS_FILESYSTEM_FSBLOBSTORE_UTILS_DIRENTRYLIST_H
#include <cpp-utils/data/Data.h>
#include <fspp/fs_interface/Context.h>
#include "DirEntry.h"
#include <vector>
#include <string>
#include "TimestampUpdateBehavior.h"
//TODO Address elements by name instead of by blockId when accessing them. Who knows whether there is two hard links for the same blob.
@ -40,7 +40,7 @@ namespace cryfs {
void setMode(const blockstore::BlockId &blockId, fspp::mode_t mode);
bool setUidGid(const blockstore::BlockId &blockId, fspp::uid_t uid, fspp::gid_t gid);
void setAccessTimes(const blockstore::BlockId &blockId, timespec lastAccessTime, timespec lastModificationTime);
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);
bool updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior);
void updateModificationTimestampForChild(const blockstore::BlockId &blockId);
private:

View File

@ -1,17 +0,0 @@
#pragma once
#ifndef CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
#define CRYFS_TIMESTAMPUPDATEBEHAVIOR_H
namespace cryfs {
namespace fsblobstore {
enum class TimestampUpdateBehavior : uint8_t {
// currently only relatime supported
RELATIME
};
}
}
#endif

View File

@ -4,7 +4,6 @@
#include "FsBlobRef.h"
#include "cryfs/impl/filesystem/cachingfsblobstore/DirBlobRef.h"
#include "cryfs/impl/filesystem/fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
namespace cryfs {
@ -58,7 +57,7 @@ public:
return _base->statChildWithKnownSize(blockId, size);
}
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
return _base->updateAccessTimestampForChild(blockId, timestampUpdateBehavior);
}

View File

@ -7,7 +7,9 @@ set(SOURCES
Node.cpp
OpenFile.cpp
Symlink.cpp
Types.cpp)
Types.cpp
Context.cpp
)
add_library(${PROJECT_NAME} STATIC ${SOURCES})

View File

@ -0,0 +1 @@
#include "Context.h"

View File

@ -0,0 +1,128 @@
#pragma once
#ifndef MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
#define MESSMER_FSPP_FSINTERFACE_CONTEXT_H_
#include <cstdint>
#include <memory>
#include <cpp-utils/system/time.h>
namespace fspp {
namespace detail {
class TimestampUpdateBehaviorBase {
public:
virtual bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
virtual bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const = 0;
};
}
// Defines how atime timestamps of files and directories are accessed on read accesses
// (e.g. atime, strictatime, relatime, nodiratime)
using TimestampUpdateBehavior = std::shared_ptr<detail::TimestampUpdateBehaviorBase>;
// atime attribute (of both files and directories) is updated only during write access.
inline TimestampUpdateBehavior noatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// This causes the atime attribute to update with every file access. (accessing file data, not just the metadata/attributes)
inline TimestampUpdateBehavior strictatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return true;
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return true;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
inline TimestampUpdateBehavior relatime() {
// This option causes the atime attribute to update only if the previous atime is older than mtime or ctime, or the previous atime is over 24 hours old.
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
const timespec yesterday {
/*.tv_sec = */ newATime.tv_sec - 60*60*24,
/*.tv_nsec = */ newATime.tv_nsec
};
return oldATime < oldMTime || oldATime < yesterday;
}
bool shouldUpdateATimeOnDirectoryRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the relatime rules.
inline TimestampUpdateBehavior nodiratime_relatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return relatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
// atime of directories is updated only during write access, can be combined with relatime. atime of files follows the strictatime rules.
inline TimestampUpdateBehavior nodiratime_strictatime() {
class BehaviorImpl final : public detail::TimestampUpdateBehaviorBase {
public:
bool shouldUpdateATimeOnFileRead(timespec oldATime, timespec oldMTime, timespec newATime) const override {
return strictatime()->shouldUpdateATimeOnFileRead(oldATime, oldMTime, newATime);
}
bool shouldUpdateATimeOnDirectoryRead(timespec /*oldATime*/, timespec /*oldMTime*/, timespec /*newATime*/) const override {
return false;
}
};
static std::shared_ptr<BehaviorImpl> singleton = std::make_shared<BehaviorImpl>();
return singleton;
}
class Context final {
public:
explicit Context(TimestampUpdateBehavior timestampUpdateBehavior)
: _timestampUpdateBehavior(std::move(timestampUpdateBehavior)) {}
const TimestampUpdateBehavior& timestampUpdateBehavior() const {
return _timestampUpdateBehavior;
}
void setTimestampUpdateBehavior(TimestampUpdateBehavior value) {
_timestampUpdateBehavior = std::move(value);
}
private:
TimestampUpdateBehavior _timestampUpdateBehavior;
};
}
#endif

View File

@ -5,6 +5,8 @@
#include <boost/filesystem.hpp>
#include <cpp-utils/pointer/unique_ref.h>
#include "Types.h"
#include "Context.h"
#include <boost/optional.hpp>
namespace fspp {
class Node;
@ -28,6 +30,19 @@ public:
virtual boost::optional<cpputils::unique_ref<Dir>> LoadDir(const boost::filesystem::path &path) = 0;
virtual boost::optional<cpputils::unique_ref<Symlink>> LoadSymlink(const boost::filesystem::path &path) = 0;
const Context& getContext() const {
ASSERT(_context != boost::none, "Tried to call getContext() but file system isn't running yet.");
return *_context;
}
// called by fspp system on file system init. Don't call this manually.
// TODO Is there a better way to do this?
void setContext(Context&& context) {
_context = std::move(context);
}
private:
boost::optional<Context> _context;
};
}

View File

@ -8,30 +8,36 @@ template<class ConcreteFileSystemTestFixture>
class FsppDeviceTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
public:
void Test_Load_While_Loaded() {
auto node = this->CreateNode("/mynode");
auto operation = [this] () {
this->device->Load("/mynode");
auto operation = [this] {
auto node = this->CreateNode("/mynode");
return [this] {
this->device->Load("/mynode");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Load_While_Not_Loaded() {
fspp::Node::stat_info oldStat{};
{
auto node = this->CreateNode("/mynode");
oldStat = this->stat(*node);
this->ensureNodeTimestampsAreOld(oldStat);
}
this->testBuilder().withAnyAtimeConfig([&] {
fspp::Node::stat_info oldStat{};
{
auto node = this->CreateNode("/mynode");
oldStat = this->stat(*node);
this->ensureNodeTimestampsAreOld(oldStat);
}
this->device->Load("/myfile");
this->device->Load("/myfile");
auto node = this->device->Load("/mynode");
auto node = this->device->Load("/mynode");
//Test that timestamps didn't change
fspp::Node::stat_info newStat = this->stat(*node.value());
EXPECT_EQ(oldStat.atime, newStat.atime);
EXPECT_EQ(oldStat.mtime, newStat.mtime);
EXPECT_EQ(oldStat.ctime, newStat.ctime);
//Test that timestamps didn't change
fspp::Node::stat_info newStat = this->stat(*node.value());
EXPECT_EQ(oldStat.atime, newStat.atime);
EXPECT_EQ(oldStat.mtime, newStat.mtime);
EXPECT_EQ(oldStat.ctime, newStat.ctime);
});
}
};

View File

@ -11,225 +11,501 @@ public:
TYPED_TEST_SUITE_P(FsppDirTest_Timestamps);
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile) {
auto dir = this->CreateDir("/mydir");
auto operation = [&dir] () {
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
return [dir = std::move(dir)] {
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->LoadDir("/");
return [dir = std::move(dir)] {
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}*/
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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);
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
return [dir = std::move(dir)] {
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->LoadDir("/");
return [dir = std::move(dir)] {
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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);
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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", fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
return [dir = std::move(dir)] {
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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", fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [this] {
auto dir = this->LoadDir("/");
return [dir = std::move(dir)] {
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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);
this->testBuilder().withAnyAtimeConfig([&] {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = cpputils::time::now();
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = cpputils::time::now();
auto child = this->Load("/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");
this->setModificationTimestampLaterThanAccessTimestamp("/mydir"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
auto operation = [&dir] () {
dir->children();
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setAtimeOlderThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setAtimeNewerThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
/* 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();
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
this->setAtimeOlderThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}*/
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, children_nonempty) {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [&dir] () {
dir->children();
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
this->setAtimeNewerThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_empty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
*/
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeOlderThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtime("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty) {
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtimeButBeforeYesterday("/mydir");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
/* 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", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
auto operation = [&dir] () {
dir->children();
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeOlderThanMtime_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeOlderThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}*/
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtime_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtime("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppDirTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty_inRootDir) {
auto operation = [this] {
auto dir = this->LoadDir("/");
dir->createAndOpenFile("filename", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
this->setAtimeNewerThanMtimeButBeforeYesterday("/");
return [dir = std::move(dir)] {
dir->children();
};
};
this->testBuilder().withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
*/
template<class ConcreteFileSystemTestFixture>
class FsppDirTest_Timestamps_Entries: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
public:
void Test_deleteChild() {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
auto operation = [&child]() {
child->remove();
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
return [child = std::move(child)] {
child->remove();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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();
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/mydir/childname");
return [child = std::move(child)] {
child->remove();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", 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");
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
return [child = std::move(child)] {
child->rename("/mydir/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&]{
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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");
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/childname");
return [child = std::move(child)] {
child->rename("/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&]{
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", 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");
auto operation = [this] {
auto sourceDir = this->CreateDir("/sourcedir");
auto child = this->CreateNode("/sourcedir/childname");
auto dir = this->CreateDir("/mydir");
return [child = std::move(child)] {
child->rename("/mydir/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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");
auto operation = [this] {
auto sourceDir = this->CreateDir("/sourcedir");
auto child = this->CreateNode("/sourcedir/childname");
auto dir = this->LoadDir("/");
return [child = std::move(child)] {
child->rename("/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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");
auto operation = [this] {
auto dir = this->CreateDir("/mydir");
auto child = this->CreateNode("/mydir/childname");
this->CreateDir("/targetdir");
return [child = std::move(child)] {
child->rename("/targetdir/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectUpdatesModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", 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");
auto operation = [this] {
auto dir = this->LoadDir("/");
auto child = this->CreateNode("/childname");
this->CreateDir("/targetdir");
return [child = std::move(child)] {
child->rename("/targetdir/mychild");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
}*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
*/
};
REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
@ -239,8 +515,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppDirTest_Timestamps,
createDir_TimestampsOfCreatedDir,
createSymlink,
createSymlink_TimestampsOfCreatedSymlink,
children_empty,
children_nonempty
givenAtimeNewerThanMtime_children_empty,
givenAtimeOlderThanMtime_children_empty,
givenAtimeNewerThanMtimeButBeforeYesterday_children_empty,
givenAtimeNewerThanMtime_children_nonempty,
givenAtimeOlderThanMtime_children_nonempty,
givenAtimeNewerThanMtimeButBeforeYesterday_children_nonempty
);
REGISTER_NODE_TEST_SUITE(FsppDirTest_Timestamps_Entries,

View File

@ -17,75 +17,111 @@ public:
TYPED_TEST_SUITE_P(FsppFileTest_Timestamps);
TYPED_TEST_P(FsppFileTest_Timestamps, open_nomode) {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
file->open(fspp::openflags_t(0));
auto operation = [this] {
auto file = this->CreateFile("/myfile");
return [file = std::move(file)] {
file->open(fspp::openflags_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdonly) {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
file->open(fspp::openflags_t::RDONLY());
auto operation = [this] {
auto file = this->CreateFile("/myfile");
return [file = std::move(file)] {
file->open(fspp::openflags_t::RDONLY());
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_wronly) {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
file->open(fspp::openflags_t::WRONLY());
auto operation = [this] {
auto file = this->CreateFile("/myfile");
return [file = std::move(file)] {
file->open(fspp::openflags_t::WRONLY());
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, open_rdwr) {
auto file = this->CreateFile("/myfile");
auto operation = [&file] () {
file->open(fspp::openflags_t::RDWR());
auto operation = [this] {
auto file = this->CreateFile("/myfile");
return [file = std::move(file)] {
file->open(fspp::openflags_t::RDWR());
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_empty) {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&file] () {
file->truncate(fspp::num_bytes_t(0));
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_empty_to_nonempty) {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&file] () {
file->truncate(fspp::num_bytes_t(10));
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(0));
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(10));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_empty) {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
file->truncate(fspp::num_bytes_t(0));
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
file->truncate(fspp::num_bytes_t(5));
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(5));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&file] () {
file->truncate(fspp::num_bytes_t(20));
auto operation = [this] {
auto file = this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
return [file = std::move(file)] {
file->truncate(fspp::num_bytes_t(20));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/myfile", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppFileTest_Timestamps,

View File

@ -13,339 +13,361 @@ class FsppNodeTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture
public:
void Test_Create() {
timespec lowerBound = cpputils::time::now();
auto node = this->CreateNode("/mynode");
timespec upperBound = cpputils::time::now();
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
this->testBuilder().withAnyAtimeConfig([&] {
timespec lowerBound = cpputils::time::now();
auto node = this->CreateNode("/mynode");
timespec upperBound = cpputils::time::now();
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);
});
}
void Test_Stat() {
auto node = this->CreateNode("/mynode");
auto operation = [&node] () {
node->stat();
auto operation = [this] {
auto node = this->CreateNode("/mynode");
return [node = std::move(node)] {
node->stat();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Chmod() {
auto node = this->CreateNode("/mynode");
fspp::mode_t mode = this->stat(*node).mode;
auto operation = [&node, mode] () {
node->chmod(mode);
auto operation = [this] {
auto node = this->CreateNode("/mynode");
fspp::mode_t mode = this->stat(*node).mode;
return [mode, node = std::move(node)] {
node->chmod(mode);
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Chown() {
auto node = this->CreateNode("/mynode");
fspp::uid_t uid = this->stat(*node).uid;
fspp::gid_t gid = this->stat(*node).gid;
auto operation = [&node, uid, gid] () {
node->chown(uid, gid);
auto operation = [this] {
auto node = this->CreateNode("/mynode");
fspp::uid_t uid = this->stat(*node).uid;
fspp::gid_t gid = this->stat(*node).gid;
return [uid, gid, node = std::move(node)] {
node->chown(uid, gid);
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Access() {
auto node = this->CreateNode("/mynode");
auto operation = [&node] () {
node->access(0);
auto operation = [this] {
auto node = this->CreateNode("/mynode");
return [node = std::move(node)] {
node->access(0);
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_TargetParentDirDoesntExist() {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
try {
node->rename("/notexistingdir/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOENT, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
auto node = this->CreateNode("/oldname");
return [node = std::move(node)] {
try {
node->rename("/notexistingdir/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOENT, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_TargetParentDirIsFile() {
auto node = this->CreateNode("/oldname");
this->CreateFile("/somefile");
auto operation = [&node] () {
try {
node->rename("/somefile/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
auto node = this->CreateNode("/oldname");
this->CreateFile("/somefile");
return [node = std::move(node)] {
try {
node->rename("/somefile/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Error_RootDir() {
// TODO Re-enable this test once the root dir stores timestamps correctly
/*
auto root = this->Load("/");
auto operation = [&root] () {
try {
root->rename("/newname");
EXPECT_TRUE(false); // expect throws
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EBUSY, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
auto root = this->Load("/");
return [root = std::move(root)] {
try {
root->rename("/newname");
EXPECT_TRUE(false); // expect throws
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EBUSY, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps
});
*/
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
*/
}
void Test_Rename_InRoot() {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
node->rename("/newname");
auto operation = [this] {
auto node = this->CreateNode("/oldname");
return [node = std::move(node)] {
node->rename("/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_InNested() {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
node->rename("/mydir/newname");
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
return [node = std::move(node)] {
node->rename("/mydir/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_RootToNested_SameName() {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
node->rename("/mydir/oldname");
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
return [node = std::move(node)] {
node->rename("/mydir/oldname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_RootToNested_NewName() {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
node->rename("/mydir/newname");
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/oldname");
return [node = std::move(node)] {
node->rename("/mydir/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/mydir/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToRoot_SameName() {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
node->rename("/oldname");
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
return [node = std::move(node)] {
node->rename("/oldname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToRoot_NewName() {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
auto operation = [&node] () {
node->rename("/newname");
auto operation = [this] {
this->CreateDir("/mydir");
auto node = this->CreateNode("/mydir/oldname");
return [node = std::move(node)] {
node->rename("/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToNested_SameName() {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
node->rename("/mydir2/oldname");
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
return [node = std::move(node)] {
node->rename("/mydir2/oldname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_NestedToNested_NewName() {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
node->rename("/mydir2/newname");
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
auto node = this->CreateNode("/mydir1/oldname");
return [node = std::move(node)] {
node->rename("/mydir2/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_ToItself() {
auto node = this->CreateNode("/oldname");
auto operation = [&node] () {
node->rename("/oldname");
auto operation = [this] {
auto node = this->CreateNode("/oldname");
return [node = std::move(node)] {
node->rename("/oldname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/oldname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_InSameDir() {
auto node = this->CreateNode("/oldname");
this->CreateNode("/newname");
auto operation = [&node] () {
node->rename("/newname");
auto operation = [this] {
auto node = this->CreateNode("/oldname");
this->CreateNode("/newname");
return [node = std::move(node)] {
node->rename("/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", "/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_InDifferentDir() {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateNode("/mydir2/newname");
auto node = this->CreateNode("/mydir1/oldname");
auto operation = [&node] () {
node->rename("/mydir2/newname");
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateNode("/mydir2/newname");
auto node = this->CreateNode("/mydir1/oldname");
return [node = std::move(node)] {
node->rename("/mydir2/newname");
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation, {
this->ExpectDoesntUpdateAccessTimestamp,
this->ExpectDoesntUpdateModificationTimestamp,
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", "/mydir2/newname", operation(), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
void Test_Rename_Overwrite_Error_DirWithFile_InSameDir() {
this->CreateFile("/oldname");
this->CreateDir("/newname");
auto node = this->Load("/oldname");
auto operation = [&node] () {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EISDIR, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
this->CreateFile("/oldname");
this->CreateDir("/newname");
auto node = this->Load("/oldname");
return [node = std::move(node)] {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EISDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_DirWithFile_InDifferentDir() {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateFile("/mydir1/oldname");
this->CreateDir("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
auto operation = [&node] () {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EISDIR, e.getErrno());//Rename fails, everything is ok.
}
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateFile("/mydir1/oldname");
this->CreateDir("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
return [node = std::move(node)] {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(EISDIR, e.getErrno());//Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_FileWithDir_InSameDir() {
this->CreateDir("/oldname");
this->CreateFile("/newname");
auto node = this->Load("/oldname");
auto operation = [&node] () {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
this->CreateDir("/oldname");
this->CreateFile("/newname");
auto node = this->Load("/oldname");
return [node = std::move(node)] {
try {
node->rename("/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Rename_Overwrite_Error_FileWithDir_InDifferentDir() {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateDir("/mydir1/oldname");
this->CreateFile("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
auto operation = [&node] () {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
auto operation = [this] {
this->CreateDir("/mydir1");
this->CreateDir("/mydir2");
this->CreateDir("/mydir1/oldname");
this->CreateFile("/mydir2/newname");
auto node = this->Load("/mydir1/oldname");
return [node = std::move(node)] {
try {
node->rename("/mydir2/newname");
EXPECT_TRUE(false); // expect rename to fail
} catch (const fspp::fuse::FuseErrnoException &e) {
EXPECT_EQ(ENOTDIR, e.getErrno()); //Rename fails, everything is ok.
}
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation, {
this->ExpectDoesntUpdateAnyTimestamps
this->testBuilder().withAnyAtimeConfig([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mydir1/oldname", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
void Test_Utimens() {
auto node = this->CreateNode("/mynode");
timespec atime = this->xSecondsAgo(100);
timespec mtime = this->xSecondsAgo(200);
auto operation = [&node, atime, mtime] () {
node->utimens(atime, mtime);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectUpdatesMetadataTimestamp
this->testBuilder().withAnyAtimeConfig([&] {
auto node = this->CreateNode("/mynode");
timespec atime = this->xSecondsAgo(100);
timespec mtime = this->xSecondsAgo(200);
auto operation = [atime, mtime, &node] {
node->utimens(atime, mtime);
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectUpdatesMetadataTimestamp
});
EXPECT_EQ(atime, this->stat(*node).atime);
EXPECT_EQ(mtime, this->stat(*node).mtime);
});
EXPECT_EQ(atime, this->stat(*node).atime);
EXPECT_EQ(mtime, this->stat(*node).mtime);
}
};

View File

@ -14,120 +14,343 @@ public:
auto file = this->CreateFile(path);
file->truncate(size);
auto openFile = file->open(fspp::openflags_t::RDWR());
assert(this->stat(*openFile).size == size);
assert(this->stat(*this->Load(path)).size == size);
ASSERT(this->stat(*openFile).size == size, "");
ASSERT(this->stat(*this->Load(path)).size == size, "");
return openFile;
}
void CreateFileWithSize(const boost::filesystem::path &path, fspp::num_bytes_t size) {
auto file = this->CreateFile(path);
file->truncate(size);
}
cpputils::unique_ref<fspp::OpenFile> OpenFile(const boost::filesystem::path &path, fspp::num_bytes_t size) {
auto file = this->LoadFile(path);
auto openFile = file->open(fspp::openflags_t::RDWR());
ASSERT(this->stat(*openFile).size == size, "");
ASSERT(this->stat(*this->Load(path)).size == size, "");
return openFile;
}
};
TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps);
TYPED_TEST_P(FsppOpenFileTest_Timestamps, stat) {
auto openFile = this->CreateAndOpenFile("/mynode");
auto operation = [&openFile] () {
openFile->stat();
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->stat();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFile("/mynode");
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_empty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->truncate(fspp::num_bytes_t(0));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_empty_to_nonempty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->truncate(fspp::num_bytes_t(10));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(10));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_empty) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
openFile->truncate(fspp::num_bytes_t(0));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_shrink) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
openFile->truncate(fspp::num_bytes_t(5));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(5));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, truncate_nonempty_to_nonempty_grow) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
openFile->truncate(fspp::num_bytes_t(20));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->truncate(fspp::num_bytes_t(20));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_inbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, read_outofbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_inbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeOlderThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(0));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtimeButBeforeYesterday("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeNewerThanMtime_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeNewerThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, givenAtimeOlderThanMtime_read_outofbounds) {
auto operation = [this] () {
this->CreateFileWithSize("/myfile", fspp::num_bytes_t(10));
this->setAtimeOlderThanMtime("/myfile");
auto openFile = this->OpenFile("/myfile", fspp::num_bytes_t(10));
auto* openFilePtr = openFile.get();
return std::make_pair(openFilePtr, [openFile = std::move(openFile)] {
std::array<char, 5> buffer{};
openFile->read(buffer.data(), fspp::num_bytes_t(5), fspp::num_bytes_t(2));
});
};
this->testBuilder()
.withNoatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
auto op = operation();
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*op.first, std::move(op.second), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_inbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
auto operation = [&openFile] () {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, write_outofbounds) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(2));
auto operation = [] (fspp::OpenFile* openFile){
return [openFile] {
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(2));
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(0));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAccessTimestamp, this->ExpectUpdatesModificationTimestamp, this->ExpectUpdatesMetadataTimestamp});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, flush) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->flush();
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
return [openFile] {
openFile->flush();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fsync) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->fsync();
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
return [openFile] {
openFile->fsync();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
TYPED_TEST_P(FsppOpenFileTest_Timestamps, fdatasync) {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
auto operation = [&openFile] () {
openFile->fdatasync();
auto operation = [] (fspp::OpenFile* openFile){
openFile->write("content", fspp::num_bytes_t(7), fspp::num_bytes_t(0));
return [openFile] {
openFile->fdatasync();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
this->testBuilder().withAnyAtimeConfig([&] {
auto openFile = this->CreateAndOpenFileWithSize("/myfile", fspp::num_bytes_t(10));
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation(openFile.get()), {this->ExpectDoesntUpdateAnyTimestamps});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
@ -137,8 +360,12 @@ REGISTER_TYPED_TEST_SUITE_P(FsppOpenFileTest_Timestamps,
truncate_nonempty_to_empty,
truncate_nonempty_to_nonempty_shrink,
truncate_nonempty_to_nonempty_grow,
read_inbounds,
read_outofbounds,
givenAtimeNewerThanMtimeButBeforeYesterday_read_inbounds,
givenAtimeNewerThanMtime_read_inbounds,
givenAtimeOlderThanMtime_read_inbounds,
givenAtimeNewerThanMtimeButBeforeYesterday_read_outofbounds,
givenAtimeNewerThanMtime_read_outofbounds,
givenAtimeOlderThanMtime_read_outofbounds,
write_inbounds,
write_outofbounds,
flush,

View File

@ -10,17 +10,76 @@ public:
};
TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps);
TYPED_TEST_P(FsppSymlinkTest_Timestamps, target) {
auto symlink = this->CreateSymlink("/mysymlink");
this->setModificationTimestampLaterThanAccessTimestamp("/mysymlink"); // to make sure that even in relatime behavior, the read access below changes the access timestamp
auto operation = [&symlink] () {
symlink->target();
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtimeButBeforeYesterday_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setAtimeNewerThanMtimeButBeforeYesterday("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
};
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation, {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeOlderThanMtime_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setAtimeOlderThanMtime("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
};
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
TYPED_TEST_P(FsppSymlinkTest_Timestamps, givenAtimeNewerThanMtime_target) {
auto operation = [this] {
auto symlink = this->CreateSymlink("/mysymlink");
this->setAtimeNewerThanMtime("/mysymlink");
return [symlink = std::move(symlink)] {
symlink->target();
};
};
this->testBuilder()
.withNoatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
}).withRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeRelatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectDoesntUpdateAnyTimestamps});
}).withNodiratimeStrictatime([&] {
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mysymlink", operation(), {this->ExpectUpdatesAccessTimestamp, this->ExpectDoesntUpdateModificationTimestamp, this->ExpectDoesntUpdateMetadataTimestamp});
});
}
REGISTER_TYPED_TEST_SUITE_P(FsppSymlinkTest_Timestamps,
target
givenAtimeNewerThanMtimeButBeforeYesterday_target,
givenAtimeNewerThanMtime_target,
givenAtimeOlderThanMtime_target
);
#endif

View File

@ -30,10 +30,20 @@ public:
"Given test fixture for instantiating the (type parameterized) FileSystemTest must inherit from FileSystemTestFixture"
);
FileSystemTest(): fixture(), device(fixture.createDevice()) {}
FileSystemTest(): fixture(nullptr), device(nullptr) {
resetFilesystem(fspp::Context{fspp::relatime()});
}
ConcreteFileSystemTestFixture fixture;
cpputils::unique_ref<fspp::Device> device;
void resetFilesystem(fspp::Context&& context) {
device = nullptr;
fixture = nullptr;
fixture = std::make_unique<ConcreteFileSystemTestFixture>();
device = fixture->createDevice();
device->setContext(std::move(context));
}
std::unique_ptr<ConcreteFileSystemTestFixture> fixture;
std::unique_ptr<fspp::Device> device;
static constexpr fspp::mode_t MODE_PUBLIC = fspp::mode_t()
.addUserReadFlag().addUserWriteFlag().addUserExecFlag()
@ -91,15 +101,41 @@ public:
EXPECT_NE(nullptr, dynamic_cast<const fspp::Symlink*>(node.get()));
}
void setModificationTimestampLaterThanAccessTimestamp(const boost::filesystem::path& path) {
void setAtimeOlderThanMtime(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
st.mtime.tv_nsec = st.mtime.tv_nsec + 1;
st.atime.tv_nsec = st.mtime.tv_nsec - 1;
node->utimens(
st.atime,
st.mtime
);
}
void setAtimeNewerThanMtime(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
st.atime.tv_nsec = st.mtime.tv_nsec + 1;
node->utimens(
st.atime,
st.mtime
);
}
void setAtimeNewerThanMtimeButBeforeYesterday(const boost::filesystem::path& path) {
auto node = device->Load(path).value();
auto st = node->stat();
const timespec now = cpputils::time::now();
const timespec before_yesterday {
/*.tv_sec = */ now.tv_sec - 60*60*24 - 1,
/*.tv_nsec = */ now.tv_nsec
};
st.atime = before_yesterday;
st.mtime.tv_nsec = st.atime.tv_nsec - 1;
node->utimens(
st.atime,
st.mtime
);
}
};
template<class ConcreteFileSystemTestFixture> constexpr fspp::mode_t FileSystemTest<ConcreteFileSystemTestFixture>::MODE_PUBLIC;

View File

@ -10,17 +10,22 @@
template<class ConcreteFileSystemTestFixture>
class TimestampTestUtils : public virtual FileSystemTest<ConcreteFileSystemTestFixture> {
public:
using TimestampUpdateBehavior = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
using TimestampUpdateExpectation = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
static TimestampUpdateBehavior ExpectUpdatesAccessTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAccessTimestamp;
static TimestampUpdateBehavior ExpectUpdatesModificationTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateModificationTimestamp;
static TimestampUpdateBehavior ExpectUpdatesMetadataTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateMetadataTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAnyTimestamps;
static TimestampUpdateExpectation ExpectUpdatesAccessTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateAccessTimestamp;
static TimestampUpdateExpectation ExpectUpdatesModificationTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateModificationTimestamp;
static TimestampUpdateExpectation ExpectUpdatesMetadataTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateMetadataTimestamp;
static TimestampUpdateExpectation ExpectDoesntUpdateAnyTimestamps;
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
void setTimestampUpdateBehavior(fspp::TimestampUpdateBehavior timestampUpdateBehavior) {
FileSystemTest<ConcreteFileSystemTestFixture>::device->setContext(fspp::Context { timestampUpdateBehavior });
}
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<fspp::Node::stat_info()> statOld, std::function<fspp::Node::stat_info()> statNew, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
auto oldStat = statOld();
ensureNodeTimestampsAreOld(oldStat);
timespec timeBeforeOperation = cpputils::time::now();
@ -32,26 +37,29 @@ public:
}
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const fspp::OpenFile &node, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
[this, &node](){return this->stat(node);},
[this, &node](){return this->stat(node);},
operation,
std::forward<Operation>(operation),
behaviorChecks
);
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &oldPath, const boost::filesystem::path &newPath, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(
[this, oldPath](){return this->stat(*this->Load(oldPath));},
[this, newPath](){return this->stat(*this->Load(newPath));},
operation,
std::forward<Operation>(operation),
behaviorChecks
);
}
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, operation, behaviorChecks);
template<class Operation>
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(const boost::filesystem::path &path, Operation&& operation, std::initializer_list<TimestampUpdateExpectation> behaviorChecks) {
EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(path, path, std::forward<Operation>(operation), behaviorChecks);
}
void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
@ -90,6 +98,55 @@ public:
EXPECT_LT(nodeStat.ctime, cpputils::time::now());
}
class TestBuilder final {
public:
explicit TestBuilder(TimestampTestUtils* fixture): _fixture(fixture) {}
const TestBuilder& withNoatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::noatime()});
expectations();
return *this;
}
const TestBuilder& withStrictatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::strictatime()});
expectations();
return *this;
}
const TestBuilder& withRelatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::relatime()});
expectations();
return *this;
}
const TestBuilder& withNodiratimeRelatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_relatime()});
expectations();
return *this;
}
const TestBuilder& withNodiratimeStrictatime(std::function<void()> expectations) const {
_fixture->resetFilesystem(fspp::Context {fspp::nodiratime_strictatime()});
expectations();
return *this;
}
const TestBuilder& withAnyAtimeConfig(std::function<void()> expectations) const {
return withNoatime(expectations)
.withStrictatime(expectations)
.withRelatime(expectations)
.withNodiratimeRelatime(expectations)
.withNodiratimeStrictatime(expectations);
}
private:
TimestampTestUtils* _fixture;
};
TestBuilder testBuilder() {
return TestBuilder(this);
}
private:
void waitUntilClockProgresses() {
@ -101,7 +158,7 @@ private:
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
UNUSED(timeBeforeOperation);
@ -111,7 +168,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -119,7 +176,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.mtime);
@ -127,7 +184,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -135,7 +192,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.ctime);
@ -143,7 +200,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation);
@ -151,7 +208,7 @@ typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehav
};
template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateExpectation TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
[] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
ExpectDoesntUpdateAccessTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
ExpectDoesntUpdateModificationTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);

View File

@ -6,6 +6,7 @@
#include <cpp-utils/pointer/unique_ref.h>
#include <sys/stat.h>
#include "../fs_interface/Dir.h"
#include "../fs_interface/Context.h"
#if defined(_MSC_VER)
#include <fuse/fuse.h>
#else
@ -19,6 +20,8 @@ class Filesystem {
public:
virtual ~Filesystem() {}
virtual void setContext(Context&& context) = 0;
//TODO Test uid/gid parameters of createAndOpenFile
virtual int createAndOpenFile(const boost::filesystem::path &path, ::mode_t mode, ::uid_t uid, ::gid_t gid) = 0;
virtual int openFile(const boost::filesystem::path &path, int flags) = 0;

View File

@ -13,6 +13,10 @@
#include "InvalidFilesystem.h"
#include <codecvt>
#include <range/v3/view/split.hpp>
#include <range/v3/view/join.hpp>
#include <range/v3/view/filter.hpp>
#if defined(_MSC_VER)
#include <codecvt>
#include <dokan/dokan.h>
@ -265,19 +269,19 @@ void Fuse::_logUnknownException() {
LOG(ERR, "Unknown exception thrown");
}
void Fuse::runInForeground(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<string> realFuseOptions = fuseOptions;
void Fuse::runInForeground(const bf::path &mountdir, vector<string> fuseOptions) {
vector<string> realFuseOptions = std::move(fuseOptions);
if (std::find(realFuseOptions.begin(), realFuseOptions.end(), "-f") == realFuseOptions.end()) {
realFuseOptions.push_back("-f");
}
_run(mountdir, realFuseOptions);
_run(mountdir, std::move(realFuseOptions));
}
void Fuse::runInBackground(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<string> realFuseOptions = fuseOptions;
void Fuse::runInBackground(const bf::path &mountdir, vector<string> fuseOptions) {
vector<string> realFuseOptions = std::move(fuseOptions);
_removeAndWarnIfExists(&realFuseOptions, "-f");
_removeAndWarnIfExists(&realFuseOptions, "-d");
_run(mountdir, realFuseOptions);
_run(mountdir, std::move(realFuseOptions));
}
void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string &option) {
@ -291,7 +295,52 @@ void Fuse::_removeAndWarnIfExists(vector<string> *fuseOptions, const std::string
}
}
void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
namespace {
void extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(string* csv_options, vector<string>* result) {
const auto is_fuse_supported_atime_flag = [] (const std::string& flag) {
constexpr std::array<const char*, 2> flags = {"noatime", "atime"};
return flags.end() != std::find(flags.begin(), flags.end(), flag);
};
const auto is_fuse_unsupported_atime_flag = [] (const std::string& flag) {
constexpr std::array<const char*, 3> flags = {"strictatime", "relatime", "nodiratime"};
return flags.end() != std::find(flags.begin(), flags.end(), flag);
};
*csv_options = *csv_options
| ranges::view::split(',')
| ranges::view::filter(
[&] (const std::string& elem) {
if (is_fuse_unsupported_atime_flag(elem)) {
result->push_back(elem);
return false;
}
if (is_fuse_supported_atime_flag(elem)) {
result->push_back(elem);
}
return true;
})
| ranges::view::join(',')
| ranges::to<string>();
}
// Return a list of all atime options (e.g. atime, noatime, relatime, strictatime, nodiratime) that occur in the
// fuseOptions input. They must be preceded by a '-o', i.e. {..., '-o', 'noatime', ...} and multiple ones can be
// csv-concatenated, i.e. {..., '-o', 'atime,nodiratime', ...}.
// Also, this function removes all of these atime options that are unknown to libfuse (i.e. all except atime and noatime)
// from the input fuseOptions so we can pass it on to libfuse without crashing.
vector<string> extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(vector<string>* fuseOptions) {
vector<string> result;
bool lastOptionWasDashO = false;
for (string& option : *fuseOptions) {
if (lastOptionWasDashO) {
extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&option, &result);
}
lastOptionWasDashO = (option == "-o");
}
return result;
}
}
void Fuse::_run(const bf::path &mountdir, vector<string> fuseOptions) {
#if defined(__GLIBC__)|| defined(__APPLE__) || defined(_MSC_VER)
// Avoid encoding errors for non-utf8 characters, see https://github.com/cryfs/cryfs/issues/247
// this is ifdef'd out for non-glibc linux, because musl doesn't handle this correctly.
@ -302,11 +351,66 @@ void Fuse::_run(const bf::path &mountdir, const vector<string> &fuseOptions) {
ASSERT(_argv.size() == 0, "Filesystem already started");
vector<string> atimeOptions = extractAllAtimeOptionsAndRemoveOnesUnknownToLibfuse_(&fuseOptions);
_createContext(atimeOptions);
_argv = _build_argv(mountdir, fuseOptions);
fuse_main(_argv.size(), _argv.data(), operations(), this);
}
void Fuse::_createContext(const vector<string> &fuseOptions) {
const bool has_atime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "atime");
const bool has_noatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "noatime");
const bool has_relatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "relatime");
const bool has_strictatime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "strictatime");
const bool has_nodiratime_flag = fuseOptions.end() != std::find(fuseOptions.begin(), fuseOptions.end(), "nodiratime");
// Default is RELATIME
_context = Context(relatime());
if (has_noatime_flag) {
ASSERT(!has_atime_flag, "Cannot have both, noatime and atime flags set.");
ASSERT(!has_relatime_flag, "Cannot have both, noatime and relatime flags set.");
ASSERT(!has_strictatime_flag, "Cannot have both, noatime and strictatime flags set.");
// note: can have nodiratime flag set but that is ignored because it is already included in the noatime policy.
_context->setTimestampUpdateBehavior(noatime());
} else if (has_relatime_flag) {
// note: can have atime and relatime both set, they're identical
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above.");
ASSERT(!has_strictatime_flag, "Cannot have both, relatime and strictatime flags set.");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_relatime());
} else {
_context->setTimestampUpdateBehavior(relatime());
}
} else if (has_atime_flag) {
// note: can have atime and relatime both set, they're identical
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_strictatime_flag, "Cannot have both, atime and strictatime flags set.");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_relatime());
} else {
_context->setTimestampUpdateBehavior(relatime());
}
} else if (has_strictatime_flag) {
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
if (has_nodiratime_flag) {
_context->setTimestampUpdateBehavior(nodiratime_strictatime());
} else {
_context->setTimestampUpdateBehavior(strictatime());
}
} else if (has_nodiratime_flag) {
ASSERT(!has_noatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_atime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_relatime_flag, "This shouldn't happen, or we would have hit a case above");
ASSERT(!has_strictatime_flag, "This shouldn't happen, or we would have hit a case above");
_context->setTimestampUpdateBehavior(nodiratime_relatime()); // use relatime by default
}
}
vector<char *> Fuse::_build_argv(const bf::path &mountdir, const vector<string> &fuseOptions) {
vector<char *> argv;
argv.reserve(6 + fuseOptions.size()); // fuseOptions + executable name + mountdir + 2x fuse options (subtype, fsname), each taking 2 entries ("-o", "key=value").
@ -1100,6 +1204,9 @@ void Fuse::init(fuse_conn_info *conn) {
ThreadNameForDebugging _threadName("init");
_fs = _init(this);
ASSERT(_context != boost::none, "Context should have been initialized in Fuse::run() but somehow didn't");
_fs->setContext(fspp::Context { *_context });
LOG(INFO, "Filesystem started.");
_running = true;

View File

@ -12,6 +12,7 @@
#include <cpp-utils/macros.h>
#include <atomic>
#include "stat_compatibility.h"
#include <fspp/fs_interface/Context.h>
namespace fspp {
class Device;
@ -24,8 +25,8 @@ public:
explicit Fuse(std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> init, std::function<void()> onMounted, std::string fstype, boost::optional<std::string> fsname);
~Fuse();
void runInBackground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void runInForeground(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void runInBackground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
void runInForeground(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
bool running() const;
void stop();
@ -67,11 +68,12 @@ private:
static void _logUnknownException();
static char *_create_c_string(const std::string &str);
static void _removeAndWarnIfExists(std::vector<std::string> *fuseOptions, const std::string &option);
void _run(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void _run(const boost::filesystem::path &mountdir, std::vector<std::string> fuseOptions);
static bool _has_option(const std::vector<char *> &vec, const std::string &key);
static bool _has_entry_with_prefix(const std::string &prefix, const std::vector<char *> &vec);
std::vector<char *> _build_argv(const boost::filesystem::path &mountdir, const std::vector<std::string> &fuseOptions);
void _add_fuse_option_if_not_exists(std::vector<char *> *argv, const std::string &key, const std::string &value);
void _createContext(const std::vector<std::string> &fuseOptions);
std::function<std::shared_ptr<Filesystem> (Fuse *fuse)> _init;
std::function<void()> _onMounted;
@ -81,6 +83,7 @@ private:
std::atomic<bool> _running;
std::string _fstype;
boost::optional<std::string> _fsname;
boost::optional<Context> _context;
DISALLOW_COPY_AND_ASSIGN(Fuse);
};

View File

@ -7,6 +7,10 @@
namespace fspp {
namespace fuse {
class InvalidFilesystem final : public Filesystem {
void setContext(Context&&) override {
throw std::logic_error("Filesystem not initialized yet");
}
int createAndOpenFile(const boost::filesystem::path &, ::mode_t , ::uid_t , ::gid_t ) override {
throw std::logic_error("Filesystem not initialized yet");
}

View File

@ -91,6 +91,10 @@ FilesystemImpl::~FilesystemImpl() {
#endif
}
void FilesystemImpl::setContext(Context&& context) {
_device->setContext(std::move(context));
}
unique_ref<File> FilesystemImpl::LoadFile(const bf::path &path) {
PROFILE(_loadFileNanosec);
auto file = _device->LoadFile(path);

View File

@ -24,6 +24,8 @@ public:
explicit FilesystemImpl(cpputils::unique_ref<Device> device);
virtual ~FilesystemImpl();
void setContext(Context&& context) override;
int openFile(const boost::filesystem::path &path, int flags) override;
void flush(int descriptor) override;
void closeFile(int descriptor) override;

View File

@ -91,7 +91,7 @@ TEST(BacktraceTest, ContainsBacktrace) {
}
TEST(BacktraceTest, ShowBacktraceOnNullptrAccess) {
auto output = call_process_exiting_with_nullptr_violation();
auto output = call_process_exiting_with_nullptr_violation();
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
@ -120,7 +120,7 @@ TEST(BacktraceTest, ShowBacktraceOnUnhandledException) {
TEST(BacktraceTest, ShowBacktraceOnSigIll) {
auto output = call_process_exiting_with_sigill();
#if defined(_MSC_VER)
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
EXPECT_THAT(output, HasSubstr("handle_exit_signal"));
#else
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
#endif
@ -153,8 +153,8 @@ TEST(BacktraceTest, ShowBacktraceOnSigIll) {
#if !defined(_MSC_VER)
TEST(BacktraceTest, ShowBacktraceOnSigAbrt) {
auto output = call_process_exiting_with_sigabrt();
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
auto output = call_process_exiting_with_sigabrt();
EXPECT_THAT(output, HasSubstr("cpputils::backtrace"));
}
TEST(BacktraceTest, ShowBacktraceOnSigAbrt_ShowsCorrectSignalName) {

View File

@ -137,4 +137,3 @@ TEST_F(ProgramOptionsTest, SomeFuseOptions) {
//Fuse should have the mount dir as first parameter
EXPECT_VECTOR_EQ({"-f", "--longoption"}, testobj.fuseOptions());
}

View File

@ -65,8 +65,10 @@ auto failOnIntegrityViolation() {
TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
auto rootDir = dev.LoadDir(bf::path("/"));
rootDir.value()->children();
}
@ -74,10 +76,12 @@ TEST_F(CryFsTest, CreatedRootdirIsLoadableAfterClosing) {
TEST_F(CryFsTest, LoadingFilesystemDoesntModifyConfigFile) {
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
Data configAfterCreating = Data::LoadFromFile(config.path()).value();
{
CryDevice dev(loadOrCreateConfig(), blockStore(), localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
dev.setContext(fspp::Context {fspp::relatime()});
}
Data configAfterLoading = Data::LoadFromFile(config.path()).value();
EXPECT_EQ(configAfterCreating, configAfterLoading);

View File

@ -20,6 +20,7 @@ public:
CryTestBase(): _tempLocalStateDir(), _localStateDir(_tempLocalStateDir.path()), _configFile(false), _device(nullptr) {
auto fakeBlockStore = cpputils::make_unique_ref<blockstore::inmemory::InMemoryBlockStore2>();
_device = std::make_unique<cryfs::CryDevice>(configFile(), std::move(fakeBlockStore), _localStateDir, 0x12345678, false, false, failOnIntegrityViolation());
_device->setContext(fspp::Context { fspp::relatime() });
}
std::shared_ptr<cryfs::CryConfigFile> configFile() {

View File

@ -69,6 +69,7 @@ set(SOURCES
fuse/access/FuseAccessModeTest.cpp
fuse/access/FuseAccessErrorTest.cpp
fuse/BasicFuseTest.cpp
fuse/TimestampTest.cpp
fuse/rmdir/testutils/FuseRmdirTest.cpp
fuse/rmdir/FuseRmdirErrorTest.cpp
fuse/rmdir/FuseRmdirDirnameTest.cpp

View File

@ -0,0 +1,313 @@
#include "../testutils/FuseTest.h"
#include <gmock/gmock.h>
using namespace fspp::fuse;
typedef FuseTest FuseTimestampTest;
// Single flag
TEST_F(FuseTimestampTest, whenCalledWithoutAnyAtimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeFlag_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeFlag_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeFlag_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeFlag_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
// Flag combinations
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime,noatime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNoatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime", "-o", "noatime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime,strictatime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeStrictatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "atime", "-o", "strictatime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "atime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithAtimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "atime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,atime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeAtime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "atime"}),
"Cannot have both, noatime and atime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime,noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime", "-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,relatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeRelatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "relatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime,strictatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeStrictatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "noatime", "-o", "strictatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime,nodiratime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNoatimeNodiratimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "noatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeAtimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "atime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime,noatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNoatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime", "-o", "noatime"}),
"Cannot have both, noatime and relatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withCsv_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeRelatimeFlag_withSeparateFlags_thenHasRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "relatime"});
EXPECT_EQ(fspp::relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime,strictatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeStrictatime_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "relatime", "-o", "strictatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithRelatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "relatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,atime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeAtimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "atime"}),
"Cannot have both, atime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,noatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNoatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "noatime"}),
"Cannot have both, noatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withCsv_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime,relatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeRelatimeFlag_withSeparateFlags_thenFails) {
EXPECT_DEATH(
TestFS({"-o", "strictatime", "-o", "relatime"}),
"Cannot have both, relatime and strictatime flags set.");
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withCsv_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime,strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeStrictatimeFlag_withSeparateFlags_thenHasStrictatimeBehavior) {
auto fs = TestFS({"-o", "strictatime", "-o", "strictatime"});
EXPECT_EQ(fspp::strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "strictatime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithStrictatimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "strictatime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,atime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeAtimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "atime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withCsv_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNoatimeFlag_withSeparateFlags_thenHasNoatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "noatime"});
EXPECT_EQ(fspp::noatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,relatime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeRelatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "relatime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,strictatime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeStrictatimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "strictatime"});
EXPECT_EQ(fspp::nodiratime_strictatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withCsv_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime,nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}
TEST_F(FuseTimestampTest, whenCalledWithNodiratimeNodiratimeFlag_withSeparateFlags_thenHasNodiratimeRelatimeBehavior) {
auto fs = TestFS({"-o", "nodiratime", "-o", "nodiratime"});
EXPECT_EQ(fspp::nodiratime_relatime().get(), context().timestampUpdateBehavior().get());
}

View File

@ -19,7 +19,7 @@ using namespace fspp::fuse;
MockFilesystem::MockFilesystem() {}
MockFilesystem::~MockFilesystem() {}
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()), _context(boost::none) {
auto defaultAction = Throw(FuseErrnoException(EIO));
ON_CALL(*fsimpl, openFile(_,_)).WillByDefault(defaultAction);
ON_CALL(*fsimpl, closeFile(_)).WillByDefault(defaultAction);
@ -48,18 +48,23 @@ FuseTest::FuseTest(): fsimpl(make_shared<MockFilesystem>()) {
ON_CALL(*fsimpl, readSymlink(_,_,_)).WillByDefault(defaultAction);
EXPECT_CALL(*fsimpl, access(_,_)).WillRepeatedly(Return());
ON_CALL(*fsimpl, setContext(_)).WillByDefault(Invoke([this] (fspp::Context context) {
_context = std::move(context);
}));
ReturnIsDirOnLstat("/");
}
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS() {
return make_unique_ref<TempTestFS>(fsimpl);
unique_ref<FuseTest::TempTestFS> FuseTest::TestFS(const std::vector<std::string>& fuseOptions) {
return make_unique_ref<TempTestFS>(fsimpl, fuseOptions);
}
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl)
FuseTest::TempTestFS::TempTestFS(shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions)
:_mountDir(),
_fuse([fsimpl] (Fuse*) {return fsimpl;}, []{}, "fusetest", boost::none), _fuse_thread(&_fuse) {
_fuse_thread.start(_mountDir.path(), {});
_fuse_thread.start(_mountDir.path(), fuseOptions);
}
FuseTest::TempTestFS::~TempTestFS() {

View File

@ -20,6 +20,7 @@ public:
MockFilesystem();
virtual ~MockFilesystem();
MOCK_METHOD(void, setContext, (fspp::Context&&), (override));
MOCK_METHOD(int, openFile, (const boost::filesystem::path&, int), (override));
MOCK_METHOD(void, closeFile, (int), (override));
MOCK_METHOD(void, lstat, (const boost::filesystem::path&, fspp::fuse::STAT*), (override));
@ -54,7 +55,7 @@ public:
class TempTestFS {
public:
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl);
TempTestFS(std::shared_ptr<MockFilesystem> fsimpl, const std::vector<std::string>& fuseOptions = {});
virtual ~TempTestFS();
public:
const boost::filesystem::path &mountDir() const;
@ -64,10 +65,18 @@ public:
FuseThread _fuse_thread;
};
cpputils::unique_ref<TempTestFS> TestFS();
cpputils::unique_ref<TempTestFS> TestFS(const std::vector<std::string>& fuseOptions = {});
std::shared_ptr<MockFilesystem> fsimpl;
const fspp::Context& context() const {
ASSERT(_context != boost::none, "Context wasn't correctly initialized");
return *_context;
}
private:
boost::optional<fspp::Context> _context;
public:
//TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros
static ::testing::Action<void(const boost::filesystem::path&, fspp::fuse::STAT*)> ReturnIsFile;