|
@@ -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
|