diff --git a/src/cache.cpp b/src/cache.cpp index 74ef968..53c5614 100644 --- a/src/cache.cpp +++ b/src/cache.cpp @@ -620,6 +620,9 @@ bool convert_header_to_stat(const char* path, headers_t& meta, struct stat* pst, // mtime pst->st_mtime = get_mtime(meta); + // ctime + pst->st_ctime = get_ctime(meta); + // size pst->st_size = get_size(meta); diff --git a/src/fdcache.cpp b/src/fdcache.cpp index 0572c7f..f1dc511 100644 --- a/src/fdcache.cpp +++ b/src/fdcache.cpp @@ -1050,11 +1050,23 @@ int FdEntity::SetMtime(time_t time) return -errno; } } + orgmeta["x-amz-meta-ctime"] = str(time); orgmeta["x-amz-meta-mtime"] = str(time); return 0; } +bool FdEntity::UpdateCtime(void) +{ + AutoLock auto_lock(&fdent_lock); + struct stat st; + if(!GetStats(st)){ + return false; + } + orgmeta["x-amz-meta-ctime"] = str(st.st_ctime); + return true; +} + bool FdEntity::UpdateMtime(void) { AutoLock auto_lock(&fdent_lock); @@ -1062,6 +1074,7 @@ bool FdEntity::UpdateMtime(void) if(!GetStats(st)){ return false; } + orgmeta["x-amz-meta-ctime"] = str(st.st_ctime); orgmeta["x-amz-meta-mtime"] = str(st.st_mtime); return true; } diff --git a/src/fdcache.h b/src/fdcache.h index beac6f9..2e9622e 100644 --- a/src/fdcache.h +++ b/src/fdcache.h @@ -155,6 +155,7 @@ class FdEntity bool GetStats(struct stat& st); int SetMtime(time_t time); + bool UpdateCtime(void); bool UpdateMtime(void); bool GetSize(size_t& size); bool SetMode(mode_t mode); diff --git a/src/s3fs.cpp b/src/s3fs.cpp index cadad3d..8d5f49d 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -963,12 +963,14 @@ static int create_file_object(const char* path, mode_t mode, uid_t uid, gid_t gi { S3FS_PRN_INFO2("[path=%s][mode=%04o]", path, mode); + time_t now = time(NULL); headers_t meta; meta["Content-Type"] = S3fsCurl::LookupMimeType(string(path)); meta["x-amz-meta-uid"] = str(uid); meta["x-amz-meta-gid"] = str(gid); meta["x-amz-meta-mode"] = str(mode); - meta["x-amz-meta-mtime"] = str(time(NULL)); + meta["x-amz-meta-ctime"] = str(now); + meta["x-amz-meta-mtime"] = str(now); S3fsCurl s3fscurl(true); return s3fscurl.PutRequest(path, meta, -1); // fd=-1 means for creating zero byte object. @@ -1055,6 +1057,7 @@ static int create_directory_object(const char* path, mode_t mode, time_t time, u meta["x-amz-meta-uid"] = str(uid); meta["x-amz-meta-gid"] = str(gid); meta["x-amz-meta-mode"] = str(mode); + meta["x-amz-meta-ctime"] = str(time); meta["x-amz-meta-mtime"] = str(time); S3fsCurl s3fscurl; @@ -1199,10 +1202,12 @@ static int s3fs_symlink(const char* from, const char* to) return result; } + time_t now = time(NULL); headers_t headers; headers["Content-Type"] = string("application/octet-stream"); // Static headers["x-amz-meta-mode"] = str(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); - headers["x-amz-meta-mtime"] = str(time(NULL)); + headers["x-amz-meta-ctime"] = str(now); + headers["x-amz-meta-mtime"] = str(now); headers["x-amz-meta-uid"] = str(pcxt->uid); headers["x-amz-meta-gid"] = str(pcxt->gid); @@ -1591,6 +1596,7 @@ static int s3fs_chmod(const char* path, mode_t mode) } }else{ // normal object or directory object of newer version + meta["x-amz-meta-ctime"] = str(time(NULL)); meta["x-amz-meta-mode"] = str(mode); meta["x-amz-copy-source"] = urlEncode(service_path + bucket + get_realpath(strpath.c_str())); meta["x-amz-metadata-directive"] = "REPLACE"; @@ -1607,6 +1613,7 @@ static int s3fs_chmod(const char* path, mode_t mode) // FdEntity* ent; if(NULL != (ent = FdManager::get()->ExistOpen(path))){ + ent->UpdateCtime(); ent->SetMode(mode); // Set new mode to opened fd. FdManager::get()->Close(ent); } @@ -1747,6 +1754,7 @@ static int s3fs_chown(const char* path, uid_t uid, gid_t gid) return result; } }else{ + meta["x-amz-meta-ctime"] = str(time(NULL)); meta["x-amz-meta-uid"] = str(uid); meta["x-amz-meta-gid"] = str(gid); meta["x-amz-copy-source"] = urlEncode(service_path + bucket + get_realpath(strpath.c_str())); @@ -2032,9 +2040,11 @@ static int s3fs_truncate(const char* path, off_t size) if(NULL == (pcxt = fuse_get_context())){ return -EIO; } + time_t now = time(NULL); meta["Content-Type"] = string("application/octet-stream"); // Static meta["x-amz-meta-mode"] = str(S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO); - meta["x-amz-meta-mtime"] = str(time(NULL)); + meta["x-amz-meta-ctime"] = str(now); + meta["x-amz-meta-mtime"] = str(now); meta["x-amz-meta-uid"] = str(pcxt->uid); meta["x-amz-meta-gid"] = str(pcxt->gid); @@ -3103,6 +3113,7 @@ static int s3fs_setxattr(const char* path, const char* name, const char* value, } // set xattr all object + meta["x-amz-meta-ctime"] = str(time(NULL)); meta["x-amz-copy-source"] = urlEncode(service_path + bucket + get_realpath(strpath.c_str())); meta["x-amz-metadata-directive"] = "REPLACE"; diff --git a/src/s3fs_util.cpp b/src/s3fs_util.cpp index 6e5bcf1..0584e03 100644 --- a/src/s3fs_util.cpp +++ b/src/s3fs_util.cpp @@ -753,10 +753,10 @@ time_t get_mtime(const char *s) return static_cast(s3fs_strtoofft(s)); } -time_t get_mtime(headers_t& meta, bool overcheck) +static time_t get_time(headers_t& meta, bool overcheck, const char *header) { headers_t::const_iterator iter; - if(meta.end() == (iter = meta.find("x-amz-meta-mtime"))){ + if(meta.end() == (iter = meta.find(header))){ if(overcheck){ return get_lastmodified(meta); } @@ -765,6 +765,16 @@ time_t get_mtime(headers_t& meta, bool overcheck) return get_mtime((*iter).second.c_str()); } +time_t get_mtime(headers_t& meta, bool overcheck) +{ + return get_time(meta, overcheck, "x-amz-meta-mtime"); +} + +time_t get_ctime(headers_t& meta, bool overcheck) +{ + return get_time(meta, overcheck, "x-amz-meta-ctime"); +} + off_t get_size(const char *s) { return s3fs_strtoofft(s); diff --git a/src/s3fs_util.h b/src/s3fs_util.h index 4d8ff09..61bddf2 100644 --- a/src/s3fs_util.h +++ b/src/s3fs_util.h @@ -119,6 +119,7 @@ bool delete_files_in_dir(const char* dir, bool is_remove_own); time_t get_mtime(const char *s); time_t get_mtime(headers_t& meta, bool overcheck = true); +time_t get_ctime(headers_t& meta, bool overcheck = true); off_t get_size(const char *s); off_t get_size(headers_t& meta); mode_t get_mode(const char *s); diff --git a/test/integration-test-main.sh b/test/integration-test-main.sh index d59c047..1fbaf9c 100755 --- a/test/integration-test-main.sh +++ b/test/integration-test-main.sh @@ -400,13 +400,8 @@ function test_mtime_file { #copy the test file with preserve mode cp -p $TEST_TEXT_FILE $ALT_TEST_TEXT_FILE - if [ `uname` = "Darwin" ]; then - testmtime=`stat -f "%m" $TEST_TEXT_FILE` - altmtime=`stat -f "%m" $ALT_TEST_TEXT_FILE` - else - testmtime=`stat -c %Y $TEST_TEXT_FILE` - altmtime=`stat -c %Y $ALT_TEST_TEXT_FILE` - fi + testmtime=`get_mtime $TEST_TEXT_FILE` + altmtime=`get_mtime $ALT_TEST_TEXT_FILE` if [ "$testmtime" -ne "$altmtime" ] then echo "File times do not match: $testmtime != $altmtime" @@ -414,6 +409,61 @@ function test_mtime_file { fi } +function test_update_time() { + describe "Testing update time function ..." + + # create the test + mk_test_file + mtime=`get_ctime $TEST_TEXT_FILE` + ctime=`get_mtime $TEST_TEXT_FILE` + + sleep 2 + chmod +x $TEST_TEXT_FILE + + ctime2=`get_ctime $TEST_TEXT_FILE` + mtime2=`get_mtime $TEST_TEXT_FILE` + if [ $ctime -eq $ctime2 -o $mtime -ne $mtime2 ]; then + echo "Expected updated ctime: $ctime != $ctime2 and same mtime: $mtime == $mtime2" + return 1 + fi + + sleep 2 + chown $UID:$UID $TEST_TEXT_FILE; + + ctime3=`get_ctime $TEST_TEXT_FILE` + mtime3=`get_mtime $TEST_TEXT_FILE` + if [ $ctime2 -eq $ctime3 -o $mtime2 -ne $mtime3 ]; then + echo "Expected updated ctime: $ctime2 != $ctime3 and same mtime: $mtime2 == $mtime3" + return 1 + fi + + if command -v setfattr >/dev/null 2>&1; then + sleep 2 + setfattr -n key -v value $TEST_TEXT_FILE + + ctime4=`get_ctime $TEST_TEXT_FILE` + mtime4=`get_mtime $TEST_TEXT_FILE` + if [ $ctime3 -eq $ctime4 -o $mtime3 -ne $mtime4 ]; then + echo "Expected updated ctime: $ctime3 != $ctime4 and same mtime: $mtime3 == $mtime4" + return 1 + fi + else + echo "Skipping extended attribute test" + ctime4=`get_ctime $TEST_TEXT_FILE` + mtime4=`get_mtime $TEST_TEXT_FILE` + fi + + sleep 2 + echo foo >> $TEST_TEXT_FILE + + ctime5=`get_ctime $TEST_TEXT_FILE` + mtime5=`get_mtime $TEST_TEXT_FILE` + if [ $ctime4 -eq $ctime5 -o $mtime4 -eq $mtime5 ]; then + echo "Expected updated ctime: $ctime4 != $ctime5 and updated mtime: $mtime4 != $mtime5" + return 1 + fi +} + function test_rm_rf_dir { describe "Test that rm -rf will remove directory with contents" # Create a dir with some files and directories @@ -458,6 +508,7 @@ function add_all_tests { add_tests test_symlink add_tests test_extended_attributes add_tests test_mtime_file + add_tests test_update_time add_tests test_rm_rf_dir add_tests test_write_after_seek_ahead } diff --git a/test/test-utils.sh b/test/test-utils.sh index 71ebcab..5d8872c 100644 --- a/test/test-utils.sh +++ b/test/test-utils.sh @@ -169,3 +169,19 @@ function run_suite { return 0 fi } + +function get_ctime() { + if [ `uname` = "Darwin" ]; then + stat -f "%c" "$1" + else + stat -c %Z "$1" + fi +} + +function get_mtime() { + if [ `uname` = "Darwin" ]; then + stat -f "%m" "$1" + else + stat -c %Y "$1" + fi +}