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);
|
||||
}
|
||||
|
||||
void CryNode::utimens(const timespec times[2]) {
|
||||
void CryNode::utimens(timespec lastAccessTime, timespec lastModificationTime) {
|
||||
device()->callFsActionCallbacks();
|
||||
//TODO
|
||||
throw FuseErrnoException(ENOTSUP);
|
||||
if (_parent == none) {
|
||||
//We are the root direcory.
|
||||
//TODO What should we do?
|
||||
throw FuseErrnoException(EIO);
|
||||
}
|
||||
(*_parent)->utimensChild(_key, lastAccessTime, lastModificationTime);
|
||||
}
|
||||
|
||||
void CryNode::remove() {
|
||||
@ -100,6 +104,7 @@ void CryNode::stat(struct ::stat *result) const {
|
||||
//We are the root directory.
|
||||
//TODO What should we do?
|
||||
result->st_mode = S_IFDIR;
|
||||
result->st_size = fsblobstore::DirBlob::DIR_LSTAT_SIZE;
|
||||
} else {
|
||||
(*_parent)->statChild(_key, result);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
void chmod(mode_t mode) override;
|
||||
void chown(uid_t uid, gid_t gid) 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;
|
||||
|
||||
protected:
|
||||
|
@ -51,6 +51,10 @@ public:
|
||||
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) {
|
||||
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ using boost::none;
|
||||
namespace cryfs {
|
||||
namespace fsblobstore {
|
||||
|
||||
constexpr off_t DirBlob::DIR_LSTAT_SIZE;
|
||||
|
||||
DirBlob::DirBlob(unique_ref<Blob> blob, std::function<off_t (const blockstore::Key&)> getLstatSize) :
|
||||
FsBlob(std::move(blob)), _getLstatSize(getLstatSize), _entries(), _mutex(), _changed(false) {
|
||||
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 {
|
||||
//TODO Why do dirs have 4096 bytes in size? Does that make sense?
|
||||
return 4096;
|
||||
return DIR_LSTAT_SIZE;
|
||||
}
|
||||
|
||||
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;
|
||||
//TODO If possible without performance loss, then for a directory, st_nlink should return number of dir entries (including "." and "..")
|
||||
result->st_nlink = 1;
|
||||
//TODO Handle file access times
|
||||
result->st_mtime = result->st_ctime = result->st_atime = 0;
|
||||
result->st_atim = child.lastAccessTime;
|
||||
result->st_mtim = child.lastModificationTime;
|
||||
result->st_ctim = child.lastMetadataChangeTime;
|
||||
result->st_size = _getLstatSize(key);
|
||||
//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);
|
||||
@ -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) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_getLstatSize = getLstatSize;
|
||||
|
@ -15,6 +15,7 @@ namespace cryfs {
|
||||
|
||||
class DirBlob final : public FsBlob {
|
||||
public:
|
||||
constexpr static off_t DIR_LSTAT_SIZE = 4096;
|
||||
|
||||
static cpputils::unique_ref<DirBlob> InitializeEmptyDir(cpputils::unique_ref<blobstore::Blob> blob,
|
||||
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 utimensChild(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime);
|
||||
|
||||
void setLstatSizeGetter(std::function<off_t(const blockstore::Key&)> getLstatSize);
|
||||
|
||||
private:
|
||||
|
@ -26,10 +26,21 @@ namespace cryfs {
|
||||
*reinterpret_cast<mode_t*>(dest+offset) = mode;
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -55,7 +66,20 @@ namespace cryfs {
|
||||
mode_t mode = *(mode_t*)pos;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,11 @@ namespace cryfs {
|
||||
|
||||
struct DirEntry final {
|
||||
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) {
|
||||
case fspp::Dir::EntryType::FILE:
|
||||
mode |= S_IFREG;
|
||||
@ -37,6 +41,9 @@ namespace cryfs {
|
||||
mode_t mode;
|
||||
uid_t uid;
|
||||
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);
|
||||
}
|
||||
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 {
|
||||
@ -137,6 +138,7 @@ void DirEntryList::setMode(const Key &key, mode_t mode) {
|
||||
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");
|
||||
found->mode = mode;
|
||||
found->lastMetadataChangeTime = _now();
|
||||
}
|
||||
|
||||
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;
|
||||
if (uid != (uid_t)-1) {
|
||||
found->uid = uid;
|
||||
found->lastMetadataChangeTime = _now();
|
||||
changed = true;
|
||||
}
|
||||
if (gid != (gid_t)-1) {
|
||||
found->gid = gid;
|
||||
found->lastMetadataChangeTime = _now();
|
||||
changed = true;
|
||||
}
|
||||
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);
|
||||
bool setUidGid(const blockstore::Key &key, uid_t uid, gid_t gid);
|
||||
void setAccessTimes(const blockstore::Key &key, timespec lastAccessTime, timespec lastModificationTime);
|
||||
|
||||
private:
|
||||
uint64_t _serializedSize() const;
|
||||
@ -40,6 +41,7 @@ namespace cryfs {
|
||||
std::vector<DirEntry>::iterator _findUpperBound(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);
|
||||
static timespec _now();
|
||||
|
||||
std::vector<DirEntry> _entries;
|
||||
|
||||
|
@ -47,6 +47,10 @@ public:
|
||||
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) {
|
||||
return _base->AddChildDir(name, blobKey, mode, uid, gid);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user