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_pseudofd.cpp \
fdcache_untreated.cpp \
filetimes.cpp \
addhead.cpp \
sighandlers.cpp \
threadpoolman.cpp \

View File

@ -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<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]){
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<FdEntity>(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;

View File

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

View File

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

View File

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

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),
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<std::mutex> 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<std::mutex> lock(fdent_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]
// 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<CacheFileStat> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<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;
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<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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;

View File

@ -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<FdEntity>
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<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
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<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
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<FdEntity>
const std::lock_guard<std::mutex> 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<std::mutex> lock(fdent_lock);
@ -182,28 +187,23 @@ class FdEntity : public std::enable_shared_from_this<FdEntity>
return GetStatsHasLock(st);
}
bool GetStatsHasLock(struct stat& st) const REQUIRES(FdEntity::fdent_lock);
int SetCtime(struct timespec time) {
const std::lock_guard<std::mutex> lock(fdent_lock);
return SetCtimeHasLock(time);
const std::lock_guard<std::mutex> lock(fdent_lock);
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) {
const std::lock_guard<std::mutex> lock(fdent_lock);
return SetAtimeHasLock(time);
const std::lock_guard<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> 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<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> lock2(fdent_data_lock);
return SetMCtimeHasLock(mtime, ctime);
int SetMtime(struct timespec time) {
const std::lock_guard<std::mutex> lock(fdent_lock);
const std::lock_guard<std::mutex> 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);

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 "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)){

View File

@ -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<ssize_t>(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<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)))){
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))){
// [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<int>(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<int>(fi->fh), false);
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());
}
//
// 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)
{
if(fp == nullptr){

View File

@ -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<class Callable>
explicit scope_guard(Callable&& undo_func)
: func(std::forward<Callable>(undo_func))
{}
class scope_guard
{
public:
template<class Callable>
~scope_guard() {
if(func != nullptr) {
func();
explicit scope_guard(Callable&& undo_func)
: func(std::forward<Callable>(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<void()> func;
private:
std::function<void()> func;
};
#endif // S3FS_S3FS_UTIL_H_

View File

@ -27,24 +27,28 @@
#include <sstream>
#include <string>
#include <utility>
#include <fcntl.h>
#include <sys/stat.h>
#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();
}