diff options
author | Carlos Maiolino <cmaiolino@redhat.com> | 2014-12-23 16:51:42 -0500 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-12-23 16:51:42 -0500 |
commit | d31a1825450062b85282b4afed1c840fd306d012 (patch) | |
tree | e0ef4c10543a9d78088b887c22d79a65d8eda4e0 /fs | |
parent | dbe1b5ca26396b6c61d711c8ac4de13ebb02e9f6 (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/xfs/xfs_inode.c | 134 | ||||
-rw-r--r-- | fs/xfs/xfs_inode.h | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_iops.c | 12 |
3 files changed, 142 insertions, 6 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 41f804e740d7..9916aef60997 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -2656,6 +2656,124 @@ xfs_sort_for_rename( | |||
2656 | } | 2656 | } |
2657 | 2657 | ||
2658 | /* | 2658 | /* |
2659 | * xfs_cross_rename() | ||
2660 | * | ||
2661 | * responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall | ||
2662 | */ | ||
2663 | STATIC int | ||
2664 | xfs_cross_rename( | ||
2665 | struct xfs_trans *tp, | ||
2666 | struct xfs_inode *dp1, | ||
2667 | struct xfs_name *name1, | ||
2668 | struct xfs_inode *ip1, | ||
2669 | struct xfs_inode *dp2, | ||
2670 | struct xfs_name *name2, | ||
2671 | struct xfs_inode *ip2, | ||
2672 | struct xfs_bmap_free *free_list, | ||
2673 | xfs_fsblock_t *first_block, | ||
2674 | int spaceres) | ||
2675 | { | ||
2676 | int error = 0; | ||
2677 | int ip1_flags = 0; | ||
2678 | int ip2_flags = 0; | ||
2679 | int dp2_flags = 0; | ||
2680 | |||
2681 | /* Swap inode number for dirent in first parent */ | ||
2682 | error = xfs_dir_replace(tp, dp1, name1, | ||
2683 | ip2->i_ino, | ||
2684 | first_block, free_list, spaceres); | ||
2685 | if (error) | ||
2686 | goto out; | ||
2687 | |||
2688 | /* Swap inode number for dirent in second parent */ | ||
2689 | error = xfs_dir_replace(tp, dp2, name2, | ||
2690 | ip1->i_ino, | ||
2691 | first_block, free_list, spaceres); | ||
2692 | if (error) | ||
2693 | goto out; | ||
2694 | |||
2695 | /* | ||
2696 | * If we're renaming one or more directories across different parents, | ||
2697 | * update the respective ".." entries (and link counts) to match the new | ||
2698 | * parents. | ||
2699 | */ | ||
2700 | if (dp1 != dp2) { | ||
2701 | dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; | ||
2702 | |||
2703 | if (S_ISDIR(ip2->i_d.di_mode)) { | ||
2704 | error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot, | ||
2705 | dp1->i_ino, first_block, | ||
2706 | free_list, spaceres); | ||
2707 | if (error) | ||
2708 | goto out; | ||
2709 | |||
2710 | /* transfer ip2 ".." reference to dp1 */ | ||
2711 | if (!S_ISDIR(ip1->i_d.di_mode)) { | ||
2712 | error = xfs_droplink(tp, dp2); | ||
2713 | if (error) | ||
2714 | goto out; | ||
2715 | error = xfs_bumplink(tp, dp1); | ||
2716 | if (error) | ||
2717 | goto out; | ||
2718 | } | ||
2719 | |||
2720 | /* | ||
2721 | * Although ip1 isn't changed here, userspace needs | ||
2722 | * to be warned about the change, so that applications | ||
2723 | * relying on it (like backup ones), will properly | ||
2724 | * notify the change | ||
2725 | */ | ||
2726 | ip1_flags |= XFS_ICHGTIME_CHG; | ||
2727 | ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; | ||
2728 | } | ||
2729 | |||
2730 | if (S_ISDIR(ip1->i_d.di_mode)) { | ||
2731 | error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot, | ||
2732 | dp2->i_ino, first_block, | ||
2733 | free_list, spaceres); | ||
2734 | if (error) | ||
2735 | goto out; | ||
2736 | |||
2737 | /* transfer ip1 ".." reference to dp2 */ | ||
2738 | if (!S_ISDIR(ip2->i_d.di_mode)) { | ||
2739 | error = xfs_droplink(tp, dp1); | ||
2740 | if (error) | ||
2741 | goto out; | ||
2742 | error = xfs_bumplink(tp, dp2); | ||
2743 | if (error) | ||
2744 | goto out; | ||
2745 | } | ||
2746 | |||
2747 | /* | ||
2748 | * Although ip2 isn't changed here, userspace needs | ||
2749 | * to be warned about the change, so that applications | ||
2750 | * relying on it (like backup ones), will properly | ||
2751 | * notify the change | ||
2752 | */ | ||
2753 | ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG; | ||
2754 | ip2_flags |= XFS_ICHGTIME_CHG; | ||
2755 | } | ||
2756 | } | ||
2757 | |||
2758 | if (ip1_flags) { | ||
2759 | xfs_trans_ichgtime(tp, ip1, ip1_flags); | ||
2760 | xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE); | ||
2761 | } | ||
2762 | if (ip2_flags) { | ||
2763 | xfs_trans_ichgtime(tp, ip2, ip2_flags); | ||
2764 | xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE); | ||
2765 | } | ||
2766 | if (dp2_flags) { | ||
2767 | xfs_trans_ichgtime(tp, dp2, dp2_flags); | ||
2768 | xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE); | ||
2769 | } | ||
2770 | xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
2771 | xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); | ||
2772 | out: | ||
2773 | return error; | ||
2774 | } | ||
2775 | |||
2776 | /* | ||
2659 | * xfs_rename | 2777 | * xfs_rename |
2660 | */ | 2778 | */ |
2661 | int | 2779 | int |
@@ -2665,7 +2783,8 @@ xfs_rename( | |||
2665 | xfs_inode_t *src_ip, | 2783 | xfs_inode_t *src_ip, |
2666 | xfs_inode_t *target_dp, | 2784 | xfs_inode_t *target_dp, |
2667 | struct xfs_name *target_name, | 2785 | struct xfs_name *target_name, |
2668 | xfs_inode_t *target_ip) | 2786 | xfs_inode_t *target_ip, |
2787 | unsigned int flags) | ||
2669 | { | 2788 | { |
2670 | xfs_trans_t *tp = NULL; | 2789 | xfs_trans_t *tp = NULL; |
2671 | xfs_mount_t *mp = src_dp->i_mount; | 2790 | xfs_mount_t *mp = src_dp->i_mount; |
@@ -2743,6 +2862,18 @@ xfs_rename( | |||
2743 | } | 2862 | } |
2744 | 2863 | ||
2745 | /* | 2864 | /* |
2865 | * Handle RENAME_EXCHANGE flags | ||
2866 | */ | ||
2867 | if (flags & RENAME_EXCHANGE) { | ||
2868 | error = xfs_cross_rename(tp, src_dp, src_name, src_ip, | ||
2869 | target_dp, target_name, target_ip, | ||
2870 | &free_list, &first_block, spaceres); | ||
2871 | if (error) | ||
2872 | goto abort_return; | ||
2873 | goto finish_rename; | ||
2874 | } | ||
2875 | |||
2876 | /* | ||
2746 | * Set up the target. | 2877 | * Set up the target. |
2747 | */ | 2878 | */ |
2748 | if (target_ip == NULL) { | 2879 | if (target_ip == NULL) { |
@@ -2881,6 +3012,7 @@ xfs_rename( | |||
2881 | if (new_parent) | 3012 | if (new_parent) |
2882 | xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); | 3013 | xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); |
2883 | 3014 | ||
3015 | finish_rename: | ||
2884 | /* | 3016 | /* |
2885 | * If this is a synchronous mount, make sure that the | 3017 | * If this is a synchronous mount, make sure that the |
2886 | * rename transaction goes to disk before returning to | 3018 | * rename transaction goes to disk before returning to |
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 4ed2ba9342dc..f7722960b69c 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h | |||
@@ -338,7 +338,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip, | |||
338 | int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name, | 338 | int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name, |
339 | struct xfs_inode *src_ip, struct xfs_inode *target_dp, | 339 | struct xfs_inode *src_ip, struct xfs_inode *target_dp, |
340 | struct xfs_name *target_name, | 340 | struct xfs_name *target_name, |
341 | struct xfs_inode *target_ip); | 341 | struct xfs_inode *target_ip, unsigned int flags); |
342 | 342 | ||
343 | void xfs_ilock(xfs_inode_t *, uint); | 343 | void xfs_ilock(xfs_inode_t *, uint); |
344 | int xfs_ilock_nowait(xfs_inode_t *, uint); | 344 | int xfs_ilock_nowait(xfs_inode_t *, uint); |
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index abb838a565ee..ce80eeb8faa4 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
@@ -384,19 +384,23 @@ xfs_vn_rename( | |||
384 | unsigned int flags) | 384 | unsigned int flags) |
385 | { | 385 | { |
386 | struct inode *new_inode = ndentry->d_inode; | 386 | struct inode *new_inode = ndentry->d_inode; |
387 | int omode = 0; | ||
387 | struct xfs_name oname; | 388 | struct xfs_name oname; |
388 | struct xfs_name nname; | 389 | struct xfs_name nname; |
389 | 390 | ||
390 | /* XFS does not support RENAME_EXCHANGE yet */ | 391 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) |
391 | if (flags & ~RENAME_NOREPLACE) | ||
392 | return -EINVAL; | 392 | return -EINVAL; |
393 | 393 | ||
394 | xfs_dentry_to_name(&oname, odentry, 0); | 394 | /* if we are exchanging files, we need to set i_mode of both files */ |
395 | if (flags & RENAME_EXCHANGE) | ||
396 | omode = ndentry->d_inode->i_mode; | ||
397 | |||
398 | xfs_dentry_to_name(&oname, odentry, omode); | ||
395 | xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode); | 399 | xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode); |
396 | 400 | ||
397 | return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), | 401 | return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode), |
398 | XFS_I(ndir), &nname, | 402 | XFS_I(ndir), &nname, |
399 | new_inode ? XFS_I(new_inode) : NULL); | 403 | new_inode ? XFS_I(new_inode) : NULL, flags); |
400 | } | 404 | } |
401 | 405 | ||
402 | /* | 406 | /* |