Skip to content

Commit edd7fe7

Browse files
committed
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 <[email protected]>
1 parent 92c5ab9 commit edd7fe7

File tree

5 files changed

+47
-6
lines changed

5 files changed

+47
-6
lines changed

src/client/Client.cc

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12243,19 +12243,32 @@ int Client::statxat(int dirfd, const char *relpath,
1224312243

1224412244
unsigned mask = statx_to_mask(flags, want);
1224512245

12246+
InodeRef in;
1224612247
InodeRef dirinode;
1224712248
std::scoped_lock lock(client_lock);
1224812249
int r = get_fd_inode(dirfd, &dirinode);
1224912250
if (r < 0) {
1225012251
return r;
1225112252
}
1225212253

12253-
InodeRef in;
12254-
filepath path(relpath);
12255-
r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), mask, dirinode);
12256-
if (r < 0) {
12257-
return r;
12254+
if (!strcmp(relpath, "")) {
12255+
#if defined(__linux__) && defined(AT_EMPTY_PATH)
12256+
if (flags & AT_EMPTY_PATH) {
12257+
in = dirinode;
12258+
goto out;
12259+
}
12260+
#endif
12261+
return -CEPHFS_ENOENT;
12262+
} else {
12263+
filepath path(relpath);
12264+
r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), mask, dirinode);
12265+
if (r < 0) {
12266+
return r;
12267+
}
1225812268
}
12269+
12270+
out:
12271+
1225912272
r = _getattr(in, mask, perms);
1226012273
if (r < 0) {
1226112274
ldout(cct, 3) << __func__ << " exit on error!" << dendl;

src/include/cephfs/ceph_ll_client.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ struct ceph_statx {
110110
* others in the future, we disallow setting any that aren't recognized.
111111
*/
112112
#define CEPH_REQ_FLAG_MASK (AT_SYMLINK_NOFOLLOW|AT_STATX_DONT_SYNC)
113+
#if defined(__linux__) && defined(AT_EMPTY_PATH)
114+
#define CEPH_AT_EMPTY_PATH (CEPH_REQ_FLAG_MASK|AT_EMPTY_PATH)
115+
#endif
113116

114117
/* fallocate mode flags */
115118
#ifndef FALLOC_FL_KEEP_SIZE

src/include/cephfs/libcephfs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,7 @@ int ceph_fstatx(struct ceph_mount_info *cmount, int fd, struct ceph_statx *stx,
937937
* @param relpath to the file/directory to get statistics of
938938
* @param stx the ceph_statx struct that will be filled in with the file's statistics.
939939
* @param want bitfield of CEPH_STATX_* flags showing designed attributes
940-
* @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_DONT_SYNC and AT_SYMLINK_NOFOLLOW)
940+
* @param flags bitfield that can be used to set AT_* modifier flags (AT_STATX_DONT_SYNC, AT_SYMLINK_NOFOLLOW and AT_EMPTY_PATH)
941941
* @returns 0 on success or negative error code on failure.
942942
*/
943943
int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const char *relpath,

src/libcephfs.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,11 @@ extern "C" int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const cha
982982
{
983983
if (!cmount->is_mounted())
984984
return -CEPHFS_ENOTCONN;
985+
#ifdef CEPH_AT_EMPTY_PATH
986+
if (flags & ~CEPH_AT_EMPTY_PATH)
987+
#else
985988
if (flags & ~CEPH_REQ_FLAG_MASK)
989+
#endif
986990
return -CEPHFS_EINVAL;
987991
return cmount->get_client()->statxat(dirfd, relpath, stx, cmount->default_perms,
988992
want, flags);

src/test/libcephfs/test.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,13 +2706,34 @@ TEST(LibCephFS, Statxat) {
27062706
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR);
27072707
ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_2, &stx, 0, 0), 0);
27082708
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
2709+
// test relative to root with empty relpath
2710+
#if defined(__linux__) && defined(AT_EMPTY_PATH)
2711+
int dir_fd = ceph_openat(cmount, fd, dir_name, O_DIRECTORY | O_RDONLY, 0);
2712+
ASSERT_LE(0, dir_fd);
2713+
ASSERT_EQ(ceph_statxat(cmount, dir_fd, "", &stx, 0, AT_EMPTY_PATH), 0);
2714+
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFDIR);
2715+
ASSERT_EQ(0, ceph_close(cmount, dir_fd));
2716+
int file_fd = ceph_openat(cmount, fd, rel_file_name_2, O_RDONLY, 0);
2717+
ASSERT_LE(0, file_fd);
2718+
ASSERT_EQ(ceph_statxat(cmount, file_fd, "", &stx, 0, AT_EMPTY_PATH), 0);
2719+
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
2720+
ASSERT_EQ(0, ceph_close(cmount, file_fd));
2721+
#endif
27092722
ASSERT_EQ(0, ceph_close(cmount, fd));
27102723

27112724
// test relative to dir
27122725
fd = ceph_open(cmount, dir_path, O_DIRECTORY | O_RDONLY, 0);
27132726
ASSERT_LE(0, fd);
27142727
ASSERT_EQ(ceph_statxat(cmount, fd, rel_file_name_1, &stx, 0, 0), 0);
27152728
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
2729+
// test relative to dir with empty relpath
2730+
#if defined(__linux__) && defined(AT_EMPTY_PATH)
2731+
int rel_file_fd = ceph_openat(cmount, fd, rel_file_name_1, O_RDONLY, 0);
2732+
ASSERT_LE(0, rel_file_fd);
2733+
ASSERT_EQ(ceph_statxat(cmount, rel_file_fd, "", &stx, 0, AT_EMPTY_PATH), 0);
2734+
ASSERT_EQ(stx.stx_mode & S_IFMT, S_IFREG);
2735+
ASSERT_EQ(0, ceph_close(cmount, rel_file_fd));
2736+
#endif
27162737

27172738
// delete the dirtree, recreate and verify
27182739
ASSERT_EQ(0, ceph_unlink(cmount, file_path));

0 commit comments

Comments
 (0)