From 8cfe15a59cce2e7c295d7c8108a39f75ab843a44 Mon Sep 17 00:00:00 2001 From: Anoop C S Date: Mon, 23 Sep 2024 12:36:55 +0530 Subject: [PATCH] client: Gracefully handle empty pathname for statxat() man statx(2)[1] says the following: . . . AT_EMPTY_PATH 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 _getattr(). Fixes: https://tracker.ceph.com/issues/68189 Review with: git show -w [1] https://www.man7.org/linux/man-pages/man2/statx.2.html Signed-off-by: Anoop C S (cherry picked from commit edd7fe76c4919bc243377c6d7aae20b0606b89c3) Conflicts: src/client/Client.cc - path_walk() refactor from https://github.com/ceph/ceph/pull/62095 included the required core changes. --- src/include/cephfs/ceph_ll_client.h | 3 +++ src/include/cephfs/libcephfs.h | 2 +- src/libcephfs.cc | 4 ++++ src/test/libcephfs/test.cc | 21 +++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/include/cephfs/ceph_ll_client.h b/src/include/cephfs/ceph_ll_client.h index ac5b7c22471..458edce590b 100644 --- a/src/include/cephfs/ceph_ll_client.h +++ b/src/include/cephfs/ceph_ll_client.h @@ -110,6 +110,9 @@ struct ceph_statx { * others in the future, we disallow setting any that aren't recognized. */ #define CEPH_REQ_FLAG_MASK (AT_SYMLINK_NOFOLLOW|AT_STATX_DONT_SYNC) +#if defined(__linux__) && defined(AT_EMPTY_PATH) +#define CEPH_AT_EMPTY_PATH (CEPH_REQ_FLAG_MASK|AT_EMPTY_PATH) +#endif /* fallocate mode flags */ #ifndef FALLOC_FL_KEEP_SIZE diff --git a/src/include/cephfs/libcephfs.h b/src/include/cephfs/libcephfs.h index 28f7e6adb0e..0a08e89566c 100644 --- a/src/include/cephfs/libcephfs.h +++ b/src/include/cephfs/libcephfs.h @@ -1013,7 +1013,7 @@ int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx, * @param relpath to the file/directory to get statistics of * @param stx the ceph_statx struct that will be filled in with the file's statistics. * @param want bitfield of CEPH_STATX_* flags showing designed attributes - * @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_DONT_SYNC and AT_SYMLINK_NOFOLLOW) + * @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_DONT_SYNC, AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH) * @returns 0 on success or negative error code on failure. */ int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath, diff --git a/src/libcephfs.cc b/src/libcephfs.cc index 2cdaff51dc8..3c3cfe4d35a 100644 --- a/src/libcephfs.cc +++ b/src/libcephfs.cc @@ -1138,7 +1138,11 @@ extern "C" int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const cha { if (!cmount->is_mounted()) return -ENOTCONN; +#ifdef CEPH_AT_EMPTY_PATH + if (flags & ~CEPH_AT_EMPTY_PATH) +#else if (flags & ~CEPH_REQ_FLAG_MASK) +#endif return -EINVAL; return cmount->get_client()->statxat(dirfd, relpath, stx, cmount->default_perms, want, flags); diff --git a/src/test/libcephfs/test.cc b/src/test/libcephfs/test.cc index cb684d338ce..9a74603c25b 100644 --- a/src/test/libcephfs/test.cc +++ b/src/test/libcephfs/test.cc @@ -2931,6 +2931,19 @@ TEST(LibCephFS, Statxat) { ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR); ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_2, &stx, 0, 0), 0); ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG); + // test relative to root with empty relpath +#if defined(__linux__) && defined(AT_EMPTY_PATH) + int dir_fd = ceph_openat(cmount, fd, dir_name, O_DIRECTORY | O_RDONLY, 0); + ASSERT_LE(0, dir_fd); + ASSERT_EQ(ceph_statxat(cmount, dir_fd, "", &stx, 0, AT_EMPTY_PATH), 0); + ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR); + ASSERT_EQ(0, ceph_close(cmount, dir_fd)); + int file_fd = ceph_openat(cmount, fd, rel_file_name_2, O_RDONLY, 0); + ASSERT_LE(0, file_fd); + ASSERT_EQ(ceph_statxat(cmount, file_fd, "", &stx, 0, AT_EMPTY_PATH), 0); + ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG); + ASSERT_EQ(0, ceph_close(cmount, file_fd)); +#endif ASSERT_EQ(0, ceph_close(cmount, fd)); // test relative to dir @@ -2938,6 +2951,14 @@ TEST(LibCephFS, Statxat) { ASSERT_LE(0, fd); ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_1, &stx, 0, 0), 0); ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG); + // test relative to dir with empty relpath +#if defined(__linux__) && defined(AT_EMPTY_PATH) + int rel_file_fd = ceph_openat(cmount, fd, rel_file_name_1, O_RDONLY, 0); + ASSERT_LE(0, rel_file_fd); + ASSERT_EQ(ceph_statxat(cmount, rel_file_fd, "", &stx, 0, AT_EMPTY_PATH), 0); + ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG); + ASSERT_EQ(0, ceph_close(cmount, rel_file_fd)); +#endif // delete the dirtree, recreate and verify ASSERT_EQ(0, ceph_unlink(cmount, file_path)); -- 2.39.5