]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: dump garbage parent pointer attributes
authorDarrick J. Wong <djwong@kernel.org>
Mon, 29 Jul 2024 23:23:27 +0000 (16:23 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:01:12 +0000 (17:01 -0700)
Delete xattrs that have ATTR_PARENT set but are so garbage that they
clearly aren't parent pointers.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
libxfs/libxfs_api_defs.h
repair/pptr.c

index d3611e05bade7996d900dc50a7044ace8d574115..e12f0a40b00a2ebf73c966767e6284d61f8c1218 100644 (file)
@@ -46,6 +46,7 @@
 #define xfs_attr_is_leaf               libxfs_attr_is_leaf
 #define xfs_attr_leaf_newentsize       libxfs_attr_leaf_newentsize
 #define xfs_attr_namecheck             libxfs_attr_namecheck
+#define xfs_attr_removename            libxfs_attr_removename
 #define xfs_attr_set                   libxfs_attr_set
 #define xfs_attr_sethash               libxfs_attr_sethash
 #define xfs_attr_sf_firstentry         libxfs_attr_sf_firstentry
index bd9bb6f8bf367967ee1025dbef2b652a63e83579..61466009d88b5862954fdab9a8ee97d452158e58 100644 (file)
@@ -198,6 +198,29 @@ struct file_scan {
 
        /* Does this file have garbage xattrs with ATTR_PARENT set? */
        bool                    have_garbage;
+
+       /* xattrs that we have to remove from this file */
+       struct xfs_slab         *garbage_xattr_recs;
+
+       /* attr names associated with garbage_xattr_recs */
+       struct xfblob           *garbage_xattr_names;
+};
+
+struct garbage_xattr {
+       /* xfs_da_args.attr_filter for the attribute being removed */
+       unsigned int            attr_filter;
+
+       /* attribute name length */
+       unsigned int            attrnamelen;
+
+       /* attribute value length */
+       unsigned int            attrvaluelen;
+
+       /* cookie for the attribute name */
+       xfblob_cookie           attrname_cookie;
+
+       /* cookie for the attribute value */
+       xfblob_cookie           attrvalue_cookie;
 };
 
 /* Global names storage file. */
@@ -392,6 +415,82 @@ add_parent_ptr(
                        (unsigned long long)ag_pptr.name_cookie);
 }
 
+/* Remove garbage extended attributes that have ATTR_PARENT set. */
+static void
+remove_garbage_xattrs(
+       struct xfs_inode        *ip,
+       struct file_scan        *fscan)
+{
+       struct xfs_slab_cursor  *cur;
+       struct garbage_xattr    *ga;
+       void                    *buf = NULL;
+       size_t                  bufsize = 0;
+       int                     error;
+
+       error = -init_slab_cursor(fscan->garbage_xattr_recs, NULL, &cur);
+       if (error)
+               do_error(_("init garbage xattr cursor failed: %s\n"),
+                               strerror(error));
+
+       while ((ga = pop_slab_cursor(cur)) != NULL) {
+               struct xfs_da_args      args = {
+                       .dp             = ip,
+                       .attr_filter    = ga->attr_filter,
+                       .namelen        = ga->attrnamelen,
+                       .valuelen       = ga->attrvaluelen,
+                       .owner          = ip->i_ino,
+                       .geo            = ip->i_mount->m_attr_geo,
+                       .whichfork      = XFS_ATTR_FORK,
+                       .op_flags       = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED,
+               };
+               size_t          desired = ga->attrnamelen + ga->attrvaluelen;
+
+               if (desired > bufsize) {
+                       free(buf);
+                       buf = malloc(desired);
+                       if (!buf)
+                               do_error(
+ _("allocating %zu bytes to remove ino %llu garbage xattr failed: %s\n"),
+                                               desired,
+                                               (unsigned long long)ip->i_ino,
+                                               strerror(errno));
+                       bufsize = desired;
+               }
+
+               args.name = buf;
+               args.value = buf + ga->attrnamelen;
+
+               error = -xfblob_load(fscan->garbage_xattr_names,
+                               ga->attrname_cookie, buf, ga->attrnamelen);
+               if (error)
+                       do_error(
+ _("loading garbage xattr name failed: %s\n"),
+                                       strerror(error));
+
+               error = -xfblob_load(fscan->garbage_xattr_names,
+                               ga->attrvalue_cookie, args.value,
+                               ga->attrvaluelen);
+               if (error)
+                       do_error(
+ _("loading garbage xattr value failed: %s\n"),
+                                       strerror(error));
+
+               libxfs_attr_sethash(&args);
+               error = -libxfs_attr_set(&args, XFS_ATTRUPDATE_REMOVE, true);
+               if (error)
+                       do_error(
+ _("removing ino %llu garbage xattr failed: %s\n"),
+                                       (unsigned long long)ip->i_ino,
+                                       strerror(error));
+       }
+
+       free(buf);
+       free_slab_cursor(&cur);
+       free_slab(&fscan->garbage_xattr_recs);
+       xfblob_destroy(fscan->garbage_xattr_names);
+       fscan->garbage_xattr_names = NULL;
+}
+
 /* Schedule this ATTR_PARENT extended attribute for deletion. */
 static void
 record_garbage_xattr(
@@ -403,6 +502,15 @@ record_garbage_xattr(
        const void              *value,
        unsigned int            valuelen)
 {
+       struct garbage_xattr    garbage_xattr = {
+               .attr_filter    = attr_filter,
+               .attrnamelen    = namelen,
+               .attrvaluelen   = valuelen,
+       };
+       struct xfs_mount        *mp = ip->i_mount;
+       char                    *descr;
+       int                     error;
+
        if (no_modify) {
                if (!fscan->have_garbage)
                        do_warn(
@@ -413,13 +521,47 @@ record_garbage_xattr(
        }
 
        if (fscan->have_garbage)
-               return;
+               goto stuffit;
        fscan->have_garbage = true;
 
        do_warn(
  _("deleting garbage parent pointer extended attributes in ino %llu\n"),
                        (unsigned long long)ip->i_ino);
-       /* XXX do the work */
+
+       error = -init_slab(&fscan->garbage_xattr_recs,
+                       sizeof(struct garbage_xattr));
+       if (error)
+               do_error(_("init garbage xattr recs failed: %s\n"),
+                               strerror(error));
+
+       descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): garbage xattr names",
+                       mp->m_fsname);
+       error = -xfblob_create(descr, &fscan->garbage_xattr_names);
+       kfree(descr);
+       if (error)
+               do_error("init garbage xattr names failed: %s\n",
+                               strerror(error));
+
+stuffit:
+       error = -xfblob_store(fscan->garbage_xattr_names,
+                       &garbage_xattr.attrname_cookie, name, namelen);
+       if (error)
+               do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       error = -xfblob_store(fscan->garbage_xattr_names,
+                       &garbage_xattr.attrvalue_cookie, value, valuelen);
+       if (error)
+               do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr);
+       if (error)
+               do_error(_("storing ino %llu garbage xattr rec failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
 }
 
 /*
@@ -968,6 +1110,9 @@ check_file_parent_ptrs(
                goto out_free;
        }
 
+       if (!no_modify && fscan->have_garbage)
+               remove_garbage_xattrs(ip, fscan);
+
        crosscheck_file_parent_ptrs(ip, fscan);
 
 out_free: