diff options
author | Dave Chinner <david@fromorbit.com> | 2015-03-24 23:29:13 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2015-03-24 23:29:13 -0400 |
commit | d41bb03444147305e955cdd53753f0493e4d9e28 (patch) | |
tree | 37535eb32e725933b0c24df5c52c123b187fa910 /fs/xfs | |
parent | 88e8fda99a4c99a1a6482510655dbd88cccd221b (diff) | |
parent | 7dcf5c3e4527cfa2807567b00387cf2ed5e07f00 (diff) |
Merge branch 'xfs-rename-whiteout' into for-next
Conflicts:
fs/xfs/xfs_inode.c
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/xfs_inode.c | 408 | ||||
-rw-r--r-- | fs/xfs/xfs_iops.c | 2 |
2 files changed, 239 insertions, 171 deletions
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 5a44f1cc820c..8394f6f17120 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c | |||
@@ -391,15 +391,14 @@ xfs_lock_inumorder(int lock_mode, int subclass) | |||
391 | } | 391 | } |
392 | 392 | ||
393 | /* | 393 | /* |
394 | * The following routine will lock n inodes in exclusive mode. | 394 | * The following routine will lock n inodes in exclusive mode. We assume the |
395 | * We assume the caller calls us with the inodes in i_ino order. | 395 | * caller calls us with the inodes in i_ino order. |
396 | * | 396 | * |
397 | * We need to detect deadlock where an inode that we lock | 397 | * We need to detect deadlock where an inode that we lock is in the AIL and we |
398 | * is in the AIL and we start waiting for another inode that is locked | 398 | * start waiting for another inode that is locked by a thread in a long running |
399 | * by a thread in a long running transaction (such as truncate). This can | 399 | * transaction (such as truncate). This can result in deadlock since the long |
400 | * result in deadlock since the long running trans might need to wait | 400 | * running trans might need to wait for the inode we just locked in order to |
401 | * for the inode we just locked in order to push the tail and free space | 401 | * push the tail and free space in the log. |
402 | * in the log. | ||
403 | */ | 402 | */ |
404 | void | 403 | void |
405 | xfs_lock_inodes( | 404 | xfs_lock_inodes( |
@@ -410,30 +409,27 @@ xfs_lock_inodes( | |||
410 | int attempts = 0, i, j, try_lock; | 409 | int attempts = 0, i, j, try_lock; |
411 | xfs_log_item_t *lp; | 410 | xfs_log_item_t *lp; |
412 | 411 | ||
413 | ASSERT(ips && (inodes >= 2)); /* we need at least two */ | 412 | /* currently supports between 2 and 5 inodes */ |
413 | ASSERT(ips && inodes >= 2 && inodes <= 5); | ||
414 | 414 | ||
415 | try_lock = 0; | 415 | try_lock = 0; |
416 | i = 0; | 416 | i = 0; |
417 | |||
418 | again: | 417 | again: |
419 | for (; i < inodes; i++) { | 418 | for (; i < inodes; i++) { |
420 | ASSERT(ips[i]); | 419 | ASSERT(ips[i]); |
421 | 420 | ||
422 | if (i && (ips[i] == ips[i-1])) /* Already locked */ | 421 | if (i && (ips[i] == ips[i - 1])) /* Already locked */ |
423 | continue; | 422 | continue; |
424 | 423 | ||
425 | /* | 424 | /* |
426 | * If try_lock is not set yet, make sure all locked inodes | 425 | * If try_lock is not set yet, make sure all locked inodes are |
427 | * are not in the AIL. | 426 | * not in the AIL. If any are, set try_lock to be used later. |
428 | * If any are, set try_lock to be used later. | ||
429 | */ | 427 | */ |
430 | |||
431 | if (!try_lock) { | 428 | if (!try_lock) { |
432 | for (j = (i - 1); j >= 0 && !try_lock; j--) { | 429 | for (j = (i - 1); j >= 0 && !try_lock; j--) { |
433 | lp = (xfs_log_item_t *)ips[j]->i_itemp; | 430 | lp = (xfs_log_item_t *)ips[j]->i_itemp; |
434 | if (lp && (lp->li_flags & XFS_LI_IN_AIL)) { | 431 | if (lp && (lp->li_flags & XFS_LI_IN_AIL)) |
435 | try_lock++; | 432 | try_lock++; |
436 | } | ||
437 | } | 433 | } |
438 | } | 434 | } |
439 | 435 | ||
@@ -443,51 +439,42 @@ again: | |||
443 | * we can't get any, we must release all we have | 439 | * we can't get any, we must release all we have |
444 | * and try again. | 440 | * and try again. |
445 | */ | 441 | */ |
442 | if (!try_lock) { | ||
443 | xfs_ilock(ips[i], xfs_lock_inumorder(lock_mode, i)); | ||
444 | continue; | ||
445 | } | ||
446 | |||
447 | /* try_lock means we have an inode locked that is in the AIL. */ | ||
448 | ASSERT(i != 0); | ||
449 | if (xfs_ilock_nowait(ips[i], xfs_lock_inumorder(lock_mode, i))) | ||
450 | continue; | ||
446 | 451 | ||
447 | if (try_lock) { | 452 | /* |
448 | /* try_lock must be 0 if i is 0. */ | 453 | * Unlock all previous guys and try again. xfs_iunlock will try |
454 | * to push the tail if the inode is in the AIL. | ||
455 | */ | ||
456 | attempts++; | ||
457 | for (j = i - 1; j >= 0; j--) { | ||
449 | /* | 458 | /* |
450 | * try_lock means we have an inode locked | 459 | * Check to see if we've already unlocked this one. Not |
451 | * that is in the AIL. | 460 | * the first one going back, and the inode ptr is the |
461 | * same. | ||
452 | */ | 462 | */ |
453 | ASSERT(i != 0); | 463 | if (j != (i - 1) && ips[j] == ips[j + 1]) |
454 | if (!xfs_ilock_nowait(ips[i], xfs_lock_inumorder(lock_mode, i))) { | 464 | continue; |
455 | attempts++; | ||
456 | |||
457 | /* | ||
458 | * Unlock all previous guys and try again. | ||
459 | * xfs_iunlock will try to push the tail | ||
460 | * if the inode is in the AIL. | ||
461 | */ | ||
462 | |||
463 | for(j = i - 1; j >= 0; j--) { | ||
464 | |||
465 | /* | ||
466 | * Check to see if we've already | ||
467 | * unlocked this one. | ||
468 | * Not the first one going back, | ||
469 | * and the inode ptr is the same. | ||
470 | */ | ||
471 | if ((j != (i - 1)) && ips[j] == | ||
472 | ips[j+1]) | ||
473 | continue; | ||
474 | |||
475 | xfs_iunlock(ips[j], lock_mode); | ||
476 | } | ||
477 | 465 | ||
478 | if ((attempts % 5) == 0) { | 466 | xfs_iunlock(ips[j], lock_mode); |
479 | delay(1); /* Don't just spin the CPU */ | 467 | } |
468 | |||
469 | if ((attempts % 5) == 0) { | ||
470 | delay(1); /* Don't just spin the CPU */ | ||
480 | #ifdef DEBUG | 471 | #ifdef DEBUG |
481 | xfs_lock_delays++; | 472 | xfs_lock_delays++; |
482 | #endif | 473 | #endif |
483 | } | ||
484 | i = 0; | ||
485 | try_lock = 0; | ||
486 | goto again; | ||
487 | } | ||
488 | } else { | ||
489 | xfs_ilock(ips[i], xfs_lock_inumorder(lock_mode, i)); | ||
490 | } | 474 | } |
475 | i = 0; | ||
476 | try_lock = 0; | ||
477 | goto again; | ||
491 | } | 478 | } |
492 | 479 | ||
493 | #ifdef DEBUG | 480 | #ifdef DEBUG |
@@ -2681,19 +2668,22 @@ xfs_remove( | |||
2681 | /* | 2668 | /* |
2682 | * Enter all inodes for a rename transaction into a sorted array. | 2669 | * Enter all inodes for a rename transaction into a sorted array. |
2683 | */ | 2670 | */ |
2671 | #define __XFS_SORT_INODES 5 | ||
2684 | STATIC void | 2672 | STATIC void |
2685 | xfs_sort_for_rename( | 2673 | xfs_sort_for_rename( |
2686 | xfs_inode_t *dp1, /* in: old (source) directory inode */ | 2674 | struct xfs_inode *dp1, /* in: old (source) directory inode */ |
2687 | xfs_inode_t *dp2, /* in: new (target) directory inode */ | 2675 | struct xfs_inode *dp2, /* in: new (target) directory inode */ |
2688 | xfs_inode_t *ip1, /* in: inode of old entry */ | 2676 | struct xfs_inode *ip1, /* in: inode of old entry */ |
2689 | xfs_inode_t *ip2, /* in: inode of new entry, if it | 2677 | struct xfs_inode *ip2, /* in: inode of new entry */ |
2690 | already exists, NULL otherwise. */ | 2678 | struct xfs_inode *wip, /* in: whiteout inode */ |
2691 | xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ | 2679 | struct xfs_inode **i_tab,/* out: sorted array of inodes */ |
2692 | int *num_inodes) /* out: number of inodes in array */ | 2680 | int *num_inodes) /* in/out: inodes in array */ |
2693 | { | 2681 | { |
2694 | xfs_inode_t *temp; | ||
2695 | int i, j; | 2682 | int i, j; |
2696 | 2683 | ||
2684 | ASSERT(*num_inodes == __XFS_SORT_INODES); | ||
2685 | memset(i_tab, 0, *num_inodes * sizeof(struct xfs_inode *)); | ||
2686 | |||
2697 | /* | 2687 | /* |
2698 | * i_tab contains a list of pointers to inodes. We initialize | 2688 | * i_tab contains a list of pointers to inodes. We initialize |
2699 | * the table here & we'll sort it. We will then use it to | 2689 | * the table here & we'll sort it. We will then use it to |
@@ -2701,25 +2691,24 @@ xfs_sort_for_rename( | |||
2701 | * | 2691 | * |
2702 | * Note that the table may contain duplicates. e.g., dp1 == dp2. | 2692 | * Note that the table may contain duplicates. e.g., dp1 == dp2. |
2703 | */ | 2693 | */ |
2704 | i_tab[0] = dp1; | 2694 | i = 0; |
2705 | i_tab[1] = dp2; | 2695 | i_tab[i++] = dp1; |
2706 | i_tab[2] = ip1; | 2696 | i_tab[i++] = dp2; |
2707 | if (ip2) { | 2697 | i_tab[i++] = ip1; |
2708 | *num_inodes = 4; | 2698 | if (ip2) |
2709 | i_tab[3] = ip2; | 2699 | i_tab[i++] = ip2; |
2710 | } else { | 2700 | if (wip) |
2711 | *num_inodes = 3; | 2701 | i_tab[i++] = wip; |
2712 | i_tab[3] = NULL; | 2702 | *num_inodes = i; |
2713 | } | ||
2714 | 2703 | ||
2715 | /* | 2704 | /* |
2716 | * Sort the elements via bubble sort. (Remember, there are at | 2705 | * Sort the elements via bubble sort. (Remember, there are at |
2717 | * most 4 elements to sort, so this is adequate.) | 2706 | * most 5 elements to sort, so this is adequate.) |
2718 | */ | 2707 | */ |
2719 | for (i = 0; i < *num_inodes; i++) { | 2708 | for (i = 0; i < *num_inodes; i++) { |
2720 | for (j = 1; j < *num_inodes; j++) { | 2709 | for (j = 1; j < *num_inodes; j++) { |
2721 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { | 2710 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { |
2722 | temp = i_tab[j]; | 2711 | struct xfs_inode *temp = i_tab[j]; |
2723 | i_tab[j] = i_tab[j-1]; | 2712 | i_tab[j] = i_tab[j-1]; |
2724 | i_tab[j-1] = temp; | 2713 | i_tab[j-1] = temp; |
2725 | } | 2714 | } |
@@ -2727,6 +2716,31 @@ xfs_sort_for_rename( | |||
2727 | } | 2716 | } |
2728 | } | 2717 | } |
2729 | 2718 | ||
2719 | static int | ||
2720 | xfs_finish_rename( | ||
2721 | struct xfs_trans *tp, | ||
2722 | struct xfs_bmap_free *free_list) | ||
2723 | { | ||
2724 | int committed = 0; | ||
2725 | int error; | ||
2726 | |||
2727 | /* | ||
2728 | * If this is a synchronous mount, make sure that the rename transaction | ||
2729 | * goes to disk before returning to the user. | ||
2730 | */ | ||
2731 | if (tp->t_mountp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) | ||
2732 | xfs_trans_set_sync(tp); | ||
2733 | |||
2734 | error = xfs_bmap_finish(&tp, free_list, &committed); | ||
2735 | if (error) { | ||
2736 | xfs_bmap_cancel(free_list); | ||
2737 | xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); | ||
2738 | return error; | ||
2739 | } | ||
2740 | |||
2741 | return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | ||
2742 | } | ||
2743 | |||
2730 | /* | 2744 | /* |
2731 | * xfs_cross_rename() | 2745 | * xfs_cross_rename() |
2732 | * | 2746 | * |
@@ -2755,14 +2769,14 @@ xfs_cross_rename( | |||
2755 | ip2->i_ino, | 2769 | ip2->i_ino, |
2756 | first_block, free_list, spaceres); | 2770 | first_block, free_list, spaceres); |
2757 | if (error) | 2771 | if (error) |
2758 | goto out; | 2772 | goto out_trans_abort; |
2759 | 2773 | ||
2760 | /* Swap inode number for dirent in second parent */ | 2774 | /* Swap inode number for dirent in second parent */ |
2761 | error = xfs_dir_replace(tp, dp2, name2, | 2775 | error = xfs_dir_replace(tp, dp2, name2, |
2762 | ip1->i_ino, | 2776 | ip1->i_ino, |
2763 | first_block, free_list, spaceres); | 2777 | first_block, free_list, spaceres); |
2764 | if (error) | 2778 | if (error) |
2765 | goto out; | 2779 | goto out_trans_abort; |
2766 | 2780 | ||
2767 | /* | 2781 | /* |
2768 | * If we're renaming one or more directories across different parents, | 2782 | * If we're renaming one or more directories across different parents, |
@@ -2777,16 +2791,16 @@ xfs_cross_rename( | |||
2777 | dp1->i_ino, first_block, | 2791 | dp1->i_ino, first_block, |
2778 | free_list, spaceres); | 2792 | free_list, spaceres); |
2779 | if (error) | 2793 | if (error) |
2780 | goto out; | 2794 | goto out_trans_abort; |
2781 | 2795 | ||
2782 | /* transfer ip2 ".." reference to dp1 */ | 2796 | /* transfer ip2 ".." reference to dp1 */ |
2783 | if (!S_ISDIR(ip1->i_d.di_mode)) { | 2797 | if (!S_ISDIR(ip1->i_d.di_mode)) { |
2784 | error = xfs_droplink(tp, dp2); | 2798 | error = xfs_droplink(tp, dp2); |
2785 | if (error) | 2799 | if (error) |
2786 | goto out; | 2800 | goto out_trans_abort; |
2787 | error = xfs_bumplink(tp, dp1); | 2801 | error = xfs_bumplink(tp, dp1); |
2788 | if (error) | 2802 | if (error) |
2789 | goto out; | 2803 | goto out_trans_abort; |
2790 | } | 2804 | } |
2791 | 2805 | ||
2792 | /* | 2806 | /* |
@@ -2804,16 +2818,16 @@ xfs_cross_rename( | |||
2804 | dp2->i_ino, first_block, | 2818 | dp2->i_ino, first_block, |
2805 | free_list, spaceres); | 2819 | free_list, spaceres); |
2806 | if (error) | 2820 | if (error) |
2807 | goto out; | 2821 | goto out_trans_abort; |
2808 | 2822 | ||
2809 | /* transfer ip1 ".." reference to dp2 */ | 2823 | /* transfer ip1 ".." reference to dp2 */ |
2810 | if (!S_ISDIR(ip2->i_d.di_mode)) { | 2824 | if (!S_ISDIR(ip2->i_d.di_mode)) { |
2811 | error = xfs_droplink(tp, dp1); | 2825 | error = xfs_droplink(tp, dp1); |
2812 | if (error) | 2826 | if (error) |
2813 | goto out; | 2827 | goto out_trans_abort; |
2814 | error = xfs_bumplink(tp, dp2); | 2828 | error = xfs_bumplink(tp, dp2); |
2815 | if (error) | 2829 | if (error) |
2816 | goto out; | 2830 | goto out_trans_abort; |
2817 | } | 2831 | } |
2818 | 2832 | ||
2819 | /* | 2833 | /* |
@@ -2841,66 +2855,108 @@ xfs_cross_rename( | |||
2841 | } | 2855 | } |
2842 | xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | 2856 | xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); |
2843 | xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); | 2857 | xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE); |
2844 | out: | 2858 | return xfs_finish_rename(tp, free_list); |
2859 | |||
2860 | out_trans_abort: | ||
2861 | xfs_bmap_cancel(free_list); | ||
2862 | xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); | ||
2845 | return error; | 2863 | return error; |
2846 | } | 2864 | } |
2847 | 2865 | ||
2848 | /* | 2866 | /* |
2867 | * xfs_rename_alloc_whiteout() | ||
2868 | * | ||
2869 | * Return a referenced, unlinked, unlocked inode that that can be used as a | ||
2870 | * whiteout in a rename transaction. We use a tmpfile inode here so that if we | ||
2871 | * crash between allocating the inode and linking it into the rename transaction | ||
2872 | * recovery will free the inode and we won't leak it. | ||
2873 | */ | ||
2874 | static int | ||
2875 | xfs_rename_alloc_whiteout( | ||
2876 | struct xfs_inode *dp, | ||
2877 | struct xfs_inode **wip) | ||
2878 | { | ||
2879 | struct xfs_inode *tmpfile; | ||
2880 | int error; | ||
2881 | |||
2882 | error = xfs_create_tmpfile(dp, NULL, S_IFCHR | WHITEOUT_MODE, &tmpfile); | ||
2883 | if (error) | ||
2884 | return error; | ||
2885 | |||
2886 | /* Satisfy xfs_bumplink that this is a real tmpfile */ | ||
2887 | xfs_finish_inode_setup(tmpfile); | ||
2888 | VFS_I(tmpfile)->i_state |= I_LINKABLE; | ||
2889 | |||
2890 | *wip = tmpfile; | ||
2891 | return 0; | ||
2892 | } | ||
2893 | |||
2894 | /* | ||
2849 | * xfs_rename | 2895 | * xfs_rename |
2850 | */ | 2896 | */ |
2851 | int | 2897 | int |
2852 | xfs_rename( | 2898 | xfs_rename( |
2853 | xfs_inode_t *src_dp, | 2899 | struct xfs_inode *src_dp, |
2854 | struct xfs_name *src_name, | 2900 | struct xfs_name *src_name, |
2855 | xfs_inode_t *src_ip, | 2901 | struct xfs_inode *src_ip, |
2856 | xfs_inode_t *target_dp, | 2902 | struct xfs_inode *target_dp, |
2857 | struct xfs_name *target_name, | 2903 | struct xfs_name *target_name, |
2858 | xfs_inode_t *target_ip, | 2904 | struct xfs_inode *target_ip, |
2859 | unsigned int flags) | 2905 | unsigned int flags) |
2860 | { | 2906 | { |
2861 | xfs_trans_t *tp = NULL; | 2907 | struct xfs_mount *mp = src_dp->i_mount; |
2862 | xfs_mount_t *mp = src_dp->i_mount; | 2908 | struct xfs_trans *tp; |
2863 | int new_parent; /* moving to a new dir */ | 2909 | struct xfs_bmap_free free_list; |
2864 | int src_is_directory; /* src_name is a directory */ | 2910 | xfs_fsblock_t first_block; |
2865 | int error; | 2911 | struct xfs_inode *wip = NULL; /* whiteout inode */ |
2866 | xfs_bmap_free_t free_list; | 2912 | struct xfs_inode *inodes[__XFS_SORT_INODES]; |
2867 | xfs_fsblock_t first_block; | 2913 | int num_inodes = __XFS_SORT_INODES; |
2868 | int cancel_flags; | 2914 | int new_parent = (src_dp != target_dp); |
2869 | int committed; | 2915 | int src_is_directory = S_ISDIR(src_ip->i_d.di_mode); |
2870 | xfs_inode_t *inodes[4]; | 2916 | int cancel_flags = 0; |
2871 | int spaceres; | 2917 | int spaceres; |
2872 | int num_inodes; | 2918 | int error; |
2873 | 2919 | ||
2874 | trace_xfs_rename(src_dp, target_dp, src_name, target_name); | 2920 | trace_xfs_rename(src_dp, target_dp, src_name, target_name); |
2875 | 2921 | ||
2876 | new_parent = (src_dp != target_dp); | 2922 | if ((flags & RENAME_EXCHANGE) && !target_ip) |
2877 | src_is_directory = S_ISDIR(src_ip->i_d.di_mode); | 2923 | return -EINVAL; |
2924 | |||
2925 | /* | ||
2926 | * If we are doing a whiteout operation, allocate the whiteout inode | ||
2927 | * we will be placing at the target and ensure the type is set | ||
2928 | * appropriately. | ||
2929 | */ | ||
2930 | if (flags & RENAME_WHITEOUT) { | ||
2931 | ASSERT(!(flags & (RENAME_NOREPLACE | RENAME_EXCHANGE))); | ||
2932 | error = xfs_rename_alloc_whiteout(target_dp, &wip); | ||
2933 | if (error) | ||
2934 | return error; | ||
2935 | |||
2936 | /* setup target dirent info as whiteout */ | ||
2937 | src_name->type = XFS_DIR3_FT_CHRDEV; | ||
2938 | } | ||
2878 | 2939 | ||
2879 | xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, | 2940 | xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip, |
2880 | inodes, &num_inodes); | 2941 | inodes, &num_inodes); |
2881 | 2942 | ||
2882 | xfs_bmap_init(&free_list, &first_block); | ||
2883 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); | 2943 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); |
2884 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; | ||
2885 | spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len); | 2944 | spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len); |
2886 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, spaceres, 0); | 2945 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, spaceres, 0); |
2887 | if (error == -ENOSPC) { | 2946 | if (error == -ENOSPC) { |
2888 | spaceres = 0; | 2947 | spaceres = 0; |
2889 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, 0, 0); | 2948 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_rename, 0, 0); |
2890 | } | 2949 | } |
2891 | if (error) { | 2950 | if (error) |
2892 | xfs_trans_cancel(tp, 0); | 2951 | goto out_trans_cancel; |
2893 | goto std_return; | 2952 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; |
2894 | } | ||
2895 | 2953 | ||
2896 | /* | 2954 | /* |
2897 | * Attach the dquots to the inodes | 2955 | * Attach the dquots to the inodes |
2898 | */ | 2956 | */ |
2899 | error = xfs_qm_vop_rename_dqattach(inodes); | 2957 | error = xfs_qm_vop_rename_dqattach(inodes); |
2900 | if (error) { | 2958 | if (error) |
2901 | xfs_trans_cancel(tp, cancel_flags); | 2959 | goto out_trans_cancel; |
2902 | goto std_return; | ||
2903 | } | ||
2904 | 2960 | ||
2905 | /* | 2961 | /* |
2906 | * Lock all the participating inodes. Depending upon whether | 2962 | * Lock all the participating inodes. Depending upon whether |
@@ -2921,6 +2977,8 @@ xfs_rename( | |||
2921 | xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); | 2977 | xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); |
2922 | if (target_ip) | 2978 | if (target_ip) |
2923 | xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); | 2979 | xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); |
2980 | if (wip) | ||
2981 | xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL); | ||
2924 | 2982 | ||
2925 | /* | 2983 | /* |
2926 | * If we are using project inheritance, we only allow renames | 2984 | * If we are using project inheritance, we only allow renames |
@@ -2930,24 +2988,16 @@ xfs_rename( | |||
2930 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && | 2988 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && |
2931 | (xfs_get_projid(target_dp) != xfs_get_projid(src_ip)))) { | 2989 | (xfs_get_projid(target_dp) != xfs_get_projid(src_ip)))) { |
2932 | error = -EXDEV; | 2990 | error = -EXDEV; |
2933 | goto error_return; | 2991 | goto out_trans_cancel; |
2934 | } | 2992 | } |
2935 | 2993 | ||
2936 | /* | 2994 | xfs_bmap_init(&free_list, &first_block); |
2937 | * Handle RENAME_EXCHANGE flags | 2995 | |
2938 | */ | 2996 | /* RENAME_EXCHANGE is unique from here on. */ |
2939 | if (flags & RENAME_EXCHANGE) { | 2997 | if (flags & RENAME_EXCHANGE) |
2940 | if (target_ip == NULL) { | 2998 | return xfs_cross_rename(tp, src_dp, src_name, src_ip, |
2941 | error = -EINVAL; | 2999 | target_dp, target_name, target_ip, |
2942 | goto error_return; | 3000 | &free_list, &first_block, spaceres); |
2943 | } | ||
2944 | error = xfs_cross_rename(tp, src_dp, src_name, src_ip, | ||
2945 | target_dp, target_name, target_ip, | ||
2946 | &free_list, &first_block, spaceres); | ||
2947 | if (error) | ||
2948 | goto abort_return; | ||
2949 | goto finish_rename; | ||
2950 | } | ||
2951 | 3001 | ||
2952 | /* | 3002 | /* |
2953 | * Set up the target. | 3003 | * Set up the target. |
@@ -2960,7 +3010,7 @@ xfs_rename( | |||
2960 | if (!spaceres) { | 3010 | if (!spaceres) { |
2961 | error = xfs_dir_canenter(tp, target_dp, target_name); | 3011 | error = xfs_dir_canenter(tp, target_dp, target_name); |
2962 | if (error) | 3012 | if (error) |
2963 | goto error_return; | 3013 | goto out_trans_cancel; |
2964 | } | 3014 | } |
2965 | /* | 3015 | /* |
2966 | * If target does not exist and the rename crosses | 3016 | * If target does not exist and the rename crosses |
@@ -2971,9 +3021,9 @@ xfs_rename( | |||
2971 | src_ip->i_ino, &first_block, | 3021 | src_ip->i_ino, &first_block, |
2972 | &free_list, spaceres); | 3022 | &free_list, spaceres); |
2973 | if (error == -ENOSPC) | 3023 | if (error == -ENOSPC) |
2974 | goto error_return; | 3024 | goto out_bmap_cancel; |
2975 | if (error) | 3025 | if (error) |
2976 | goto abort_return; | 3026 | goto out_trans_abort; |
2977 | 3027 | ||
2978 | xfs_trans_ichgtime(tp, target_dp, | 3028 | xfs_trans_ichgtime(tp, target_dp, |
2979 | XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | 3029 | XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); |
@@ -2981,7 +3031,7 @@ xfs_rename( | |||
2981 | if (new_parent && src_is_directory) { | 3031 | if (new_parent && src_is_directory) { |
2982 | error = xfs_bumplink(tp, target_dp); | 3032 | error = xfs_bumplink(tp, target_dp); |
2983 | if (error) | 3033 | if (error) |
2984 | goto abort_return; | 3034 | goto out_trans_abort; |
2985 | } | 3035 | } |
2986 | } else { /* target_ip != NULL */ | 3036 | } else { /* target_ip != NULL */ |
2987 | /* | 3037 | /* |
@@ -2996,7 +3046,7 @@ xfs_rename( | |||
2996 | if (!(xfs_dir_isempty(target_ip)) || | 3046 | if (!(xfs_dir_isempty(target_ip)) || |
2997 | (target_ip->i_d.di_nlink > 2)) { | 3047 | (target_ip->i_d.di_nlink > 2)) { |
2998 | error = -EEXIST; | 3048 | error = -EEXIST; |
2999 | goto error_return; | 3049 | goto out_trans_cancel; |
3000 | } | 3050 | } |
3001 | } | 3051 | } |
3002 | 3052 | ||
@@ -3013,7 +3063,7 @@ xfs_rename( | |||
3013 | src_ip->i_ino, | 3063 | src_ip->i_ino, |
3014 | &first_block, &free_list, spaceres); | 3064 | &first_block, &free_list, spaceres); |
3015 | if (error) | 3065 | if (error) |
3016 | goto abort_return; | 3066 | goto out_trans_abort; |
3017 | 3067 | ||
3018 | xfs_trans_ichgtime(tp, target_dp, | 3068 | xfs_trans_ichgtime(tp, target_dp, |
3019 | XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | 3069 | XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); |
@@ -3024,7 +3074,7 @@ xfs_rename( | |||
3024 | */ | 3074 | */ |
3025 | error = xfs_droplink(tp, target_ip); | 3075 | error = xfs_droplink(tp, target_ip); |
3026 | if (error) | 3076 | if (error) |
3027 | goto abort_return; | 3077 | goto out_trans_abort; |
3028 | 3078 | ||
3029 | if (src_is_directory) { | 3079 | if (src_is_directory) { |
3030 | /* | 3080 | /* |
@@ -3032,7 +3082,7 @@ xfs_rename( | |||
3032 | */ | 3082 | */ |
3033 | error = xfs_droplink(tp, target_ip); | 3083 | error = xfs_droplink(tp, target_ip); |
3034 | if (error) | 3084 | if (error) |
3035 | goto abort_return; | 3085 | goto out_trans_abort; |
3036 | } | 3086 | } |
3037 | } /* target_ip != NULL */ | 3087 | } /* target_ip != NULL */ |
3038 | 3088 | ||
@@ -3049,7 +3099,7 @@ xfs_rename( | |||
3049 | &first_block, &free_list, spaceres); | 3099 | &first_block, &free_list, spaceres); |
3050 | ASSERT(error != -EEXIST); | 3100 | ASSERT(error != -EEXIST); |
3051 | if (error) | 3101 | if (error) |
3052 | goto abort_return; | 3102 | goto out_trans_abort; |
3053 | } | 3103 | } |
3054 | 3104 | ||
3055 | /* | 3105 | /* |
@@ -3075,49 +3125,67 @@ xfs_rename( | |||
3075 | */ | 3125 | */ |
3076 | error = xfs_droplink(tp, src_dp); | 3126 | error = xfs_droplink(tp, src_dp); |
3077 | if (error) | 3127 | if (error) |
3078 | goto abort_return; | 3128 | goto out_trans_abort; |
3079 | } | 3129 | } |
3080 | 3130 | ||
3081 | error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino, | 3131 | /* |
3132 | * For whiteouts, we only need to update the source dirent with the | ||
3133 | * inode number of the whiteout inode rather than removing it | ||
3134 | * altogether. | ||
3135 | */ | ||
3136 | if (wip) { | ||
3137 | error = xfs_dir_replace(tp, src_dp, src_name, wip->i_ino, | ||
3082 | &first_block, &free_list, spaceres); | 3138 | &first_block, &free_list, spaceres); |
3139 | } else | ||
3140 | error = xfs_dir_removename(tp, src_dp, src_name, src_ip->i_ino, | ||
3141 | &first_block, &free_list, spaceres); | ||
3083 | if (error) | 3142 | if (error) |
3084 | goto abort_return; | 3143 | goto out_trans_abort; |
3085 | |||
3086 | xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
3087 | xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE); | ||
3088 | if (new_parent) | ||
3089 | xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); | ||
3090 | 3144 | ||
3091 | finish_rename: | ||
3092 | /* | 3145 | /* |
3093 | * If this is a synchronous mount, make sure that the | 3146 | * For whiteouts, we need to bump the link count on the whiteout inode. |
3094 | * rename transaction goes to disk before returning to | 3147 | * This means that failures all the way up to this point leave the inode |
3095 | * the user. | 3148 | * on the unlinked list and so cleanup is a simple matter of dropping |
3149 | * the remaining reference to it. If we fail here after bumping the link | ||
3150 | * count, we're shutting down the filesystem so we'll never see the | ||
3151 | * intermediate state on disk. | ||
3096 | */ | 3152 | */ |
3097 | if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) { | 3153 | if (wip) { |
3098 | xfs_trans_set_sync(tp); | 3154 | ASSERT(wip->i_d.di_nlink == 0); |
3099 | } | 3155 | error = xfs_bumplink(tp, wip); |
3156 | if (error) | ||
3157 | goto out_trans_abort; | ||
3158 | error = xfs_iunlink_remove(tp, wip); | ||
3159 | if (error) | ||
3160 | goto out_trans_abort; | ||
3161 | xfs_trans_log_inode(tp, wip, XFS_ILOG_CORE); | ||
3100 | 3162 | ||
3101 | error = xfs_bmap_finish(&tp, &free_list, &committed); | 3163 | /* |
3102 | if (error) { | 3164 | * Now we have a real link, clear the "I'm a tmpfile" state |
3103 | xfs_bmap_cancel(&free_list); | 3165 | * flag from the inode so it doesn't accidentally get misused in |
3104 | xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | | 3166 | * future. |
3105 | XFS_TRANS_ABORT)); | 3167 | */ |
3106 | goto std_return; | 3168 | VFS_I(wip)->i_state &= ~I_LINKABLE; |
3107 | } | 3169 | } |
3108 | 3170 | ||
3109 | /* | 3171 | xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); |
3110 | * trans_commit will unlock src_ip, target_ip & decrement | 3172 | xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE); |
3111 | * the vnode references. | 3173 | if (new_parent) |
3112 | */ | 3174 | xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); |
3113 | return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | ||
3114 | 3175 | ||
3115 | abort_return: | 3176 | error = xfs_finish_rename(tp, &free_list); |
3177 | if (wip) | ||
3178 | IRELE(wip); | ||
3179 | return error; | ||
3180 | |||
3181 | out_trans_abort: | ||
3116 | cancel_flags |= XFS_TRANS_ABORT; | 3182 | cancel_flags |= XFS_TRANS_ABORT; |
3117 | error_return: | 3183 | out_bmap_cancel: |
3118 | xfs_bmap_cancel(&free_list); | 3184 | xfs_bmap_cancel(&free_list); |
3185 | out_trans_cancel: | ||
3119 | xfs_trans_cancel(tp, cancel_flags); | 3186 | xfs_trans_cancel(tp, cancel_flags); |
3120 | std_return: | 3187 | if (wip) |
3188 | IRELE(wip); | ||
3121 | return error; | 3189 | return error; |
3122 | } | 3190 | } |
3123 | 3191 | ||
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 8b9e6887e315..015d6a366b16 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
@@ -394,7 +394,7 @@ xfs_vn_rename( | |||
394 | struct xfs_name oname; | 394 | struct xfs_name oname; |
395 | struct xfs_name nname; | 395 | struct xfs_name nname; |
396 | 396 | ||
397 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) | 397 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) |
398 | return -EINVAL; | 398 | return -EINVAL; |
399 | 399 | ||
400 | /* if we are exchanging files, we need to set i_mode of both files */ | 400 | /* if we are exchanging files, we need to set i_mode of both files */ |