]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: create libxfs helper to exchange two directory entries
authorDarrick J. Wong <djwong@kernel.org>
Tue, 2 Jul 2024 18:22:46 +0000 (11:22 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 2 Jul 2024 18:36:59 +0000 (11:36 -0700)
Create a new libxfs function to exchange two directory entries.
The upcoming metadata directory feature will need this to replace a
metadata inode directory entry.

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

index f3ac3d55bc3853d56f0caa365bb02ba9b0c6d402..d650cfa023fdb582f221077f0991866fd4bf3686 100644 (file)
@@ -955,3 +955,128 @@ xfs_dir_remove_child(
 
        return 0;
 }
+
+/*
+ * Exchange the entry (@name1, @ip1) in directory @dp1 with the entry (@name2,
+ * @ip2) in directory @dp2, and update '..' @ip1 and @ip2's entries as needed.
+ * @ip1 and @ip2 need not be of the same type.
+ *
+ * All inodes must have the ILOCK held, and both entries must already exist.
+ */
+int
+xfs_dir_exchange_children(
+       struct xfs_trans        *tp,
+       struct xfs_dir_update   *du1,
+       struct xfs_dir_update   *du2,
+       unsigned int            spaceres)
+{
+       struct xfs_inode        *dp1 = du1->dp;
+       const struct xfs_name   *name1 = du1->name;
+       struct xfs_inode        *ip1 = du1->ip;
+       struct xfs_inode        *dp2 = du2->dp;
+       const struct xfs_name   *name2 = du2->name;
+       struct xfs_inode        *ip2 = du2->ip;
+       int                     ip1_flags = 0;
+       int                     ip2_flags = 0;
+       int                     dp2_flags = 0;
+       int                     error;
+
+       /* Swap inode number for dirent in first parent */
+       error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
+       if (error)
+               return error;
+
+       /* Swap inode number for dirent in second parent */
+       error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres);
+       if (error)
+               return error;
+
+       /*
+        * If we're renaming one or more directories across different parents,
+        * update the respective ".." entries (and link counts) to match the new
+        * parents.
+        */
+       if (dp1 != dp2) {
+               dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
+
+               if (S_ISDIR(VFS_I(ip2)->i_mode)) {
+                       error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
+                                               dp1->i_ino, spaceres);
+                       if (error)
+                               return error;
+
+                       /* transfer ip2 ".." reference to dp1 */
+                       if (!S_ISDIR(VFS_I(ip1)->i_mode)) {
+                               error = xfs_droplink(tp, dp2);
+                               if (error)
+                                       return error;
+                               xfs_bumplink(tp, dp1);
+                       }
+
+                       /*
+                        * Although ip1 isn't changed here, userspace needs
+                        * to be warned about the change, so that applications
+                        * relying on it (like backup ones), will properly
+                        * notify the change
+                        */
+                       ip1_flags |= XFS_ICHGTIME_CHG;
+                       ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
+               }
+
+               if (S_ISDIR(VFS_I(ip1)->i_mode)) {
+                       error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
+                                               dp2->i_ino, spaceres);
+                       if (error)
+                               return error;
+
+                       /* transfer ip1 ".." reference to dp2 */
+                       if (!S_ISDIR(VFS_I(ip2)->i_mode)) {
+                               error = xfs_droplink(tp, dp1);
+                               if (error)
+                                       return error;
+                               xfs_bumplink(tp, dp2);
+                       }
+
+                       /*
+                        * Although ip2 isn't changed here, userspace needs
+                        * to be warned about the change, so that applications
+                        * relying on it (like backup ones), will properly
+                        * notify the change
+                        */
+                       ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
+                       ip2_flags |= XFS_ICHGTIME_CHG;
+               }
+       }
+
+       if (ip1_flags) {
+               xfs_trans_ichgtime(tp, ip1, ip1_flags);
+               xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE);
+       }
+       if (ip2_flags) {
+               xfs_trans_ichgtime(tp, ip2, ip2_flags);
+               xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE);
+       }
+       if (dp2_flags) {
+               xfs_trans_ichgtime(tp, dp2, dp2_flags);
+               xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE);
+       }
+       xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+       xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
+
+       /* Schedule parent pointer replacements */
+       if (du1->ppargs) {
+               error = xfs_parent_replacename(tp, du1->ppargs, dp1, name1,
+                               dp2, name2, ip1);
+               if (error)
+                       return error;
+       }
+
+       if (du2->ppargs) {
+               error = xfs_parent_replacename(tp, du2->ppargs, dp2, name2,
+                               dp1, name1, ip2);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
index c89916d1c040adc2e186d1f727dbdbe701fec10d..8b1e192bd7a83dc3ecd2ba4f137fe8e6fc4b6b4d 100644 (file)
@@ -325,4 +325,7 @@ int xfs_dir_add_child(struct xfs_trans *tp, unsigned int resblks,
 int xfs_dir_remove_child(struct xfs_trans *tp, unsigned int resblks,
                struct xfs_dir_update *du);
 
+int xfs_dir_exchange_children(struct xfs_trans *tp, struct xfs_dir_update *du1,
+               struct xfs_dir_update *du2, unsigned int spaceres);
+
 #endif /* __XFS_DIR2_H__ */
index 8da67322791f99293ab7e44e1c82471e6dc0ad45..363e98ee974c715031ba9396df7e5a11fa5e3e1e 100644 (file)
@@ -2238,108 +2238,24 @@ xfs_cross_rename(
        struct xfs_parent_args  *ip2_ppargs,
        int                     spaceres)
 {
-       int                     error = 0;
-       int                     ip1_flags = 0;
-       int                     ip2_flags = 0;
-       int                     dp2_flags = 0;
-
-       /* Swap inode number for dirent in first parent */
-       error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
-       if (error)
-               goto out_trans_abort;
+       struct xfs_dir_update   du1 = {
+               .dp             = dp1,
+               .name           = name1,
+               .ip             = ip1,
+               .ppargs         = ip1_ppargs,
+       };
+       struct xfs_dir_update   du2 = {
+               .dp             = dp2,
+               .name           = name2,
+               .ip             = ip2,
+               .ppargs         = ip2_ppargs,
+       };
+       int                     error;
 
-       /* Swap inode number for dirent in second parent */
-       error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, spaceres);
+       error = xfs_dir_exchange_children(tp, &du1, &du2, spaceres);
        if (error)
                goto out_trans_abort;
 
-       /*
-        * If we're renaming one or more directories across different parents,
-        * update the respective ".." entries (and link counts) to match the new
-        * parents.
-        */
-       if (dp1 != dp2) {
-               dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
-
-               if (S_ISDIR(VFS_I(ip2)->i_mode)) {
-                       error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
-                                               dp1->i_ino, spaceres);
-                       if (error)
-                               goto out_trans_abort;
-
-                       /* transfer ip2 ".." reference to dp1 */
-                       if (!S_ISDIR(VFS_I(ip1)->i_mode)) {
-                               error = xfs_droplink(tp, dp2);
-                               if (error)
-                                       goto out_trans_abort;
-                               xfs_bumplink(tp, dp1);
-                       }
-
-                       /*
-                        * Although ip1 isn't changed here, userspace needs
-                        * to be warned about the change, so that applications
-                        * relying on it (like backup ones), will properly
-                        * notify the change
-                        */
-                       ip1_flags |= XFS_ICHGTIME_CHG;
-                       ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
-               }
-
-               if (S_ISDIR(VFS_I(ip1)->i_mode)) {
-                       error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
-                                               dp2->i_ino, spaceres);
-                       if (error)
-                               goto out_trans_abort;
-
-                       /* transfer ip1 ".." reference to dp2 */
-                       if (!S_ISDIR(VFS_I(ip2)->i_mode)) {
-                               error = xfs_droplink(tp, dp1);
-                               if (error)
-                                       goto out_trans_abort;
-                               xfs_bumplink(tp, dp2);
-                       }
-
-                       /*
-                        * Although ip2 isn't changed here, userspace needs
-                        * to be warned about the change, so that applications
-                        * relying on it (like backup ones), will properly
-                        * notify the change
-                        */
-                       ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
-                       ip2_flags |= XFS_ICHGTIME_CHG;
-               }
-       }
-
-       /* Schedule parent pointer replacements */
-       if (ip1_ppargs) {
-               error = xfs_parent_replacename(tp, ip1_ppargs, dp1, name1, dp2,
-                               name2, ip1);
-               if (error)
-                       goto out_trans_abort;
-       }
-
-       if (ip2_ppargs) {
-               error = xfs_parent_replacename(tp, ip2_ppargs, dp2, name2, dp1,
-                               name1, ip2);
-               if (error)
-                       goto out_trans_abort;
-       }
-
-       if (ip1_flags) {
-               xfs_trans_ichgtime(tp, ip1, ip1_flags);
-               xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE);
-       }
-       if (ip2_flags) {
-               xfs_trans_ichgtime(tp, ip2, ip2_flags);
-               xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE);
-       }
-       if (dp2_flags) {
-               xfs_trans_ichgtime(tp, dp2, dp2_flags);
-               xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE);
-       }
-       xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-       xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
-
        /*
         * Inform our hook clients that we've finished an exchange operation as
         * follows: removed the source and target files from their directories;