]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: allow xattr matching on name and value for parent pointers
authorDarrick J. Wong <djwong@kernel.org>
Mon, 29 Jul 2024 23:22:46 +0000 (16:22 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:01:02 +0000 (17:01 -0700)
Source kernel commit: f041455eb5773eda3291903ad6d1f33d4798e9a2

If a file is hardlinked with the same name but from multiple parents,
the parent pointers will all have the same dirent name (== attr name)
but with different parent_ino/parent_gen values.  To disambiguate, we
need to be able to match on both the attr name and the attr value.  This
is in contrast to regular xattrs, which are matchtg edit
d only on name.

Therefore, plumb in the ability to match shortform and local attrs on
name and value in the XFS_ATTR_PARENT namespace.  Parent pointer attr
values are never large enough to be stored in a remote attr, so we need
can reject these cases as corruption.

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

index 212347bc3bdb7b9764e99013ece8a3d80a961294..faa357f15658fd085c8191e959cce1e99ce541df 100644 (file)
@@ -511,12 +511,37 @@ static inline unsigned int xfs_attr_match_mask(const struct xfs_da_args *args)
        return XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE;
 }
 
+static inline bool
+xfs_attr_parent_match(
+       const struct xfs_da_args        *args,
+       const void                      *value,
+       unsigned int                    valuelen)
+{
+       ASSERT(args->value != NULL);
+
+       /* Parent pointers do not use remote values */
+       if (!value)
+               return false;
+
+       /*
+        * The only value we support is a parent rec.  However, we'll accept
+        * any valuelen so that offline repair can delete ATTR_PARENT values
+        * that are not parent pointers.
+        */
+       if (valuelen != args->valuelen)
+               return false;
+
+       return memcmp(args->value, value, valuelen) == 0;
+}
+
 static bool
 xfs_attr_match(
        struct xfs_da_args      *args,
        unsigned int            attr_flags,
        const unsigned char     *name,
-       unsigned int            namelen)
+       unsigned int            namelen,
+       const void              *value,
+       unsigned int            valuelen)
 {
        unsigned int            mask = xfs_attr_match_mask(args);
 
@@ -527,6 +552,9 @@ xfs_attr_match(
        if (memcmp(args->name, name, namelen) != 0)
                return false;
 
+       if (attr_flags & XFS_ATTR_PARENT)
+               return xfs_attr_parent_match(args, value, valuelen);
+
        return true;
 }
 
@@ -536,6 +564,13 @@ xfs_attr_copy_value(
        unsigned char           *value,
        int                     valuelen)
 {
+       /*
+        * Parent pointer lookups require the caller to specify the name and
+        * value, so don't copy anything.
+        */
+       if (args->attr_filter & XFS_ATTR_PARENT)
+               return 0;
+
        /*
         * No copy if all we have to do is get the length
         */
@@ -745,7 +780,8 @@ xfs_attr_sf_findname(
             sfe < xfs_attr_sf_endptr(sf);
             sfe = xfs_attr_sf_nextentry(sfe)) {
                if (xfs_attr_match(args, sfe->flags, sfe->nameval,
-                                       sfe->namelen))
+                               sfe->namelen, &sfe->nameval[sfe->namelen],
+                               sfe->valuelen))
                        return sfe;
        }
 
@@ -2441,18 +2477,22 @@ xfs_attr3_leaf_lookup_int(
                if (entry->flags & XFS_ATTR_LOCAL) {
                        name_loc = xfs_attr3_leaf_name_local(leaf, probe);
                        if (!xfs_attr_match(args, entry->flags,
-                                               name_loc->nameval,
-                                               name_loc->namelen))
+                                       name_loc->nameval, name_loc->namelen,
+                                       &name_loc->nameval[name_loc->namelen],
+                                       be16_to_cpu(name_loc->valuelen)))
                                continue;
                        args->index = probe;
                        return -EEXIST;
                } else {
+                       unsigned int    valuelen;
+
                        name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
+                       valuelen = be32_to_cpu(name_rmt->valuelen);
                        if (!xfs_attr_match(args, entry->flags, name_rmt->name,
-                                               name_rmt->namelen))
+                                       name_rmt->namelen, NULL, valuelen))
                                continue;
                        args->index = probe;
-                       args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
+                       args->rmtvaluelen = valuelen;
                        args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
                        args->rmtblkcnt = xfs_attr3_rmt_blocks(
                                                        args->dp->i_mount,