Changed file stat times(a/c/mtime) management

This commit is contained in:
Takeshi Nakatani
2025-10-01 15:18:28 +00:00
committed by Andrew Gaul
parent 60fb557f14
commit 37e593aeb0
14 changed files with 675 additions and 523 deletions

View File

@ -53,6 +53,7 @@ s3fs_SOURCES = \
fdcache_fdinfo.cpp \ fdcache_fdinfo.cpp \
fdcache_pseudofd.cpp \ fdcache_pseudofd.cpp \
fdcache_untreated.cpp \ fdcache_untreated.cpp \
filetimes.cpp \
addhead.cpp \ addhead.cpp \
sighandlers.cpp \ sighandlers.cpp \
threadpoolman.cpp \ threadpoolman.cpp \

View File

@ -527,9 +527,9 @@ FdEntity* FdManager::GetFdEntityHasLock(const char* path, int& existfd, bool new
return nullptr; 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<long long>(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<long long>(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]){ if(!path || '\0' == path[0]){
return nullptr; return nullptr;
@ -573,7 +573,7 @@ FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off
} }
// (re)open // (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); S3FS_PRN_ERR("failed to (re)open and create new pseudo fd for path(%s).", path);
return nullptr; return nullptr;
} }
@ -590,7 +590,7 @@ FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off
auto ent = std::make_shared<FdEntity>(path, cache_path.c_str()); auto ent = std::make_shared<FdEntity>(path, cache_path.c_str());
// open // 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); S3FS_PRN_ERR("failed to open and create new pseudo fd for path(%s) errno:%d.", path, fd);
return nullptr; 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); S3FS_PRN_DBG("[path=%s][flags=0x%x]", SAFESTRPTR(path), flags);
// search entity by path, and create pseudo fd // 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){ if(!ent){
// Not found entity // Not found entity
return nullptr; return nullptr;

View File

@ -111,7 +111,7 @@ class FdManager
return GetFdEntityHasLock(path, existfd, newfd); return GetFdEntityHasLock(path, existfd, newfd);
} }
FdEntity* GetFdEntityHasLock(const char* path, int& existfd, bool newfd = true) REQUIRES(FdManager::fd_manager_lock); 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* GetExistFdEntity(const char* path, int existfd = -1);
FdEntity* OpenExistFdEntity(const char* path, int& fd, int flags = O_RDONLY); FdEntity* OpenExistFdEntity(const char* path, int& fd, int flags = O_RDONLY);
void Rename(const std::string &from, const std::string &to); void Rename(const std::string &from, const std::string &to);

View File

@ -78,11 +78,11 @@ FdEntity* AutoFdEntity::Attach(const char* path, int existfd)
return pFdEntity; 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(); 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){ if(error){
*error = pseudo_fd; *error = pseudo_fd;
} }

View File

@ -25,6 +25,7 @@
#include "common.h" #include "common.h"
#include "metaheader.h" #include "metaheader.h"
#include "filetimes.h"
class FdEntity; class FdEntity;
@ -55,7 +56,7 @@ class AutoFdEntity
FdEntity* Attach(const char* path, int existfd); FdEntity* Attach(const char* path, int existfd);
int GetPseudoFd() const { return pseudo_fd; } 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* GetExistFdEntity(const char* path, int existfd = -1);
FdEntity* OpenExistFdEntity(const char* path, int flags = O_RDONLY); FdEntity* OpenExistFdEntity(const char* path, int flags = O_RDONLY);
}; };

View File

@ -119,8 +119,6 @@ FdEntity::FdEntity(const char* tpath, const char* cpath) :
cachepath(SAFESTRPTR(cpath)), pending_status(pending_status_t::NO_UPDATE_PENDING), cachepath(SAFESTRPTR(cpath)), pending_status(pending_status_t::NO_UPDATE_PENDING),
ro_path(SAFESTRPTR(tpath)) ro_path(SAFESTRPTR(tpath))
{ {
holding_mtime.tv_sec = -1;
holding_mtime.tv_nsec = 0;
} }
FdEntity::~FdEntity() FdEntity::~FdEntity()
@ -168,6 +166,8 @@ void FdEntity::Clear()
// set backup(read only) variable // set backup(read only) variable
const std::lock_guard<std::mutex> ro_lock(ro_path_lock); const std::lock_guard<std::mutex> ro_lock(ro_path_lock);
ro_path = path; ro_path = path;
timestamps.Clear();
} }
// [NOTE] // [NOTE]
@ -380,18 +380,18 @@ bool FdEntity::IsUploading()
// If the open is successful, returns pseudo fd. // If the open is successful, returns pseudo fd.
// If it fails, it returns an error code with a negative value. // If it fails, it returns an error code with a negative value.
// //
// ts_mctime argument is a variable for mtime/ctime. // The ts_times argument is a variable for atime/mtime/ctime.
// If you want to disable this variable, specify UTIME_OMIT for // If you want to disable each value, set the tv_nsec of the timespec
// tv_nsec in timespec member(in this case tv_sec member is ignored). // member corresponding to atime/ctime/mtime to UTIME_OMIT. (In this
// This is similar to utimens operation. // case, the tv_sec member will be ignored.)
// You can use "S3FS_OMIT_TS" global variable for UTIME_OMIT. // 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<std::mutex> lock(fdent_lock); const std::lock_guard<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> data_lock(fdent_data_lock); const std::lock_guard<std::mutex> 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<long long>(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<long long>(size), str(ts_times.ctime()).c_str(), str(ts_times.atime()).c_str(), str(ts_times.mtime()).c_str(), flags);
// [NOTE] // [NOTE]
// When the file size is incremental by truncating, it must be keeped // 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 need_save_csf = false; // need to save(reset) cache stat file
bool is_truncate = false; // need to truncate bool is_truncate = false; // need to truncate
int result = 0;
std::unique_ptr<CacheFileStat> pcfstat; std::unique_ptr<CacheFileStat> pcfstat;
@ -447,7 +448,8 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts
// using cache // using cache
struct stat st; struct stat st;
if(stat(cachepath.c_str(), &st) == 0){ 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()); S3FS_PRN_DBG("cache file stale, removing: %s", cachepath.c_str());
if(unlink(cachepath.c_str()) != 0){ if(unlink(cachepath.c_str()) != 0){
return (0 == errno ? -EIO : -errno); 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); S3FS_PRN_ERR("failed to open file(%s). errno(%d)", cachepath.c_str(), errno);
// remove cache stat file if it is existed // remove cache stat file if it is existed
int result;
if(0 != (result = CacheFileStat::DeleteCacheFileStat(path.c_str()))){ if(0 != (result = CacheFileStat::DeleteCacheFileStat(path.c_str()))){
if(-ENOENT != result){ if(-ENOENT != result){
S3FS_PRN_WARN("failed to delete current cache stat file(%s) by errno(%d), but continue...", path.c_str(), 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{ }else{
// [NOTE] // [NOTE]
// The modify flag must not be set when opening a file, // 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. // and the cache file does not exist.
// If mtime is specified for the file and the cache file // If mtime is specified for the file and the cache file
// mtime is older than it, the cache file is removed and // mtime is older than it, the cache file is removed and
// the processing comes here. // 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; is_truncate = true;
} }
} }
@ -570,14 +570,13 @@ int FdEntity::Open(const headers_t* pmeta, off_t size, const struct timespec& ts
}else{ }else{
// [NOTE] // [NOTE]
// The modify flag must not be set when opening a file, // 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. // and the cache file does not exist.
// If mtime is specified for the file and the cache file // If mtime is specified for the file and the cache file
// mtime is older than it, the cache file is removed and // mtime is older than it, the cache file is removed and
// the processing comes here. // 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; 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; truncated_size = size - size_orgmeta;
} }
// set mtime and ctime(set "x-amz-meta-mtime" and "x-amz-meta-ctime" in orgmeta) // set file timespecs(to internal varibales, cache file and original header)
if(UTIME_OMIT != ts_mctime.tv_nsec){ if(0 != (result = SetFileTimesHasLock(ts_times))){
if(0 != SetMCtimeHasLock(ts_mctime, ts_mctime)){ S3FS_PRN_ERR("failed to set file timespecs by result(%d)", result);
S3FS_PRN_ERR("failed to set mtime/ctime. errno(%d)", errno); pfile.reset();
pfile.reset(); physical_fd = -1;
physical_fd = -1; inode = 0;
inode = 0; return result;
return (0 == errno ? -EIO : -errno);
}
} }
} }
@ -755,6 +752,10 @@ bool FdEntity::GetStatsHasLock(struct stat& st) const
S3FS_PRN_ERR("fstat failed. errno(%d)", errno); S3FS_PRN_ERR("fstat failed. errno(%d)", errno);
return false; return false;
} }
const std::lock_guard<std::mutex> data_lock(fdent_data_lock);
timestamps.RefrectFileTimes(st);
return true; 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()); 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; return 0;
} }
timestamps.SetCTime(time);
orgmeta["x-amz-meta-ctime"] = str(time); orgmeta["x-amz-meta-ctime"] = str(time);
return 0; 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()); 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; return 0;
} }
timestamps.SetATime(time);
orgmeta["x-amz-meta-atime"] = str(time); orgmeta["x-amz-meta-atime"] = str(time);
return 0; return 0;
} }
// [NOTE] int FdEntity::SetMtimeHasLock(struct timespec time)
// This method updates mtime as well as ctime.
//
int FdEntity::SetMCtimeHasLock(struct timespec mtime, struct timespec ctime)
{ {
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; return 0;
} }
timestamps.SetMTime(time);
if(-1 != physical_fd){ orgmeta["x-amz-meta-mtime"] = str(time);
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);
return 0; 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<std::mutex> lock(fdent_lock); 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());
struct stat st;
if(!GetStatsHasLock(st)){
return false;
}
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() if(-1 != physical_fd){
{ if(-1 == futimens(physical_fd, ts)){
const std::lock_guard<std::mutex> lock(fdent_lock); S3FS_PRN_ERR("futimens failed. errno(%d)", errno);
struct stat st; return -errno;
if(!GetStatsHasLock(st)){ }
return false; }else{
} if(-1 == utimensat(AT_FDCWD, cachepath.c_str(), ts, 0)){
S3FS_PRN_ERR("utimensat failed. errno(%d)", errno);
orgmeta["x-amz-meta-atime"] = str_stat_time(st, stat_time_type::ATIME); return -errno;
return true;
}
bool FdEntity::UpdateMtime(bool clear_holding_mtime)
{
const std::lock_guard<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> 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;
} }
// [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{ }else{
struct stat st; struct timespec meta_atime = get_atime(orgmeta, false);
if(!GetStatsHasLock(st)){ if(0 != meta_atime.tv_sec && UTIME_OMIT != meta_atime.tv_nsec){
return false; timestamps.SetATime(meta_atime);
} }
orgmeta["x-amz-meta-mtime"] = str_stat_time(st, stat_time_type::MTIME); }
} if(!timestamps.IsOmitMTime()){
return true; orgmeta["x-amz-meta-mtime"] = str(timestamps.mtime());
} }else{
struct timespec meta_mtime = get_mtime(orgmeta, false);
bool FdEntity::SetHoldingMtime(struct timespec mtime) if(0 != meta_mtime.tv_sec && UTIME_OMIT != meta_mtime.tv_nsec){
{ timestamps.SetMTime(meta_mtime);
const std::lock_guard<std::mutex> lock(fdent_lock); }
const std::lock_guard<std::mutex> data_lock(fdent_data_lock); }
if(!timestamps.IsOmitCTime()){
S3FS_PRN_INFO3("[path=%s][physical_fd=%d][mtime=%s]", path.c_str(), physical_fd, str(mtime).c_str()); orgmeta["x-amz-meta-ctime"] = str(timestamps.ctime());
}else{
if(mtime.tv_sec < 0){ struct timespec meta_ctime = get_ctime(orgmeta, false);
return false; if(0 != meta_ctime.tv_sec && UTIME_OMIT != meta_ctime.tv_nsec){
} timestamps.SetCTime(meta_ctime);
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;
} }
} }
holding_mtime.tv_sec = -1;
holding_mtime.tv_nsec = 0;
return true; return 0;
} }
bool FdEntity::GetSize(off_t& size) const bool FdEntity::GetSize(off_t& size) const
@ -1015,6 +925,8 @@ bool FdEntity::GetStatsFromMeta(struct stat& st) const
const std::lock_guard<std::mutex> data_lock(fdent_data_lock); const std::lock_guard<std::mutex> data_lock(fdent_data_lock);
st.st_size = pagelist.Size(); // set current file size st.st_size = pagelist.Size(); // set current file size
timestamps.RefrectFileTimes(st);
return true; return true;
} }
@ -2459,6 +2371,7 @@ bool FdEntity::MergeOrgMeta(headers_t& updatemeta)
const std::lock_guard<std::mutex> data_lock(fdent_data_lock); const std::lock_guard<std::mutex> data_lock(fdent_data_lock);
merge_headers(orgmeta, updatemeta, true); // overwrite all keys merge_headers(orgmeta, updatemeta, true); // overwrite all keys
// [NOTE] // [NOTE]
// this is special cases, we remove the key which has empty values. // this is special cases, we remove the key which has empty values.
for(auto hiter = orgmeta.cbegin(); hiter != orgmeta.cend(); ){ 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 mtime = get_mtime(updatemeta, false); // not overcheck
struct timespec ctime = get_ctime(updatemeta, false); // not overcheck struct timespec ctime = get_ctime(updatemeta, false); // not overcheck
struct timespec atime = get_atime(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)); timestamps.SetAll(ctime, atime, mtime); // set all timespecs to internal data
}
if(0 <= atime.tv_sec){
SetAtimeHasLock(atime);
}
if(pending_status_t::NO_UPDATE_PENDING == pending_status && (IsUploading() || pagelist.IsModified())){ if(pending_status_t::NO_UPDATE_PENDING == pending_status && (IsUploading() || pagelist.IsModified())){
pending_status = pending_status_t::UPDATE_META_PENDING; pending_status = pending_status_t::UPDATE_META_PENDING;

View File

@ -32,6 +32,7 @@
#include "fdcache_untreated.h" #include "fdcache_untreated.h"
#include "metaheader.h" #include "metaheader.h"
#include "s3fs_util.h" #include "s3fs_util.h"
#include "filetimes.h"
//---------------------------------------------- //----------------------------------------------
// Typedef // Typedef
@ -60,25 +61,24 @@ class FdEntity : public std::enable_shared_from_this<FdEntity>
static bool streamupload; // whether stream uploading. static bool streamupload; // whether stream uploading.
mutable std::mutex fdent_lock; mutable std::mutex fdent_lock;
std::string path GUARDED_BY(fdent_lock); // object path std::string path GUARDED_BY(fdent_lock); // object path
int physical_fd GUARDED_BY(fdent_lock); // physical file(cache or temporary file) descriptor 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) 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 fdinfo_map_t pseudo_fd_map GUARDED_BY(fdent_lock); // pseudo file descriptor information map
std::unique_ptr<FILE, decltype(&s3fs_fclose)> pfile GUARDED_BY(fdent_lock) = {nullptr, &s3fs_fclose}; // file pointer(tmp file or cache file) std::unique_ptr<FILE, decltype(&s3fs_fclose)> 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 ino_t inode GUARDED_BY(fdent_lock); // inode number for cache file
headers_t orgmeta GUARDED_BY(fdent_lock); // original headers at opening 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 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 mutable std::mutex fdent_data_lock ACQUIRED_AFTER(fdent_lock); // protects the following members
PageList pagelist GUARDED_BY(fdent_data_lock); PageList pagelist GUARDED_BY(fdent_data_lock);
std::string cachepath GUARDED_BY(fdent_data_lock); // local cache file path std::string cachepath GUARDED_BY(fdent_data_lock); // local cache file path
// (if this is empty, does not load/save pagelist.) // (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 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 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 FileTimes timestamps GUARDED_BY(fdent_data_lock); // file timestamps(atime/ctime/mtime)
mutable std::mutex ro_path_lock; // for only the ro_path variable
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.
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: private:
static int FillFile(int fd, unsigned char byte, off_t size, off_t start); 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<FdEntity>
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 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); PseudoFdInfo* CheckPseudoFdFlags(int fd, bool writable) REQUIRES(FdEntity::fdent_lock);
bool IsUploading() 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 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); } 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); int PreMultipartUploadRequest(PseudoFdInfo* pseudo_obj) REQUIRES(FdEntity::fdent_lock, fdent_data_lock);
@ -143,7 +147,8 @@ class FdEntity : public std::enable_shared_from_this<FdEntity>
const std::lock_guard<std::mutex> ro_lock(ro_path_lock); const std::lock_guard<std::mutex> ro_lock(ro_path_lock);
return ro_path; 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); bool LoadAll(int fd, off_t* size = nullptr, bool force_load = false);
int Dup(int fd) { int Dup(int fd) {
const std::lock_guard<std::mutex> lock(fdent_lock); const std::lock_guard<std::mutex> lock(fdent_lock);
@ -182,28 +187,23 @@ class FdEntity : public std::enable_shared_from_this<FdEntity>
return GetStatsHasLock(st); return GetStatsHasLock(st);
} }
bool GetStatsHasLock(struct stat& st) const REQUIRES(FdEntity::fdent_lock); bool GetStatsHasLock(struct stat& st) const REQUIRES(FdEntity::fdent_lock);
int SetCtime(struct timespec time) { int SetCtime(struct timespec time) {
const std::lock_guard<std::mutex> lock(fdent_lock); const std::lock_guard<std::mutex> lock(fdent_lock);
return SetCtimeHasLock(time); const std::lock_guard<std::mutex> lock2(fdent_data_lock);
return SetCtimeHasLock(time);
} }
int SetCtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock);
int SetAtime(struct timespec time) { int SetAtime(struct timespec time) {
const std::lock_guard<std::mutex> lock(fdent_lock); const std::lock_guard<std::mutex> lock(fdent_lock);
return SetAtimeHasLock(time); const std::lock_guard<std::mutex> lock2(fdent_data_lock);
return SetAtimeHasLock(time);
} }
int SetAtimeHasLock(struct timespec time) REQUIRES(FdEntity::fdent_lock); int SetMtime(struct timespec time) {
int SetMCtime(struct timespec mtime, struct timespec ctime) { const std::lock_guard<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> lock(fdent_lock); const std::lock_guard<std::mutex> lock2(fdent_data_lock);
const std::lock_guard<std::mutex> lock2(fdent_data_lock); return SetMtimeHasLock(time);
return SetMCtimeHasLock(mtime, ctime);
} }
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 GetSize(off_t& size) const;
bool GetXattr(std::string& xattr) const; bool GetXattr(std::string& xattr) const;
bool SetXattr(const std::string& xattr); bool SetXattr(const std::string& xattr);

303
src/filetimes.cpp Normal file
View File

@ -0,0 +1,303 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Takeshi Nakatani <ggtakec.com>
*
* 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<int>(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<int>(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<clockid_t>(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
*/

120
src/filetimes.h Normal file
View File

@ -0,0 +1,120 @@
/*
* s3fs - FUSE-based file system backed by Amazon S3
*
* Copyright(C) 2007 Randy Rizun <rrizun@gmail.com>
*
* 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 <cstdint>
#include <string>
#include <sys/stat.h>
//-------------------------------------------------------------------
// 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
*/

View File

@ -27,8 +27,10 @@
#include "metaheader.h" #include "metaheader.h"
#include "string_util.h" #include "string_util.h"
#include "s3fs_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 // 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; headers_t::const_iterator iter;
if(meta.cend() == (iter = meta.find(header))){ if(meta.cend() == (iter = meta.find(header))){
return DEFAULT_TIMESPEC; return ERROR_TIMESPEC;
} }
return cvt_string_to_time((*iter).second.c_str()); return cvt_string_to_time((*iter).second.c_str());
} }
struct timespec get_mtime(const headers_t& meta, bool overcheck) struct timespec get_mtime(const headers_t& meta, bool overcheck)
{ {
struct timespec t = get_time(meta, "x-amz-meta-mtime"); struct timespec mtime = get_time(meta, "x-amz-meta-mtime");
if(0 < t.tv_sec){ if(0 <= mtime.tv_sec && UTIME_OMIT != mtime.tv_nsec){
return t; return mtime;
} }
t = get_time(meta, "x-amz-meta-goog-reserved-file-mtime");
if(0 < t.tv_sec){ mtime = get_time(meta, "x-amz-meta-goog-reserved-file-mtime");
return t; if(0 <= mtime.tv_sec && UTIME_OMIT != mtime.tv_nsec){
return mtime;
} }
if(overcheck){ if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0}; mtime = {get_lastmodified(meta), 0};
return ts; return mtime;
} }
return DEFAULT_TIMESPEC; return OMIT_TIMESPEC;
} }
struct timespec get_ctime(const headers_t& meta, bool overcheck) struct timespec get_ctime(const headers_t& meta, bool overcheck)
{ {
struct timespec t = get_time(meta, "x-amz-meta-ctime"); struct timespec ctime = get_time(meta, "x-amz-meta-ctime");
if(0 < t.tv_sec){ if(0 <= ctime.tv_sec && UTIME_OMIT != ctime.tv_nsec){
return t; return ctime;
} }
if(overcheck){ if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0}; ctime = {get_lastmodified(meta), 0};
return ts; return ctime;
} }
return DEFAULT_TIMESPEC; return OMIT_TIMESPEC;
} }
struct timespec get_atime(const headers_t& meta, bool overcheck) struct timespec get_atime(const headers_t& meta, bool overcheck)
{ {
struct timespec t = get_time(meta, "x-amz-meta-atime"); struct timespec atime = get_time(meta, "x-amz-meta-atime");
if(0 < t.tv_sec){ if(0 <= atime.tv_sec && UTIME_OMIT != atime.tv_nsec){
return t; return atime;
} }
if(overcheck){ if(overcheck){
struct timespec ts = {get_lastmodified(meta), 0}; atime = {get_lastmodified(meta), 0};
return ts; return atime;
} }
return DEFAULT_TIMESPEC; return OMIT_TIMESPEC;
} }
off_t get_size(const char *s) 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 // mtime
struct timespec mtime = get_mtime(meta); struct timespec mtime = get_mtime(meta);
if(stbuf.st_mtime < 0){ if(mtime.tv_sec < 0){
stbuf.st_mtime = 0L; mtime = {0, 0};
}else{
if(mtime.tv_sec < 0){
mtime.tv_sec = 0;
mtime.tv_nsec = 0;
}
set_timespec_to_stat(stbuf, stat_time_type::MTIME, mtime);
} }
set_timespec_to_stat(stbuf, stat_time_type::MTIME, mtime);
// ctime // ctime
struct timespec ctime = get_ctime(meta); struct timespec ctime = get_ctime(meta);
if(stbuf.st_ctime < 0){ if(ctime.tv_sec < 0){
stbuf.st_ctime = 0L; ctime = {0, 0};
}else{
if(ctime.tv_sec < 0){
ctime.tv_sec = 0;
ctime.tv_nsec = 0;
}
set_timespec_to_stat(stbuf, stat_time_type::CTIME, ctime);
} }
set_timespec_to_stat(stbuf, stat_time_type::CTIME, ctime);
// atime // atime
struct timespec atime = get_atime(meta); struct timespec atime = get_atime(meta);
if(stbuf.st_atime < 0){ if(atime.tv_sec < 0){
stbuf.st_atime = 0L; atime = {0, 0};
}else{
if(atime.tv_sec < 0){
atime.tv_sec = 0;
atime.tv_nsec = 0;
}
set_timespec_to_stat(stbuf, stat_time_type::ATIME, atime);
} }
set_timespec_to_stat(stbuf, stat_time_type::ATIME, atime);
// size // size
if(S_ISDIR(stbuf.st_mode)){ if(S_ISDIR(stbuf.st_mode)){

View File

@ -825,6 +825,7 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char*
struct stat stobj; struct stat stobj;
FdEntity* ent; FdEntity* ent;
headers_t meta; headers_t meta;
FileTimes ts_times; // default: all timespecs are UTIME_OMIT
S3FS_PRN_INFO2("[path=%s]", path); S3FS_PRN_INFO2("[path=%s]", path);
@ -833,15 +834,12 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char*
} }
// open // open
struct timespec st_mctime; if(S_ISREG(stobj.st_mode) || S_ISLNK(stobj.st_mode)){
if(!S_ISREG(stobj.st_mode) && !S_ISLNK(stobj.st_mode)){ ts_times.SetAll(stobj);
st_mctime = S3FS_OMIT_TS;
}else{
set_stat_to_timespec(stobj, stat_time_type::MTIME, st_mctime);
} }
bool force_tmpfile = S_ISREG(stobj.st_mode) ? false : true; 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); S3FS_PRN_ERR("Could not open file. errno(%d)", errno);
return -EIO; return -EIO;
} }
@ -1173,8 +1171,9 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi
AutoFdEntity autoent; AutoFdEntity autoent;
FdEntity* ent; FdEntity* ent;
int error = 0; int error = 0;
if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, S3FS_OMIT_TS, fi->flags, false, true, false, &error))){ FileTimes ts_times;
if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, ts_times, fi->flags, false, true, false, &error))){
// remove from cache // remove from cache
StatCache::getStatCacheData()->DelStat(strpath); StatCache::getStatCacheData()->DelStat(strpath);
return error; return error;
@ -1447,10 +1446,12 @@ static int s3fs_symlink(const char* _from, const char* _to)
{ // scope for AutoFdEntity { // scope for AutoFdEntity
AutoFdEntity autoent; AutoFdEntity autoent;
FdEntity* ent; 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); S3FS_PRN_ERR("could not open tmpfile(errno=%d)", errno);
return -errno; return -errno;
} }
// write(without space words) // write(without space words)
auto from_size = static_cast<ssize_t>(strFrom.length()); auto from_size = static_cast<ssize_t>(strFrom.length());
ssize_t ressize; 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 mtime = get_mtime(meta);
struct timespec ctime = get_ctime(meta); struct timespec ctime = get_ctime(meta);
struct timespec atime = get_atime(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()){ if(FdManager::IsCacheDir()){
// create cache file if be needed // create cache file if be needed
// //
// [NOTE] // [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 // 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 recreated, but the entire file area indicated by this pagelist
// will be in the "modified" state. // will be in the "modified" state.
// This does not affect the rename process, but the cache information in // This does not affect the rename process, but the cache information in
// the "modified" state remains, making it impossible to read the file correctly. // 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(ent){
if(0 != (result = ent->SetMCtime(mtime, ctime))){ if(0 != (result = ent->SetMtime(mtime)) || 0 != (result = ent->SetCtime(ctime)) || 0 != (result = ent->SetAtime(atime))){
S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", from, result); S3FS_PRN_ERR("could not set mtime/ctime/atime to file(%s): result=%d", from, result);
return 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 // Add only stat structure to stat cache
std::string strTo = to; std::string strTo = to;
stbuf = {}; stbuf = {};
if(convert_header_to_stat(strTo, meta, stbuf)){ if(convert_header_to_stat(strTo, meta, stbuf)){
objtype_t ObjType = derive_object_type(strTo, meta, objtype_t::FILE); 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; AutoFdEntity autoent;
FdEntity* ent; FdEntity* ent;
bool need_put_header = true; bool need_put_header = true;
bool keep_mtime = false;
if(nullptr != (ent = autoent.OpenExistFdEntity(curpath.c_str()))){ if(nullptr != (ent = autoent.OpenExistFdEntity(curpath.c_str()))){
if(ent->MergeOrgMeta(updatemeta)){ if(ent->MergeOrgMeta(updatemeta)){
// meta is changed, but now uploading. // meta is changed, but now uploading.
// then the meta is pending and accumulated to be put after the upload is complete. // then the meta is pending and accumulated to be put after the upload is complete.
S3FS_PRN_INFO("meta pending until upload is complete"); S3FS_PRN_INFO("meta pending until upload is complete");
need_put_header = false; need_put_header = false;
if(!ent->SetHoldingMtime(mtime)){
return -EIO;
}
// get current entity's meta headers // get current entity's meta headers
if(!ent->GetOrgMeta(meta)){ if(!ent->GetOrgMeta(meta)){
@ -2732,13 +2720,6 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2])
}else{ }else{
S3FS_PRN_INFO("meta is not pending, but need to keep current mtime."); 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){ 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()); S3FS_PRN_ERR("failed convert headers to stat[path=%s]. So remove stat cache.", curpath.c_str());
StatCache::getStatCacheData()->DelStat(curpath); 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; return result;
} }
// set mtime/ctime // set mtime/ctime/atime
if(0 != (result = ent->SetMCtime(mtime, ctime))){ if(0 != (result = ent->SetMtime(mtime)) || 0 != (result = ent->SetCtime(ctime)) || 0 != (result = ent->SetAtime(atime))){
S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", curpath.c_str(), result); S3FS_PRN_ERR("could not set mtime/ctime/atime 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);
return result; return result;
} }
@ -2936,12 +2905,9 @@ static int s3fs_truncate(const char* _path, off_t size)
ignore_modify = true; ignore_modify = true;
} }
if(nullptr == (ent = autoent.Open(path, &meta, size, S3FS_OMIT_TS, O_RDWR, false, true, ignore_modify))){ FileTimes ts_times; // Default: all time values are set UTIME_OMIT
S3FS_PRN_ERR("could not open file(%s): errno=%d", path, errno); if(nullptr == (ent = autoent.Open(path, &meta, size, ts_times, O_RDWR, false, true, ignore_modify))){
return -EIO; 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
}
if(!ent->UpdateCtime()){
S3FS_PRN_ERR("could not update ctime file(%s)", path);
return -EIO; 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-uid"] = std::to_string(pcxt->uid);
meta["x-amz-meta-gid"] = std::to_string(pcxt->gid); 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); S3FS_PRN_ERR("could not open file(%s): errno=%d", path, errno);
return -EIO; 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 if(0 != (result = get_object_attribute(path, nullptr, &meta, true, nullptr, true))){ // no truncate cache
return result; return result;
} }
struct timespec st_mctime; FileTimes ts_times; // Default: all time values are set UTIME_OMIT
set_stat_to_timespec(st, stat_time_type::MTIME, st_mctime); ts_times.SetAll(st);
if(nullptr == (ent = autoent.Open(path, &meta, st.st_size, ts_times, fi->flags, false, true, false))){
if(nullptr == (ent = autoent.Open(path, &meta, st.st_size, st_mctime, fi->flags, false, true, false))){
// remove stat cache // remove stat cache
StatCache::getStatCacheData()->DelStat(path); StatCache::getStatCacheData()->DelStat(path);
return -EIO; return -EIO;
} }
if (needs_flush){ if (needs_flush){
struct timespec ts; struct timespec ts_now;
s3fs_realtime(ts); s3fs_realtime(ts_now);
if(0 != (result = ent->SetCtime(ts_now)) || 0 != (result = ent->SetMtime(ts_now))){
if(0 != (result = ent->SetMCtime(ts, ts))){
S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", path, result); S3FS_PRN_ERR("could not set mtime and ctime to file(%s): result=%d", path, result);
return 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); 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){ if(max_dirty_data != -1 && ent->BytesModified() >= max_dirty_data){
int flushres; int flushres;
if(0 != (flushres = ent->RowFlush(static_cast<int>(fi->fh), path, true))){ if(0 != (flushres = ent->RowFlush(static_cast<int>(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<int>(fi->fh)))){ if(nullptr != (ent = autoent.GetExistFdEntity(path, static_cast<int>(fi->fh)))){
bool is_new_file = ent->IsDirtyNewFile(); 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<int>(fi->fh), false))){ if(0 == (result = ent->Flush(static_cast<int>(fi->fh), false))){
// [NOTE] // [NOTE]
// If there is any meta information that needs to be updated, update it now. // 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<int>(fi->fh)))){ if(nullptr != (ent = autoent.GetExistFdEntity(path, static_cast<int>(fi->fh)))){
bool is_new_file = ent->IsDirtyNewFile(); 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<int>(fi->fh), false); result = ent->Flush(static_cast<int>(fi->fh), false);
if(0 != datasync){ if(0 != datasync){

View File

@ -413,124 +413,6 @@ void print_launch_message(int argc, char** argv)
S3FS_PRN_LAUNCH_INFO("%s", message.c_str()); 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<int>(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<int>(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<clockid_t>(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) int s3fs_fclose(FILE* fp)
{ {
if(fp == nullptr){ if(fp == nullptr){

View File

@ -59,55 +59,40 @@ bool compare_sysname(const char* target);
void print_launch_message(int argc, char** argv); 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) // 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 // Wrap fclose since it is illegal to take the address of a stdlib function
int s3fs_fclose(FILE* fp); int s3fs_fclose(FILE* fp);
class scope_guard { class scope_guard
public: {
template<class Callable> public:
explicit scope_guard(Callable&& undo_func) template<class Callable>
: func(std::forward<Callable>(undo_func))
{}
~scope_guard() { explicit scope_guard(Callable&& undo_func)
if(func != nullptr) { : func(std::forward<Callable>(undo_func))
func(); {}
~scope_guard()
{
if(func != nullptr) {
func();
}
} }
}
void dismiss() { void dismiss()
func = nullptr; {
} func = nullptr;
}
scope_guard(const scope_guard&) = delete; scope_guard(const scope_guard&) = delete;
scope_guard(scope_guard&& other) = delete; scope_guard(scope_guard&& other) = delete;
scope_guard& operator=(const scope_guard&) = delete; scope_guard& operator=(const scope_guard&) = delete;
scope_guard& operator=(scope_guard&&) = delete; scope_guard& operator=(scope_guard&&) = delete;
private: private:
std::function<void()> func; std::function<void()> func;
}; };
#endif // S3FS_S3FS_UTIL_H_ #endif // S3FS_S3FS_UTIL_H_

View File

@ -27,24 +27,28 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <utility> #include <utility>
#include <fcntl.h>
#include <sys/stat.h>
#include "s3fs_logger.h" #include "s3fs_logger.h"
#include "string_util.h" #include "string_util.h"
//-------------------------------------------------------------------
// Global variables
//-------------------------------------------------------------------
//------------------------------------------------------------------- //-------------------------------------------------------------------
// Functions // Functions
//------------------------------------------------------------------- //-------------------------------------------------------------------
std::string str(const struct timespec& value) std::string str(const struct timespec& value)
{ {
std::ostringstream s; std::ostringstream s;
s << value.tv_sec;
if(value.tv_nsec != 0){ if(UTIME_OMIT == value.tv_nsec){
s << "." << std::setfill('0') << std::setw(9) << 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(); return s.str();
} }