]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs: create libxfs helper to remove an existing inode/name from a directory
authorDarrick J. Wong <djwong@kernel.org>
Tue, 9 Jan 2024 17:42:21 +0000 (09:42 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 10 Apr 2024 00:21:34 +0000 (17:21 -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>
libxfs/xfs_dir2.c
libxfs/xfs_dir2.h

index 76f49294f98b1931cf7a2ecea2ce912e2ec09fb5..38748ca9a3e246ad2196df303733fa0149e2109f 100644 (file)
@@ -894,3 +894,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 f9e384f36a9149263a1991e3bcda34ffaa025dcb..6bf967a957ad31893a262af38f1f4ae0fe8e8e76 100644 (file)
@@ -309,5 +309,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__ */