Decouple stat. Dokan in Windows and Fuse in Linux use different structs for that.

This commit is contained in:
Sebastian Messmer 2018-09-13 20:45:31 -07:00
parent d6b1a6e25d
commit 01dece6cd0
22 changed files with 169 additions and 145 deletions

View File

@ -157,23 +157,25 @@ const blockstore::BlockId &CryNode::blockId() const {
return _blockId; return _blockId;
} }
void CryNode::stat(struct ::stat *result) const { CryNode::stat_info CryNode::stat() const {
device()->callFsActionCallbacks(); device()->callFsActionCallbacks();
if(_parent == none) { if(_parent == none) {
stat_info result;
//We are the root directory. //We are the root directory.
//TODO What should we do? //TODO What should we do?
result->st_uid = getuid(); result.uid = getuid();
result->st_gid = getgid(); result.gid = getgid();
result->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR; result.mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR;
result->st_size = fsblobstore::DirBlob::DIR_LSTAT_SIZE; result.size = fsblobstore::DirBlob::DIR_LSTAT_SIZE;
//TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..") //TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..")
result->st_nlink = 1; result.nlink = 1;
struct timespec now = cpputils::time::now(); struct timespec now = cpputils::time::now();
result->st_atim = now; result.atime = now;
result->st_mtim = now; result.mtime = now;
result->st_ctim = now; result.ctime = now;
return result;
} else { } else {
(*_parent)->statChild(_blockId, result); return (*_parent)->statChild(_blockId);
} }
} }

View File

@ -17,7 +17,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. // 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); 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; void access(int mask) const override;
void stat(struct ::stat *result) const override; stat_info stat() const override;
void chmod(mode_t mode) override; void chmod(mode_t mode) override;
void chown(uid_t uid, gid_t gid) override; void chown(uid_t uid, gid_t gid) override;
void rename(const boost::filesystem::path &to) override; void rename(const boost::filesystem::path &to) override;

View File

@ -30,10 +30,9 @@ void CryOpenFile::flush() {
_parent->flush(); _parent->flush();
} }
void CryOpenFile::stat(struct ::stat *result) const { fspp::Node::stat_info CryOpenFile::stat() const {
_device->callFsActionCallbacks(); _device->callFsActionCallbacks();
result->st_size = _fileBlob->size(); return _parent->statChildWithKnownSize(_fileBlob->blockId(), _fileBlob->size());
_parent->statChildWithSizeAlreadySet(_fileBlob->blockId(), result);
} }
void CryOpenFile::truncate(off_t size) const { void CryOpenFile::truncate(off_t size) const {

View File

@ -14,7 +14,7 @@ public:
explicit CryOpenFile(const CryDevice *device, std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent, cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob); explicit CryOpenFile(const CryDevice *device, std::shared_ptr<parallelaccessfsblobstore::DirBlobRef> parent, cpputils::unique_ref<parallelaccessfsblobstore::FileBlobRef> fileBlob);
~CryOpenFile(); ~CryOpenFile();
void stat(struct ::stat *result) const override; stat_info stat() const override;
void truncate(off_t size) const override; void truncate(off_t size) const override;
size_t read(void *buf, size_t count, off_t offset) const override; size_t read(void *buf, size_t count, off_t offset) const override;
void write(const void *buf, size_t count, off_t offset) override; void write(const void *buf, size_t count, off_t offset) override;

View File

@ -5,6 +5,7 @@
#include "FsBlobRef.h" #include "FsBlobRef.h"
#include "../fsblobstore/DirBlob.h" #include "../fsblobstore/DirBlob.h"
#include "../fsblobstore/utils/TimestampUpdateBehavior.h" #include "../fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
namespace cryfs { namespace cryfs {
namespace cachingfsblobstore { namespace cachingfsblobstore {
@ -53,12 +54,12 @@ public:
return _base->RenameChild(blockId, newName, onOverwritten); return _base->RenameChild(blockId, newName, onOverwritten);
} }
void statChild(const blockstore::BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info statChild(const blockstore::BlockId &blockId) const {
return _base->statChild(blockId, result); return _base->statChild(blockId);
} }
void statChildWithSizeAlreadySet(const blockstore::BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, uint64_t size) const {
return _base->statChildWithSizeAlreadySet(blockId, result); return _base->statChildWithKnownSize(blockId, size);
} }
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) { void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {

View File

@ -132,28 +132,30 @@ off_t DirBlob::lstat_size() const {
return DIR_LSTAT_SIZE; return DIR_LSTAT_SIZE;
} }
void DirBlob::statChild(const BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info DirBlob::statChild(const BlockId &blockId) const {
result->st_size = _getLstatSize(blockId); return statChildWithKnownSize(blockId, _getLstatSize(blockId));
statChildWithSizeAlreadySet(blockId, result);
} }
void DirBlob::statChildWithSizeAlreadySet(const BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info DirBlob::statChildWithKnownSize(const BlockId &blockId, uint64_t size) const {
fspp::Node::stat_info result;
auto childOpt = GetChild(blockId); auto childOpt = GetChild(blockId);
if (childOpt == boost::none) { if (childOpt == boost::none) {
throw fspp::fuse::FuseErrnoException(ENOENT); throw fspp::fuse::FuseErrnoException(ENOENT);
} }
const auto &child = *childOpt; const auto &child = *childOpt;
result->st_mode = child.mode(); result.mode = child.mode();
result->st_uid = child.uid(); result.uid = child.uid();
result->st_gid = child.gid(); result.gid = child.gid();
//TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..") //TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..")
result->st_nlink = 1; result.nlink = 1;
result->st_atim = child.lastAccessTime(); result.size = size;
result->st_mtim = child.lastModificationTime(); result.atime = child.lastAccessTime();
result->st_ctim = child.lastMetadataChangeTime(); result.mtime = child.lastModificationTime();
result.ctime = child.lastMetadataChangeTime();
//TODO Move ceilDivision to general utils which can be used by cryfs as well //TODO Move ceilDivision to general utils which can be used by cryfs as well
result->st_blocks = blobstore::onblocks::utils::ceilDivision(result->st_size, static_cast<off_t>(512)); result.blocks = blobstore::onblocks::utils::ceilDivision(size, static_cast<uint64_t>(512));
result->st_blksize = _fsBlobStore->virtualBlocksizeBytes(); return result;
} }
void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) { void DirBlob::updateAccessTimestampForChild(const BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior) {

View File

@ -5,6 +5,7 @@
#include <blockstore/utils/BlockId.h> #include <blockstore/utils/BlockId.h>
#include <cpp-utils/macros.h> #include <cpp-utils/macros.h>
#include <fspp/fs_interface/Dir.h> #include <fspp/fs_interface/Dir.h>
#include <fspp/fs_interface/Node.h>
#include "FsBlob.h" #include "FsBlob.h"
#include "utils/DirEntryList.h" #include "utils/DirEntryList.h"
#include <mutex> #include <mutex>
@ -56,9 +57,9 @@ namespace cryfs {
void flush(); void flush();
void statChild(const blockstore::BlockId &blockId, struct ::stat *result) const; fspp::Node::stat_info statChild(const blockstore::BlockId &blockId) const;
void statChildWithSizeAlreadySet(const blockstore::BlockId &blockId, struct ::stat *result) const; fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, uint64_t size) const;
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior); void updateAccessTimestampForChild(const blockstore::BlockId &blockId, TimestampUpdateBehavior timestampUpdateBehavior);

View File

@ -5,6 +5,7 @@
#include "FsBlobRef.h" #include "FsBlobRef.h"
#include "../cachingfsblobstore/DirBlobRef.h" #include "../cachingfsblobstore/DirBlobRef.h"
#include "../fsblobstore/utils/TimestampUpdateBehavior.h" #include "../fsblobstore/utils/TimestampUpdateBehavior.h"
#include <fspp/fs_interface/Node.h>
namespace cryfs { namespace cryfs {
namespace parallelaccessfsblobstore { namespace parallelaccessfsblobstore {
@ -49,12 +50,12 @@ public:
return _base->RenameChild(blockId, newName, onOverwritten); return _base->RenameChild(blockId, newName, onOverwritten);
} }
void statChild(const blockstore::BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info statChild(const blockstore::BlockId &blockId) const {
return _base->statChild(blockId, result); return _base->statChild(blockId);
} }
void statChildWithSizeAlreadySet(const blockstore::BlockId &blockId, struct ::stat *result) const { fspp::Node::stat_info statChildWithKnownSize(const blockstore::BlockId &blockId, uint64_t size) const {
return _base->statChildWithSizeAlreadySet(blockId, result); return _base->statChildWithKnownSize(blockId, size);
} }
void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) { void updateAccessTimestampForChild(const blockstore::BlockId &blockId, fsblobstore::TimestampUpdateBehavior timestampUpdateBehavior) {

View File

@ -4,15 +4,25 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <sys/stat.h>
namespace fspp { namespace fspp {
class Node { class Node {
public: public:
virtual ~Node() {} virtual ~Node() {}
virtual void stat(struct ::stat *result) const = 0; struct stat_info final {
uint32_t nlink;
uint32_t mode;
uint32_t uid;
uint32_t gid;
uint64_t size;
uint64_t blocks;
struct timespec atime;
struct timespec mtime;
struct timespec ctime;
};
virtual stat_info stat() const = 0;
virtual void chmod(mode_t mode) = 0; virtual void chmod(mode_t mode) = 0;
virtual void chown(uid_t uid, gid_t gid) = 0; virtual void chown(uid_t uid, gid_t gid) = 0;
virtual void access(int mask) const = 0; virtual void access(int mask) const = 0;

View File

@ -3,7 +3,7 @@
#define MESSMER_FSPP_FSINTERFACE_OPENFILE_H_ #define MESSMER_FSPP_FSINTERFACE_OPENFILE_H_
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <sys/stat.h> #include "Node.h"
namespace fspp { namespace fspp {
class Device; class Device;
@ -12,7 +12,9 @@ class OpenFile {
public: public:
virtual ~OpenFile() {} virtual ~OpenFile() {}
virtual void stat(struct ::stat *result) const = 0; using stat_info = typename Node::stat_info;
virtual stat_info stat() const = 0;
virtual void truncate(off_t size) const = 0; virtual void truncate(off_t size) const = 0;
virtual size_t read(void *buf, size_t count, off_t offset) const = 0; virtual size_t read(void *buf, size_t count, off_t offset) const = 0;
virtual void write(const void *buf, size_t count, off_t offset) = 0; virtual void write(const void *buf, size_t count, off_t offset) = 0;

View File

@ -16,7 +16,7 @@ public:
} }
void Test_Load_While_Not_Loaded() { void Test_Load_While_Not_Loaded() {
struct stat oldStat{}; fspp::Node::stat_info oldStat{};
{ {
auto node = this->CreateNode("/mynode"); auto node = this->CreateNode("/mynode");
oldStat = this->stat(*node); oldStat = this->stat(*node);
@ -28,10 +28,10 @@ public:
auto node = this->device->Load("/mynode"); auto node = this->device->Load("/mynode");
//Test that timestamps didn't change //Test that timestamps didn't change
struct stat newStat = this->stat(*node.value()); fspp::Node::stat_info newStat = this->stat(*node.value());
EXPECT_EQ(oldStat.st_atim, newStat.st_atim); EXPECT_EQ(oldStat.atime, newStat.atime);
EXPECT_EQ(oldStat.st_mtim, newStat.st_mtim); EXPECT_EQ(oldStat.mtime, newStat.mtime);
EXPECT_EQ(oldStat.st_ctim, newStat.st_ctim); EXPECT_EQ(oldStat.ctime, newStat.ctime);
} }
}; };

View File

@ -58,22 +58,22 @@ public:
void Test_Chown_Uid(fspp::File *file, fspp::Node *node) { void Test_Chown_Uid(fspp::File *file, fspp::Node *node) {
node->chown(100, 200); node->chown(100, 200);
this->IN_STAT(file, node, [] (struct stat st){ this->IN_STAT(file, node, [] (const fspp::Node::stat_info& st){
EXPECT_EQ(100u, st.st_uid); EXPECT_EQ(100u, st.uid);
}); });
} }
void Test_Chown_Gid(fspp::File *file, fspp::Node *node) { void Test_Chown_Gid(fspp::File *file, fspp::Node *node) {
node->chown(100, 200); node->chown(100, 200);
this->IN_STAT(file, node, [] (struct stat st){ this->IN_STAT(file, node, [] (const fspp::Node::stat_info& st){
EXPECT_EQ(200u, st.st_gid); EXPECT_EQ(200u, st.gid);
}); });
} }
void Test_Chmod(fspp::File *file, fspp::Node *node) { void Test_Chmod(fspp::File *file, fspp::Node *node) {
node->chmod(S_IFREG | S_IRUSR | S_IWOTH); node->chmod(S_IFREG | S_IRUSR | S_IWOTH);
this->IN_STAT(file, node, [] (struct stat st){ this->IN_STAT(file, node, [] (const fspp::Node::stat_info& st){
EXPECT_EQ(static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWOTH), st.st_mode); EXPECT_EQ(static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWOTH), st.mode);
}); });
} }
@ -81,7 +81,7 @@ public:
struct timespec ATIME{}; ATIME.tv_sec = 1458086400; ATIME.tv_nsec = 34525; struct timespec ATIME{}; ATIME.tv_sec = 1458086400; ATIME.tv_nsec = 34525;
struct timespec MTIME{}; MTIME.tv_sec = 1458086300; MTIME.tv_nsec = 48293; struct timespec MTIME{}; MTIME.tv_sec = 1458086300; MTIME.tv_nsec = 48293;
node->utimens(ATIME, MTIME); node->utimens(ATIME, MTIME);
this->IN_STAT(file, node, [this, ATIME, MTIME] (struct stat st) { this->IN_STAT(file, node, [this, ATIME, MTIME] (const fspp::Node::stat_info& st) {
this->EXPECT_ATIME_EQ(ATIME, st); this->EXPECT_ATIME_EQ(ATIME, st);
this->EXPECT_MTIME_EQ(MTIME, st); this->EXPECT_MTIME_EQ(MTIME, st);
}); });

View File

@ -11,8 +11,8 @@ public:
void Test_Nlink() { void Test_Nlink() {
this->CreateNode("/mynode"); this->CreateNode("/mynode");
auto node = this->Load("/mynode"); auto node = this->Load("/mynode");
this->IN_STAT(node.get(), [] (struct stat st) { this->IN_STAT(node.get(), [] (const fspp::Node::stat_info& st) {
EXPECT_EQ(1u, st.st_nlink); EXPECT_EQ(1u, st.nlink);
}); });
} }
}; };
@ -32,8 +32,8 @@ TYPED_TEST_P(FsppNodeTest_Stat_FileOnly, CreatedFileIsEmpty) {
TYPED_TEST_P(FsppNodeTest_Stat_FileOnly, FileIsFile) { TYPED_TEST_P(FsppNodeTest_Stat_FileOnly, FileIsFile) {
this->CreateFile("/myfile"); this->CreateFile("/myfile");
auto node = this->Load("/myfile"); auto node = this->Load("/myfile");
this->IN_STAT(node.get(), [] (struct stat st) { this->IN_STAT(node.get(), [] (const fspp::Node::stat_info& st) {
EXPECT_TRUE(S_ISREG(st.st_mode)); EXPECT_TRUE(S_ISREG(st.mode));
}); });
} }
@ -46,8 +46,8 @@ TYPED_TEST_CASE_P(FsppNodeTest_Stat_DirOnly);
TYPED_TEST_P(FsppNodeTest_Stat_DirOnly, DirIsDir) { TYPED_TEST_P(FsppNodeTest_Stat_DirOnly, DirIsDir) {
this->CreateDir("/mydir"); this->CreateDir("/mydir");
auto node = this->Load("/mydir"); auto node = this->Load("/mydir");
this->IN_STAT(node.get(), [] (struct stat st) { this->IN_STAT(node.get(), [] (const fspp::Node::stat_info& st) {
EXPECT_TRUE(S_ISDIR(st.st_mode)); EXPECT_TRUE(S_ISDIR(st.mode));
}); });
} }
@ -60,8 +60,8 @@ TYPED_TEST_CASE_P(FsppNodeTest_Stat_SymlinkOnly);
TYPED_TEST_P(FsppNodeTest_Stat_SymlinkOnly, SymlinkIsSymlink) { TYPED_TEST_P(FsppNodeTest_Stat_SymlinkOnly, SymlinkIsSymlink) {
this->CreateSymlink("/mysymlink"); this->CreateSymlink("/mysymlink");
auto node = this->Load("/mysymlink"); auto node = this->Load("/mysymlink");
this->IN_STAT(node.get(), [] (struct stat st) { this->IN_STAT(node.get(), [] (const fspp::Node::stat_info& st) {
EXPECT_TRUE(S_ISLNK(st.st_mode)); EXPECT_TRUE(S_ISLNK(st.mode));
}); });
} }

View File

@ -26,8 +26,7 @@ public:
void Test_Stat() { void Test_Stat() {
auto node = this->CreateNode("/mynode"); auto node = this->CreateNode("/mynode");
auto operation = [&node] () { auto operation = [&node] () {
struct stat st{}; node->stat();
node->stat(&st);
}; };
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, { this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectDoesntUpdateAnyTimestamps this->ExpectDoesntUpdateAnyTimestamps
@ -36,7 +35,7 @@ public:
void Test_Chmod() { void Test_Chmod() {
auto node = this->CreateNode("/mynode"); auto node = this->CreateNode("/mynode");
mode_t mode = this->stat(*node).st_mode; mode_t mode = this->stat(*node).mode;
auto operation = [&node, mode] () { auto operation = [&node, mode] () {
node->chmod(mode); node->chmod(mode);
}; };
@ -49,8 +48,8 @@ public:
void Test_Chown() { void Test_Chown() {
auto node = this->CreateNode("/mynode"); auto node = this->CreateNode("/mynode");
uid_t uid = this->stat(*node).st_uid; uid_t uid = this->stat(*node).uid;
gid_t gid = this->stat(*node).st_gid; gid_t gid = this->stat(*node).gid;
auto operation = [&node, uid, gid] () { auto operation = [&node, uid, gid] () {
node->chown(uid, gid); node->chown(uid, gid);
}; };
@ -347,8 +346,8 @@ public:
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, { this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS("/mynode", operation, {
this->ExpectUpdatesMetadataTimestamp this->ExpectUpdatesMetadataTimestamp
}); });
EXPECT_EQ(atime, this->stat(*node).st_atim); EXPECT_EQ(atime, this->stat(*node).atime);
EXPECT_EQ(mtime, this->stat(*node).st_mtim); EXPECT_EQ(mtime, this->stat(*node).mtime);
} }
}; };

View File

@ -7,15 +7,14 @@
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
class FsppOpenFileTest: public FileSystemTest<ConcreteFileSystemTestFixture> { class FsppOpenFileTest: public FileSystemTest<ConcreteFileSystemTestFixture> {
public: public:
void IN_STAT(fspp::OpenFile *openFile, std::function<void (struct stat)> callback) { void IN_STAT(fspp::OpenFile *openFile, std::function<void (const fspp::OpenFile::stat_info&)> callback) {
struct stat st{}; auto st = openFile->stat();
openFile->stat(&st);
callback(st); callback(st);
} }
void EXPECT_SIZE(uint64_t expectedSize, fspp::OpenFile *openFile) { void EXPECT_SIZE(uint64_t expectedSize, fspp::OpenFile *openFile) {
IN_STAT(openFile, [expectedSize] (struct stat st) { IN_STAT(openFile, [expectedSize] (const fspp::OpenFile::stat_info& st) {
EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.st_size)); EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.size));
}); });
EXPECT_NUMBYTES_READABLE(expectedSize, openFile); EXPECT_NUMBYTES_READABLE(expectedSize, openFile);
@ -41,8 +40,8 @@ TYPED_TEST_P(FsppOpenFileTest, CreatedFileIsEmpty) {
TYPED_TEST_P(FsppOpenFileTest, FileIsFile) { TYPED_TEST_P(FsppOpenFileTest, FileIsFile) {
auto file = this->CreateFile("/myfile"); auto file = this->CreateFile("/myfile");
auto openFile = this->LoadFile("/myfile")->open(O_RDONLY); auto openFile = this->LoadFile("/myfile")->open(O_RDONLY);
this->IN_STAT(openFile.get(), [] (struct stat st) { this->IN_STAT(openFile.get(), [] (const fspp::OpenFile::stat_info& st) {
EXPECT_TRUE(S_ISREG(st.st_mode)); EXPECT_TRUE(S_ISREG(st.mode));
}); });
} }

View File

@ -14,8 +14,8 @@ public:
auto file = this->CreateFile(path); auto file = this->CreateFile(path);
file->truncate(size); file->truncate(size);
auto openFile = file->open(O_RDWR); auto openFile = file->open(O_RDWR);
assert(this->stat(*openFile).st_size == size); assert(this->stat(*openFile).size == size);
assert(this->stat(*this->Load(path)).st_size == size); assert(this->stat(*this->Load(path)).size == size);
return openFile; return openFile;
} }
}; };
@ -24,8 +24,7 @@ TYPED_TEST_CASE_P(FsppOpenFileTest_Timestamps);
TYPED_TEST_P(FsppOpenFileTest_Timestamps, stat) { TYPED_TEST_P(FsppOpenFileTest_Timestamps, stat) {
auto openFile = this->CreateAndOpenFile("/mynode"); auto openFile = this->CreateAndOpenFile("/mynode");
auto operation = [&openFile] () { auto operation = [&openFile] () {
struct ::stat st{}; openFile->stat();
openFile->stat(&st);
}; };
this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps}); this->EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(*openFile, operation, {this->ExpectDoesntUpdateAnyTimestamps});
} }

View File

@ -90,12 +90,11 @@ public:
void setModificationTimestampLaterThanAccessTimestamp(const boost::filesystem::path& path) { void setModificationTimestampLaterThanAccessTimestamp(const boost::filesystem::path& path) {
auto node = device->Load(path).value(); auto node = device->Load(path).value();
struct stat st{}; auto st = node->stat();
node->stat(&st); st.mtime.tv_nsec = st.mtime.tv_nsec + 1;
st.st_mtim.tv_nsec = st.st_mtim.tv_nsec + 1;
node->utimens( node->utimens(
st.st_atim, st.atime,
st.st_mtim st.mtime
); );
} }
}; };

View File

@ -28,17 +28,16 @@ public:
std::unique_ptr<fspp::Node> file_nested_node; std::unique_ptr<fspp::Node> file_nested_node;
//TODO IN_STAT still needed after moving it to FsppNodeTest? //TODO IN_STAT still needed after moving it to FsppNodeTest?
void IN_STAT(fspp::File *file, fspp::Node *node, std::function<void (struct stat)> callback) { void IN_STAT(fspp::File *file, fspp::Node *node, std::function<void (const fspp::Node::stat_info&)> callback) {
struct stat st1{}, st2{}; auto st1 = node->stat();
node->stat(&st1);
callback(st1); callback(st1);
file->open(O_RDONLY)->stat(&st2); auto st2 = file->open(O_RDONLY)->stat();
callback(st2); callback(st2);
} }
void EXPECT_SIZE(uint64_t expectedSize, fspp::File *file, fspp::Node *node) { void EXPECT_SIZE(uint64_t expectedSize, fspp::File *file, fspp::Node *node) {
IN_STAT(file, node, [expectedSize] (struct stat st) { IN_STAT(file, node, [expectedSize] (const fspp::Node::stat_info& st) {
EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.st_size)); EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.size));
}); });
EXPECT_NUMBYTES_READABLE(expectedSize, file); EXPECT_NUMBYTES_READABLE(expectedSize, file);
@ -53,14 +52,14 @@ public:
EXPECT_EQ(expectedSize, static_cast<uint64_t>(readBytes)); EXPECT_EQ(expectedSize, static_cast<uint64_t>(readBytes));
} }
void EXPECT_ATIME_EQ(struct timespec expected, struct stat st) { void EXPECT_ATIME_EQ(struct timespec expected, const fspp::Node::stat_info& st) {
EXPECT_EQ(expected.tv_sec, st.st_atim.tv_sec); EXPECT_EQ(expected.tv_sec, st.atime.tv_sec);
EXPECT_EQ(expected.tv_nsec, st.st_atim.tv_nsec); EXPECT_EQ(expected.tv_nsec, st.atime.tv_nsec);
} }
void EXPECT_MTIME_EQ(struct timespec expected, struct stat st) { void EXPECT_MTIME_EQ(struct timespec expected, const fspp::Node::stat_info& st) {
EXPECT_EQ(expected.tv_sec, st.st_mtim.tv_sec); EXPECT_EQ(expected.tv_sec, st.mtime.tv_sec);
EXPECT_EQ(expected.tv_nsec, st.st_mtim.tv_nsec); EXPECT_EQ(expected.tv_nsec, st.mtime.tv_nsec);
} }
}; };

View File

@ -9,15 +9,14 @@
class FsppNodeTestHelper { class FsppNodeTestHelper {
public: public:
void IN_STAT(fspp::Node *file, std::function<void (struct stat)> callback) { void IN_STAT(fspp::Node *file, std::function<void (const fspp::Node::stat_info& stat)> callback) {
struct stat st{}; auto st = file->stat();
file->stat(&st);
callback(st); callback(st);
} }
void EXPECT_SIZE(uint64_t expectedSize, fspp::Node *node) { void EXPECT_SIZE(uint64_t expectedSize, fspp::Node *node) {
IN_STAT(node, [expectedSize] (struct stat st) { IN_STAT(node, [expectedSize] (const fspp::Node::stat_info& st) {
EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.st_size)); EXPECT_EQ(expectedSize, static_cast<uint64_t>(st.size));
}); });
} }
}; };

View File

@ -10,7 +10,7 @@
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
class TimestampTestUtils : public virtual FileSystemTest<ConcreteFileSystemTestFixture> { class TimestampTestUtils : public virtual FileSystemTest<ConcreteFileSystemTestFixture> {
public: public:
using TimestampUpdateBehavior = std::function<void (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>; using TimestampUpdateBehavior = std::function<void (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation)>;
static TimestampUpdateBehavior ExpectUpdatesAccessTimestamp; static TimestampUpdateBehavior ExpectUpdatesAccessTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAccessTimestamp; static TimestampUpdateBehavior ExpectDoesntUpdateAccessTimestamp;
@ -20,13 +20,13 @@ public:
static TimestampUpdateBehavior ExpectDoesntUpdateMetadataTimestamp; static TimestampUpdateBehavior ExpectDoesntUpdateMetadataTimestamp;
static TimestampUpdateBehavior ExpectDoesntUpdateAnyTimestamps; static TimestampUpdateBehavior ExpectDoesntUpdateAnyTimestamps;
void EXPECT_OPERATION_UPDATES_TIMESTAMPS_AS(std::function<struct stat()> statOld, std::function<struct stat()> statNew, std::function<void()> operation, std::initializer_list<TimestampUpdateBehavior> behaviorChecks) { 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) {
struct stat oldStat = statOld(); auto oldStat = statOld();
ensureNodeTimestampsAreOld(oldStat); ensureNodeTimestampsAreOld(oldStat);
timespec timeBeforeOperation = cpputils::time::now(); timespec timeBeforeOperation = cpputils::time::now();
operation(); operation();
timespec timeAfterOperation = cpputils::time::now(); timespec timeAfterOperation = cpputils::time::now();
struct stat newStat = statNew(); auto newStat = statNew();
for (auto behaviorCheck : behaviorChecks) { for (auto behaviorCheck : behaviorChecks) {
behaviorCheck(oldStat, newStat, timeBeforeOperation, timeAfterOperation); behaviorCheck(oldStat, newStat, timeBeforeOperation, timeAfterOperation);
} }
@ -55,30 +55,26 @@ public:
} }
void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
EXPECT_LE(lowerBound, stat(node).st_atim); EXPECT_LE(lowerBound, stat(node).atime);
EXPECT_GE(upperBound, stat(node).st_atim); EXPECT_GE(upperBound, stat(node).atime);
} }
void EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { void EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
EXPECT_LE(lowerBound, stat(node).st_mtim); EXPECT_LE(lowerBound, stat(node).mtime);
EXPECT_GE(upperBound, stat(node).st_mtim); EXPECT_GE(upperBound, stat(node).mtime);
} }
void EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { void EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) {
EXPECT_LE(lowerBound, stat(node).st_ctim); EXPECT_LE(lowerBound, stat(node).ctime);
EXPECT_GE(upperBound, stat(node).st_ctim); EXPECT_GE(upperBound, stat(node).ctime);
} }
static struct stat stat(const fspp::Node &node) { static fspp::Node::stat_info stat(const fspp::Node &node) {
struct stat st{}; return node.stat();
node.stat(&st);
return st;
} }
static struct stat stat(const fspp::OpenFile &openFile) { static fspp::Node::stat_info stat(const fspp::OpenFile &openFile) {
struct stat st{}; return openFile.stat();
openFile.stat(&st);
return st;
} }
timespec xSecondsAgo(int sec) { timespec xSecondsAgo(int sec) {
@ -87,11 +83,11 @@ public:
return result; return result;
} }
void ensureNodeTimestampsAreOld(const struct stat &nodeStat) { void ensureNodeTimestampsAreOld(const fspp::Node::stat_info &nodeStat) {
waitUntilClockProgresses(); waitUntilClockProgresses();
EXPECT_LT(nodeStat.st_atim, cpputils::time::now()); EXPECT_LT(nodeStat.atime, cpputils::time::now());
EXPECT_LT(nodeStat.st_mtim, cpputils::time::now()); EXPECT_LT(nodeStat.mtime, cpputils::time::now());
EXPECT_LT(nodeStat.st_ctim, cpputils::time::now()); EXPECT_LT(nodeStat.ctime, cpputils::time::now());
} }
private: private:
@ -106,57 +102,57 @@ private:
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesAccessTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation); UNUSED(statBeforeOperation);
UNUSED(timeBeforeOperation); UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation); UNUSED(timeAfterOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.st_atim); EXPECT_LE(timeBeforeOperation, statAfterOperation.atime);
EXPECT_GE(timeAfterOperation, statAfterOperation.st_atim); EXPECT_GE(timeAfterOperation, statAfterOperation.atime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAccessTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation); UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation); UNUSED(timeAfterOperation);
EXPECT_EQ(statBeforeOperation.st_atim, statAfterOperation.st_atim); EXPECT_EQ(statBeforeOperation.atime, statAfterOperation.atime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesModificationTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation); UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.st_mtim); EXPECT_LE(timeBeforeOperation, statAfterOperation.mtime);
EXPECT_GE(timeAfterOperation, statAfterOperation.st_mtim); EXPECT_GE(timeAfterOperation, statAfterOperation.mtime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateModificationTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation); UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation); UNUSED(timeAfterOperation);
EXPECT_EQ(statBeforeOperation.st_mtim, statAfterOperation.st_mtim); EXPECT_EQ(statBeforeOperation.mtime, statAfterOperation.mtime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectUpdatesMetadataTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(statBeforeOperation); UNUSED(statBeforeOperation);
EXPECT_LE(timeBeforeOperation, statAfterOperation.st_ctim); EXPECT_LE(timeBeforeOperation, statAfterOperation.ctime);
EXPECT_GE(timeAfterOperation, statAfterOperation.st_ctim); EXPECT_GE(timeAfterOperation, statAfterOperation.ctime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateMetadataTimestamp =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
UNUSED(timeBeforeOperation); UNUSED(timeBeforeOperation);
UNUSED(timeAfterOperation); UNUSED(timeAfterOperation);
EXPECT_EQ(statBeforeOperation.st_ctim, statAfterOperation.st_ctim); EXPECT_EQ(statBeforeOperation.ctime, statAfterOperation.ctime);
}; };
template<class ConcreteFileSystemTestFixture> template<class ConcreteFileSystemTestFixture>
typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps = typename TimestampTestUtils<ConcreteFileSystemTestFixture>::TimestampUpdateBehavior TimestampTestUtils<ConcreteFileSystemTestFixture>::ExpectDoesntUpdateAnyTimestamps =
[] (struct stat statBeforeOperation, struct stat statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) { [] (const fspp::Node::stat_info& statBeforeOperation, const fspp::Node::stat_info& statAfterOperation, timespec timeBeforeOperation, timespec timeAfterOperation) {
ExpectDoesntUpdateAccessTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation); ExpectDoesntUpdateAccessTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
ExpectDoesntUpdateModificationTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation); ExpectDoesntUpdateModificationTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);
ExpectDoesntUpdateMetadataTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation); ExpectDoesntUpdateMetadataTimestamp(statBeforeOperation, statAfterOperation, timeBeforeOperation, timeAfterOperation);

View File

@ -137,19 +137,35 @@ void FilesystemImpl::closeFile(int descriptor) {
_open_files.close(descriptor); _open_files.close(descriptor);
} }
namespace {
void convert_stat_info(const fspp::Node::stat_info& input, struct ::stat *output) {
output->st_nlink = input.nlink;
output->st_mode = input.mode;
output->st_uid = input.uid;
output->st_gid = input.gid;
output->st_size = input.size;
output->st_blocks = input.blocks;
output->st_atim = input.atime;
output->st_mtim = input.mtime;
output->st_ctim = input.ctime;
}
}
void FilesystemImpl::lstat(const bf::path &path, struct ::stat *stbuf) { void FilesystemImpl::lstat(const bf::path &path, struct ::stat *stbuf) {
PROFILE(_lstatNanosec); PROFILE(_lstatNanosec);
auto node = _device->Load(path); auto node = _device->Load(path);
if(node == none) { if(node == none) {
throw fuse::FuseErrnoException(ENOENT); throw fuse::FuseErrnoException(ENOENT);
} else { } else {
(*node)->stat(stbuf); auto stat_info = (*node)->stat();
convert_stat_info(stat_info, stbuf);
} }
} }
void FilesystemImpl::fstat(int descriptor, struct ::stat *stbuf) { void FilesystemImpl::fstat(int descriptor, struct ::stat *stbuf) {
PROFILE(_fstatNanosec); PROFILE(_fstatNanosec);
_open_files.get(descriptor)->stat(stbuf); auto stat_info = _open_files.get(descriptor)->stat();
convert_stat_info(stat_info, stbuf);
} }
void FilesystemImpl::chmod(const boost::filesystem::path &path, mode_t mode) { void FilesystemImpl::chmod(const boost::filesystem::path &path, mode_t mode) {

View File

@ -17,7 +17,7 @@ public:
~MockOpenFile() {destructed = true;} ~MockOpenFile() {destructed = true;}
MOCK_CONST_METHOD1(stat, void(struct ::stat*)); MOCK_CONST_METHOD0(stat, OpenFile::stat_info());
MOCK_CONST_METHOD1(truncate, void(off_t)); MOCK_CONST_METHOD1(truncate, void(off_t));
MOCK_CONST_METHOD3(read, size_t(void*, size_t, off_t)); MOCK_CONST_METHOD3(read, size_t(void*, size_t, off_t));
MOCK_METHOD3(write, void(const void*, size_t, off_t)); MOCK_METHOD3(write, void(const void*, size_t, off_t));