]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
xfs: create libxfs helper to remove an existing inode/name from a directory
authorDarrick J. Wong <djwong@kernel.org>
Tue, 2 Jul 2024 18:22:45 +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 remove a (name, inode) entry from a
directory.  The upcoming metadata directory feature will need this to
create a metadata directory tree.

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 5a75f60e851867ff5b10dc86d4563b23eccc493c..f3ac3d55bc3853d56f0caa365bb02ba9b0c6d402 100644 (file)
@@ -874,3 +874,84 @@ xfs_dir_add_child(
 
        return 0;
 }
+
+/*
+ * Given a directory @dp, a child @ip, and a @name, remove the (@name, @ip)
+ * entry from the directory.  Both inodes must have the ILOCK held.
+ */
+int
+xfs_dir_remove_child(
+       struct xfs_trans        *tp,
+       unsigned int            resblks,
+       struct xfs_dir_update   *du)
+{
+       struct xfs_inode        *dp = du->dp;
+       const struct xfs_name   *name = du->name;
+       struct xfs_inode        *ip = du->ip;
+       int                     error;
+
+       xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
+       xfs_assert_ilocked(dp, XFS_ILOCK_EXCL);
+
+       /*
+        * If we're removing a directory perform some additional validation.
+        */
+       if (S_ISDIR(VFS_I(ip)->i_mode)) {
+               ASSERT(VFS_I(ip)->i_nlink >= 2);
+               if (VFS_I(ip)->i_nlink != 2)
+                       return -ENOTEMPTY;
+               if (!xfs_dir_isempty(ip))
+                       return -ENOTEMPTY;
+
+               /* Drop the link from ip's "..".  */
+               error = xfs_droplink(tp, dp);
+               if (error)
+                       return error;
+
+               /* Drop the "." link from ip to self.  */
+               error = xfs_droplink(tp, ip);
+               if (error)
+                       return error;
+
+               /*
+                * Point the unlinked child directory's ".." entry to the root
+                * directory to eliminate back-references to inodes that may
+                * get freed before the child directory is closed.  If the fs
+                * gets shrunk, this can lead to dirent inode validation errors.
+                */
+               if (dp->i_ino != tp->t_mountp->m_sb.sb_rootino) {
+                       error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
+                                       tp->t_mountp->m_sb.sb_rootino, 0);
+                       if (error)
+                               return error;
+               }
+       } else {
+               /*
+                * When removing a non-directory we need to log the parent
+                * inode here.  For a directory this is done implicitly
+                * by the xfs_droplink call for the ".." entry.
+                */
+               xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+       }
+       xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
+
+       /* Drop the link from dp to ip. */
+       error = xfs_droplink(tp, ip);
+       if (error)
+               return error;
+
+       error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks);
+       if (error) {
+               ASSERT(error != -ENOENT);
+               return error;
+       }
+
+       /* Remove parent pointer. */
+       if (du->ppargs) {
+               error = xfs_parent_removename(tp, du->ppargs, dp, name, ip);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
index 4f9711509571a1ab73e3e370e2439b7aeb24dc66..c89916d1c040adc2e186d1f727dbdbe701fec10d 100644 (file)
@@ -322,5 +322,7 @@ int xfs_dir_create_child(struct xfs_trans *tp, unsigned int resblks,
                struct xfs_dir_update *du);
 int xfs_dir_add_child(struct xfs_trans *tp, unsigned int resblks,
                struct xfs_dir_update *du);
+int xfs_dir_remove_child(struct xfs_trans *tp, unsigned int resblks,
+               struct xfs_dir_update *du);
 
 #endif /* __XFS_DIR2_H__ */
index caccb6296a487410dfc9be2e96660312a7ab351f..8da67322791f99293ab7e44e1c82471e6dc0ad45 100644 (file)
@@ -2040,13 +2040,17 @@ xfs_remove(
        struct xfs_name         *name,
        struct xfs_inode        *ip)
 {
+       struct xfs_dir_update   du = {
+               .dp             = dp,
+               .name           = name,
+               .ip             = ip,
+       };
        struct xfs_mount        *mp = dp->i_mount;
        struct xfs_trans        *tp = NULL;
        int                     is_dir = S_ISDIR(VFS_I(ip)->i_mode);
        int                     dontcare;
        int                     error = 0;
        uint                    resblks;
-       struct xfs_parent_args  *ppargs;
 
        trace_xfs_remove(dp, name);
 
@@ -2063,7 +2067,7 @@ xfs_remove(
        if (error)
                goto std_return;
 
-       error = xfs_parent_start(mp, &ppargs);
+       error = xfs_parent_start(mp, &du.ppargs);
        if (error)
                goto std_return;
 
@@ -2086,70 +2090,10 @@ xfs_remove(
                goto out_parent;
        }
 
-       /*
-        * If we're removing a directory perform some additional validation.
-        */
-       if (is_dir) {
-               ASSERT(VFS_I(ip)->i_nlink >= 2);
-               if (VFS_I(ip)->i_nlink != 2) {
-                       error = -ENOTEMPTY;
-                       goto out_trans_cancel;
-               }
-               if (!xfs_dir_isempty(ip)) {
-                       error = -ENOTEMPTY;
-                       goto out_trans_cancel;
-               }
-
-               /* Drop the link from ip's "..".  */
-               error = xfs_droplink(tp, dp);
-               if (error)
-                       goto out_trans_cancel;
-
-               /* Drop the "." link from ip to self.  */
-               error = xfs_droplink(tp, ip);
-               if (error)
-                       goto out_trans_cancel;
-
-               /*
-                * Point the unlinked child directory's ".." entry to the root
-                * directory to eliminate back-references to inodes that may
-                * get freed before the child directory is closed.  If the fs
-                * gets shrunk, this can lead to dirent inode validation errors.
-                */
-               if (dp->i_ino != tp->t_mountp->m_sb.sb_rootino) {
-                       error = xfs_dir_replace(tp, ip, &xfs_name_dotdot,
-                                       tp->t_mountp->m_sb.sb_rootino, 0);
-                       if (error)
-                               goto out_trans_cancel;
-               }
-       } else {
-               /*
-                * When removing a non-directory we need to log the parent
-                * inode here.  For a directory this is done implicitly
-                * by the xfs_droplink call for the ".." entry.
-                */
-               xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
-       }
-       xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
-
-       /* Drop the link from dp to ip. */
-       error = xfs_droplink(tp, ip);
+       error = xfs_dir_remove_child(tp, resblks, &du);
        if (error)
                goto out_trans_cancel;
 
-       error = xfs_dir_removename(tp, dp, name, ip->i_ino, resblks);
-       if (error) {
-               ASSERT(error != -ENOENT);
-               goto out_trans_cancel;
-       }
-
-       /* Remove parent pointer. */
-       if (ppargs) {
-               error = xfs_parent_removename(tp, ppargs, dp, name, ip);
-               if (error)
-                       goto out_trans_cancel;
-       }
-
        /*
         * Drop the link from dp to ip, and if ip was a directory, remove the
         * '.' and '..' references since we freed the directory.
@@ -2173,7 +2117,7 @@ xfs_remove(
 
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
        xfs_iunlock(dp, XFS_ILOCK_EXCL);
-       xfs_parent_finish(mp, ppargs);
+       xfs_parent_finish(mp, du.ppargs);
        return 0;
 
  out_trans_cancel:
@@ -2182,7 +2126,7 @@ xfs_remove(
        xfs_iunlock(ip, XFS_ILOCK_EXCL);
        xfs_iunlock(dp, XFS_ILOCK_EXCL);
  out_parent:
-       xfs_parent_finish(mp, ppargs);
+       xfs_parent_finish(mp, du.ppargs);
  std_return:
        return error;
 }