]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: create libxfs helper to remove an existing inode/name from a directory
authorDarrick J. Wong <djwong@kernel.org>
Wed, 2 Oct 2024 01:15:07 +0000 (18:15 -0700)
committerAndrey Albershteyn <aalbersh@redhat.com>
Fri, 4 Oct 2024 10:42:07 +0000 (12:42 +0200)
Source kernel commit: 90636e4531a8bfb5ef37d38a76eb97e5f5793deb

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>
libxfs/xfs_dir2.c
libxfs/xfs_dir2.h

index 802b9a1b3dbd2755fa6d20d759f0b03e9f52f8fb..e46f7f489ffba44bff47f4bb8add25155c424cb7 100644 (file)
@@ -873,3 +873,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__ */