Changed file stat times(a/c/mtime) management
This commit is contained in:
committed by
Andrew Gaul
parent
60fb557f14
commit
37e593aeb0
@ -53,6 +53,7 @@ s3fs_SOURCES = \
|
||||
fdcache_fdinfo.cpp \
|
||||
fdcache_pseudofd.cpp \
|
||||
fdcache_untreated.cpp \
|
||||
filetimes.cpp \
|
||||
addhead.cpp \
|
||||
sighandlers.cpp \
|
||||
threadpoolman.cpp \
|
||||
|
||||
@ -527,9 +527,9 @@ FdEntity* FdManager::GetFdEntityHasLock(const char* path, int& existfd, bool new
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const struct timespec& ts_mctime, int flags, bool force_tmpfile, bool is_create, bool ignore_modify)
|
||||
FdEntity* FdManager::Open(int& fd, const char* path, const headers_t* pmeta, off_t size, const FileTimes& ts_times, int flags, bool force_tmpfile, bool is_create, bool ignore_modify)
|
||||
{
|
||||
S3FS_PRN_DBG("[path=%s][size=%lld][ts_mctime=%s][flags=0x%x][force_tmpfile=%s][create=%s][ignore_modify=%s]", SAFESTRPTR(path), static_cast<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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
303
src/filetimes.cpp
Normal 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
120
src/filetimes.h
Normal 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
|
||||
*/
|
||||
@ -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)){
|
||||
|
||||
131
src/s3fs.cpp
131
src/s3fs.cpp
@ -825,6 +825,7 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char*
|
||||
struct stat stobj;
|
||||
FdEntity* ent;
|
||||
headers_t meta;
|
||||
FileTimes ts_times; // default: all timespecs are UTIME_OMIT
|
||||
|
||||
S3FS_PRN_INFO2("[path=%s]", path);
|
||||
|
||||
@ -833,15 +834,12 @@ static int get_local_fent(AutoFdEntity& autoent, FdEntity **entity, const char*
|
||||
}
|
||||
|
||||
// open
|
||||
struct timespec st_mctime;
|
||||
if(!S_ISREG(stobj.st_mode) && !S_ISLNK(stobj.st_mode)){
|
||||
st_mctime = S3FS_OMIT_TS;
|
||||
}else{
|
||||
set_stat_to_timespec(stobj, stat_time_type::MTIME, st_mctime);
|
||||
if(S_ISREG(stobj.st_mode) || S_ISLNK(stobj.st_mode)){
|
||||
ts_times.SetAll(stobj);
|
||||
}
|
||||
bool force_tmpfile = S_ISREG(stobj.st_mode) ? false : true;
|
||||
|
||||
if(nullptr == (ent = autoent.Open(path, &meta, stobj.st_size, st_mctime, flags, force_tmpfile, true, false))){
|
||||
if(nullptr == (ent = autoent.Open(path, &meta, stobj.st_size, ts_times, flags, force_tmpfile, true, false))){
|
||||
S3FS_PRN_ERR("Could not open file. errno(%d)", errno);
|
||||
return -EIO;
|
||||
}
|
||||
@ -1173,8 +1171,9 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi
|
||||
|
||||
AutoFdEntity autoent;
|
||||
FdEntity* ent;
|
||||
int error = 0;
|
||||
if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, S3FS_OMIT_TS, fi->flags, false, true, false, &error))){
|
||||
int error = 0;
|
||||
FileTimes ts_times;
|
||||
if(nullptr == (ent = autoent.Open(strpath.c_str(), &meta, 0, ts_times, fi->flags, false, true, false, &error))){
|
||||
// remove from cache
|
||||
StatCache::getStatCacheData()->DelStat(strpath);
|
||||
return error;
|
||||
@ -1447,10 +1446,12 @@ static int s3fs_symlink(const char* _from, const char* _to)
|
||||
{ // scope for AutoFdEntity
|
||||
AutoFdEntity autoent;
|
||||
FdEntity* ent;
|
||||
if(nullptr == (ent = autoent.Open(strTo.c_str(), &headers, 0, S3FS_OMIT_TS, O_RDWR, true, true, false))){
|
||||
FileTimes ts_times; // Default: all time values are set UTIME_OMIT
|
||||
if(nullptr == (ent = autoent.Open(strTo.c_str(), &headers, 0, ts_times, O_RDWR, true, true, false))){
|
||||
S3FS_PRN_ERR("could not open tmpfile(errno=%d)", errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// write(without space words)
|
||||
auto from_size = static_cast<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){
|
||||
|
||||
@ -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){
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user