Implement storing file access times.
Feature is not enabled yet, because this would break compatiblity with file systems from old CryFS versions (will be enabled in CryFS 0.9.0). However, it already fixes https://github.com/cryfs/cryfs/issues/4 because it doesn't say 'operation not supported' anymore when modifying file times
This commit is contained in:
parent
029fb73218
commit
452519ed0b
@ -64,10 +64,14 @@ void CryNode::rename(const bf::path &to) {
|
|||||||
targetDir->AddChild(to.filename().native(), _key, getType(), mode, uid, gid);
|
targetDir->AddChild(to.filename().native(), _key, getType(), mode, uid, gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryNode::utimens(const timespec times[2]) {
|
void CryNode::utimens(timespec lastAccessTime, timespec lastModificationTime) {
|
||||||
device()->callFsActionCallbacks();
|
device()->callFsActionCallbacks();
|
||||||
//TODO
|
if (_parent == none) {
|
||||||
throw FuseErrnoException(ENOTSUP);
|
//We are the root direcory.
|
||||||
|
//TODO What should we do?
|
||||||
|
throw FuseErrnoException(EIO);
|
||||||
|
}
|
||||||
|
(*_parent)->utimensChild(_key, lastAccessTime, lastModificationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CryNode::remove() {
|
void CryNode::remove() {
|
||||||
@ -100,6 +104,7 @@ void CryNode::stat(struct ::stat *result) const {
|
|||||||
//We are the root directory.
|
//We are the root directory.
|
||||||
//TODO What should we do?
|
//TODO What should we do?
|
||||||
result->st_mode = S_IFDIR;
|
result->st_mode = S_IFDIR;
|
||||||
|
result->st_size = fsblobstore::DirBlob::DIR_LSTAT_SIZE;
|
||||||
} else {
|
} else {
|
||||||
(*_parent)->statChild(_key, result);
|
(*_parent)->statChild(_key, result);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public:
|
|||||||
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;
|
||||||
void utimens(const timespec times[2]) override;
|
void utimens(timespec lastAccessTime, timespec lastModificationTime) override;
|
||||||
void remove() override;
|
void remove() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -51,6 +51,10 @@ public:
|
|||||||
return _base->chownChild(key, uid, gid);
|
return _base->chownChild(key, uid, gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void utimensChild(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime) {
|
||||||
|
return _base->utimensChild(key, lastAccessTime, lastModificationTime);
|
||||||
|
}
|
||||||
|
|
||||||
void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid) {
|
void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid) {
|
||||||
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ using boost::none;
|
|||||||
namespace cryfs {
|
namespace cryfs {
|
||||||
namespace fsblobstore {
|
namespace fsblobstore {
|
||||||
|
|
||||||
|
constexpr off_t DirBlob::DIR_LSTAT_SIZE;
|
||||||
|
|
||||||
DirBlob::DirBlob(unique_ref<Blob> blob, std::function<off_t (const blockstore::Key&)> getLstatSize) :
|
DirBlob::DirBlob(unique_ref<Blob> blob, std::function<off_t (const blockstore::Key&)> getLstatSize) :
|
||||||
FsBlob(std::move(blob)), _getLstatSize(getLstatSize), _entries(), _mutex(), _changed(false) {
|
FsBlob(std::move(blob)), _getLstatSize(getLstatSize), _entries(), _mutex(), _changed(false) {
|
||||||
ASSERT(magicNumber() == MagicNumbers::DIR, "Loaded blob is not a directory");
|
ASSERT(magicNumber() == MagicNumbers::DIR, "Loaded blob is not a directory");
|
||||||
@ -107,8 +109,7 @@ void DirBlob::AppendChildrenTo(vector<fspp::Dir::Entry> *result) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
off_t DirBlob::lstat_size() const {
|
off_t DirBlob::lstat_size() const {
|
||||||
//TODO Why do dirs have 4096 bytes in size? Does that make sense?
|
return DIR_LSTAT_SIZE;
|
||||||
return 4096;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirBlob::statChild(const Key &key, struct ::stat *result) const {
|
void DirBlob::statChild(const Key &key, struct ::stat *result) const {
|
||||||
@ -124,8 +125,9 @@ void DirBlob::statChild(const Key &key, struct ::stat *result) const {
|
|||||||
result->st_gid = child.gid;
|
result->st_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->st_nlink = 1;
|
||||||
//TODO Handle file access times
|
result->st_atim = child.lastAccessTime;
|
||||||
result->st_mtime = result->st_ctime = result->st_atime = 0;
|
result->st_mtim = child.lastModificationTime;
|
||||||
|
result->st_ctim = child.lastMetadataChangeTime;
|
||||||
result->st_size = _getLstatSize(key);
|
result->st_size = _getLstatSize(key);
|
||||||
//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, (off_t)512);
|
result->st_blocks = blobstore::onblocks::utils::ceilDivision(result->st_size, (off_t)512);
|
||||||
@ -145,6 +147,12 @@ void DirBlob::chownChild(const Key &key, uid_t uid, gid_t gid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DirBlob::utimensChild(const Key &key, timespec lastAccessTime, timespec lastModificationTime) {
|
||||||
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
|
_entries.setAccessTimes(key, lastAccessTime, lastModificationTime);
|
||||||
|
_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
void DirBlob::setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize) {
|
void DirBlob::setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize) {
|
||||||
std::unique_lock<std::mutex> lock(_mutex);
|
std::unique_lock<std::mutex> lock(_mutex);
|
||||||
_getLstatSize = getLstatSize;
|
_getLstatSize = getLstatSize;
|
||||||
|
@ -15,6 +15,7 @@ namespace cryfs {
|
|||||||
|
|
||||||
class DirBlob final : public FsBlob {
|
class DirBlob final : public FsBlob {
|
||||||
public:
|
public:
|
||||||
|
constexpr static off_t DIR_LSTAT_SIZE = 4096;
|
||||||
|
|
||||||
static cpputils::unique_ref<DirBlob> InitializeEmptyDir(cpputils::unique_ref<blobstore::Blob> blob,
|
static cpputils::unique_ref<DirBlob> InitializeEmptyDir(cpputils::unique_ref<blobstore::Blob> blob,
|
||||||
std::function<off_t (const blockstore::Key&)> getLstatSize);
|
std::function<off_t (const blockstore::Key&)> getLstatSize);
|
||||||
@ -53,6 +54,8 @@ namespace cryfs {
|
|||||||
|
|
||||||
void chownChild(const blockstore::Key &key, uid_t uid, gid_t gid);
|
void chownChild(const blockstore::Key &key, uid_t uid, gid_t gid);
|
||||||
|
|
||||||
|
void utimensChild(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime);
|
||||||
|
|
||||||
void setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize);
|
void setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -26,10 +26,21 @@ namespace cryfs {
|
|||||||
*reinterpret_cast<mode_t*>(dest+offset) = mode;
|
*reinterpret_cast<mode_t*>(dest+offset) = mode;
|
||||||
offset += sizeof(mode_t);
|
offset += sizeof(mode_t);
|
||||||
|
|
||||||
|
//TODO Persist times, see comment in deserializeAndAddToVector()
|
||||||
|
//static_assert(sizeof(timespec) == 16, "Ensure platform independence of the serialization");
|
||||||
|
//*reinterpret_cast<timespec*>(dest+offset) = lastAccessTime;
|
||||||
|
//offset += sizeof(timespec);
|
||||||
|
//*reinterpret_cast<timespec*>(dest+offset) = lastModificationTime;
|
||||||
|
//offset += sizeof(timespec);
|
||||||
|
//*reinterpret_cast<timespec*>(dest+offset) = lastMetadataChangeTime;
|
||||||
|
//offset += sizeof(timespec);
|
||||||
|
|
||||||
ASSERT(offset == serializedSize(), "Didn't write correct number of elements");
|
ASSERT(offset == serializedSize(), "Didn't write correct number of elements");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DirEntry::serializedSize() const {
|
size_t DirEntry::serializedSize() const {
|
||||||
|
//TODO Persist times, see comment in deserializeAndAddToVector()
|
||||||
|
//return 1 + (name.size() + 1) + key.BINARY_LENGTH + sizeof(uid_t) + sizeof(gid_t) + sizeof(mode_t) + 3*sizeof(timespec);
|
||||||
return 1 + (name.size() + 1) + key.BINARY_LENGTH + sizeof(uid_t) + sizeof(gid_t) + sizeof(mode_t);
|
return 1 + (name.size() + 1) + key.BINARY_LENGTH + sizeof(uid_t) + sizeof(gid_t) + sizeof(mode_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +66,20 @@ namespace cryfs {
|
|||||||
mode_t mode = *(mode_t*)pos;
|
mode_t mode = *(mode_t*)pos;
|
||||||
pos += sizeof(mode_t);
|
pos += sizeof(mode_t);
|
||||||
|
|
||||||
result->emplace_back(type, name, key, mode, uid, gid);
|
//TODO Persist times. This breaks compatibility though - so change cryfs::InnerConfig::HEADER
|
||||||
|
// This is already implemented, but I commented it out for now, because it would break compatibility.
|
||||||
|
//timespec lastAccessTime = *(timespec*)pos;
|
||||||
|
//pos += sizeof(timespec);
|
||||||
|
//timespec lastModificationTime = *(timespec*)pos;
|
||||||
|
//pos += sizeof(timespec);
|
||||||
|
//timespec lastMetadataChangeTime = *(timespec*)pos;
|
||||||
|
//pos += sizeof(timespec);
|
||||||
|
timespec lastAccessTime;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &lastAccessTime);
|
||||||
|
timespec lastModificationTime = lastAccessTime;
|
||||||
|
timespec lastMetadataChangeTime = lastAccessTime;
|
||||||
|
|
||||||
|
result->emplace_back(type, name, key, mode, uid, gid, lastAccessTime, lastModificationTime, lastMetadataChangeTime);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,11 @@ namespace cryfs {
|
|||||||
|
|
||||||
struct DirEntry final {
|
struct DirEntry final {
|
||||||
DirEntry(fspp::Dir::EntryType type_, const std::string &name_, const blockstore::Key &key_, mode_t mode_,
|
DirEntry(fspp::Dir::EntryType type_, const std::string &name_, const blockstore::Key &key_, mode_t mode_,
|
||||||
uid_t uid_, gid_t gid_) : type(type_), name(name_), key(key_), mode(mode_), uid(uid_), gid(gid_) {
|
uid_t uid_, gid_t gid_, timespec lastAccessTime_, timespec lastModificationTime_,
|
||||||
|
timespec lastMetadataChangeTime_) : type(type_), name(name_), key(key_), mode(mode_), uid(uid_),
|
||||||
|
gid(gid_), lastAccessTime(lastAccessTime_),
|
||||||
|
lastModificationTime(lastModificationTime_),
|
||||||
|
lastMetadataChangeTime(lastMetadataChangeTime_) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case fspp::Dir::EntryType::FILE:
|
case fspp::Dir::EntryType::FILE:
|
||||||
mode |= S_IFREG;
|
mode |= S_IFREG;
|
||||||
@ -37,6 +41,9 @@ namespace cryfs {
|
|||||||
mode_t mode;
|
mode_t mode;
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
gid_t gid;
|
gid_t gid;
|
||||||
|
struct timespec lastAccessTime;
|
||||||
|
struct timespec lastModificationTime;
|
||||||
|
struct timespec lastMetadataChangeTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@ void DirEntryList::add(const string &name, const Key &blobKey, fspp::Dir::EntryT
|
|||||||
throw fspp::fuse::FuseErrnoException(EEXIST);
|
throw fspp::fuse::FuseErrnoException(EEXIST);
|
||||||
}
|
}
|
||||||
auto insert_pos = _findUpperBound(blobKey);
|
auto insert_pos = _findUpperBound(blobKey);
|
||||||
_entries.emplace(insert_pos, entryType, name, blobKey, mode, uid, gid);
|
auto now = _now();
|
||||||
|
_entries.emplace(insert_pos, entryType, name, blobKey, mode, uid, gid, now, now, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<const DirEntry&> DirEntryList::get(const string &name) const {
|
boost::optional<const DirEntry&> DirEntryList::get(const string &name) const {
|
||||||
@ -137,6 +138,7 @@ void DirEntryList::setMode(const Key &key, mode_t mode) {
|
|||||||
auto found = _find(key);
|
auto found = _find(key);
|
||||||
ASSERT ((S_ISREG(mode) && S_ISREG(found->mode)) || (S_ISDIR(mode) && S_ISDIR(found->mode)) || (S_ISLNK(mode)), "Unknown mode in entry");
|
ASSERT ((S_ISREG(mode) && S_ISREG(found->mode)) || (S_ISDIR(mode) && S_ISDIR(found->mode)) || (S_ISLNK(mode)), "Unknown mode in entry");
|
||||||
found->mode = mode;
|
found->mode = mode;
|
||||||
|
found->lastMetadataChangeTime = _now();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirEntryList::setUidGid(const Key &key, uid_t uid, gid_t gid) {
|
bool DirEntryList::setUidGid(const Key &key, uid_t uid, gid_t gid) {
|
||||||
@ -144,14 +146,29 @@ bool DirEntryList::setUidGid(const Key &key, uid_t uid, gid_t gid) {
|
|||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (uid != (uid_t)-1) {
|
if (uid != (uid_t)-1) {
|
||||||
found->uid = uid;
|
found->uid = uid;
|
||||||
|
found->lastMetadataChangeTime = _now();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
if (gid != (gid_t)-1) {
|
if (gid != (gid_t)-1) {
|
||||||
found->gid = gid;
|
found->gid = gid;
|
||||||
|
found->lastMetadataChangeTime = _now();
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timespec DirEntryList::_now() {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirEntryList::setAccessTimes(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime) {
|
||||||
|
auto found = _find(key);
|
||||||
|
found->lastAccessTime = lastAccessTime;
|
||||||
|
found->lastModificationTime = lastModificationTime;
|
||||||
|
found->lastMetadataChangeTime = lastModificationTime;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -31,6 +31,7 @@ namespace cryfs {
|
|||||||
|
|
||||||
void setMode(const blockstore::Key &key, mode_t mode);
|
void setMode(const blockstore::Key &key, mode_t mode);
|
||||||
bool setUidGid(const blockstore::Key &key, uid_t uid, gid_t gid);
|
bool setUidGid(const blockstore::Key &key, uid_t uid, gid_t gid);
|
||||||
|
void setAccessTimes(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _serializedSize() const;
|
uint64_t _serializedSize() const;
|
||||||
@ -40,6 +41,7 @@ namespace cryfs {
|
|||||||
std::vector<DirEntry>::iterator _findUpperBound(const blockstore::Key &key);
|
std::vector<DirEntry>::iterator _findUpperBound(const blockstore::Key &key);
|
||||||
std::vector<DirEntry>::iterator _findLowerBound(const blockstore::Key &key);
|
std::vector<DirEntry>::iterator _findLowerBound(const blockstore::Key &key);
|
||||||
std::vector<DirEntry>::iterator _findFirst(const blockstore::Key &hint, std::function<bool (const DirEntry&)> pred);
|
std::vector<DirEntry>::iterator _findFirst(const blockstore::Key &hint, std::function<bool (const DirEntry&)> pred);
|
||||||
|
static timespec _now();
|
||||||
|
|
||||||
std::vector<DirEntry> _entries;
|
std::vector<DirEntry> _entries;
|
||||||
|
|
||||||
|
@ -47,6 +47,10 @@ public:
|
|||||||
return _base->chownChild(key, uid, gid);
|
return _base->chownChild(key, uid, gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void utimensChild(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime) {
|
||||||
|
return _base->utimensChild(key, lastAccessTime, lastModificationTime);
|
||||||
|
}
|
||||||
|
|
||||||
void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid) {
|
void AddChildDir(const std::string &name, const blockstore::Key &blobKey, mode_t mode, uid_t uid, gid_t gid) {
|
||||||
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user