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:
Sebastian Messmer 2016-02-09 09:43:18 +01:00
parent 029fb73218
commit 452519ed0b
10 changed files with 85 additions and 11 deletions

View File

@ -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);
}

View File

@ -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:

View File

@ -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);
}

View File

@ -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;

View File

@ -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:

View File

@ -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;
}

View File

@ -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;
};
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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);
}