From 2e77920943ccdd0027f07759ff46316f2a2c55ca Mon Sep 17 00:00:00 2001 From: Takeshi Nakatani Date: Sat, 22 Oct 2022 21:48:02 +0900 Subject: [PATCH] Added support for xattr as POSIX ACL (#2039) --- src/s3fs.cpp | 265 ++++++++++++++++++++++++++++++--- test/integration-test-main.sh | 227 ++++++++++++++++++++++++++-- test/small-integration-test.sh | 2 +- 3 files changed, 456 insertions(+), 38 deletions(-) diff --git a/src/s3fs.cpp b/src/s3fs.cpp index b43fcfd..63896c7 100644 --- a/src/s3fs.cpp +++ b/src/s3fs.cpp @@ -123,15 +123,20 @@ static int list_bucket(const char* path, S3ObjList& head, const char* delimiter, static int directory_empty(const char* path); static int rename_large_object(const char* from, const char* to); static int create_file_object(const char* path, mode_t mode, uid_t uid, gid_t gid); -static int create_directory_object(const char* path, mode_t mode, const struct timespec& ts_atime, const struct timespec& ts_mtime, const struct timespec& ts_ctime, uid_t uid, gid_t gid); +static int create_directory_object(const char* path, mode_t mode, const struct timespec& ts_atime, const struct timespec& ts_mtime, const struct timespec& ts_ctime, uid_t uid, gid_t gid, const char* pxattrvalue); 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); +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 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); +static bool get_xattr_posix_key_value(const char* path, std::string& xattrvalue, bool default_key); +static bool build_inherited_xattr_value(const char* path, std::string& xattrvalue); static void free_xattrs(xattrs_t& xattrs); static bool parse_xattr_keyval(const std::string& xattrpair, std::string& key, PXATTRVAL& pval); static size_t parse_xattrs(const std::string& strxattrs, xattrs_t& xattrs); +static std::string raw_build_xattrs(const xattrs_t& xattrs); static std::string build_xattrs(const xattrs_t& xattrs); static int s3fs_check_service(); static bool set_mountpoint_attribute(struct stat& mpst); @@ -1029,6 +1034,12 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi meta["x-amz-meta-mtime"] = strnow; meta["x-amz-meta-ctime"] = strnow; + std::string xattrvalue; + if(build_inherited_xattr_value(path, xattrvalue)){ + S3FS_PRN_DBG("Set xattrs = %s", urlDecode(xattrvalue).c_str()); + meta["x-amz-meta-xattr"] = xattrvalue; + } + // [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. @@ -1055,7 +1066,7 @@ static int s3fs_create(const char* _path, mode_t mode, struct fuse_file_info* fi return 0; } -static int create_directory_object(const char* path, mode_t mode, const struct timespec& ts_atime, const struct timespec& ts_mtime, const struct timespec& ts_ctime, uid_t uid, gid_t gid) +static int create_directory_object(const char* path, mode_t mode, const struct timespec& ts_atime, const struct timespec& ts_mtime, const struct timespec& ts_ctime, uid_t uid, gid_t gid, const char* pxattrvalue) { S3FS_PRN_INFO1("[path=%s][mode=%04o][atime=%s][mtime=%s][ctime=%s][uid=%u][gid=%u]", path, mode, str(ts_atime).c_str(), str(ts_mtime).c_str(), str(ts_ctime).c_str(), (unsigned int)uid, (unsigned int)gid); @@ -1077,6 +1088,11 @@ static int create_directory_object(const char* path, mode_t mode, const struct t meta["x-amz-meta-mtime"] = str(ts_mtime); meta["x-amz-meta-ctime"] = str(ts_ctime); + if(pxattrvalue){ + S3FS_PRN_DBG("Set xattrs = %s", urlDecode(std::string(pxattrvalue)).c_str()); + meta["x-amz-meta-xattr"] = std::string(pxattrvalue); + } + S3fsCurl s3fscurl; return s3fscurl.PutRequest(tpath.c_str(), meta, -1); // fd=-1 means for creating zero byte object. } @@ -1104,9 +1120,17 @@ static int s3fs_mkdir(const char* _path, mode_t mode) return result; } + std::string xattrvalue; + const char* pxattrvalue; + if(get_parent_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + struct timespec now; s3fs_realtime(now); - result = create_directory_object(path, mode, now, now, now, pcxt->uid, pcxt->gid); + result = create_directory_object(path, mode, now, now, now, pcxt->uid, pcxt->gid, pxattrvalue); StatCache::getStatCacheData()->DelStat(path); S3FS_MALLOCTRIM(0); @@ -1238,6 +1262,9 @@ static int s3fs_symlink(const char* _from, const char* _to) headers["x-amz-meta-uid"] = str(pcxt->uid); headers["x-amz-meta-gid"] = str(pcxt->gid); + // [NOTE] + // Symbolic links do not set xattrs. + // open tmpfile std::string strFrom; { // scope for AutoFdEntity @@ -1303,6 +1330,12 @@ static int rename_object(const char* from, const char* to, bool update_ctime) meta["Content-Type"] = S3fsCurl::LookupMimeType(std::string(to)); meta["x-amz-metadata-directive"] = "REPLACE"; + std::string xattrvalue; + if(get_meta_xattr_value(from, xattrvalue)){ + S3FS_PRN_DBG("Set xattrs = %s", urlDecode(xattrvalue).c_str()); + meta["x-amz-meta-xattr"] = xattrvalue; + } + // [NOTE] // If it has a cache, open it first and leave it open until rename. // The cache is renamed after put_header, because it must be open @@ -1444,7 +1477,7 @@ static int rename_large_object(const char* from, const char* to) return result; } -static int clone_directory_object(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) { int result = -1; struct stat stbuf; @@ -1466,7 +1499,7 @@ static int clone_directory_object(const char* from, const char* to, bool update_ }else{ set_stat_to_timespec(stbuf, ST_TYPE_CTIME, ts_ctime); } - result = create_directory_object(to, stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid); + result = create_directory_object(to, stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue); StatCache::getStatCacheData()->DelStat(to); @@ -1565,11 +1598,19 @@ static int rename_directory(const char* from, const char* to) // rename directory objects. for(mn_cur = mn_head; mn_cur; mn_cur = mn_cur->next){ if(mn_cur->is_dir && mn_cur->old_path && '\0' != mn_cur->old_path[0]){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(mn_cur->old_path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + // [NOTE] // The ctime is updated only for the top (from) directory. // Other than that, it will not be updated. // - if(0 != (result = clone_directory_object(mn_cur->old_path, mn_cur->new_path, (strfrom == mn_cur->old_path)))){ + if(0 != (result = clone_directory_object(mn_cur->old_path, mn_cur->new_path, (strfrom == mn_cur->old_path), pxattrvalue))){ S3FS_PRN_ERR("clone_directory_object returned an error(%d)", result); free_mvnodes(mn_head); return result; @@ -1708,6 +1749,13 @@ static int s3fs_chmod(const char* _path, mode_t mode) } if(S_ISDIR(stbuf.st_mode) && (IS_REPLACEDIR(nDirType) || IS_CREATE_MP_STAT(path))){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } if(IS_REPLACEDIR(nDirType)){ // Should rebuild directory object(except new type) // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -1727,7 +1775,7 @@ static int s3fs_chmod(const char* _path, mode_t mode) set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); s3fs_realtime(ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue))){ return result; } }else{ @@ -1807,6 +1855,14 @@ static int s3fs_chmod_nocopy(const char* _path, mode_t mode) } if(S_ISDIR(stbuf.st_mode)){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + if(IS_REPLACEDIR(nDirType)){ // Should rebuild all directory object // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -1826,7 +1882,7 @@ static int s3fs_chmod_nocopy(const char* _path, mode_t mode) set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); s3fs_realtime(ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue))){ return result; } }else{ @@ -1897,6 +1953,14 @@ static int s3fs_chown(const char* _path, uid_t uid, gid_t gid) } if(S_ISDIR(stbuf.st_mode) && (IS_REPLACEDIR(nDirType) || IS_CREATE_MP_STAT(path))){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + if(IS_REPLACEDIR(nDirType)){ // Should rebuild directory object(except new type) // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -1916,7 +1980,7 @@ static int s3fs_chown(const char* _path, uid_t uid, gid_t gid) set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); s3fs_realtime(ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, uid, gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, uid, gid, pxattrvalue))){ return result; } }else{ @@ -2003,6 +2067,14 @@ static int s3fs_chown_nocopy(const char* _path, uid_t uid, gid_t gid) } if(S_ISDIR(stbuf.st_mode)){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + if(IS_REPLACEDIR(nDirType)){ // Should rebuild all directory object // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -2022,7 +2094,7 @@ static int s3fs_chown_nocopy(const char* _path, uid_t uid, gid_t gid) set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); s3fs_realtime(ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, uid, gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, uid, gid, pxattrvalue))){ return result; } }else{ @@ -2115,6 +2187,14 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) } if(S_ISDIR(stbuf.st_mode) && (IS_REPLACEDIR(nDirType) || IS_CREATE_MP_STAT(path))){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + if(IS_REPLACEDIR(nDirType)){ // Should rebuild directory object(except new type) // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -2127,7 +2207,7 @@ static int s3fs_utimens(const char* _path, const struct timespec ts[2]) StatCache::getStatCacheData()->DelStat(nowcache); // Make new directory object("dir/") - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, atime, mtime, ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, atime, mtime, ctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue))){ return result; } }else{ @@ -2239,6 +2319,14 @@ static int s3fs_utimens_nocopy(const char* _path, const struct timespec ts[2]) } if(S_ISDIR(stbuf.st_mode)){ + std::string xattrvalue; + const char* pxattrvalue; + if(get_meta_xattr_value(path, xattrvalue)){ + pxattrvalue = xattrvalue.c_str(); + }else{ + pxattrvalue = NULL; + } + if(IS_REPLACEDIR(nDirType)){ // Should rebuild all directory object // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -2251,7 +2339,7 @@ static int s3fs_utimens_nocopy(const char* _path, const struct timespec ts[2]) StatCache::getStatCacheData()->DelStat(nowcache); // Make new directory object("dir/") - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, atime, mtime, ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, atime, mtime, ctime, stbuf.st_uid, stbuf.st_gid, pxattrvalue))){ return result; } }else{ @@ -3094,6 +3182,123 @@ static int remote_mountpath_exists(const char* path) return 0; } +static bool get_meta_xattr_value(const char* path, std::string& rawvalue) +{ + if(!path || '\0' == path[0]){ + S3FS_PRN_ERR("path is empty."); + return false; + } + S3FS_PRN_DBG("[path=%s]", path); + + rawvalue.erase(); + + headers_t meta; + if(0 != get_object_attribute(path, NULL, &meta)){ + S3FS_PRN_ERR("Failed to get object(%s) headers", path); + return false; + } + + headers_t::const_iterator iter; + if(meta.end() == (iter = meta.find("x-amz-meta-xattr"))){ + return false; + } + rawvalue = iter->second; + return true; +} + +static bool get_parent_meta_xattr_value(const char* path, std::string& rawvalue) +{ + if(0 == strcmp(path, "/") || 0 == strcmp(path, ".")){ + // path is mount point, thus does not have parent. + return false; + } + + std::string parent = mydirname(path); + if(parent.empty()){ + S3FS_PRN_ERR("Could not get parent path for %s.", path); + return false; + } + return get_meta_xattr_value(parent.c_str(), rawvalue); +} + +static bool get_xattr_posix_key_value(const char* path, std::string& xattrvalue, bool default_key) +{ + xattrvalue.erase(); + + std::string rawvalue; + if(!get_meta_xattr_value(path, rawvalue)){ + return false; + } + + xattrs_t xattrs; + if(0 == parse_xattrs(rawvalue, xattrs)){ + return false; + } + + std::string targetkey; + if(default_key){ + targetkey = "system.posix_acl_default"; + }else{ + targetkey = "system.posix_acl_access"; + } + + xattrs_t::iterator iter; + if(xattrs.end() == (iter = xattrs.find(targetkey)) || !(iter->second)){ + free_xattrs(xattrs); + return false; + } + + // convert value by base64 + char* base64val = s3fs_base64((iter->second)->pvalue, (iter->second)->length); + if(!base64val){ + free_xattrs(xattrs); + return false; + } + free_xattrs(xattrs); + + xattrvalue = base64val; + delete[] base64val; + + return true; +} + +// [NOTE] +// Converts and returns the POSIX ACL default(system.posix_acl_default) value of +// the parent directory as a POSIX ACL(system.posix_acl_access) value. +// Returns false if the parent directory has no POSIX ACL defaults. +// +static bool build_inherited_xattr_value(const char* path, std::string& xattrvalue) +{ + S3FS_PRN_DBG("[path=%s]", path); + + xattrvalue.erase(); + + if(0 == strcmp(path, "/") || 0 == strcmp(path, ".")){ + // path is mount point, thus does not have parent. + return false; + } + + std::string parent = mydirname(path); + if(parent.empty()){ + S3FS_PRN_ERR("Could not get parent path for %s.", path); + return false; + } + + // get parent's "system.posix_acl_default" value(base64'd). + std::string parent_default_value; + if(!get_xattr_posix_key_value(parent.c_str(), parent_default_value, true)){ + return false; + } + + // build "system.posix_acl_access" from parent's default value + std::string raw_xattr_value; + raw_xattr_value = "{\"system.posix_acl_access\":\""; + raw_xattr_value += parent_default_value; + raw_xattr_value += "\"}"; + + xattrvalue = urlEncode(raw_xattr_value); + return true; +} static void free_xattrs(xattrs_t& xattrs) { @@ -3163,16 +3368,16 @@ static size_t parse_xattrs(const std::string& strxattrs, xattrs_t& xattrs) return xattrs.size(); } -static std::string build_xattrs(const xattrs_t& xattrs) +static std::string raw_build_xattrs(const xattrs_t& xattrs) { - std::string strxattrs("{"); - - bool is_set = false; + std::string strxattrs; + bool is_set = false; for(xattrs_t::const_iterator iter = xattrs.begin(); iter != xattrs.end(); ++iter){ if(is_set){ strxattrs += ','; }else{ - is_set = true; + is_set = true; + strxattrs = "{"; } strxattrs += '\"'; strxattrs += iter->first; @@ -3187,8 +3392,18 @@ static std::string build_xattrs(const xattrs_t& xattrs) } strxattrs += '\"'; } - strxattrs += '}'; + if(is_set){ + strxattrs += "}"; + } + return strxattrs; +} +static std::string build_xattrs(const xattrs_t& xattrs) +{ + std::string strxattrs = raw_build_xattrs(xattrs); + if(strxattrs.empty()){ + strxattrs = "{}"; + } strxattrs = urlEncode(strxattrs); return strxattrs; @@ -3240,6 +3455,8 @@ static int set_xattrs_to_header(headers_t& meta, const char* name, const char* v // build new strxattrs(not encoded) and set it to headers_t meta["x-amz-meta-xattr"] = build_xattrs(xattrs); + S3FS_PRN_DBG("Set xattrs(after adding %s key) = %s", name, raw_build_xattrs(xattrs).c_str()); + free_xattrs(xattrs); return 0; @@ -3311,7 +3528,7 @@ static int s3fs_setxattr(const char* path, const char* name, const char* value, set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); set_stat_to_timespec(stbuf, ST_TYPE_CTIME, ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid, NULL))){ return result; } @@ -3421,6 +3638,8 @@ static int s3fs_getxattr(const char* path, const char* name, char* value, size_t parse_xattrs(strxattrs, xattrs); + S3FS_PRN_DBG("Get xattrs = %s", raw_build_xattrs(xattrs).c_str()); + // search name std::string strname = name; xattrs_t::iterator xiter = xattrs.find(strname); @@ -3485,6 +3704,8 @@ static int s3fs_listxattr(const char* path, char* list, size_t size) parse_xattrs(strxattrs, xattrs); + S3FS_PRN_DBG("Get xattrs = %s", raw_build_xattrs(xattrs).c_str()); + // calculate total name length size_t total = 0; for(xattrs_t::const_iterator xiter = xattrs.begin(); xiter != xattrs.end(); ++xiter){ @@ -3582,6 +3803,8 @@ static int s3fs_removexattr(const char* path, const char* name) delete xiter->second; xattrs.erase(xiter); + S3FS_PRN_DBG("Reset xattrs(after delete %s key) = %s", name, raw_build_xattrs(xattrs).c_str()); + if(S_ISDIR(stbuf.st_mode) && IS_REPLACEDIR(nDirType)){ // Should rebuild directory object(except new type) // Need to remove old dir("dir" etc) and make new dir("dir/") @@ -3600,7 +3823,7 @@ static int s3fs_removexattr(const char* path, const char* name) set_stat_to_timespec(stbuf, ST_TYPE_MTIME, ts_mtime); set_stat_to_timespec(stbuf, ST_TYPE_CTIME, ts_ctime); - if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid))){ + if(0 != (result = create_directory_object(newpath.c_str(), stbuf.st_mode, ts_atime, ts_mtime, ts_ctime, stbuf.st_uid, stbuf.st_gid, NULL))){ free_xattrs(xattrs); return result; } diff --git a/test/integration-test-main.sh b/test/integration-test-main.sh index 5bc3da5..c8fb1e4 100755 --- a/test/integration-test-main.sh +++ b/test/integration-test-main.sh @@ -1309,6 +1309,196 @@ function test_rm_rf_dir { fi } +function test_posix_acl { + describe "Testing posix acl function ..." + + #------------------------------------------------------ + # Directory + #------------------------------------------------------ + local POSIX_ACL_TEST_DIR1="posix_acl_dir1" + local POSIX_ACL_TEST_DIR2="posix_acl_dir2" + mkdir "${POSIX_ACL_TEST_DIR1}" + + # + # Set posix acl(not default) + # + setfacl -m "u:${USER}:rwx" "${POSIX_ACL_TEST_DIR1}" + if ! getfacl "${POSIX_ACL_TEST_DIR1}" | grep -q "^user:${USER}:rwx"; then + echo "Could not set posix acl(not default) to ${POSIX_ACL_TEST_DIR1} directory" + return 1 + fi + + # + # Set posix acl(default) + # + setfacl -d -m "u:${USER}:rwx" "${POSIX_ACL_TEST_DIR1}" + if ! getfacl "${POSIX_ACL_TEST_DIR1}" | grep -q "^default:user:${USER}:rwx"; then + echo "Could not set posix acl(default) to ${POSIX_ACL_TEST_DIR1} directory" + return 1 + fi + + # + # Rename + # + mv "${POSIX_ACL_TEST_DIR1}" "${POSIX_ACL_TEST_DIR2}" + if ! getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^user:${USER}:rwx"; then + echo "Could not move with posix acl(not default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + if ! getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^default:user:${USER}:rwx"; then + echo "Could not move with posix acl(default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + # + # Copy with permission + # + cp -rp "${POSIX_ACL_TEST_DIR2}" "${POSIX_ACL_TEST_DIR1}" + if ! getfacl "${POSIX_ACL_TEST_DIR1}" | grep -q "^user:${USER}:rwx"; then + echo "Could not copy with posix acl(not default) to ${POSIX_ACL_TEST_DIR1} directory" + return 1 + fi + if ! getfacl "${POSIX_ACL_TEST_DIR1}" | grep -q "^default:user:${USER}:rwx"; then + echo "Could not copy with posix acl(default) to ${POSIX_ACL_TEST_DIR1} directory" + return 1 + fi + + # + # Overwrite posix acl(not default) + # + setfacl -m "u:${USER}:r-x" "${POSIX_ACL_TEST_DIR2}" + if ! getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^user:${USER}:r-x"; then + echo "Could not set posix acl(not default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + # + # Overwrite posix acl(default) + # + setfacl -d -m "u:${USER}:r-x" "${POSIX_ACL_TEST_DIR2}" + if ! getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^default:user:${USER}:r-x"; then + echo "Could not set posix acl(default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + # + # Remove posix acl(default) + # + setfacl -k "${POSIX_ACL_TEST_DIR2}" + if getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^default:user:${USER}"; then + echo "Could not remove posix acl(default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + # + # Remove posix acl(all) + # + setfacl -b "${POSIX_ACL_TEST_DIR2}" + if getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^user:${USER}"; then + echo "Could not remove posix acl(all) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + # + # Copy without permission + # + rm -rf "${POSIX_ACL_TEST_DIR2}" + cp -r "${POSIX_ACL_TEST_DIR1}" "${POSIX_ACL_TEST_DIR2}" + if getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^default:user:${USER}"; then + echo "Could not copy without posix acl(default) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + if getfacl "${POSIX_ACL_TEST_DIR2}" | grep -q "^user:${USER}"; then + echo "Could not copy without posix acl(all) to ${POSIX_ACL_TEST_DIR2} directory" + return 1 + fi + + #------------------------------------------------------ + # File + #------------------------------------------------------ + local POSIX_ACL_TEST_FILE1="posix_acl_dir1/posix_acl_file1" + local POSIX_ACL_TEST_FILE2="posix_acl_dir1/posix_acl_file2" + local POSIX_ACL_TEST_FILE3="posix_acl_dir2/posix_acl_file3" + local POSIX_ACL_TEST_FILE4="posix_acl_dir2/posix_acl_file4" + mkdir "${POSIX_ACL_TEST_DIR2}" + touch "${POSIX_ACL_TEST_FILE1}" + + # + # Check default inherited posix acl + # + if ! getfacl "${POSIX_ACL_TEST_FILE1}" | grep -q "^user:${USER}:rwx"; then + echo "Could not set posix acl(inherited default) to ${POSIX_ACL_TEST_FILE1} file" + return 1 + fi + + # + # Overwrite posix acl + # + setfacl -m "u:${USER}:r-x" "${POSIX_ACL_TEST_FILE1}" + if ! getfacl "${POSIX_ACL_TEST_FILE1}" | grep -q "^user:${USER}:r-x"; then + echo "Could not overwrite posix acl to ${POSIX_ACL_TEST_FILE1} file" + return 1 + fi + + # + # Rename + # + mv "${POSIX_ACL_TEST_FILE1}" "${POSIX_ACL_TEST_FILE2}" + if ! getfacl "${POSIX_ACL_TEST_FILE2}" | grep -q "^user:${USER}:r-x"; then + echo "Could not move with posix acl to ${POSIX_ACL_TEST_FILE2} file" + return 1 + fi + + # + # Copy with permission + # + cp -p "${POSIX_ACL_TEST_FILE2}" "${POSIX_ACL_TEST_FILE1}" + if ! getfacl "${POSIX_ACL_TEST_FILE1}" | grep -q "^user:${USER}:r-x"; then + echo "Could not copy with posix acl to ${POSIX_ACL_TEST_FILE1} file" + return 1 + fi + + # + # Remove posix acl + # + setfacl -b "${POSIX_ACL_TEST_FILE2}" + if getfacl "${POSIX_ACL_TEST_FILE2}" | grep -q "^default:user:${USER}"; then + echo "Could not remove posix acl to ${POSIX_ACL_TEST_FILE2} file" + return 1 + fi + + # + # Copy without permission(set parent directory default acl) + # + rm -f "${POSIX_ACL_TEST_FILE2}" + cp "${POSIX_ACL_TEST_FILE1}" "${POSIX_ACL_TEST_FILE2}" + if ! getfacl "${POSIX_ACL_TEST_FILE2}" | grep -q "^user:${USER}:rwx"; then + echo "Could not copy without posix acl(inherited parent) to ${POSIX_ACL_TEST_FILE2} file" + return 1 + fi + + # + # Copy with permission(to no-acl directory) + # + cp -p "${POSIX_ACL_TEST_FILE1}" "${POSIX_ACL_TEST_FILE3}" + if ! getfacl "${POSIX_ACL_TEST_FILE3}" | grep -q "^user:${USER}:r-x"; then + echo "Could not copy with posix acl to ${POSIX_ACL_TEST_FILE3} file in no-acl directory" + return 1 + fi + + # + # Copy without permission(to no-acl directory) + # + cp "${POSIX_ACL_TEST_FILE1}" "${POSIX_ACL_TEST_FILE4}" + if getfacl "${POSIX_ACL_TEST_FILE4}" | grep -q "^user:${USER}"; then + echo "Could not copy without posix acl to ${POSIX_ACL_TEST_FILE4} file in no-acl directory" + return 1 + fi + + rm -rf "${POSIX_ACL_TEST_DIR1}" + rm -rf "${POSIX_ACL_TEST_DIR2}" +} + function test_copy_file { describe "Test simple copy ..." @@ -1764,8 +1954,8 @@ function test_not_existed_dir_obj() { # with "compat_dir", found directories and files # - # Top directory - # shellcheck disable=SC2010 + # Top directory + # shellcheck disable=SC2010 if ! ls -1 | grep -q '^not_existed_dir_single$'; then echo "Expect to find \"not_existed_dir_single\" directory, but it is not found" return 1; @@ -1776,8 +1966,8 @@ function test_not_existed_dir_obj() { return 1; fi - # Single nest directory - # shellcheck disable=SC2010 + # Single nest directory + # shellcheck disable=SC2010 if ! ls -d not_existed_dir_single | grep -q '^not_existed_dir_single$'; then echo "Expect to find \"not_existed_dir_single\" directory, but it is not found" return 1; @@ -1793,8 +1983,8 @@ function test_not_existed_dir_obj() { return 1; fi - # Double nest directory - # shellcheck disable=SC2010 + # Double nest directory + # shellcheck disable=SC2010 if ! ls -d not_existed_dir_parent | grep -q '^not_existed_dir_parent'; then echo "Expect to find \"not_existed_dir_parent\" directory, but it is not found" return 1; @@ -1820,8 +2010,8 @@ function test_not_existed_dir_obj() { return 1; fi - rm -rf not_existed_dir_single - rm -rf not_existed_dir_parent + rm -rf not_existed_dir_single + rm -rf not_existed_dir_parent else # @@ -1832,8 +2022,8 @@ function test_not_existed_dir_obj() { # And if specify a file full path, it will be found. # - # Top directory - # shellcheck disable=SC2010 + # Top directory + # shellcheck disable=SC2010 if ls -1 | grep -q '^not_existed_dir_single$'; then echo "Expect to not find \"not_existed_dir_single\" directory, but it is found" return 1; @@ -1844,8 +2034,8 @@ function test_not_existed_dir_obj() { return 1; fi - # Single nest directory - # shellcheck disable=SC2010 + # Single nest directory + # shellcheck disable=SC2010 if ! ls -d not_existed_dir_single | grep -q '^not_existed_dir_single$'; then echo "Expect to find \"not_existed_dir_single\" directory, but it is not found" return 1; @@ -1861,8 +2051,8 @@ function test_not_existed_dir_obj() { return 1; fi - # Double nest directory - # shellcheck disable=SC2010 + # Double nest directory + # shellcheck disable=SC2010 if ! ls -d not_existed_dir_parent | grep -q '^not_existed_dir_parent'; then echo "Expect to find \"not_existed_dir_parent\" directory, but it is not found" return 1; @@ -1888,11 +2078,11 @@ function test_not_existed_dir_obj() { return 1; fi - rm -rf not_existed_dir_single + rm -rf not_existed_dir_single # [NOTE] # This case could not remove sub directory, then below command will be failed. - #rm -rf not_existed_dir_parent + #rm -rf not_existed_dir_parent fi } @@ -2190,6 +2380,11 @@ function add_all_tests { 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 use_xattr; then + add_tests test_posix_acl + fi + add_tests test_rm_rf_dir add_tests test_copy_file add_tests test_write_after_seek_ahead diff --git a/test/small-integration-test.sh b/test/small-integration-test.sh index c51a809..98ffbdd 100755 --- a/test/small-integration-test.sh +++ b/test/small-integration-test.sh @@ -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}" + "use_cache=${CACHE_DIR} -o ensure_diskfree=${ENSURE_DISKFREE_SIZE} -o fake_diskfree=${FAKE_FREE_DISK_SIZE} -o use_xattr" enable_content_md5 disable_noobj_cache "max_stat_cache_size=100"