]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xfs: add a method to replace shortform attrs
authorDarrick J. Wong <djwong@kernel.org>
Fri, 23 Jan 2026 17:27:36 +0000 (09:27 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Fri, 23 Jan 2026 17:27:36 +0000 (09:27 -0800)
If we're trying to replace an xattr in a shortform attr structure and
the old entry fits the new entry, we can just memcpy and exit without
having to delete, compact, and re-add the entry (or worse use the attr
intent machinery).  For parent pointers this only advantages renaming
where the filename length stays the same (e.g. mv autoexec.bat
scandisk.exe) but for regular xattrs it might be useful for updating
security labels and the like.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_attr.c
fs/xfs/libxfs/xfs_attr_leaf.c
fs/xfs/libxfs/xfs_attr_leaf.h
fs/xfs/xfs_trace.h

index 54be75edb2eba9598198132f6d477522d0df17f6..93caa1dae50106246c1d064877ed362f1a9a17a2 100644 (file)
@@ -1085,6 +1085,10 @@ xfs_attr_replacename(
                return 0;
        }
 
+       error = xfs_attr_shortform_replace(args);
+       if (error != -ENOSPC)
+               return error;
+
        args->op_flags |= XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE;
 
        error = xfs_attr_sf_removename(args);
index c3327b10709c8af03b79b7c7fb19811125038fca..47f48ae555c0398b51f84d06c4896f683d5ad2fc 100644 (file)
@@ -842,6 +842,44 @@ xfs_attr_sf_findname(
        return NULL;
 }
 
+/*
+ * Replace a shortform xattr if it's the right length.  Returns 0 on success,
+ * -ENOSPC if the length is wrong, or -ENOATTR if the attr was not found.
+ */
+int
+xfs_attr_shortform_replace(
+       struct xfs_da_args              *args)
+{
+       struct xfs_attr_sf_entry        *sfe;
+
+       ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL);
+
+       trace_xfs_attr_sf_replace(args);
+
+       sfe = xfs_attr_sf_findname(args);
+       if (!sfe)
+               return -ENOATTR;
+
+       if (args->attr_filter & XFS_ATTR_PARENT) {
+               if (sfe->namelen != args->new_namelen ||
+                   sfe->valuelen != args->new_valuelen)
+                       return -ENOSPC;
+
+               memcpy(sfe->nameval, args->new_name, sfe->namelen);
+               memcpy(&sfe->nameval[sfe->namelen], args->new_value,
+                               sfe->valuelen);
+       } else {
+               if (sfe->valuelen != args->valuelen)
+                       return -ENOSPC;
+               memcpy(&sfe->nameval[sfe->namelen], args->value,
+                               sfe->valuelen);
+       }
+
+       xfs_trans_log_inode(args->trans, args->dp,
+                       XFS_ILOG_CORE | XFS_ILOG_ADATA);
+       return 0;
+}
+
 /*
  * Add a name/value pair to the shortform attribute list.
  * Overflow from the inode has already been checked for.
index 589f810eedc0d846fa56e1d72c8b69dba03969a7..aca46da2bc502ee672008bce8adf0152c3e0424f 100644 (file)
@@ -46,6 +46,7 @@ struct xfs_attr3_icleaf_hdr {
  * Internal routines when attribute fork size < XFS_LITINO(mp).
  */
 void   xfs_attr_shortform_create(struct xfs_da_args *args);
+int    xfs_attr_shortform_replace(struct xfs_da_args *args);
 void   xfs_attr_shortform_add(struct xfs_da_args *args, int forkoff);
 int    xfs_attr_shortform_getvalue(struct xfs_da_args *args);
 int    xfs_attr_shortform_to_leaf(struct xfs_da_args *args);
index f70afbf3cb196b2e2f738a2b73790d9066c1626d..a8bea99e0024d1a0b9e09715c0370b24f218aaa7 100644 (file)
@@ -2410,6 +2410,7 @@ DEFINE_ATTR_EVENT(xfs_attr_sf_addname);
 DEFINE_ATTR_EVENT(xfs_attr_sf_create);
 DEFINE_ATTR_EVENT(xfs_attr_sf_lookup);
 DEFINE_ATTR_EVENT(xfs_attr_sf_remove);
+DEFINE_ATTR_EVENT(xfs_attr_sf_replace);
 DEFINE_ATTR_EVENT(xfs_attr_sf_to_leaf);
 
 DEFINE_ATTR_EVENT(xfs_attr_leaf_add);