diff --git a/src/cache.cpp b/src/cache.cpp index 29a25cf..09863d9 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -100,7 +100,9 @@ inline bool IsExpireStatCacheTime(const struct timespec& ts, const time_t& expir { struct timespec nowts; SetStatCacheTime(nowts); - return ((ts.tv_sec + expire) < nowts.tv_sec); + nowts.tv_sec -= expire; + + return (0 < CompareStatCacheTime(nowts, ts)); } // @@ -258,7 +260,7 @@ bool StatCache::GetStat(const std::string& key, struct stat* pst, headers_t* met if(iter != stat_cache.end() && (*iter).second){ stat_cache_entry* ent = (*iter).second; - if(!IsExpireTime || !IsExpireStatCacheTime(ent->cache_date, ExpireTime)){ + if(0 < ent->notruncate || !IsExpireTime || !IsExpireStatCacheTime(ent->cache_date, ExpireTime)){ if(ent->noobjcache){ if(!IsCacheNoObject){ // need to delete this cache. @@ -343,7 +345,8 @@ bool StatCache::IsNoObjectCache(const std::string& key, bool overcheck) } if(iter != stat_cache.end() && (*iter).second) { - if(!IsExpireTime || !IsExpireStatCacheTime((*iter).second->cache_date, ExpireTime)){ + stat_cache_entry* ent = (*iter).second; + if(0 < ent->notruncate || !IsExpireTime || !IsExpireStatCacheTime((*iter).second->cache_date, ExpireTime)){ if((*iter).second->noobjcache){ // noobjcache = true means no object. SetStatCacheTime((*iter).second->cache_date); @@ -434,6 +437,47 @@ bool StatCache::AddStat(const std::string& key, headers_t& meta, bool forcedir, return true; } +// [NOTE] +// Updates only meta data if cached data exists. +// And when these are updated, it also updates the cache time. +// +bool StatCache::UpdateMetaStats(const std::string& key, headers_t& meta) +{ + if(CacheSize < 1){ + return true; + } + S3FS_PRN_INFO3("update stat cache entry[path=%s]", key.c_str()); + + AutoLock lock(&StatCache::stat_cache_lock); + stat_cache_t::iterator iter = stat_cache.find(key); + if(stat_cache.end() == iter || !(iter->second)){ + return true; + } + stat_cache_entry* ent = iter->second; + + // update only meta keys + for(headers_t::iterator metaiter = meta.begin(); metaiter != meta.end(); ++metaiter){ + std::string tag = lower(metaiter->first); + std::string value = metaiter->second; + if(tag == "content-type"){ + ent->meta[metaiter->first] = value; + }else if(tag == "content-length"){ + ent->meta[metaiter->first] = value; + }else if(tag == "etag"){ + ent->meta[metaiter->first] = value; + }else if(tag == "last-modified"){ + ent->meta[metaiter->first] = value; + }else if(is_prefix(tag.c_str(), "x-amz")){ + ent->meta[tag] = value; // key is lower case for "x-amz" + } + } + + // Update time. + SetStatCacheTime(ent->cache_date); + + return true; +} + bool StatCache::AddNoObjectCache(const std::string& key) { if(!IsCacheNoObject){ diff --git a/src/cache.h b/src/cache.h index 410d447..93b1d2d 100644 --- a/src/cache.h +++ b/src/cache.h @@ -158,6 +158,9 @@ class StatCache // Add stat cache bool AddStat(const std::string& key, headers_t& meta, bool forcedir = false, bool no_truncate = false); + // Update meta stats + bool UpdateMetaStats(const std::string& key, headers_t& meta); + // Change no truncate flag void ChangeNoTruncateFlag(const std::string& key, bool no_truncate); diff --git a/src/s3fs.cpp b/src/s3fs.cpp index 545af37..a441405 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -385,7 +385,6 @@ static int get_object_attribute(const char* path, struct stat* pstbuf, headers_t strpath += "/"; } if(StatCache::getStatCacheData()->GetStat(strpath, pstat, pheader, overcheck, pisforce)){ - StatCache::getStatCacheData()->ChangeNoTruncateFlag(strpath, add_no_truncate_cache); return 0; } if(StatCache::getStatCacheData()->IsNoObjectCache(strpath)){ @@ -980,7 +979,16 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi meta["x-amz-meta-atime"] = str(now); meta["x-amz-meta-mtime"] = str(now); meta["x-amz-meta-ctime"] = str(now); - if(!StatCache::getStatCacheData()->AddStat(path, meta)){ + + // [NOTE] set no_truncate flag + // At this point, the file has not been created(uploaded) and + // the data is only present in the Stats cache. + // The Stats cache should not be deleted automatically by + // timeout. If this stats is deleted, s3fs will try to get it + // from the server with a Head request and will get an + // unexpected error because the result object does not exist. + // + if(!StatCache::getStatCacheData()->AddStat(path, meta, false, true)){ return -EIO; } @@ -1066,9 +1074,9 @@ static int s3fs_unlink(const char* _path) } S3fsCurl s3fscurl; result = s3fscurl.DeleteRequest(path); - FdManager::DeleteCacheFile(path); StatCache::getStatCacheData()->DelStat(path); StatCache::getStatCacheData()->DelSymlink(path); + FdManager::DeleteCacheFile(path); S3FS_MALLOCTRIM(0); return result; @@ -1671,6 +1679,9 @@ static int s3fs_chmod(const char* _path, mode_t mode) // 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 there is data in the Stats cache, update the Stats cache. + StatCache::getStatCacheData()->UpdateMetaStats(strpath, updatemeta); } } if(need_put_header){ @@ -1845,6 +1856,9 @@ static int s3fs_chown(const char* _path, uid_t uid, gid_t gid) // 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 there is data in the Stats cache, update the Stats cache. + StatCache::getStatCacheData()->UpdateMetaStats(strpath, updatemeta); } } if(need_put_header){ @@ -2026,6 +2040,9 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) need_put_header = false; ent->SetHoldingMtime(ts[1]); // ts[1] is mtime + // If there is data in the Stats cache, update the Stats cache. + StatCache::getStatCacheData()->UpdateMetaStats(strpath, updatemeta); + }else{ S3FS_PRN_INFO("meta is not pending, but need to keep current mtime."); @@ -2223,15 +2240,20 @@ static int s3fs_open(const char* _path, struct fuse_file_info* fi) return -EACCES; } - // clear stat for reading fresh stat. - // (if object stat is changed, we refresh it. then s3fs gets always - // stat when s3fs open the object). + // [NOTE] + // Delete the Stats cache only if the file is not open. + // If the file is open, the stats cache will not be deleted as + // there are cases where the object does not exist on the server + // and only the Stats cache exists. + // if(StatCache::getStatCacheData()->HasStat(path)){ - // flush any dirty data so that subsequent stat gets correct size - if((result = s3fs_flush(_path, fi)) != 0){ - S3FS_PRN_ERR("could not flush(%s): result=%d", path, result); + AutoFdEntity autoent_local; + if(NULL == autoent_local.ExistOpen(path, -1, true)){ + if((result = s3fs_flush(_path, fi)) != 0){ + S3FS_PRN_ERR("could not flush(%s): result=%d", path, result); + } + StatCache::getStatCacheData()->DelStat(path); } - StatCache::getStatCacheData()->DelStat(path); } int mask = (O_RDONLY != (fi->flags & O_ACCMODE) ? W_OK : R_OK); @@ -2390,6 +2412,7 @@ static int s3fs_flush(const char* _path, struct fuse_file_info* fi) ent->UpdateMtime(true); // clear the flag not to update mtime. ent->UpdateCtime(); result = ent->Flush(false); + StatCache::getStatCacheData()->DelStat(path); } S3FS_MALLOCTRIM(0); @@ -3048,6 +3071,9 @@ static int s3fs_setxattr(const char* path, const char* name, const char* value, // 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 there is data in the Stats cache, update the Stats cache. + StatCache::getStatCacheData()->UpdateMetaStats(strpath, updatemeta); } } if(need_put_header){ @@ -3318,6 +3344,9 @@ static int s3fs_removexattr(const char* path, const char* name) // 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 there is data in the Stats cache, update the Stats cache. + StatCache::getStatCacheData()->UpdateMetaStats(strpath, updatemeta); } } if(need_put_header){