xfs: allow bulkstat to return metadata directories
authorDarrick J. Wong <djwong@kernel.org>
Mon, 4 Nov 2024 04:18:54 +0000 (20:18 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 5 Nov 2024 21:38:32 +0000 (13:38 -0800)
Allow the V5 bulkstat ioctl to return information about metadata
directory files so that xfs_scrub can find and scrub them, since they
are otherwise ordinary directories.

(Metadata files of course require per-file scrub code and hence do not
need exposure.)

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_fs.h
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_itable.h

index a42c1a33691c0f1ce64d89ccf36a41800835daf2..499bea4ea8067fe3127378feb728bc60ced027be 100644 (file)
@@ -490,9 +490,17 @@ struct xfs_bulk_ireq {
  */
 #define XFS_BULK_IREQ_NREXT64  (1U << 2)
 
+/*
+ * Allow bulkstat to return information about metadata directories.  This
+ * enables xfs_scrub to find them for scanning, as they are otherwise ordinary
+ * directories.
+ */
+#define XFS_BULK_IREQ_METADIR  (1U << 3)
+
 #define XFS_BULK_IREQ_FLAGS_ALL        (XFS_BULK_IREQ_AGNO |    \
                                 XFS_BULK_IREQ_SPECIAL | \
-                                XFS_BULK_IREQ_NREXT64)
+                                XFS_BULK_IREQ_NREXT64 | \
+                                XFS_BULK_IREQ_METADIR)
 
 /* Operate on the root directory inode. */
 #define XFS_BULK_IREQ_SPECIAL_ROOT     (1)
index 2567fd2a0994b97ae5c570a4bad036d94bc88621..f36fd8db388cca8ed0b9347a87e80e46f0727faa 100644 (file)
@@ -233,6 +233,10 @@ xfs_bulk_ireq_setup(
        if (hdr->flags & XFS_BULK_IREQ_NREXT64)
                breq->flags |= XFS_IBULK_NREXT64;
 
+       /* Caller wants to see metadata directories in bulkstat output. */
+       if (hdr->flags & XFS_BULK_IREQ_METADIR)
+               breq->flags |= XFS_IBULK_METADIR;
+
        return 0;
 }
 
@@ -323,6 +327,9 @@ xfs_ioc_inumbers(
        if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr)))
                return -EFAULT;
 
+       if (hdr.flags & XFS_BULK_IREQ_METADIR)
+               return -EINVAL;
+
        error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->inumbers);
        if (error == -ECANCELED)
                goto out_teardown;
index 37c2b50d877e42f4b34c5004028687f78df9de7b..1fa1c0564b0c5a3168676b29ae5aa359329f0cef 100644 (file)
@@ -36,6 +36,14 @@ struct xfs_bstat_chunk {
        struct xfs_bulkstat     *buf;
 };
 
+static inline bool
+want_metadir_file(
+       struct xfs_inode        *ip,
+       struct xfs_ibulk        *breq)
+{
+       return xfs_is_metadir_inode(ip) && (breq->flags & XFS_IBULK_METADIR);
+}
+
 /*
  * Fill out the bulkstat info for a single inode and report it somewhere.
  *
@@ -69,9 +77,6 @@ xfs_bulkstat_one_int(
        vfsuid_t                vfsuid;
        vfsgid_t                vfsgid;
 
-       if (xfs_is_sb_inum(mp, ino))
-               goto out_advance;
-
        error = xfs_iget(mp, tp, ino,
                         (XFS_IGET_DONTCACHE | XFS_IGET_UNTRUSTED),
                         XFS_ILOCK_SHARED, &ip);
@@ -97,8 +102,28 @@ xfs_bulkstat_one_int(
        vfsuid = i_uid_into_vfsuid(idmap, inode);
        vfsgid = i_gid_into_vfsgid(idmap, inode);
 
+       /*
+        * If caller wants files from the metadata directories, push out the
+        * bare minimum information for enabling scrub.
+        */
+       if (want_metadir_file(ip, bc->breq)) {
+               memset(buf, 0, sizeof(*buf));
+               buf->bs_ino = ino;
+               buf->bs_gen = inode->i_generation;
+               buf->bs_mode = inode->i_mode & S_IFMT;
+               xfs_bulkstat_health(ip, buf);
+               buf->bs_version = XFS_BULKSTAT_VERSION_V5;
+               xfs_iunlock(ip, XFS_ILOCK_SHARED);
+               xfs_irele(ip);
+
+               error = bc->formatter(bc->breq, buf);
+               if (!error || error == -ECANCELED)
+                       goto out_advance;
+               goto out;
+       }
+
        /* If this is a private inode, don't leak its details to userspace. */
-       if (IS_PRIVATE(inode)) {
+       if (IS_PRIVATE(inode) || xfs_is_sb_inum(mp, ino)) {
                xfs_iunlock(ip, XFS_ILOCK_SHARED);
                xfs_irele(ip);
                error = -EINVAL;
index 1659f13f17a89d6e93ba4cbe3267397379f8bb72..f10e8f8f2335100d07a1b631a6fa8fc38ceb3851 100644 (file)
@@ -22,6 +22,9 @@ struct xfs_ibulk {
 /* Fill out the bs_extents64 field if set. */
 #define XFS_IBULK_NREXT64      (1U << 1)
 
+/* Signal that we can return metadata directories. */
+#define XFS_IBULK_METADIR      (1U << 2)
+
 /*
  * Advance the user buffer pointer by one record of the given size.  If the
  * buffer is now full, return the appropriate error code.