xfs: Add support to RENAME_EXCHANGE flag
Adds a new function named xfs_cross_rename(), responsible for handling requests from sys_renameat2() using RENAME_EXCHANGE flag. Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Dave Chinner <dchinner@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:

committed by
Dave Chinner

parent
dbe1b5ca26
commit
d31a182545
@@ -2655,6 +2655,124 @@ xfs_sort_for_rename(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_cross_rename()
|
||||
*
|
||||
* responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall
|
||||
*/
|
||||
STATIC int
|
||||
xfs_cross_rename(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *dp1,
|
||||
struct xfs_name *name1,
|
||||
struct xfs_inode *ip1,
|
||||
struct xfs_inode *dp2,
|
||||
struct xfs_name *name2,
|
||||
struct xfs_inode *ip2,
|
||||
struct xfs_bmap_free *free_list,
|
||||
xfs_fsblock_t *first_block,
|
||||
int spaceres)
|
||||
{
|
||||
int error = 0;
|
||||
int ip1_flags = 0;
|
||||
int ip2_flags = 0;
|
||||
int dp2_flags = 0;
|
||||
|
||||
/* Swap inode number for dirent in first parent */
|
||||
error = xfs_dir_replace(tp, dp1, name1,
|
||||
ip2->i_ino,
|
||||
first_block, free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* Swap inode number for dirent in second parent */
|
||||
error = xfs_dir_replace(tp, dp2, name2,
|
||||
ip1->i_ino,
|
||||
first_block, free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* 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(ip2->i_d.di_mode)) {
|
||||
error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
|
||||
dp1->i_ino, first_block,
|
||||
free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* transfer ip2 ".." reference to dp1 */
|
||||
if (!S_ISDIR(ip1->i_d.di_mode)) {
|
||||
error = xfs_droplink(tp, dp2);
|
||||
if (error)
|
||||
goto out;
|
||||
error = xfs_bumplink(tp, dp1);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(ip1->i_d.di_mode)) {
|
||||
error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
|
||||
dp2->i_ino, first_block,
|
||||
free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* transfer ip1 ".." reference to dp2 */
|
||||
if (!S_ISDIR(ip2->i_d.di_mode)) {
|
||||
error = xfs_droplink(tp, dp1);
|
||||
if (error)
|
||||
goto out;
|
||||
error = xfs_bumplink(tp, dp2);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_rename
|
||||
*/
|
||||
@@ -2665,7 +2783,8 @@ xfs_rename(
|
||||
xfs_inode_t *src_ip,
|
||||
xfs_inode_t *target_dp,
|
||||
struct xfs_name *target_name,
|
||||
xfs_inode_t *target_ip)
|
||||
xfs_inode_t *target_ip,
|
||||
unsigned int flags)
|
||||
{
|
||||
xfs_trans_t *tp = NULL;
|
||||
xfs_mount_t *mp = src_dp->i_mount;
|
||||
@@ -2742,6 +2861,18 @@ xfs_rename(
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle RENAME_EXCHANGE flags
|
||||
*/
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
|
||||
target_dp, target_name, target_ip,
|
||||
&free_list, &first_block, spaceres);
|
||||
if (error)
|
||||
goto abort_return;
|
||||
goto finish_rename;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the target.
|
||||
*/
|
||||
@@ -2881,6 +3012,7 @@ xfs_rename(
|
||||
if (new_parent)
|
||||
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
|
||||
|
||||
finish_rename:
|
||||
/*
|
||||
* If this is a synchronous mount, make sure that the
|
||||
* rename transaction goes to disk before returning to
|
||||
|
Reference in New Issue
Block a user