]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: add xattr setname and removename functions for internal users
authorDarrick J. Wong <djwong@kernel.org>
Thu, 28 Mar 2024 20:26:10 +0000 (13:26 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 10 Apr 2024 00:21:31 +0000 (17:21 -0700)
Add a couple of internal xattr functions to set or remove attr names
from the xattr structures.  The upcoming parent pointer and fsverity
patchsets will want the ability to set and clear xattrs with a fully
initialized xfs_da_args structure.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
libxfs/xfs_attr.c
libxfs/xfs_attr.h

index 97c62ef734ec8bffa20aae66ab9c0ddcd678f501..720fec0279407b4740f9ac32b40e6b6cb6308228 100644 (file)
@@ -949,6 +949,44 @@ xfs_attr_lookup(
        return error;
 }
 
+/*
+ * Before updating xattrs, add an attribute fork if the inode doesn't have.
+ * (inode must not be locked when we call this routine)
+ */
+static int
+xfs_attr_ensure_fork(
+       struct xfs_da_args      *args,
+       bool                    rsvd)
+{
+       int                     sf_size;
+
+       if (xfs_inode_has_attr_fork(args->dp))
+               return 0;
+
+       sf_size = sizeof(struct xfs_attr_sf_hdr) +
+                       xfs_attr_sf_entsize_byname(args->namelen,
+                                                  args->valuelen);
+
+       return xfs_bmap_add_attrfork(args->dp, sf_size, rsvd);
+}
+
+/*
+ * Before updating xattrs, make sure we can handle adding to the extent count.
+ * There must be a transaction and the ILOCK must be held.
+ */
+static int
+xfs_attr_ensure_iext(
+       struct xfs_da_args      *args,
+       int                     nr)
+{
+       int                     error;
+
+       error = xfs_iext_count_may_overflow(args->dp, XFS_ATTR_FORK, nr);
+       if (error == -EFBIG)
+               return xfs_iext_count_upgrade(args->trans, args->dp, nr);
+       return error;
+}
+
 /*
  * Note: If args->value is NULL the attribute will be removed, just like the
  * Linux ->setattr API.
@@ -993,19 +1031,9 @@ xfs_attr_set(
                XFS_STATS_INC(mp, xs_attr_set);
                args->total = xfs_attr_calc_size(args, &local);
 
-               /*
-                * If the inode doesn't have an attribute fork, add one.
-                * (inode must not be locked when we call this routine)
-                */
-               if (xfs_inode_has_attr_fork(dp) == 0) {
-                       int sf_size = sizeof(struct xfs_attr_sf_hdr) +
-                               xfs_attr_sf_entsize_byname(args->namelen,
-                                               args->valuelen);
-
-                       error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
-                       if (error)
-                               return error;
-               }
+               error = xfs_attr_ensure_fork(args, rsvd);
+               if (error)
+                       return error;
 
                if (!local)
                        rmt_blks = xfs_attr3_rmt_blocks(mp, args->valuelen);
@@ -1024,11 +1052,8 @@ xfs_attr_set(
                return error;
 
        if (args->value || xfs_inode_hasattr(dp)) {
-               error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
+               error = xfs_attr_ensure_iext(args,
                                XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
-               if (error == -EFBIG)
-                       error = xfs_iext_count_upgrade(args->trans, dp,
-                                       XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
                if (error)
                        goto out_trans_cancel;
        }
@@ -1085,6 +1110,140 @@ out_trans_cancel:
        goto out_unlock;
 }
 
+/*
+ * Ensure that the xattr structure maps @args->name to @args->value.
+ *
+ * The caller must have initialized @args, attached dquots, and must not hold
+ * any ILOCKs.  Only XATTR_CREATE may be specified in @args->xattr_flags.
+ * Reserved data blocks may be used if @rsvd is set.
+ *
+ * Returns -EEXIST if XATTR_CREATE was specified and the name already exists.
+ */
+int
+xfs_attr_setname(
+       struct xfs_da_args      *args,
+       bool                    rsvd)
+{
+       struct xfs_inode        *dp = args->dp;
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_trans_res    tres;
+       unsigned int            total;
+       int                     rmt_extents = 0;
+       int                     error, local;
+
+       ASSERT(!(args->xattr_flags & XATTR_REPLACE));
+       ASSERT(!args->trans);
+
+       args->total = xfs_attr_calc_size(args, &local);
+
+       error = xfs_attr_ensure_fork(args, rsvd);
+       if (error)
+               return error;
+
+       if (!local)
+               rmt_extents = XFS_IEXT_ATTR_MANIP_CNT(
+                               xfs_attr3_rmt_blocks(mp, args->valuelen));
+
+       xfs_init_attr_trans(args, &tres, &total);
+       error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
+       if (error)
+               return error;
+
+       error = xfs_attr_ensure_iext(args, rmt_extents);
+       if (error)
+               goto out_trans_cancel;
+
+       error = xfs_attr_lookup(args);
+       switch (error) {
+       case -EEXIST:
+               /* Pure create fails if the attr already exists */
+               if (args->xattr_flags & XATTR_CREATE)
+                       goto out_trans_cancel;
+               if (args->attr_filter & XFS_ATTR_PARENT)
+                       xfs_attr_defer_parent(args, XFS_ATTR_DEFER_REPLACE);
+               else
+                       xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
+               break;
+       case -ENOATTR:
+               if (args->attr_filter & XFS_ATTR_PARENT)
+                       xfs_attr_defer_parent(args, XFS_ATTR_DEFER_SET);
+               else
+                       xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
+               break;
+       default:
+               goto out_trans_cancel;
+       }
+
+       xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+       error = xfs_trans_commit(args->trans);
+out_unlock:
+       args->trans = NULL;
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+       return error;
+
+out_trans_cancel:
+       xfs_trans_cancel(args->trans);
+       goto out_unlock;
+}
+
+/*
+ * Ensure that the xattr structure does not map @args->name to @args->value.
+ *
+ * The caller must have initialized @args, attached dquots, and must not hold
+ * any ILOCKs.  Reserved data blocks may be used if @rsvd is set.
+ *
+ * Returns -ENOATTR if the name did not already exist.
+ */
+int
+xfs_attr_removename(
+       struct xfs_da_args      *args,
+       bool                    rsvd)
+{
+       struct xfs_inode        *dp = args->dp;
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_trans_res    tres;
+       unsigned int            total;
+       int                     rmt_extents;
+       int                     error;
+
+       ASSERT(!args->trans);
+
+       rmt_extents = XFS_IEXT_ATTR_MANIP_CNT(
+                               xfs_attr3_rmt_blocks(mp, XFS_XATTR_SIZE_MAX));
+
+       xfs_init_attr_trans(args, &tres, &total);
+       error = xfs_trans_alloc_inode(dp, &tres, total, 0, rsvd, &args->trans);
+       if (error)
+               return error;
+
+       if (xfs_inode_hasattr(dp)) {
+               error = xfs_attr_ensure_iext(args, rmt_extents);
+               if (error)
+                       goto out_trans_cancel;
+       }
+
+       error = xfs_attr_lookup(args);
+       if (error != -EEXIST)
+               goto out_trans_cancel;
+
+       if (args->attr_filter & XFS_ATTR_PARENT)
+               xfs_attr_defer_parent(args, XFS_ATTR_DEFER_REMOVE);
+       else
+               xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
+       xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG);
+       xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE);
+       error = xfs_trans_commit(args->trans);
+out_unlock:
+       args->trans = NULL;
+       xfs_iunlock(dp, XFS_ILOCK_EXCL);
+       return error;
+
+out_trans_cancel:
+       xfs_trans_cancel(args->trans);
+       goto out_unlock;
+}
+
 /*========================================================================
  * External routines when attribute list is inside the inode
  *========================================================================*/
index cb5ca37000848c661c653b645303f3e46f4af090..d51001c5809fef40da900c663c28aa82ac754ad7 100644 (file)
@@ -560,6 +560,9 @@ int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
 void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
                         unsigned int *total);
 
+int xfs_attr_setname(struct xfs_da_args *args, bool rsvd);
+int xfs_attr_removename(struct xfs_da_args *args, bool rsvd);
+
 /*
  * Check to see if the attr should be upgraded from non-existent or shortform to
  * single-leaf-block attribute list.