mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 01:24:12 +08:00
xfs: create libxfs helper to exchange two directory entries
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>
This commit is contained in:
parent
90636e4531
commit
a55712b35c
@ -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;
|
||||
}
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
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 first parent */
|
||||
error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
|
||||
error = xfs_dir_exchange_children(tp, &du1, &du2, spaceres);
|
||||
if (error)
|
||||
goto out_trans_abort;
|
||||
|
||||
/* Swap inode number for dirent in second parent */
|
||||
error = xfs_dir_replace(tp, dp2, name2, ip1->i_ino, 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;
|
||||
|
Loading…
Reference in New Issue
Block a user