From 37e593aeb0fd225dd05aa772a1263fad11dc079c Mon Sep 17 00:00:00 2001 From: Takeshi Nakatani Date: Wed, 1 Oct 2025 15:18:28 +0000 Subject: [PATCH] Changed file stat times(a/c/mtime) management --- src/Makefile.am | 1 + src/fdcache.cpp | 18 ++- src/fdcache.h | 2 +- src/fdcache_auto.cpp | 4 +- src/fdcache_auto.h | 3 +- src/fdcache_entity.cpp | 265 ++++++++++++----------------------- src/fdcache_entity.h | 70 +++++----- src/filetimes.cpp | 303 +++++++++++++++++++++++++++++++++++++++++ src/filetimes.h | 120 ++++++++++++++++ src/metaheader.cpp | 82 +++++------ src/s3fs.cpp | 131 ++++++------------ src/s3fs_util.cpp | 118 ---------------- src/s3fs_util.h | 61 ++++----- src/string_util.cpp | 20 +-- 14 files changed, 675 insertions(+), 523 deletions(-) create mode 100644 src/filetimes.cpp create mode 100644 src/filetimes.h diff --git a/src/Makefile.am b/src/Makefile.am index 87153ab..317204c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,6 +53,7 @@ s3fs_SOURCES = \ fdcache_fdinfo.cpp \ fdcache_pseudofd.cpp \ fdcache_untreated.cpp \ + filetimes.cpp \ addhead.cpp \ sighandlers.cpp \ threadpoolman.cpp \ diff --git a/src/fdcache.cpp b/src/fdcache.cpp index be6828e..6710f59 100644 --- a/src/fdcache.cpp +++ b/src/fdcache.cpp @@ -527,9 +527,9 @@ FdEntity* FdManager::GetFdEntityHasLock(const char* path, int& existfd, bool new return nullptr; } -FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify) +FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags, bool force_tmpfile, bool is_create, bool ignore_modify) { - S3FS_PRN_DBG("[path=%s][size=%lld][ts_mctime=%s][flags=0x%x][force_tmpfile=%s][create=%s][ignore_modify=%s]", SAFESTRPTR(path), static_cast(size), str(ts_mctime).c_str(), flags, (force_tmpfile ? "yes" : "no"), (is_create ? "yes" : "no"), (ignore_modify ? "yes" : "no")); + S3FS_PRN_DBG("[path=%s][size=%lld][ctime=%s,atime=%s,mtime=%s][flags=0x%x][force_tmpfile=%s][create=%s][ignore_modify=%s]", SAFESTRPTR(path), static_cast(size), str(ts_times.ctime()).c_str(), str(ts_times.atime()).c_str(), str(ts_times.mtime()).c_str(), flags, (force_tmpfile ? "yes" : "no"), (is_create ? "yes" : "no"), (ignore_modify ? "yes" : "no")); if(!path || '\0' == path[0]){ return nullptr; @@ -573,7 +573,7 @@ FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off } // (re)open - if(0 > (fd = ent->Open(pmeta, size, ts_mctime, flags))){ + if(0 > (fd = ent->Open(pmeta, size, ts_times, flags))){ S3FS_PRN_ERR("failed to (re)open and create new pseudo fd for path(%s).", path); return nullptr; } @@ -590,7 +590,7 @@ FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off auto ent = std::make_shared(path, cache_path.c_str()); // open - if(0 > (fd = ent->Open(pmeta, size, ts_mctime, flags))){ + if(0 > (fd = ent->Open(pmeta, size, ts_times, flags))){ S3FS_PRN_ERR("failed to open and create new pseudo fd for path(%s) errno:%d.", path, fd); return nullptr; } @@ -656,7 +656,15 @@ FdEntity* FdManager::OpenExistFdEntity(const char* path, int& fd, int flags) S3FS_PRN_DBG("[path=%s][flags=0x%x]", SAFESTRPTR(path), flags); // search entity by path, and create pseudo fd - FdEntity* ent = Open(fd, path, nullptr, -1, S3FS_OMIT_TS, flags, false, false, false); + // + // [NOTE] + // The file timespec is set to UIMTE_OMIT. + // This means that if the file is already open(this method is called + // when expected to be open), the timespecs will not be updated. + // If the file is not open, the current time will be applied. + // + FdEntity* ent = Open(fd, path, nullptr, -1, FileTimes(), flags, false, false, false); + if(!ent){ // Not found entity return nullptr; diff --git a/src/fdcache.h b/src/fdcache.h index b737bdd..60d7b6c 100644 --- a/src/fdcache.h +++ b/src/fdcache.h @@ -111,7 +111,7 @@ class FdManager return GetFdEntityHasLock(path, existfd, newfd); } FdEntity* GetFdEntityHasLock(const char* path, int& existfd, bool newfd = true) REQUIRES(FdManager::fd_manager_lock); - FdEntity* Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify); + FdEntity* Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags, bool force_tmpfile, bool is_create, bool ignore_modify); FdEntity* GetExistFdEntity(const char* path, int existfd = -1); FdEntity* OpenExistFdEntity(const char* path, int& fd, int flags = O_RDONLY); void Rename(const std::string &from, const std::string &to); diff --git a/src/fdcache_auto.cpp b/src/fdcache_auto.cpp index 9d87e31..338708f 100644 --- a/src/fdcache_auto.cpp +++ b/src/fdcache_auto.cpp @@ -78,11 +78,11 @@ FdEntity* AutoFdEntity::Attach(const char* path, int existfd) return pFdEntity; } -FdEntity* AutoFdEntity::Open(const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, int* error) +FdEntity* AutoFdEntity::Open(const char* path, const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, int* error) { Close(); - if(nullptr == (pFdEntity = FdManager::get()->Open(pseudo_fd, path, pmeta, size, ts_mctime, flags, force_tmpfile, is_create, ignore_modify))){ + if(nullptr == (pFdEntity = FdManager::get()->Open(pseudo_fd, path, pmeta, size, ts_times, flags, force_tmpfile, is_create, ignore_modify))){ if(error){ *error = pseudo_fd; } diff --git a/src/fdcache_auto.h b/src/fdcache_auto.h index 5b63798..e5b0866 100644 --- a/src/fdcache_auto.h +++ b/src/fdcache_auto.h @@ -25,6 +25,7 @@ #include "common.h" #include "metaheader.h" +#include "filetimes.h" class FdEntity; @@ -55,7 +56,7 @@ class AutoFdEntity FdEntity* Attach(const char* path, int existfd); int GetPseudoFd() const { return pseudo_fd; } - FdEntity* Open(const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, int* error = nullptr); + FdEntity* Open(const char* path, const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags, bool force_tmpfile, bool is_create, bool ignore_modify, int* error = nullptr); FdEntity* GetExistFdEntity(const char* path, int existfd = -1); FdEntity* OpenExistFdEntity(const char* path, int flags = O_RDONLY); }; diff --git a/src/fdcache_entity.cpp b/src/fdcache_entity.cpp index 53c6d84..63f8c13 100644 --- a/src/fdcache_entity.cpp +++ b/src/fdcache_entity.cpp @@ -119,8 +119,6 @@ FdEntity::FdEntity(const char* tpath, const char* cpath) : cachepath(SAFESTRPTR(cpath)), pending_status(pending_status_t::NO_UPDATE_PENDING), ro_path(SAFESTRPTR(tpath)) { - holding_mtime.tv_sec = -1; - holding_mtime.tv_nsec = 0; } FdEntity::~FdEntity() @@ -168,6 +166,8 @@ void FdEntity::Clear() // set backup(read only) variable const std::lock_guard ro_lock(ro_path_lock); ro_path = path; + + timestamps.Clear(); } // [NOTE] @@ -380,18 +380,18 @@ bool FdEntity::IsUploading() // If the open is successful, returns pseudo fd. // If it fails, it returns an error code with a negative value. // -// ts_mctime argument is a variable for mtime/ctime. -// If you want to disable this variable, specify UTIME_OMIT for -// tv_nsec in timespec member(in this case tv_sec member is ignored). -// This is similar to utimens operation. -// You can use "S3FS_OMIT_TS" global variable for UTIME_OMIT. +// The ts_times argument is a variable for atime/mtime/ctime. +// If you want to disable each value, set the tv_nsec of the timespec +// member corresponding to atime/ctime/mtime to UTIME_OMIT. (In this +// case, the tv_sec member will be ignored.) +// This is the same as the behavior of utimens. // -int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags) +int FdEntity::Open(const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags) { const std::lock_guard lock(fdent_lock); const std::lock_guard data_lock(fdent_data_lock); - S3FS_PRN_DBG("[path=%s][physical_fd=%d][size=%lld][ts_mctime=%s][flags=0x%x]", path.c_str(), physical_fd, static_cast(size), str(ts_mctime).c_str(), flags); + S3FS_PRN_DBG("[path=%s][physical_fd=%d][size=%lld][ctime=%s,atime=%s,mtime=%s][flags=0x%x]", path.c_str(), physical_fd, static_cast(size), str(ts_times.ctime()).c_str(), str(ts_times.atime()).c_str(), str(ts_times.mtime()).c_str(), flags); // [NOTE] // When the file size is incremental by truncating, it must be keeped @@ -440,6 +440,7 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts // bool need_save_csf = false; // need to save(reset) cache stat file bool is_truncate = false; // need to truncate + int result = 0; std::unique_ptr pcfstat; @@ -447,7 +448,8 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts // using cache struct stat st; if(stat(cachepath.c_str(), &st) == 0){ - if(0 > compare_timespec(st, stat_time_type::MTIME, ts_mctime)){ + // check if the cache file stale + if(!ts_times.IsOmitMTime() && 0 > compare_timespec(st, stat_time_type::MTIME, ts_times.mtime())){ S3FS_PRN_DBG("cache file stale, removing: %s", cachepath.c_str()); if(unlink(cachepath.c_str()) != 0){ return (0 == errno ? -EIO : -errno); @@ -504,7 +506,6 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts S3FS_PRN_ERR("failed to open file(%s). errno(%d)", cachepath.c_str(), errno); // remove cache stat file if it is existed - int result; if(0 != (result = CacheFileStat::DeleteCacheFileStat(path.c_str()))){ if(-ENOENT != result){ S3FS_PRN_WARN("failed to delete current cache stat file(%s) by errno(%d), but continue...", path.c_str(), result); @@ -520,14 +521,13 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts }else{ // [NOTE] // The modify flag must not be set when opening a file, - // if the ts_mctime parameter(mtime) is specified(tv_nsec != UTIME_OMIT) + // if the ts_times parameter(mtime) is specified(tv_nsec != UTIME_OMIT) // and the cache file does not exist. // If mtime is specified for the file and the cache file // mtime is older than it, the cache file is removed and // the processing comes here. // - pagelist.Resize(size, false, (UTIME_OMIT == ts_mctime.tv_nsec ? true : false)); - + pagelist.Resize(size, false, ts_times.IsOmitMTime()); is_truncate = true; } } @@ -570,14 +570,13 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts }else{ // [NOTE] // The modify flag must not be set when opening a file, - // if the ts_mctime parameter(mtime) is specified(tv_nsec != UTIME_OMIT) + // if the ts_times parameter(mtime) is specified(tv_nsec != UTIME_OMIT) // and the cache file does not exist. // If mtime is specified for the file and the cache file // mtime is older than it, the cache file is removed and // the processing comes here. // - pagelist.Resize(size, false, (UTIME_OMIT == ts_mctime.tv_nsec ? true : false)); - + pagelist.Resize(size, false, ts_times.IsOmitMTime()); is_truncate = true; } } @@ -615,15 +614,13 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts truncated_size = size - size_orgmeta; } - // set mtime and ctime(set "x-amz-meta-mtime" and "x-amz-meta-ctime" in orgmeta) - if(UTIME_OMIT != ts_mctime.tv_nsec){ - if(0 != SetMCtimeHasLock(ts_mctime, ts_mctime)){ - S3FS_PRN_ERR("failed to set mtime/ctime. errno(%d)", errno); - pfile.reset(); - physical_fd = -1; - inode = 0; - return (0 == errno ? -EIO : -errno); - } + // set file timespecs(to internal varibales, cache file and original header) + if(0 != (result = SetFileTimesHasLock(ts_times))){ + S3FS_PRN_ERR("failed to set file timespecs by result(%d)", result); + pfile.reset(); + physical_fd = -1; + inode = 0; + return result; } } @@ -755,6 +752,10 @@ bool FdEntity::GetStatsHasLock(struct stat& st) const S3FS_PRN_ERR("fstat failed. errno(%d)", errno); return false; } + + const std::lock_guard data_lock(fdent_data_lock); + timestamps.RefrectFileTimes(st); + return true; } @@ -762,9 +763,10 @@ int FdEntity::SetCtimeHasLock(struct timespec time) { S3FS_PRN_INFO3("[path=%s][physical_fd=%d][time=%s]", path.c_str(), physical_fd, str(time).c_str()); - if(-1 == time.tv_sec){ + if(!valid_timespec(time)){ return 0; } + timestamps.SetCTime(time); orgmeta["x-amz-meta-ctime"] = str(time); return 0; } @@ -773,174 +775,82 @@ int FdEntity::SetAtimeHasLock(struct timespec time) { S3FS_PRN_INFO3("[path=%s][physical_fd=%d][time=%s]", path.c_str(), physical_fd, str(time).c_str()); - if(-1 == time.tv_sec){ + if(!valid_timespec(time)){ return 0; } + timestamps.SetATime(time); orgmeta["x-amz-meta-atime"] = str(time); return 0; } -// [NOTE] -// This method updates mtime as well as ctime. -// -int FdEntity::SetMCtimeHasLock(struct timespec mtime, struct timespec ctime) +int FdEntity::SetMtimeHasLock(struct timespec time) { - S3FS_PRN_INFO3("[path=%s][physical_fd=%d][mtime=%s][ctime=%s]", path.c_str(), physical_fd, str(mtime).c_str(), str(ctime).c_str()); + S3FS_PRN_INFO3("[path=%s][physical_fd=%d][time=%s]", path.c_str(), physical_fd, str(time).c_str()); - if(mtime.tv_sec < 0 || ctime.tv_sec < 0){ + if(!valid_timespec(time)){ return 0; } - - if(-1 != physical_fd){ - struct timespec ts[2]; - ts[0].tv_sec = mtime.tv_sec; - ts[0].tv_nsec = mtime.tv_nsec; - ts[1].tv_sec = ctime.tv_sec; - ts[1].tv_nsec = ctime.tv_nsec; - if(-1 == futimens(physical_fd, ts)){ - S3FS_PRN_ERR("futimens failed. errno(%d)", errno); - return -errno; - } - }else if(!cachepath.empty()){ - // not opened file yet. - struct timespec ts[2]; - ts[0].tv_sec = ctime.tv_sec; - ts[0].tv_nsec = ctime.tv_nsec; - ts[1].tv_sec = mtime.tv_sec; - ts[1].tv_nsec = mtime.tv_nsec; - if(-1 == utimensat(AT_FDCWD, cachepath.c_str(), ts, 0)){ - S3FS_PRN_ERR("utimensat failed. errno(%d)", errno); - return -errno; - } - } - - orgmeta["x-amz-meta-mtime"] = str(mtime); - orgmeta["x-amz-meta-ctime"] = str(ctime); - + timestamps.SetMTime(time); + orgmeta["x-amz-meta-mtime"] = str(time); return 0; } -bool FdEntity::UpdateCtime() +// [NOTE] +// This method updates timespecs(atime/mtime) and origianl meta heders +// +int FdEntity::SetFileTimesHasLock(const FileTimes& ts_times) { - const std::lock_guard lock(fdent_lock); - struct stat st; - if(!GetStatsHasLock(st)){ - return false; - } + S3FS_PRN_INFO3("[path=%s][physical_fd=%d][ctime=%s,atime=%s,mtime=%s]", path.c_str(), physical_fd, str(ts_times.ctime()).c_str(), str(ts_times.atime()).c_str(), str(ts_times.mtime()).c_str()); - orgmeta["x-amz-meta-ctime"] = str_stat_time(st, stat_time_type::CTIME); + // Set all timespecs without OMIT type + timestamps.SetAll(ts_times); - return true; -} + // Set timespecs(mtime/atime) to cache file + if(!timestamps.IsOmitATime() || !timestamps.IsOmitMTime()){ + struct timespec ts[2]; + ts[0] = timestamps.atime(); + ts[1] = timestamps.mtime(); -bool FdEntity::UpdateAtime() -{ - const std::lock_guard lock(fdent_lock); - struct stat st; - if(!GetStatsHasLock(st)){ - return false; - } - - orgmeta["x-amz-meta-atime"] = str_stat_time(st, stat_time_type::ATIME); - - return true; -} - -bool FdEntity::UpdateMtime(bool clear_holding_mtime) -{ - const std::lock_guard lock(fdent_lock); - const std::lock_guard data_lock(fdent_data_lock); - - if(0 <= holding_mtime.tv_sec){ - // [NOTE] - // This conditional statement is very special. - // If you copy a file with "cp -p" etc., utimens or chown will be - // called after opening the file, after that call to write, flush. - // If normally utimens are not called(cases like "cp" only), mtime - // should be updated at the file flush. - // Here, check the holding_mtime value to prevent mtime from being - // overwritten. - // - if(clear_holding_mtime){ - if(!ClearHoldingMtime()){ - return false; + if(-1 != physical_fd){ + if(-1 == futimens(physical_fd, ts)){ + S3FS_PRN_ERR("futimens failed. errno(%d)", errno); + return -errno; + } + }else{ + if(-1 == utimensat(AT_FDCWD, cachepath.c_str(), ts, 0)){ + S3FS_PRN_ERR("utimensat failed. errno(%d)", errno); + return -errno; } - // [NOTE] - // If come here after fdatasync has been processed, the file - // content update has already taken place. However, the metadata - // update is necessary and needs to be flagged in order to - // perform it with flush, - // - pending_status = pending_status_t::UPDATE_META_PENDING; } + } + + // Set original meta headers / Set time to stat from original meta + if(!timestamps.IsOmitATime()){ + orgmeta["x-amz-meta-atime"] = str(timestamps.atime()); }else{ - struct stat st; - if(!GetStatsHasLock(st)){ - return false; - } - orgmeta["x-amz-meta-mtime"] = str_stat_time(st, stat_time_type::MTIME); - } - return true; -} - -bool FdEntity::SetHoldingMtime(struct timespec mtime) -{ - const std::lock_guard lock(fdent_lock); - const std::lock_guard data_lock(fdent_data_lock); - - S3FS_PRN_INFO3("[path=%s][physical_fd=%d][mtime=%s]", path.c_str(), physical_fd, str(mtime).c_str()); - - if(mtime.tv_sec < 0){ - return false; - } - holding_mtime = mtime; - return true; -} - -bool FdEntity::ClearHoldingMtime() -{ - if(holding_mtime.tv_sec < 0){ - return false; - } - struct stat st; - if(!GetStatsHasLock(st)){ - return false; - } - if(-1 != physical_fd){ - struct timespec ts[2]; - struct timespec ts_ctime; - - ts[0].tv_sec = holding_mtime.tv_sec; - ts[0].tv_nsec = holding_mtime.tv_nsec; - - set_stat_to_timespec(st, stat_time_type::CTIME, ts_ctime); - ts[1].tv_sec = ts_ctime.tv_sec; - ts[1].tv_nsec = ts_ctime.tv_nsec; - - if(-1 == futimens(physical_fd, ts)){ - S3FS_PRN_ERR("futimens failed. errno(%d)", errno); - return false; - } - }else if(!cachepath.empty()){ - // not opened file yet. - struct timespec ts[2]; - struct timespec ts_ctime; - - set_stat_to_timespec(st, stat_time_type::CTIME, ts_ctime); - ts[0].tv_sec = ts_ctime.tv_sec; - ts[0].tv_nsec = ts_ctime.tv_nsec; - - ts[1].tv_sec = holding_mtime.tv_sec; - ts[1].tv_nsec = holding_mtime.tv_nsec; - if(-1 == utimensat(AT_FDCWD, cachepath.c_str(), ts, 0)){ - S3FS_PRN_ERR("utimensat failed. errno(%d)", errno); - return false; + struct timespec meta_atime = get_atime(orgmeta, false); + if(0 != meta_atime.tv_sec && UTIME_OMIT != meta_atime.tv_nsec){ + timestamps.SetATime(meta_atime); + } + } + if(!timestamps.IsOmitMTime()){ + orgmeta["x-amz-meta-mtime"] = str(timestamps.mtime()); + }else{ + struct timespec meta_mtime = get_mtime(orgmeta, false); + if(0 != meta_mtime.tv_sec && UTIME_OMIT != meta_mtime.tv_nsec){ + timestamps.SetMTime(meta_mtime); + } + } + if(!timestamps.IsOmitCTime()){ + orgmeta["x-amz-meta-ctime"] = str(timestamps.ctime()); + }else{ + struct timespec meta_ctime = get_ctime(orgmeta, false); + if(0 != meta_ctime.tv_sec && UTIME_OMIT != meta_ctime.tv_nsec){ + timestamps.SetCTime(meta_ctime); } } - holding_mtime.tv_sec = -1; - holding_mtime.tv_nsec = 0; - return true; + return 0; } bool FdEntity::GetSize(off_t& size) const @@ -1015,6 +925,8 @@ bool FdEntity::GetStatsFromMeta(struct stat& st) const const std::lock_guard data_lock(fdent_data_lock); st.st_size = pagelist.Size(); // set current file size + timestamps.RefrectFileTimes(st); + return true; } @@ -2459,6 +2371,7 @@ bool FdEntity::MergeOrgMeta(headers_t& updatemeta) const std::lock_guard data_lock(fdent_data_lock); merge_headers(orgmeta, updatemeta, true); // overwrite all keys + // [NOTE] // this is special cases, we remove the key which has empty values. for(auto hiter = orgmeta.cbegin(); hiter != orgmeta.cend(); ){ @@ -2475,12 +2388,8 @@ bool FdEntity::MergeOrgMeta(headers_t& updatemeta) struct timespec mtime = get_mtime(updatemeta, false); // not overcheck struct timespec ctime = get_ctime(updatemeta, false); // not overcheck struct timespec atime = get_atime(updatemeta, false); // not overcheck - if(0 <= mtime.tv_sec){ - SetMCtimeHasLock(mtime, (ctime.tv_sec < 0 ? mtime : ctime)); - } - if(0 <= atime.tv_sec){ - SetAtimeHasLock(atime); - } + + timestamps.SetAll(ctime, atime, mtime); // set all timespecs to internal data if(pending_status_t::NO_UPDATE_PENDING == pending_status && (IsUploading() || pagelist.IsModified())){ pending_status = pending_status_t::UPDATE_META_PENDING; diff --git a/src/fdcache_entity.h b/src/fdcache_entity.h index 42db604..ba03ee8 100644 --- a/src/fdcache_entity.h +++ b/src/fdcache_entity.h @@ -32,6 +32,7 @@ #include "fdcache_untreated.h" #include "metaheader.h" #include "s3fs_util.h" +#include "filetimes.h" //---------------------------------------------- // Typedef @@ -60,25 +61,24 @@ class FdEntity : public std::enable_shared_from_this static bool streamupload; // whether stream uploading. mutable std::mutex fdent_lock; - std::string path GUARDED_BY(fdent_lock); // object path - int physical_fd GUARDED_BY(fdent_lock); // physical file(cache or temporary file) descriptor - UntreatedParts untreated_list GUARDED_BY(fdent_lock); // list of untreated parts that have been written and not yet uploaded(for streamupload) - fdinfo_map_t pseudo_fd_map GUARDED_BY(fdent_lock); // pseudo file descriptor information map + std::string path GUARDED_BY(fdent_lock); // object path + int physical_fd GUARDED_BY(fdent_lock); // physical file(cache or temporary file) descriptor + UntreatedParts untreated_list GUARDED_BY(fdent_lock); // list of untreated parts that have been written and not yet uploaded(for streamupload) + fdinfo_map_t pseudo_fd_map GUARDED_BY(fdent_lock); // pseudo file descriptor information map std::unique_ptr pfile GUARDED_BY(fdent_lock) = {nullptr, &s3fs_fclose}; // file pointer(tmp file or cache file) - ino_t inode GUARDED_BY(fdent_lock); // inode number for cache file - headers_t orgmeta GUARDED_BY(fdent_lock); // original headers at opening - off_t size_orgmeta GUARDED_BY(fdent_lock); // original file size in original headers + ino_t inode GUARDED_BY(fdent_lock); // inode number for cache file + headers_t orgmeta GUARDED_BY(fdent_lock); // original headers at opening + off_t size_orgmeta GUARDED_BY(fdent_lock); // original file size in original headers - mutable std::mutex fdent_data_lock ACQUIRED_AFTER(fdent_lock);// protects the following members - PageList pagelist GUARDED_BY(fdent_data_lock); - std::string cachepath GUARDED_BY(fdent_data_lock); // local cache file path - // (if this is empty, does not load/save pagelist.) - std::string mirrorpath GUARDED_BY(fdent_data_lock); // mirror file path to local cache file path - pending_status_t pending_status GUARDED_BY(fdent_data_lock); // status for new file creation and meta update - struct timespec holding_mtime GUARDED_BY(fdent_data_lock); // if mtime is updated while the file is open, it is set time_t value - - mutable std::mutex ro_path_lock; // for only the ro_path variable - std::string ro_path GUARDED_BY(ro_path_lock); // holds the same value as "path". this is used as a backup(read-only variable) by special functions only. + mutable std::mutex fdent_data_lock ACQUIRED_AFTER(fdent_lock); // protects the following members + PageList pagelist GUARDED_BY(fdent_data_lock); + std::string cachepath GUARDED_BY(fdent_data_lock); // local cache file path + // (if this is empty, does not load/save pagelist.) + std::string mirrorpath GUARDED_BY(fdent_data_lock); // mirror file path to local cache file path + pending_status_t pending_status GUARDED_BY(fdent_data_lock); // status for new file creation and meta update + FileTimes timestamps GUARDED_BY(fdent_data_lock); // file timestamps(atime/ctime/mtime) + mutable std::mutex ro_path_lock; // for only the ro_path variable + std::string ro_path GUARDED_BY(ro_path_lock); // holds the same value as "path". this is used as a backup(read-only variable) by special functions only. private: static int FillFile(int fd, unsigned char byte, off_t size, off_t start); @@ -90,6 +90,10 @@ class FdEntity : public std::enable_shared_from_this int NoCacheLoadAndPost(PseudoFdInfo* pseudo_obj, off_t start = 0, off_t size = 0) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); // size=0 means loading to end PseudoFdInfo* CheckPseudoFdFlags(int fd, bool writable) REQUIRES(FdEntity::fdent_lock); bool IsUploading() REQUIRES(FdEntity::fdent_lock); + int SetCtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); + int SetAtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); + int SetMtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); + int SetFileTimesHasLock(const FileTimes& ts_times) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); bool SetAllStatus(bool is_loaded) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); bool SetAllStatusUnloaded() REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock) { return SetAllStatus(false); } int PreMultipartUploadRequest(PseudoFdInfo* pseudo_obj) REQUIRES(FdEntity::fdent_lock, fdent_data_lock); @@ -143,7 +147,8 @@ class FdEntity : public std::enable_shared_from_this const std::lock_guard ro_lock(ro_path_lock); return ro_path; } - int Open(const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags); + int Open(const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags); + bool LoadAll(int fd, off_t* size = nullptr, bool force_load = false); int Dup(int fd) { const std::lock_guard lock(fdent_lock); @@ -182,28 +187,23 @@ class FdEntity : public std::enable_shared_from_this return GetStatsHasLock(st); } bool GetStatsHasLock(struct stat& st) const REQUIRES(FdEntity::fdent_lock); + int SetCtime(struct timespec time) { - const std::lock_guard lock(fdent_lock); - return SetCtimeHasLock(time); + const std::lock_guard lock(fdent_lock); + const std::lock_guard lock2(fdent_data_lock); + return SetCtimeHasLock(time); } - int SetCtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock); int SetAtime(struct timespec time) { - const std::lock_guard lock(fdent_lock); - return SetAtimeHasLock(time); + const std::lock_guard lock(fdent_lock); + const std::lock_guard lock2(fdent_data_lock); + return SetAtimeHasLock(time); } - int SetAtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock); - int SetMCtime(struct timespec mtime, struct timespec ctime) { - const std::lock_guard lock(fdent_lock); - const std::lock_guard lock2(fdent_data_lock); - return SetMCtimeHasLock(mtime, ctime); + int SetMtime(struct timespec time) { + const std::lock_guard lock(fdent_lock); + const std::lock_guard lock2(fdent_data_lock); + return SetMtimeHasLock(time); } - int SetMCtimeHasLock(struct timespec mtime, struct timespec ctime) REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); - bool UpdateCtime(); - bool UpdateAtime(); - bool UpdateMtime(bool clear_holding_mtime = false); - bool UpdateMCtime(); - bool SetHoldingMtime(struct timespec mtime); - bool ClearHoldingMtime() REQUIRES(FdEntity::fdent_lock, FdEntity::fdent_data_lock); + bool GetSize(off_t& size) const; bool GetXattr(std::string& xattr) const; bool SetXattr(const std::string& xattr); diff --git a/src/filetimes.cpp b/src/filetimes.cpp new file mode 100644 index 0000000..6183c76 --- /dev/null +++ b/src/filetimes.cpp @@ -0,0 +1,303 @@ +/* + * s3fs - FUSE-based file system backed by Amazon S3 + * + * Copyright(C) 2007 Takeshi Nakatani + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "filetimes.h" +#include "s3fs_logger.h" +#include "string_util.h" + +//------------------------------------------------------------------- +// Utility functions +//------------------------------------------------------------------- +// +// result: -1 ts1 < ts2 +// 0 ts1 == ts2 +// 1 ts1 > ts2 +// +bool valid_timespec(const struct timespec& ts) +{ + if(0 > ts.tv_sec || UTIME_OMIT == ts.tv_nsec || UTIME_NOW == ts.tv_nsec){ + return false; + } + return true; +} + +// +// result: -1 ts1 < ts2 +// 0 ts1 == ts2 +// 1 ts1 > ts2 +// +constexpr int compare_timespec(const struct timespec& ts1, const struct timespec& ts2) +{ + if(ts1.tv_sec < ts2.tv_sec){ + return -1; + }else if(ts1.tv_sec > ts2.tv_sec){ + return 1; + }else{ + if(ts1.tv_nsec < ts2.tv_nsec){ + return -1; + }else if(ts1.tv_nsec > ts2.tv_nsec){ + return 1; + } + } + return 0; +} + +// +// result: -1 st < ts +// 0 st == ts +// 1 st > ts +// +int compare_timespec(const struct stat& st, stat_time_type type, const struct timespec& ts) +{ + struct timespec st_ts; + set_stat_to_timespec(st, type, st_ts); + + return compare_timespec(st_ts, ts); +} + +void set_timespec_to_stat(struct stat& st, stat_time_type type, const struct timespec& ts) +{ + if(stat_time_type::ATIME == type){ + #ifdef __APPLE__ + st.st_atime = ts.tv_sec; + st.st_atimespec.tv_nsec = ts.tv_nsec; + #else + st.st_atim.tv_sec = ts.tv_sec; + st.st_atim.tv_nsec = ts.tv_nsec; + #endif + }else if(stat_time_type::MTIME == type){ + #ifdef __APPLE__ + st.st_mtime = ts.tv_sec; + st.st_mtimespec.tv_nsec = ts.tv_nsec; + #else + st.st_mtim.tv_sec = ts.tv_sec; + st.st_mtim.tv_nsec = ts.tv_nsec; + #endif + }else if(stat_time_type::CTIME == type){ + #ifdef __APPLE__ + st.st_ctime = ts.tv_sec; + st.st_ctimespec.tv_nsec = ts.tv_nsec; + #else + st.st_ctim.tv_sec = ts.tv_sec; + st.st_ctim.tv_nsec = ts.tv_nsec; + #endif + }else{ + S3FS_PRN_ERR("unknown type(%d), so skip to set value.", static_cast(type)); + } +} + +struct timespec* set_stat_to_timespec(const struct stat& st, stat_time_type type, struct timespec& ts) +{ + if(stat_time_type::ATIME == type){ + #ifdef __APPLE__ + ts.tv_sec = st.st_atime; + ts.tv_nsec = st.st_atimespec.tv_nsec; + #else + ts = st.st_atim; + #endif + }else if(stat_time_type::MTIME == type){ + #ifdef __APPLE__ + ts.tv_sec = st.st_mtime; + ts.tv_nsec = st.st_mtimespec.tv_nsec; + #else + ts = st.st_mtim; + #endif + }else if(stat_time_type::CTIME == type){ + #ifdef __APPLE__ + ts.tv_sec = st.st_ctime; + ts.tv_nsec = st.st_ctimespec.tv_nsec; + #else + ts = st.st_ctim; + #endif + }else{ + S3FS_PRN_ERR("unknown type(%d), so use 0 as timespec.", static_cast(type)); + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + return &ts; +} + +std::string str_stat_time(const struct stat& st, stat_time_type type) +{ + struct timespec ts; + return str(*set_stat_to_timespec(st, type, ts)); +} + +struct timespec* s3fs_realtime(struct timespec& ts) +{ + if(-1 == clock_gettime(static_cast(CLOCK_REALTIME), &ts)){ + S3FS_PRN_WARN("failed to clock_gettime by errno(%d)", errno); + ts.tv_sec = time(nullptr); + ts.tv_nsec = 0; + } + return &ts; +} + +std::string s3fs_str_realtime() +{ + struct timespec ts; + return str(*s3fs_realtime(ts)); +} + +//------------------------------------------------------------------- +// FileTimes Class +//------------------------------------------------------------------- +void FileTimes::Clear() +{ + ClearCTime(); + ClearATime(); + ClearMTime(); +} + +void FileTimes::Clear(stat_time_type type) +{ + if(stat_time_type::CTIME == type){ + ft_ctime = {0, UTIME_OMIT}; + }else if(stat_time_type::ATIME == type){ + ft_atime = {0, UTIME_OMIT}; + }else{ // stat_time_type::MTIME + ft_mtime = {0, UTIME_OMIT}; + } +} + +const struct timespec& FileTimes::GetTime(stat_time_type type) const +{ + if(stat_time_type::CTIME == type){ + return ft_ctime; + }else if(stat_time_type::ATIME == type){ + return ft_atime; + }else{ // stat_time_type::MTIME + return ft_mtime; + } +} + +void FileTimes::GetTime(stat_time_type type, struct timespec& time) const +{ + if(stat_time_type::CTIME == type){ + time = ft_ctime; + }else if(stat_time_type::ATIME == type){ + time = ft_atime; + }else{ // stat_time_type::MTIME + time = ft_mtime; + } +} + +void FileTimes::RefrectFileTimes(struct stat& st) const +{ + if(!IsOmitCTime()){ + set_timespec_to_stat(st, stat_time_type::CTIME, ft_ctime); + } + if(!IsOmitATime()){ + set_timespec_to_stat(st, stat_time_type::ATIME, ft_atime); + } + if(!IsOmitMTime()){ + set_timespec_to_stat(st, stat_time_type::MTIME, ft_mtime); + } +} + +void FileTimes::SetTime(stat_time_type type, struct timespec time) +{ + if(UTIME_NOW == time.tv_nsec){ + s3fs_realtime(time); + } + if(stat_time_type::CTIME == type){ + ft_ctime = time; + }else if(stat_time_type::ATIME == type){ + ft_atime = time; + }else{ // stat_time_type::MTIME + ft_mtime = time; + } +} + +void FileTimes::SetAllNow() +{ + struct timespec time; + s3fs_realtime(time); + SetAll(time, time, time); +} + +void FileTimes::SetAll(const struct stat& stbuf, bool no_omit) +{ + struct timespec ts_ctime; + struct timespec ts_atime; + struct timespec ts_mtime; + set_stat_to_timespec(stbuf, stat_time_type::CTIME, ts_ctime); + set_stat_to_timespec(stbuf, stat_time_type::ATIME, ts_atime); + set_stat_to_timespec(stbuf, stat_time_type::MTIME, ts_mtime); + + SetAll(ts_ctime, ts_atime, ts_mtime, no_omit); +} + +void FileTimes::SetAll(struct timespec ts_ctime, struct timespec ts_atime, struct timespec ts_mtime, bool no_omit) +{ + struct timespec ts_now_time; + s3fs_realtime(ts_now_time); + + if(UTIME_NOW == ts_ctime.tv_nsec){ + SetCTime(ts_now_time); + }else if(!no_omit || UTIME_OMIT != ts_ctime.tv_nsec){ + SetCTime(ts_ctime); + } + + if(UTIME_NOW == ts_atime.tv_nsec){ + SetATime(ts_now_time); + }else if(!no_omit || UTIME_OMIT != ts_atime.tv_nsec){ + SetATime(ts_atime); + } + + if(UTIME_NOW == ts_mtime.tv_nsec){ + SetMTime(ts_now_time); + }else if(!no_omit || UTIME_OMIT != ts_mtime.tv_nsec){ + SetMTime(ts_mtime); + } +} + +void FileTimes::SetAll(const FileTimes& other, bool no_omit) +{ + if(!no_omit || !other.IsOmitCTime()){ + SetCTime(other.ctime()); + } + if(!no_omit || !other.IsOmitATime()){ + SetATime(other.atime()); + } + if(!no_omit || !other.IsOmitMTime()){ + SetMTime(other.mtime()); + } +} + +bool FileTimes::IsOmit(stat_time_type type) const +{ + if(stat_time_type::CTIME == type){ + return (UTIME_OMIT == ft_ctime.tv_nsec); + }else if(stat_time_type::ATIME == type){ + return (UTIME_OMIT == ft_atime.tv_nsec); + }else{ // stat_time_type::MTIME + return (UTIME_OMIT == ft_mtime.tv_nsec); + } +} + +/* +* Local variables: +* tab-width: 4 +* c-basic-offset: 4 +* End: +* vim600: expandtab sw=4 ts=4 fdm=marker +* vim<600: expandtab sw=4 ts=4 +*/ diff --git a/src/filetimes.h b/src/filetimes.h new file mode 100644 index 0000000..03434d8 --- /dev/null +++ b/src/filetimes.h @@ -0,0 +1,120 @@ +/* + * s3fs - FUSE-based file system backed by Amazon S3 + * + * Copyright(C) 2007 Randy Rizun + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef S3FS_FILETIMES_H_ +#define S3FS_FILETIMES_H_ + +#include +#include +#include + +//------------------------------------------------------------------- +// Utility for stat time type +//------------------------------------------------------------------- +enum class stat_time_type : uint8_t { + ATIME, + MTIME, + CTIME +}; + +//------------------------------------------------------------------- +// Utility Functions for timespecs +//------------------------------------------------------------------- +bool valid_timespec(const struct timespec& ts); +constexpr int compare_timespec(const struct timespec& ts1, const struct timespec& ts2); +int compare_timespec(const struct stat& st, stat_time_type type, const struct timespec& ts); +void set_timespec_to_stat(struct stat& st, stat_time_type type, const struct timespec& ts); +struct timespec* set_stat_to_timespec(const struct stat& st, stat_time_type type, struct timespec& ts); +std::string str_stat_time(const struct stat& st, stat_time_type type); +struct timespec* s3fs_realtime(struct timespec& ts); +std::string s3fs_str_realtime(); + +//------------------------------------------------------------------- +// FileTimes Class +//------------------------------------------------------------------- +// [NOTE] +// In this class, UTIME_OMIT is set when initializing or clearing +// internal data. +// Also, if UTIME_NOW is specified, the value will be corrected to +// the current time and maintained. +// +class FileTimes +{ + private: + struct timespec ft_ctime; // Change time + struct timespec ft_atime; // Access time + struct timespec ft_mtime; // Modification time + + private: + void Clear(stat_time_type type); + + const struct timespec& GetTime(stat_time_type type) const; + void GetTime(stat_time_type type, struct timespec& time) const; + + void SetTime(stat_time_type type, struct timespec time); + + bool IsOmit(stat_time_type type) const; + + public: + explicit FileTimes() : ft_ctime{0, UTIME_OMIT}, ft_atime{0, UTIME_OMIT}, ft_mtime{0, UTIME_OMIT} {} + + // Clear + void Clear(); + void ClearCTime() { Clear(stat_time_type::CTIME); } + void ClearATime() { Clear(stat_time_type::ATIME); } + void ClearMTime() { Clear(stat_time_type::MTIME); } + + // Get value + const struct timespec& ctime() const { return GetTime(stat_time_type::CTIME); } + const struct timespec& atime() const { return GetTime(stat_time_type::ATIME); } + const struct timespec& mtime() const { return GetTime(stat_time_type::MTIME); } + + void GetCTime(struct timespec& time) const { GetTime(stat_time_type::CTIME, time); } + void GetATime(struct timespec& time) const { GetTime(stat_time_type::ATIME, time); } + void GetMTime(struct timespec& time) const { GetTime(stat_time_type::MTIME, time); } + + void RefrectFileTimes(struct stat& st) const; + + // Set value + void SetCTime(struct timespec time) { SetTime(stat_time_type::CTIME, time); } + void SetATime(struct timespec time) { SetTime(stat_time_type::ATIME, time); } + void SetMTime(struct timespec time) { SetTime(stat_time_type::MTIME, time); } + + void SetAllNow(); + void SetAll(const struct stat& stbuf, bool no_omit = true); + void SetAll(struct timespec ts_ctime, struct timespec ts_atime, struct timespec ts_mtime, bool no_omit = true); + void SetAll(const FileTimes& other, bool no_omit = true); + + // Check + bool IsOmitCTime() const { return IsOmit(stat_time_type::CTIME); } + bool IsOmitATime() const { return IsOmit(stat_time_type::ATIME); } + bool IsOmitMTime() const { return IsOmit(stat_time_type::MTIME); } +}; + +#endif // S3FS_FILETIMES_H_ + +/* +* Local variables: +* tab-width: 4 +* c-basic-offset: 4 +* End: +* vim600: expandtab sw=4 ts=4 fdm=marker +* vim<600: expandtab sw=4 ts=4 +*/ diff --git a/src/metaheader.cpp b/src/metaheader.cpp index 80fef6e..f36189c 100644 --- a/src/metaheader.cpp +++ b/src/metaheader.cpp @@ -27,8 +27,10 @@ #include "metaheader.h" #include "string_util.h" #include "s3fs_util.h" +#include "filetimes.h" -static constexpr struct timespec DEFAULT_TIMESPEC = {-1, 0}; +static constexpr struct timespec ERROR_TIMESPEC = {-1, 0}; +static constexpr struct timespec OMIT_TIMESPEC = {0, UTIME_OMIT}; //------------------------------------------------------------------- // Utility functions for convert @@ -59,52 +61,53 @@ static struct timespec get_time(const headers_t& meta, const char *header) { headers_t::const_iterator iter; if(meta.cend() == (iter = meta.find(header))){ - return DEFAULT_TIMESPEC; + return ERROR_TIMESPEC; } return cvt_string_to_time((*iter).second.c_str()); } struct timespec get_mtime(const headers_t& meta, bool overcheck) { - struct timespec t = get_time(meta, "x-amz-meta-mtime"); - if(0 < t.tv_sec){ - return t; + struct timespec mtime = get_time(meta, "x-amz-meta-mtime"); + if(0 <= mtime.tv_sec && UTIME_OMIT != mtime.tv_nsec){ + return mtime; } - t = get_time(meta, "x-amz-meta-goog-reserved-file-mtime"); - if(0 < t.tv_sec){ - return t; + + mtime = get_time(meta, "x-amz-meta-goog-reserved-file-mtime"); + if(0 <= mtime.tv_sec && UTIME_OMIT != mtime.tv_nsec){ + return mtime; } if(overcheck){ - struct timespec ts = {get_lastmodified(meta), 0}; - return ts; + mtime = {get_lastmodified(meta), 0}; + return mtime; } - return DEFAULT_TIMESPEC; + return OMIT_TIMESPEC; } struct timespec get_ctime(const headers_t& meta, bool overcheck) { - struct timespec t = get_time(meta, "x-amz-meta-ctime"); - if(0 < t.tv_sec){ - return t; + struct timespec ctime = get_time(meta, "x-amz-meta-ctime"); + if(0 <= ctime.tv_sec && UTIME_OMIT != ctime.tv_nsec){ + return ctime; } if(overcheck){ - struct timespec ts = {get_lastmodified(meta), 0}; - return ts; + ctime = {get_lastmodified(meta), 0}; + return ctime; } - return DEFAULT_TIMESPEC; + return OMIT_TIMESPEC; } struct timespec get_atime(const headers_t& meta, bool overcheck) { - struct timespec t = get_time(meta, "x-amz-meta-atime"); - if(0 < t.tv_sec){ - return t; + struct timespec atime = get_time(meta, "x-amz-meta-atime"); + if(0 <= atime.tv_sec && UTIME_OMIT != atime.tv_nsec){ + return atime; } if(overcheck){ - struct timespec ts = {get_lastmodified(meta), 0}; - return ts; + atime = {get_lastmodified(meta), 0}; + return atime; } - return DEFAULT_TIMESPEC; + return OMIT_TIMESPEC; } off_t get_size(const char *s) @@ -447,39 +450,24 @@ bool convert_header_to_stat(const std::string& strpath, const headers_t& meta, s // mtime struct timespec mtime = get_mtime(meta); - if(stbuf.st_mtime < 0){ - stbuf.st_mtime = 0L; - }else{ - if(mtime.tv_sec < 0){ - mtime.tv_sec = 0; - mtime.tv_nsec = 0; - } - set_timespec_to_stat(stbuf, stat_time_type::MTIME, mtime); + if(mtime.tv_sec < 0){ + mtime = {0, 0}; } + set_timespec_to_stat(stbuf, stat_time_type::MTIME, mtime); // ctime struct timespec ctime = get_ctime(meta); - if(stbuf.st_ctime < 0){ - stbuf.st_ctime = 0L; - }else{ - if(ctime.tv_sec < 0){ - ctime.tv_sec = 0; - ctime.tv_nsec = 0; - } - set_timespec_to_stat(stbuf, stat_time_type::CTIME, ctime); + if(ctime.tv_sec < 0){ + ctime = {0, 0}; } + set_timespec_to_stat(stbuf, stat_time_type::CTIME, ctime); // atime struct timespec atime = get_atime(meta); - if(stbuf.st_atime < 0){ - stbuf.st_atime = 0L; - }else{ - if(atime.tv_sec < 0){ - atime.tv_sec = 0; - atime.tv_nsec = 0; - } - set_timespec_to_stat(stbuf, stat_time_type::ATIME, atime); + if(atime.tv_sec < 0){ + atime = {0, 0}; } + set_timespec_to_stat(stbuf, stat_time_type::ATIME, atime); // size if(S_ISDIR(stbuf.st_mode)){ diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 4755a07..2461b73 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -825,6 +825,7 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char* struct stat stobj; FdEntity* ent; headers_t meta; + FileTimes ts_times; // default: all timespecs are UTIME_OMIT S3FS_PRN_INFO2("[path=%s]", path); @@ -833,15 +834,12 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char* } // open - struct timespec st_mctime; - if(!S_ISREG(stobj.st_mode) && !S_ISLNK(stobj.st_mode)){ - st_mctime = S3FS_OMIT_TS; - }else{ - set_stat_to_timespec(stobj, stat_time_type::MTIME, st_mctime); + if(S_ISREG(stobj.st_mode) || S_ISLNK(stobj.st_mode)){ + ts_times.SetAll(stobj); } bool force_tmpfile = S_ISREG(stobj.st_mode) ? false : true; - if(nullptr == (ent = autoent.Open(path, &meta, stobj.st_size, st_mctime, flags, force_tmpfile, true, false))){ + if(nullptr == (ent = autoent.Open(path, &meta, stobj.st_size, ts_times, flags, force_tmpfile, true, false))){ S3FS_PRN_ERR("Could not open file. errno(%d)", errno); return -EIO; } @@ -1173,8 +1171,9 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi AutoFdEntity autoent; FdEntity* ent; - int error = 0; - if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, S3FS_OMIT_TS, fi->flags, false, true, false, &error))){ + int error = 0; + FileTimes ts_times; + if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, ts_times, fi->flags, false, true, false, &error))){ // remove from cache StatCache::getStatCacheData()->DelStat(strpath); return error; @@ -1447,10 +1446,12 @@ static int s3fs_symlink(const char* _from, const char* _to) { // scope for AutoFdEntity AutoFdEntity autoent; FdEntity* ent; - if(nullptr == (ent = autoent.Open(strTo.c_str(), &headers, 0, S3FS_OMIT_TS, O_RDWR, true, true, false))){ + FileTimes ts_times; // Default: all time values are set UTIME_OMIT + if(nullptr == (ent = autoent.Open(strTo.c_str(), &headers, 0, ts_times, O_RDWR, true, true, false))){ S3FS_PRN_ERR("could not open tmpfile(errno=%d)", errno); return -errno; } + // write(without space words) auto from_size = static_cast(strFrom.length()); ssize_t ressize; @@ -1543,38 +1544,28 @@ static int rename_object(const char* from, const char* to, bool update_ctime) struct timespec mtime = get_mtime(meta); struct timespec ctime = get_ctime(meta); struct timespec atime = get_atime(meta); - if(mtime.tv_sec < 0){ - mtime.tv_sec = 0L; - mtime.tv_nsec = 0L; - } - if(ctime.tv_sec < 0){ - ctime.tv_sec = 0L; - ctime.tv_nsec = 0L; - } - if(atime.tv_sec < 0){ - atime.tv_sec = 0L; - atime.tv_nsec = 0L; - } if(FdManager::IsCacheDir()){ // create cache file if be needed // // [NOTE] - // Do not specify "S3FS_OMIT_TS" for mctime parameter. + // Do not specify "UTIME_OMIT" for ts_time parameter. // This is because if the cache file does not exist, the pagelist for it // will be recreated, but the entire file area indicated by this pagelist // will be in the "modified" state. // This does not affect the rename process, but the cache information in // the "modified" state remains, making it impossible to read the file correctly. // - ent = autoent.Open(from, &meta, stbuf.st_size, mtime, O_RDONLY, false, true, false); + FileTimes ts_times; + ts_times.SetAll(ctime, atime, mtime, false); + + ent = autoent.Open(from, &meta, stbuf.st_size, ts_times, O_RDONLY, false, true, false); } if(ent){ - if(0 != (result = ent->SetMCtime(mtime, ctime))){ - S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", from, result); + if(0 != (result = ent->SetMtime(mtime)) || 0 != (result = ent->SetCtime(ctime)) || 0 != (result = ent->SetAtime(atime))){ + S3FS_PRN_ERR("could not set mtime/ctime/atime to file(%s): result=%d", from, result); return result; } - ent->SetAtime(atime); } } } @@ -1592,6 +1583,7 @@ static int rename_object(const char* from, const char* to, bool update_ctime) // Add only stat structure to stat cache std::string strTo = to; + stbuf = {}; if(convert_header_to_stat(strTo, meta, stbuf)){ objtype_t ObjType = derive_object_type(strTo, meta, objtype_t::FILE); @@ -2691,16 +2683,12 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) AutoFdEntity autoent; FdEntity* ent; bool need_put_header = true; - bool keep_mtime = false; if(nullptr != (ent = autoent.OpenExistFdEntity(curpath.c_str()))){ if(ent->MergeOrgMeta(updatemeta)){ // meta is changed, but now uploading. // then the meta is pending and accumulated to be put after the upload is complete. S3FS_PRN_INFO("meta pending until upload is complete"); need_put_header = false; - if(!ent->SetHoldingMtime(mtime)){ - return -EIO; - } // get current entity's meta headers if(!ent->GetOrgMeta(meta)){ @@ -2732,13 +2720,6 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) }else{ S3FS_PRN_INFO("meta is not pending, but need to keep current mtime."); - - // [NOTE] - // Depending on the order in which write/flush and utimens are called, - // the mtime updated here may be overwritten at the time of flush. - // To avoid that, set a special flag. - // - keep_mtime = true; } } if(need_put_header){ @@ -2761,12 +2742,6 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) S3FS_PRN_ERR("failed convert headers to stat[path=%s]. So remove stat cache.", curpath.c_str()); StatCache::getStatCacheData()->DelStat(curpath); } - - if(keep_mtime){ - if(!ent->SetHoldingMtime(mtime)){ - return -EIO; - } - } } } @@ -2860,15 +2835,9 @@ static int s3fs_utimens_nocopy(const char* _path, const struct timespec ts[2]) return result; } - // set mtime/ctime - if(0 != (result = ent->SetMCtime(mtime, ctime))){ - S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", curpath.c_str(), result); - return result; - } - - // set atime - if(0 != (result = ent->SetAtime(atime))){ - S3FS_PRN_ERR("could not set atime to file(%s): result=%d", curpath.c_str(), result); + // set mtime/ctime/atime + if(0 != (result = ent->SetMtime(mtime)) || 0 != (result = ent->SetCtime(ctime)) || 0 != (result = ent->SetAtime(atime))){ + S3FS_PRN_ERR("could not set mtime/ctime/atime to file(%s): result=%d", curpath.c_str(), result); return result; } @@ -2936,12 +2905,9 @@ static int s3fs_truncate(const char* _path, off_t size) ignore_modify = true; } - if(nullptr == (ent = autoent.Open(path, &meta, size, S3FS_OMIT_TS, O_RDWR, false, true, ignore_modify))){ - S3FS_PRN_ERR("could not open file(%s): errno=%d", path, errno); - return -EIO; - } - if(!ent->UpdateCtime()){ - S3FS_PRN_ERR("could not update ctime file(%s)", path); + FileTimes ts_times; // Default: all time values are set UTIME_OMIT + if(nullptr == (ent = autoent.Open(path, &meta, size, ts_times, O_RDWR, false, true, ignore_modify))){ + S3FS_PRN_ERR("could not open file(%s): errno=%d(ent is null(%p))", path, errno, ent); // [NOTE] read ent to avoid errors with cppcheck etc return -EIO; } @@ -2974,7 +2940,8 @@ static int s3fs_truncate(const char* _path, off_t size) meta["x-amz-meta-uid"] = std::to_string(pcxt->uid); meta["x-amz-meta-gid"] = std::to_string(pcxt->gid); - if(nullptr == (ent = autoent.Open(path, &meta, size, S3FS_OMIT_TS, O_RDWR, true, true, false))){ + FileTimes ts_times; // Default: all time values are set UTIME_OMIT + if(nullptr == (ent = autoent.Open(path, &meta, size, ts_times, O_RDWR, true, true, false))){ S3FS_PRN_ERR("could not open file(%s): errno=%d", path, errno); return -EIO; } @@ -3052,28 +3019,23 @@ static int s3fs_open(const char* _path, struct fuse_file_info* fi) } } } - if(!S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)){ - st.st_mtime = -1; - } if(0 != (result = get_object_attribute(path, nullptr, &meta, true, nullptr, true))){ // no truncate cache - return result; + return result; } - struct timespec st_mctime; - set_stat_to_timespec(st, stat_time_type::MTIME, st_mctime); - - if(nullptr == (ent = autoent.Open(path, &meta, st.st_size, st_mctime, fi->flags, false, true, false))){ + FileTimes ts_times; // Default: all time values are set UTIME_OMIT + ts_times.SetAll(st); + if(nullptr == (ent = autoent.Open(path, &meta, st.st_size, ts_times, fi->flags, false, true, false))){ // remove stat cache StatCache::getStatCacheData()->DelStat(path); return -EIO; } if (needs_flush){ - struct timespec ts; - s3fs_realtime(ts); - - if(0 != (result = ent->SetMCtime(ts, ts))){ + struct timespec ts_now; + s3fs_realtime(ts_now); + if(0 != (result = ent->SetCtime(ts_now)) || 0 != (result = ent->SetMtime(ts_now))){ S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", path, result); return result; } @@ -3137,6 +3099,14 @@ static int s3fs_write(const char* _path, const char* buf, size_t size, off_t off S3FS_PRN_WARN("failed to write file(%s). result=%zd", path, res); } + int result; + struct timespec ts_now; + s3fs_realtime(ts_now); + if(0 != (result = ent->SetCtime(ts_now)) || 0 != (result = ent->SetMtime(ts_now))){ + S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", path, result); + return result; + } + if(max_dirty_data != -1 && ent->BytesModified() >= max_dirty_data){ int flushres; if(0 != (flushres = ent->RowFlush(static_cast(fi->fh), path, true))){ @@ -3208,15 +3178,6 @@ static int s3fs_flush(const char* _path, struct fuse_file_info* fi) if(nullptr != (ent = autoent.GetExistFdEntity(path, static_cast(fi->fh)))){ bool is_new_file = ent->IsDirtyNewFile(); - if(!ent->UpdateMtime()){ // clear the flag not to update mtime. - S3FS_PRN_ERR("could not update mtime file(%s)", path); - return -EIO; - } - if(!ent->UpdateCtime()){ - S3FS_PRN_ERR("could not update ctime file(%s)", path); - return -EIO; - } - if(0 == (result = ent->Flush(static_cast(fi->fh), false))){ // [NOTE] // If there is any meta information that needs to be updated, update it now. @@ -3269,16 +3230,6 @@ static int s3fs_fsync(const char* _path, int datasync, struct fuse_file_info* fi if(nullptr != (ent = autoent.GetExistFdEntity(path, static_cast(fi->fh)))){ bool is_new_file = ent->IsDirtyNewFile(); - if(0 == datasync){ - if(!ent->UpdateMtime()){ - S3FS_PRN_ERR("could not update mtime file(%s)", path); - return -EIO; - } - if(!ent->UpdateCtime()){ - S3FS_PRN_ERR("could not update ctime file(%s)", path); - return -EIO; - } - } result = ent->Flush(static_cast(fi->fh), false); if(0 != datasync){ diff --git a/src/s3fs_util.cpp b/src/s3fs_util.cpp index 541f1bd..397a597 100644 --- a/src/s3fs_util.cpp +++ b/src/s3fs_util.cpp @@ -413,124 +413,6 @@ void print_launch_message(int argc, char** argv) S3FS_PRN_LAUNCH_INFO("%s", message.c_str()); } -// -// result: -1 ts1 < ts2 -// 0 ts1 == ts2 -// 1 ts1 > ts2 -// -constexpr int compare_timespec(const struct timespec& ts1, const struct timespec& ts2) -{ - if(ts1.tv_sec < ts2.tv_sec){ - return -1; - }else if(ts1.tv_sec > ts2.tv_sec){ - return 1; - }else{ - if(ts1.tv_nsec < ts2.tv_nsec){ - return -1; - }else if(ts1.tv_nsec > ts2.tv_nsec){ - return 1; - } - } - return 0; -} - -// -// result: -1 st < ts -// 0 st == ts -// 1 st > ts -// -int compare_timespec(const struct stat& st, stat_time_type type, const struct timespec& ts) -{ - struct timespec st_ts; - set_stat_to_timespec(st, type, st_ts); - - return compare_timespec(st_ts, ts); -} - -void set_timespec_to_stat(struct stat& st, stat_time_type type, const struct timespec& ts) -{ - if(stat_time_type::ATIME == type){ - #ifdef __APPLE__ - st.st_atime = ts.tv_sec; - st.st_atimespec.tv_nsec = ts.tv_nsec; - #else - st.st_atim.tv_sec = ts.tv_sec; - st.st_atim.tv_nsec = ts.tv_nsec; - #endif - }else if(stat_time_type::MTIME == type){ - #ifdef __APPLE__ - st.st_mtime = ts.tv_sec; - st.st_mtimespec.tv_nsec = ts.tv_nsec; - #else - st.st_mtim.tv_sec = ts.tv_sec; - st.st_mtim.tv_nsec = ts.tv_nsec; - #endif - }else if(stat_time_type::CTIME == type){ - #ifdef __APPLE__ - st.st_ctime = ts.tv_sec; - st.st_ctimespec.tv_nsec = ts.tv_nsec; - #else - st.st_ctim.tv_sec = ts.tv_sec; - st.st_ctim.tv_nsec = ts.tv_nsec; - #endif - }else{ - S3FS_PRN_ERR("unknown type(%d), so skip to set value.", static_cast(type)); - } -} - -struct timespec* set_stat_to_timespec(const struct stat& st, stat_time_type type, struct timespec& ts) -{ - if(stat_time_type::ATIME == type){ - #ifdef __APPLE__ - ts.tv_sec = st.st_atime; - ts.tv_nsec = st.st_atimespec.tv_nsec; - #else - ts = st.st_atim; - #endif - }else if(stat_time_type::MTIME == type){ - #ifdef __APPLE__ - ts.tv_sec = st.st_mtime; - ts.tv_nsec = st.st_mtimespec.tv_nsec; - #else - ts = st.st_mtim; - #endif - }else if(stat_time_type::CTIME == type){ - #ifdef __APPLE__ - ts.tv_sec = st.st_ctime; - ts.tv_nsec = st.st_ctimespec.tv_nsec; - #else - ts = st.st_ctim; - #endif - }else{ - S3FS_PRN_ERR("unknown type(%d), so use 0 as timespec.", static_cast(type)); - ts.tv_sec = 0; - ts.tv_nsec = 0; - } - return &ts; -} - -std::string str_stat_time(const struct stat& st, stat_time_type type) -{ - struct timespec ts; - return str(*set_stat_to_timespec(st, type, ts)); -} - -struct timespec* s3fs_realtime(struct timespec& ts) -{ - if(-1 == clock_gettime(static_cast(CLOCK_REALTIME), &ts)){ - S3FS_PRN_WARN("failed to clock_gettime by errno(%d)", errno); - ts.tv_sec = time(nullptr); - ts.tv_nsec = 0; - } - return &ts; -} - -std::string s3fs_str_realtime() -{ - struct timespec ts; - return str(*s3fs_realtime(ts)); -} - int s3fs_fclose(FILE* fp) { if(fp == nullptr){ diff --git a/src/s3fs_util.h b/src/s3fs_util.h index 8667b3b..6b7f498 100644 --- a/src/s3fs_util.h +++ b/src/s3fs_util.h @@ -59,55 +59,40 @@ bool compare_sysname(const char* target); void print_launch_message(int argc, char** argv); -// -// Utility for nanosecond time(timespec) -// -enum class stat_time_type : uint8_t { - ATIME, - MTIME, - CTIME -}; - //------------------------------------------------------------------- // Utility for nanosecond time(timespec) //------------------------------------------------------------------- -static constexpr struct timespec S3FS_OMIT_TS = {0, UTIME_OMIT}; - -constexpr int compare_timespec(const struct timespec& ts1, const struct timespec& ts2); -int compare_timespec(const struct stat& st, stat_time_type type, const struct timespec& ts); -void set_timespec_to_stat(struct stat& st, stat_time_type type, const struct timespec& ts); -struct timespec* set_stat_to_timespec(const struct stat& st, stat_time_type type, struct timespec& ts); -std::string str_stat_time(const struct stat& st, stat_time_type type); -struct timespec* s3fs_realtime(struct timespec& ts); -std::string s3fs_str_realtime(); - // Wrap fclose since it is illegal to take the address of a stdlib function int s3fs_fclose(FILE* fp); -class scope_guard { -public: - template - explicit scope_guard(Callable&& undo_func) - : func(std::forward(undo_func)) - {} +class scope_guard +{ + public: + template - ~scope_guard() { - if(func != nullptr) { - func(); + explicit scope_guard(Callable&& undo_func) + : func(std::forward(undo_func)) + {} + + ~scope_guard() + { + if(func != nullptr) { + func(); + } } - } - void dismiss() { - func = nullptr; - } + void dismiss() + { + func = nullptr; + } - scope_guard(const scope_guard&) = delete; - scope_guard(scope_guard&& other) = delete; - scope_guard& operator=(const scope_guard&) = delete; - scope_guard& operator=(scope_guard&&) = delete; + scope_guard(const scope_guard&) = delete; + scope_guard(scope_guard&& other) = delete; + scope_guard& operator=(const scope_guard&) = delete; + scope_guard& operator=(scope_guard&&) = delete; -private: - std::function func; + private: + std::function func; }; #endif // S3FS_S3FS_UTIL_H_ diff --git a/src/string_util.cpp b/src/string_util.cpp index 78051c6..7afa991 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -27,24 +27,28 @@ #include #include #include +#include +#include #include "s3fs_logger.h" #include "string_util.h" -//------------------------------------------------------------------- -// Global variables -//------------------------------------------------------------------- - //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- - std::string str(const struct timespec& value) { std::ostringstream s; - s << value.tv_sec; - if(value.tv_nsec != 0){ - s << "." << std::setfill('0') << std::setw(9) << value.tv_nsec; + + if(UTIME_OMIT == value.tv_nsec){ + s << "UTIME_OMIT"; + }else if(UTIME_NOW == value.tv_nsec){ + s << "UTIME_NOW"; + }else{ + s << value.tv_sec; + if(value.tv_nsec != 0){ + s << "." << std::setfill('0') << std::setw(9) << value.tv_nsec; + } } return s.str(); }