From: Anoop C S Date: Fri, 20 Sep 2024 08:49:01 +0000 (+0530) Subject: client: Gracefully handle empty pathname for chownat() X-Git-Tag: v20.0.0~516^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=829f38899226fcd1f603ba446b018f53c5b0921d;p=ceph.git client: Gracefully handle empty pathname for chownat() man fchownat(2)[1] says the following: . . . AT_EMPTY_PATH (since Linux 2.6.39) If pathname is an empty string, operate on the file referred to by dirfd (which may have been obtained using the open(2) O_PATH flag). In this case, dirfd can refer to any type of file, not just a directory. If dirfd is AT_FDCWD, the call operates on the current working directory. . . . Look out for an empty pathname and use the relative fd's inode in the presence of AT_EMPTY_PATH flag before calling internal _setattr(). Fixes: https://tracker.ceph.com/issues/68189 Review with: git show -w [1] https://www.man7.org/linux/man-pages/man2/fchownat.2.html Signed-off-by: Anoop C S --- diff --git a/src/client/Client.cc b/src/client/Client.cc index 21555d0d07c..ae1f3043d8f 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -8907,7 +8907,6 @@ int Client::chownat(int dirfd, const char *relpath, uid_t new_uid, gid_t new_gid tout(cct) << new_gid << std::endl; tout(cct) << flags << std::endl; - filepath path(relpath); InodeRef in; InodeRef dirinode; @@ -8917,10 +8916,24 @@ int Client::chownat(int dirfd, const char *relpath, uid_t new_uid, gid_t new_gid return r; } - r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), 0, dirinode); - if (r < 0) { - return r; + if (!strcmp(relpath, "")) { +#if defined(__linux__) && defined(AT_EMPTY_PATH) + if (flags & AT_EMPTY_PATH) { + in = dirinode; + goto out; + } +#endif + return -CEPHFS_ENOENT; + } else { + filepath path(relpath); + r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), 0, dirinode); + if (r < 0) { + return r; + } } + +out: + struct stat attr; attr.st_uid = new_uid; attr.st_gid = new_gid; diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h index ba0b76e072b..bf2783691e8 100644 --- a/src/include/cephfs/libcephfs.h +++ b/src/include/cephfs/libcephfs.h @@ -1104,7 +1104,7 @@ int ceph_lchown(struct ceph_mount_info *cmount, const char *path, int uid, int g * @param relpath the relpath of the file/directory to change the ownership of. * @param uid the user id to set on the file/directory. * @param gid the group id to set on the file/directory. - * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW) + * @param flags bitfield that can be used to set AT_* modifier flags (AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH) * @returns 0 on success or negative error code on failure. */ int ceph_chownat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, diff --git a/src/test/libcephfs/test.cc b/src/test/libcephfs/test.cc index 6f10d2bbd4e..fb9d3eb05ae 100644 --- a/src/test/libcephfs/test.cc +++ b/src/test/libcephfs/test.cc @@ -3265,6 +3265,13 @@ TEST(LibCephFS, Chownat) { // change ownership to nobody -- we assume nobody exists and id is always 65534 ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0); ASSERT_EQ(ceph_chownat(cmount, fd, rel_file_path, 65534, 65534, 0), 0); + // change relative fd ownership with AT_EMPTY_PATH +#if defined(__linux__) && defined(AT_EMPTY_PATH) + int file_fd = ceph_openat(cmount, fd, rel_file_path, O_RDONLY, 0); + ASSERT_LE(0, file_fd); + ASSERT_EQ(ceph_chownat(cmount, file_fd, "", 65534, 65534, AT_EMPTY_PATH), 0); + ceph_close(cmount, file_fd); +#endif ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0); ceph_close(cmount, fd);