]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
client: Gracefully handle empty pathname for statxat() 61165/head
authorAnoop C S <anoopcs@cryptolab.net>
Mon, 23 Sep 2024 07:06:55 +0000 (12:36 +0530)
committerAnoop C S <anoopcs@cryptolab.net>
Sun, 22 Dec 2024 04:28:18 +0000 (09:58 +0530)
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 <anoopcs@cryptolab.net>
(cherry picked from commit edd7fe76c4919bc243377c6d7aae20b0606b89c3)

src/client/Client.cc
src/include/cephfs/ceph_ll_client.h
src/include/cephfs/libcephfs.h
src/libcephfs.cc
src/test/libcephfs/test.cc

index f964a6fadf20932b93ca508dd94b577951e3cccc..0570c16e0e58883b5aad6b07a73bf9a3c8abc5fb 100644 (file)
@@ -11511,6 +11511,7 @@ int Client::statxat(int dirfd, const char *relpath,
 
   unsigned mask = statx_to_mask(flags, want);
 
+  InodeRef in;
   InodeRef dirinode;
   std::scoped_lock lock(client_lock);
   int r = get_fd_inode(dirfd, &dirinode);
@@ -11518,12 +11519,24 @@ int Client::statxat(int dirfd, const char *relpath,
     return r;
   }
 
-  InodeRef in;
-  filepath path(relpath);
-  r = path_walk(path, &in, perms, !(flags & AT_SYMLINK_NOFOLLOW), mask, 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), mask, dirinode);
+    if (r < 0) {
+      return r;
+    }
   }
+
+out:
+
   r = _getattr(in, mask, perms);
   if (r < 0) {
     ldout(cct, 3) << __func__ << " exit on error!" << dendl;
index ac5b7c2247178890e5e51ffe614a6dee0f6b694a..458edce590bfeb78d14c30f7766b008c886eaf6b 100644 (file)
@@ -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
index d6393b80c42e0f4e772e53fe513339df442bdc08..56d636eadd59eddb574b1a2bbf934e4cacdb1265 100644 (file)
@@ -924,7 +924,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,
index 51e73efdb65ec572bdf9dede5f17e3a4803e40f4..a3d321e28e7543367c77821149a7324740e3b93c 100644 (file)
@@ -981,7 +981,11 @@ extern "C" int ceph_statxat(struct ceph_mount_info *cmount, int dirfd, const cha
 {
   if (!cmount->is_mounted())
     return -CEPHFS_ENOTCONN;
+#ifdef CEPH_AT_EMPTY_PATH
+  if (flags & ~CEPH_AT_EMPTY_PATH)
+#else
   if (flags & ~CEPH_REQ_FLAG_MASK)
+#endif
     return -CEPHFS_EINVAL;
   return cmount->get_client()->statxat(dirfd, relpath, stx, cmount->default_perms,
                                        want, flags);
index 449774ac708eef35622ec3bb96e7c171e9e86964..cef8156ac90f2e0489ded77472949a4de5c38071 100644 (file)
@@ -2699,6 +2699,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
@@ -2706,6 +2719,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));