@@ -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
@@ -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: