xfs: Add parent pointers to rename
authorAllison Henderson <allison.henderson@oracle.com>
Mon, 22 Apr 2024 16:47:50 +0000 (09:47 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 23 Apr 2024 14:46:59 +0000 (07:46 -0700)
This patch removes the old parent pointer attribute during the rename
operation, and re-adds the updated parent pointer.

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
fs/xfs/libxfs/xfs_parent.c
fs/xfs/libxfs/xfs_parent.h
fs/xfs/libxfs/xfs_trans_space.c
fs/xfs/libxfs/xfs_trans_space.h
fs/xfs/scrub/orphanage.c
fs/xfs/scrub/parent_repair.c
fs/xfs/xfs_inode.c

index 6142e68f2338c7c3a6275532c22be71a684b069e..fdf643bfde4df734a5754ab920a5f2b283739e07 100644 (file)
@@ -227,3 +227,33 @@ xfs_parent_removename(
        xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
        return 0;
 }
+
+/* Replace one parent pointer with another to reflect a rename. */
+int
+xfs_parent_replacename(
+       struct xfs_trans        *tp,
+       struct xfs_parent_args  *ppargs,
+       struct xfs_inode        *old_dp,
+       const struct xfs_name   *old_name,
+       struct xfs_inode        *new_dp,
+       const struct xfs_name   *new_name,
+       struct xfs_inode        *child)
+{
+       int                     error;
+
+       error = xfs_parent_iread_extents(tp, child);
+       if (error)
+               return error;
+
+       xfs_inode_to_parent_rec(&ppargs->rec, old_dp);
+       xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
+                       child->i_ino, old_name);
+
+       xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
+       ppargs->args.new_name = new_name->name;
+       ppargs->args.new_namelen = new_name->len;
+       ppargs->args.new_value = &ppargs->new_rec;
+       ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);
+       xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
+       return 0;
+}
index 4a7fd48c226a4345d959ef80e9ce7a1c7c45aa73..768633b313671a32f23fc7a8477e8bed4a1b4c38 100644 (file)
@@ -45,6 +45,7 @@ extern struct kmem_cache      *xfs_parent_args_cache;
  */
 struct xfs_parent_args {
        struct xfs_parent_rec   rec;
+       struct xfs_parent_rec   new_rec;
        struct xfs_da_args      args;
 };
 
@@ -84,5 +85,10 @@ int xfs_parent_addname(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
 int xfs_parent_removename(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
                struct xfs_inode *dp, const struct xfs_name *parent_name,
                struct xfs_inode *child);
+int xfs_parent_replacename(struct xfs_trans *tp,
+               struct xfs_parent_args *ppargs,
+               struct xfs_inode *old_dp, const struct xfs_name *old_name,
+               struct xfs_inode *new_dp, const struct xfs_name *new_name,
+               struct xfs_inode *child);
 
 #endif /* __XFS_PARENT_H__ */
index df729e4f1a4c97a5caf203310db395c7c5834c99..b9dc3752f702ce9341bcd7a249c3a418140c8056 100644 (file)
@@ -94,3 +94,28 @@ xfs_remove_space_res(
 
        return ret;
 }
+
+unsigned int
+xfs_rename_space_res(
+       struct xfs_mount        *mp,
+       unsigned int            src_namelen,
+       bool                    target_exists,
+       unsigned int            target_namelen,
+       bool                    has_whiteout)
+{
+       unsigned int            ret;
+
+       ret = XFS_DIRREMOVE_SPACE_RES(mp) +
+                       XFS_DIRENTER_SPACE_RES(mp, target_namelen);
+
+       if (xfs_has_parent(mp)) {
+               if (has_whiteout)
+                       ret += xfs_parent_calc_space_res(mp, src_namelen);
+               ret += 2 * xfs_parent_calc_space_res(mp, target_namelen);
+       }
+
+       if (target_exists)
+               ret += xfs_parent_calc_space_res(mp, target_namelen);
+
+       return ret;
+}
index a4490813c56f132183159680686c9bb106f94836..1155ff2d37e29f7d1519d2f406c9137562bf9568 100644 (file)
@@ -91,8 +91,6 @@
         XFS_DQUOT_CLUSTER_SIZE_FSB)
 #define        XFS_QM_QINOCREATE_SPACE_RES(mp) \
        XFS_IALLOC_SPACE_RES(mp)
-#define        XFS_RENAME_SPACE_RES(mp,nl)     \
-       (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
 #define XFS_IFREE_SPACE_RES(mp)                \
        (xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
 
@@ -106,4 +104,8 @@ unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
                unsigned int fsblocks);
 unsigned int xfs_remove_space_res(struct xfs_mount *mp, unsigned int namelen);
 
+unsigned int xfs_rename_space_res(struct xfs_mount *mp,
+               unsigned int src_namelen, bool target_exists,
+               unsigned int target_namelen, bool has_whiteout);
+
 #endif /* __XFS_TRANS_SPACE_H__ */
index 5e2c3546f2e95dbbd1bf8ac4e33b966774b57371..94bcc2799188f8b08bc06c9f4d1c57a20b05a688 100644 (file)
@@ -328,7 +328,8 @@ xrep_adoption_trans_alloc(
        adopt->sc = sc;
        adopt->orphanage_blkres = xfs_link_space_res(mp, MAXNAMELEN);
        if (S_ISDIR(VFS_I(sc->ip)->i_mode))
-               child_blkres = XFS_RENAME_SPACE_RES(mp, xfs_name_dotdot.len);
+               child_blkres = xfs_rename_space_res(mp, 0, false,
+                                                   xfs_name_dotdot.len, false);
        adopt->child_blkres = child_blkres;
 
        /*
index ebb5791bf839ecb1619cf98b1cb54217a0d05357..63590e1b3506054531def330c1d6a7bbd7936144 100644 (file)
@@ -171,7 +171,8 @@ xrep_parent_reset_dotdot(
         * Reserve more space just in case we have to expand the dir.  We're
         * allowed to exceed quota to repair inconsistent metadata.
         */
-       spaceres = XFS_RENAME_SPACE_RES(sc->mp, xfs_name_dotdot.len);
+       spaceres = xfs_rename_space_res(sc->mp, 0, false, xfs_name_dotdot.len,
+                       false);
        error = xfs_trans_reserve_more_inode(sc->tp, sc->ip, spaceres, 0,
                        true);
        if (error)
index c4a1c2dd5261d539f0afdc1a5160f9e00fb57a20..59488c17e1c1c36a163916e04274f4ab3617c65b 100644 (file)
@@ -3148,6 +3148,9 @@ xfs_rename(
        struct xfs_trans        *tp;
        struct xfs_inode        *wip = NULL;            /* whiteout inode */
        struct xfs_inode        *inodes[__XFS_SORT_INODES];
+       struct xfs_parent_args  *src_ppargs = NULL;
+       struct xfs_parent_args  *tgt_ppargs = NULL;
+       struct xfs_parent_args  *wip_ppargs = NULL;
        int                     i;
        int                     num_inodes = __XFS_SORT_INODES;
        bool                    new_parent = (src_dp != target_dp);
@@ -3179,9 +3182,26 @@ xfs_rename(
        xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
                                inodes, &num_inodes);
 
+       error = xfs_parent_start(mp, &src_ppargs);
+       if (error)
+               goto out_release_wip;
+
+       if (wip) {
+               error = xfs_parent_start(mp, &wip_ppargs);
+               if (error)
+                       goto out_src_ppargs;
+       }
+
+       if (target_ip) {
+               error = xfs_parent_start(mp, &tgt_ppargs);
+               if (error)
+                       goto out_wip_ppargs;
+       }
+
 retry:
        nospace_error = 0;
-       spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
+       spaceres = xfs_rename_space_res(mp, src_name->len, target_ip != NULL,
+                       target_name->len, wip != NULL);
        error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);
        if (error == -ENOSPC) {
                nospace_error = error;
@@ -3190,7 +3210,17 @@ retry:
                                &tp);
        }
        if (error)
-               goto out_release_wip;
+               goto out_tgt_ppargs;
+
+       /*
+        * We don't allow reservationless renaming when parent pointers are
+        * enabled because we can't back out if the xattrs must grow.
+        */
+       if (src_ppargs && nospace_error) {
+               error = nospace_error;
+               xfs_trans_cancel(tp);
+               goto out_tgt_ppargs;
+       }
 
        /*
         * Attach the dquots to the inodes
@@ -3198,7 +3228,7 @@ retry:
        error = xfs_qm_vop_rename_dqattach(inodes);
        if (error) {
                xfs_trans_cancel(tp);
-               goto out_release_wip;
+               goto out_tgt_ppargs;
        }
 
        /*
@@ -3267,6 +3297,15 @@ retry:
                        goto out_trans_cancel;
        }
 
+       /*
+        * We don't allow quotaless renaming when parent pointers are enabled
+        * because we can't back out if the xattrs must grow.
+        */
+       if (src_ppargs && nospace_error) {
+               error = nospace_error;
+               goto out_trans_cancel;
+       }
+
        /*
         * Check for expected errors before we dirty the transaction
         * so we can return an error without a transaction abort.
@@ -3459,6 +3498,28 @@ retry:
        if (error)
                goto out_trans_cancel;
 
+       /* Schedule parent pointer updates. */
+       if (wip_ppargs) {
+               error = xfs_parent_addname(tp, wip_ppargs, src_dp, src_name,
+                               wip);
+               if (error)
+                       goto out_trans_cancel;
+       }
+
+       if (src_ppargs) {
+               error = xfs_parent_replacename(tp, src_ppargs, src_dp,
+                               src_name, target_dp, target_name, src_ip);
+               if (error)
+                       goto out_trans_cancel;
+       }
+
+       if (tgt_ppargs) {
+               error = xfs_parent_removename(tp, tgt_ppargs, target_dp,
+                               target_name, target_ip);
+               if (error)
+                       goto out_trans_cancel;
+       }
+
        xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
        xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
        if (new_parent)
@@ -3480,14 +3541,19 @@ retry:
                xfs_dir_update_hook(src_dp, wip, 1, src_name);
 
        error = xfs_finish_rename(tp);
-       xfs_iunlock_rename(inodes, num_inodes);
-       if (wip)
-               xfs_irele(wip);
-       return error;
+       nospace_error = 0;
+       goto out_unlock;
 
 out_trans_cancel:
        xfs_trans_cancel(tp);
+out_unlock:
        xfs_iunlock_rename(inodes, num_inodes);
+out_tgt_ppargs:
+       xfs_parent_finish(mp, tgt_ppargs);
+out_wip_ppargs:
+       xfs_parent_finish(mp, wip_ppargs);
+out_src_ppargs:
+       xfs_parent_finish(mp, src_ppargs);
 out_release_wip:
        if (wip)
                xfs_irele(wip);