diff --git a/src/filesystem/CryNode.cpp b/src/filesystem/CryNode.cpp index b5c1ee95..06caad7f 100644 --- a/src/filesystem/CryNode.cpp +++ b/src/filesystem/CryNode.cpp @@ -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); } diff --git a/src/filesystem/CryNode.h b/src/filesystem/CryNode.h index 8403514a..f77d7bf4 100644 --- a/src/filesystem/CryNode.h +++ b/src/filesystem/CryNode.h @@ -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: diff --git a/src/filesystem/cachingfsblobstore/DirBlobRef.h b/src/filesystem/cachingfsblobstore/DirBlobRef.h index 4d75fb0b..c4dc41ff 100644 --- a/src/filesystem/cachingfsblobstore/DirBlobRef.h +++ b/src/filesystem/cachingfsblobstore/DirBlobRef.h @@ -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); } diff --git a/src/filesystem/fsblobstore/DirBlob.cpp b/src/filesystem/fsblobstore/DirBlob.cpp index b03c263f..b9c00f9e 100644 --- a/src/filesystem/fsblobstore/DirBlob.cpp +++ b/src/filesystem/fsblobstore/DirBlob.cpp @@ -26,6 +26,8 @@ using boost::none; namespace cryfs { namespace fsblobstore { +constexpr off_t DirBlob::DIR_LSTAT_SIZE; + DirBlob::DirBlob(unique_ref blob, std::function 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 *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 lock(_mutex); + _entries.setAccessTimes(key, lastAccessTime, lastModificationTime); + _changed = true; +} + void DirBlob::setLstatSizeGetter(std::function getLstatSize) { std::unique_lock lock(_mutex); _getLstatSize = getLstatSize; diff --git a/src/filesystem/fsblobstore/DirBlob.h b/src/filesystem/fsblobstore/DirBlob.h index 7706c133..957d917f 100644 --- a/src/filesystem/fsblobstore/DirBlob.h +++ b/src/filesystem/fsblobstore/DirBlob.h @@ -15,6 +15,7 @@ namespace cryfs { class DirBlob final : public FsBlob { public: + constexpr static off_t DIR_LSTAT_SIZE = 4096; static cpputils::unique_ref InitializeEmptyDir(cpputils::unique_ref blob, std::function 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 getLstatSize); private: diff --git a/src/filesystem/fsblobstore/utils/DirEntry.cpp b/src/filesystem/fsblobstore/utils/DirEntry.cpp index e40c68ee..bb0a342f 100644 --- a/src/filesystem/fsblobstore/utils/DirEntry.cpp +++ b/src/filesystem/fsblobstore/utils/DirEntry.cpp @@ -26,10 +26,21 @@ namespace cryfs { *reinterpret_cast(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(dest+offset) = lastAccessTime; + //offset += sizeof(timespec); + //*reinterpret_cast(dest+offset) = lastModificationTime; + //offset += sizeof(timespec); + //*reinterpret_cast(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; } diff --git a/src/filesystem/fsblobstore/utils/DirEntry.h b/src/filesystem/fsblobstore/utils/DirEntry.h index 8bb16e0d..3f65541a 100644 --- a/src/filesystem/fsblobstore/utils/DirEntry.h +++ b/src/filesystem/fsblobstore/utils/DirEntry.h @@ -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; }; } diff --git a/src/filesystem/fsblobstore/utils/DirEntryList.cpp b/src/filesystem/fsblobstore/utils/DirEntryList.cpp index 01a17d27..56a5d1f9 100644 --- a/src/filesystem/fsblobstore/utils/DirEntryList.cpp +++ b/src/filesystem/fsblobstore/utils/DirEntryList.cpp @@ -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 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; +} + } } \ No newline at end of file diff --git a/src/filesystem/fsblobstore/utils/DirEntryList.h b/src/filesystem/fsblobstore/utils/DirEntryList.h index 3fb90d0a..b2b5d26a 100644 --- a/src/filesystem/fsblobstore/utils/DirEntryList.h +++ b/src/filesystem/fsblobstore/utils/DirEntryList.h @@ -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::iterator _findUpperBound(const blockstore::Key &key); std::vector::iterator _findLowerBound(const blockstore::Key &key); std::vector::iterator _findFirst(const blockstore::Key &hint, std::function pred); + static timespec _now(); std::vector _entries; diff --git a/src/filesystem/parallelaccessfsblobstore/DirBlobRef.h b/src/filesystem/parallelaccessfsblobstore/DirBlobRef.h index b20bb864..ceb7e957 100644 --- a/src/filesystem/parallelaccessfsblobstore/DirBlobRef.h +++ b/src/filesystem/parallelaccessfsblobstore/DirBlobRef.h @@ -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); }