xfs: create libxfs helper to remove an existing inode/name from a directory
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>
This commit is contained in:
parent
1964435d19
commit
90636e4531
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user