Added the function to update mtime/ctime of the parent directory (#2016)

This commit is contained in:
Takeshi Nakatani 2023-02-12 17:59:40 +09:00 committed by GitHub
parent d1388ff446
commit e715b77307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 469 additions and 9 deletions

View File

@ -422,6 +422,10 @@ If the cache is enabled, you can check the integrity of the cache file and the c
This option is specified and when sending the SIGUSR1 signal to the s3fs process checks the cache status at that time.
This option can take a file path as parameter to output the check result to that file.
The file path parameter can be omitted. If omitted, the result will be output to stdout or syslog.
.TP
\fB\-o\fR update_parent_dir_stat (default is disable)
The parent directory's mtime and ctime are updated when a file or directory is created or deleted (when the parent directory's inode is updated).
By default, parent directory statistics are not updated.
.SS "utility mode options"
.TP
\fB\-u\fR or \fB\-\-incomplete\-mpu\-list\fR

View File

@ -149,6 +149,7 @@ class FdEntity
bool PunchHole(off_t start = 0, size_t size = 0);
void MarkDirtyNewFile();
bool IsDirtyNewFile() { return (CREATE_FILE_PENDING == pending_status); }
bool GetLastUpdateUntreatedPart(off_t& start, off_t& size) const;
bool ReplaceLastUpdateUntreatedPart(off_t front_start, off_t front_size, off_t behind_start, off_t behind_size);

View File

@ -99,6 +99,7 @@ static off_t max_dirty_data = 5LL * 1024LL * 1024LL * 1024LL;
static bool use_wtf8 = false;
static off_t fake_diskfree_size = -1; // default is not set(-1)
static int max_thread_count = 5; // default is 5
static bool update_parent_dir_stat= false; // default not updating parent directory stats
//-------------------------------------------------------------------
// Global functions : prototype
@ -128,6 +129,7 @@ static int rename_object(const char* from, const char* to, bool update_ctime);
static int rename_object_nocopy(const char* from, const char* to, bool update_ctime);
static int clone_directory_object(const char* from, const char* to, bool update_ctime, const char* pxattrvalue);
static int rename_directory(const char* from, const char* to);
static int update_mctime_parent_directory(const char* _path);
static int remote_mountpath_exists(const char* path);
static bool get_meta_xattr_value(const char* path, std::string& rawvalue);
static bool get_parent_meta_xattr_value(const char* path, std::string& rawvalue);
@ -994,6 +996,13 @@ static int s3fs_mknod(const char *_path, mode_t mode, dev_t rdev)
return result;
}
StatCache::getStatCacheData()->DelStat(path);
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to mknod the file(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -1133,6 +1142,13 @@ static int s3fs_mkdir(const char* _path, mode_t mode)
result = create_directory_object(path, mode, now, now, now, pcxt->uid, pcxt->gid, pxattrvalue);
StatCache::getStatCacheData()->DelStat(path);
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to create the directory(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -1153,6 +1169,13 @@ static int s3fs_unlink(const char* _path)
StatCache::getStatCacheData()->DelStat(path);
StatCache::getStatCacheData()->DelSymlink(path);
FdManager::DeleteCacheFile(path);
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to remove the file(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -1225,6 +1248,13 @@ static int s3fs_rmdir(const char* _path)
strpath += "_$folder$";
result = s3fscurl.DeleteRequest(strpath.c_str());
}
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to remove the directory(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -1297,6 +1327,13 @@ static int s3fs_symlink(const char* _from, const char* _to)
if(!StatCache::getStatCacheData()->AddSymlink(std::string(to), strFrom)){
S3FS_PRN_ERR("failed to add symbolic link cache for %s", to);
}
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(to))){
S3FS_PRN_ERR("succeed to create symbolic link(%s), but could not update timestamp of its parent directory(result=%d).", to, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -1321,6 +1358,7 @@ static int rename_object(const char* from, const char* to, bool update_ctime)
if(0 != (result = get_object_attribute(from, &buf, &meta))){
return result;
}
std::string strSourcePath = (mount_prefix.empty() && 0 == strcmp("/", from)) ? "//" : from;
if(update_ctime){
@ -1716,6 +1754,17 @@ static int s3fs_rename(const char* _from, const char* _to)
result = rename_object_nocopy(from, to, true); // update ctime
}
}
// update parent directory timestamp
//
// [NOTE]
// already updated timestamp for original path in above functions.
//
int update_result;
if(0 != (update_result = update_mctime_parent_directory(to))){
S3FS_PRN_ERR("succeed to create the file/directory(%s), but could not update timestamp of its parent directory(result=%d).", to, update_result);
}
S3FS_MALLOCTRIM(0);
return result;
@ -2151,6 +2200,107 @@ static timespec handle_utimens_special_values(timespec ts, timespec now, timespe
}
}
static int update_mctime_parent_directory(const char* _path)
{
if(!update_parent_dir_stat){
// Disable updating parent directory stat.
S3FS_PRN_DBG("Updating parent directory stats is disabled");
return 0;
}
WTF8_ENCODE(path)
int result;
std::string parentpath; // parent directory path
std::string nowpath; // now directory object path("dir" or "dir/" or "xxx_$folder$", etc)
std::string newpath; // directory path for the current version("dir/")
std::string nowcache;
headers_t meta;
struct stat stbuf;
struct timespec mctime;
struct timespec atime;
dirtype nDirType = DIRTYPE_UNKNOWN;
S3FS_PRN_INFO2("[path=%s]", path);
// get parent directory path
parentpath = mydirname(path);
// check & get directory type
if(0 != (result = chk_dir_object_type(parentpath.c_str(), newpath, nowpath, nowcache, &meta, &nDirType))){
return result;
}
// get directory stat
//
// [NOTE]
// It is assumed that this function is called after the operation on
// the file is completed, so there is no need to check the permissions
// on the parent directory.
//
if(0 != (result = get_object_attribute(parentpath.c_str(), &stbuf))){
// If there is not the target file(object), result is -ENOENT.
return result;
}
if(!S_ISDIR(stbuf.st_mode)){
S3FS_PRN_ERR("path(%s) is not parent directory.", parentpath.c_str());
return -EIO;
}
// make atime/mtime/ctime for updating
s3fs_realtime(mctime);
set_stat_to_timespec(stbuf, ST_TYPE_ATIME, atime);
if(0 == atime.tv_sec && 0 == atime.tv_nsec){
atime = mctime;
}
if(nocopyapi || IS_REPLACEDIR(nDirType) || IS_CREATE_MP_STAT(parentpath.c_str())){
// Should rebuild directory object(except new type)
// Need to remove old dir("dir" etc) and make new dir("dir/")
std::string xattrvalue;
const char* pxattrvalue;
if(get_meta_xattr_value(path, xattrvalue)){
pxattrvalue = xattrvalue.c_str();
}else{
pxattrvalue = NULL;
}
// At first, remove directory old object
if(!nowpath.empty()){
if(0 != (result = remove_old_type_dir(nowpath, nDirType))){
return result;
}
}
if(!nowcache.empty()){
StatCache::getStatCacheData()->DelStat(nowcache);
}
// Make new directory object("dir/")
if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, atime, mctime, mctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue))){
return result;
}
}else{
std::string strSourcePath = (mount_prefix.empty() && "/" == nowpath) ? "//" : nowpath;
headers_t updatemeta;
updatemeta["x-amz-meta-mtime"] = str(mctime);
updatemeta["x-amz-meta-ctime"] = str(mctime);
updatemeta["x-amz-meta-atime"] = str(atime);
updatemeta["x-amz-copy-source"] = urlEncode(service_path + S3fsCred::GetBucket() + get_realpath(strSourcePath.c_str()));
updatemeta["x-amz-metadata-directive"] = "REPLACE";
merge_headers(meta, updatemeta, true);
// upload meta for parent directory.
if(0 != (result = put_headers(nowpath.c_str(), meta, true))){
return result;
}
StatCache::getStatCacheData()->DelStat(nowcache);
}
S3FS_MALLOCTRIM(0);
return 0;
}
static int s3fs_utimens(const char* _path, const struct timespec ts[2])
{
WTF8_ENCODE(path)
@ -2681,10 +2831,20 @@ static int s3fs_flush(const char* _path, struct fuse_file_info* fi)
AutoFdEntity autoent;
FdEntity* ent;
if(NULL != (ent = autoent.GetExistFdEntity(path, static_cast<int>(fi->fh)))){
bool is_new_file = ent->IsDirtyNewFile();
ent->UpdateMtime(true); // clear the flag not to update mtime.
ent->UpdateCtime();
result = ent->Flush(static_cast<int>(fi->fh), AutoLock::NONE, false);
StatCache::getStatCacheData()->DelStat(path);
if(is_new_file){
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to create the file(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
}
}
S3FS_MALLOCTRIM(0);
@ -2704,11 +2864,21 @@ static int s3fs_fsync(const char* _path, int datasync, struct fuse_file_info* fi
AutoFdEntity autoent;
FdEntity* ent;
if(NULL != (ent = autoent.GetExistFdEntity(path, static_cast<int>(fi->fh)))){
bool is_new_file = ent->IsDirtyNewFile();
if(0 == datasync){
ent->UpdateMtime();
ent->UpdateCtime();
}
result = ent->Flush(static_cast<int>(fi->fh), AutoLock::NONE, false);
if(is_new_file){
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to create the file(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
}
}
S3FS_MALLOCTRIM(0);
@ -2751,12 +2921,22 @@ static int s3fs_release(const char* _path, struct fuse_file_info* fi)
return -EIO;
}
bool is_new_file = ent->IsDirtyNewFile();
// TODO: correct locks held?
int result = ent->UploadPending(static_cast<int>(fi->fh), AutoLock::NONE);
if(0 != result){
S3FS_PRN_ERR("could not upload pending data(meta, etc) for pseudo_fd(%llu) / path(%s)", (unsigned long long)(fi->fh), path);
return result;
}
if(is_new_file){
// update parent directory timestamp
int update_result;
if(0 != (update_result = update_mctime_parent_directory(path))){
S3FS_PRN_ERR("succeed to create the file(%s), but could not update timestamp of its parent directory(result=%d).", path, update_result);
}
}
}
// check - for debug
@ -4214,23 +4394,21 @@ static bool set_mountpoint_attribute(struct stat& mpst)
//
static int set_bucket(const char* arg)
{
char* bucket_name = strdup(arg);
// TODO: Mutates input. Consider some other tokenization.
char *bucket_name = const_cast<char*>(arg);
if(strstr(arg, ":")){
if(strstr(arg, "://")){
S3FS_PRN_EXIT("bucket name and path(\"%s\") is wrong, it must be \"bucket[:/path]\".", arg);
free(bucket_name);
return -1;
}
if(!S3fsCred::SetBucket(strtok(bucket_name, ":"))){
S3FS_PRN_EXIT("bucket name and path(\"%s\") is wrong, it must be \"bucket[:/path]\".", arg);
free(bucket_name);
return -1;
}
char* pmount_prefix = strtok(NULL, "");
if(pmount_prefix){
if(0 == strlen(pmount_prefix) || '/' != pmount_prefix[0]){
S3FS_PRN_EXIT("path(%s) must be prefix \"/\".", pmount_prefix);
free(bucket_name);
return -1;
}
mount_prefix = pmount_prefix;
@ -4240,11 +4418,9 @@ static int set_bucket(const char* arg)
}else{
if(!S3fsCred::SetBucket(arg)){
S3FS_PRN_EXIT("bucket name and path(\"%s\") is wrong, it must be \"bucket[:/path]\".", arg);
free(bucket_name);
return -1;
}
}
free(bucket_name);
return 0;
}
@ -4763,6 +4939,10 @@ static int my_fuse_opt_proc(void* data, const char* arg, int key, struct fuse_ar
S3fsCurl::SetUnsignedPayload(true);
return 0;
}
if(0 == strcmp(arg, "update_parent_dir_stat")){
update_parent_dir_stat = true;
return 0;
}
if(is_prefix(arg, "host=")){
s3host = strchr(arg, '=') + sizeof(char);
return 0;

View File

@ -542,6 +542,12 @@ static const char help_string[] =
" check result to that file. The file path parameter can be omitted.\n"
" If omitted, the result will be output to stdout or syslog.\n"
"\n"
" update_parent_dir_stat (default is disable)\n"
" The parent directory's mtime and ctime are updated when a file or\n"
" directory is created or deleted (when the parent directory's inode is\n"
" updated).\n"
" By default, parent directory statistics are not updated.\n"
"\n"
"FUSE/mount Options:\n"
"\n"
" Most of the generic mount options described in 'man mount' are\n"

View File

@ -733,7 +733,7 @@ function test_special_characters {
# shellcheck disable=SC2010
ls 'special~' 2>&1 | grep -q 'No such file or directory'
# shellcheck disable=SC2010
ls 'specialµ' 2>&1 | grep -q 'No such file or directory'
ls 'specialμ' 2>&1 | grep -q 'No such file or directory'
)
mkdir "TOYOTA TRUCK 8.2.2"
@ -1292,6 +1292,272 @@ function test_update_chmod_opened_file() {
rm_test_file "${ALT_TEST_TEXT_FILE}"
}
function test_update_parent_directory_time_sub() {
if [ $# -ne 1 ]; then
echo "Internal error: parameter is wrong."
return 1
fi
# [NOTE]
# Skip test for mknod/mkfifo command.
# If run them, ctime/mtime of the parent directory will be updated.
#
local TEST_PARENTDIR_PARENT="${1}"
local TEST_PARENTDIR_FILE="${TEST_PARENTDIR_PARENT}/testfile"
local TEST_PARENTDIR_SYMFILE_BASE="testfile2"
local TEST_PARENTDIR_FILE_MV="${TEST_PARENTDIR_PARENT}/${TEST_PARENTDIR_SYMFILE_BASE}"
local TEST_PARENTDIR_SYMFILE="${TEST_PARENTDIR_PARENT}/symfile"
local TEST_PARENTDIR_SYMFILE_MV="${TEST_PARENTDIR_PARENT}/symfile2"
local TEST_PARENTDIR_DIR="${TEST_PARENTDIR_PARENT}/testdir"
local TEST_PARENTDIR_DIR_MV="${TEST_PARENTDIR_PARENT}/testdir2"
#
# Create file -> Update parent directory's mtime/ctime
#
local base_atime; base_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local base_ctime; base_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local base_mtime; base_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
touch "${TEST_PARENTDIR_FILE}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "creating file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Update file -> Not update parent directory's atime/mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
touch "${TEST_PARENTDIR_FILE}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" != "${after_ctime}" ] || [ "${base_mtime}" != "${after_mtime}" ]; then
echo "updating file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} == ${after_ctime} ), mtime( ${base_mtime} == ${after_mtime} )"
return 1
fi
#
# Rename file -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
mv "${TEST_PARENTDIR_FILE}" "${TEST_PARENTDIR_FILE_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "renaming file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Create symbolic link -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
ln -s "${TEST_PARENTDIR_SYMFILE_BASE}" "${TEST_PARENTDIR_SYMFILE}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "creating symbolic file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Update symbolic file -> Not update parent directory's atime/mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
touch "${TEST_PARENTDIR_SYMFILE}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" != "${after_ctime}" ] || [ "${base_mtime}" != "${after_mtime}" ]; then
echo "updating symbolic file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} == ${after_ctime} ), mtime( ${base_mtime} == ${after_mtime} )"
return 1
fi
#
# Rename symbolic link -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
mv "${TEST_PARENTDIR_SYMFILE}" "${TEST_PARENTDIR_SYMFILE_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "renaming symbolic file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Delete symbolic link -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
rm "${TEST_PARENTDIR_SYMFILE_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "deleting symbolic file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Delete file -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
rm "${TEST_PARENTDIR_FILE_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "deleting file expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Create directory -> Update parent directory's mtime/ctime
#
local base_atime; base_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local base_ctime; base_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local base_mtime; base_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
mkdir "${TEST_PARENTDIR_DIR}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "creating directory expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Update directory -> Not update parent directory's atime/mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
touch "${TEST_PARENTDIR_DIR}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" != "${after_ctime}" ] || [ "${base_mtime}" != "${after_mtime}" ]; then
echo "updating directory expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} == ${after_ctime} ), mtime( ${base_mtime} == ${after_mtime} )"
return 1
fi
#
# Rename directory -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
mv "${TEST_PARENTDIR_DIR}" "${TEST_PARENTDIR_DIR_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "renaming directory expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
#
# Delete directory -> Update parent directory's mtime/ctime
#
base_atime="${after_atime}"
base_ctime="${after_ctime}"
base_mtime="${after_mtime}"
rm -r "${TEST_PARENTDIR_DIR_MV}"
local after_atime; after_atime=$(get_atime "${TEST_PARENTDIR_PARENT}")
local after_ctime; after_ctime=$(get_ctime "${TEST_PARENTDIR_PARENT}")
local after_mtime; after_mtime=$(get_mtime "${TEST_PARENTDIR_PARENT}")
if [ "${base_atime}" != "${after_atime}" ] || [ "${base_ctime}" = "${after_ctime}" ] || [ "${base_mtime}" = "${after_mtime}" ]; then
echo "deleting directory expected updating ctime/mtime: atime( ${base_atime} == ${after_atime} ), ctime( ${base_ctime} != ${after_ctime} ), mtime( ${base_mtime} != ${after_mtime} )"
return 1
fi
return 0
}
function test_update_parent_directory_time() {
describe "Testing update time of parent directory..."
#
# Test sub directory
#
mk_test_dir
if ! test_update_parent_directory_time_sub "${TEST_DIR}"; then
echo "failed test about updating time of parent directory: ${TEST_DIR}"
return 1
fi
rm -rf "${TEST_DIR}"
#
# Test bucket top directory
#
# [NOTE]
# The current directory for test execution is "<mount point>/testrun-xxxx".
# This test checks in the directory at the top of the bucket.
#
if ! test_update_parent_directory_time_sub ".."; then
echo "failed test about updating time of parent directory: ${TEST_DIR}"
return 1
fi
return 0
}
function test_rm_rf_dir {
describe "Test that rm -rf will remove directory with contents ..."
# Create a dir with some files and directories
@ -2389,7 +2655,10 @@ function add_all_tests {
fi
add_tests test_update_directory_time_subdir
add_tests test_update_chmod_opened_file
# shellcheck disable=SC2009
if ps u -p "${S3FS_PID}" | grep -q update_parent_dir_stat; then
add_tests test_update_parent_directory_time
fi
# shellcheck disable=SC2009
if ! ps u -p "${S3FS_PID}" | grep -q use_xattr; then
add_tests test_posix_acl

View File

@ -42,7 +42,7 @@ export CACHE_DIR
export ENSURE_DISKFREE_SIZE
if [ -n "${ALL_TESTS}" ]; then
FLAGS=(
"use_cache=${CACHE_DIR} -o ensure_diskfree=${ENSURE_DISKFREE_SIZE} -o fake_diskfree=${FAKE_FREE_DISK_SIZE} -o use_xattr"
"use_cache=${CACHE_DIR} -o ensure_diskfree=${ENSURE_DISKFREE_SIZE} -o fake_diskfree=${FAKE_FREE_DISK_SIZE} -o use_xattr -o update_parent_dir_stat"
enable_content_md5
disable_noobj_cache
"max_stat_cache_size=100"