diff options
Diffstat (limited to 'fs/xfs/xfs_vnodeops.c')
-rw-r--r-- | fs/xfs/xfs_vnodeops.c | 324 |
1 files changed, 84 insertions, 240 deletions
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index d76565bfcb7b..8297a8c5af90 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c | |||
@@ -2116,13 +2116,6 @@ again: | |||
2116 | #endif | 2116 | #endif |
2117 | } | 2117 | } |
2118 | 2118 | ||
2119 | #ifdef DEBUG | ||
2120 | #define REMOVE_DEBUG_TRACE(x) {remove_which_error_return = (x);} | ||
2121 | int remove_which_error_return = 0; | ||
2122 | #else /* ! DEBUG */ | ||
2123 | #define REMOVE_DEBUG_TRACE(x) | ||
2124 | #endif /* ! DEBUG */ | ||
2125 | |||
2126 | int | 2119 | int |
2127 | xfs_remove( | 2120 | xfs_remove( |
2128 | xfs_inode_t *dp, | 2121 | xfs_inode_t *dp, |
@@ -2131,6 +2124,7 @@ xfs_remove( | |||
2131 | { | 2124 | { |
2132 | xfs_mount_t *mp = dp->i_mount; | 2125 | xfs_mount_t *mp = dp->i_mount; |
2133 | xfs_trans_t *tp = NULL; | 2126 | xfs_trans_t *tp = NULL; |
2127 | int is_dir = S_ISDIR(ip->i_d.di_mode); | ||
2134 | int error = 0; | 2128 | int error = 0; |
2135 | xfs_bmap_free_t free_list; | 2129 | xfs_bmap_free_t free_list; |
2136 | xfs_fsblock_t first_block; | 2130 | xfs_fsblock_t first_block; |
@@ -2138,8 +2132,10 @@ xfs_remove( | |||
2138 | int committed; | 2132 | int committed; |
2139 | int link_zero; | 2133 | int link_zero; |
2140 | uint resblks; | 2134 | uint resblks; |
2135 | uint log_count; | ||
2141 | 2136 | ||
2142 | xfs_itrace_entry(dp); | 2137 | xfs_itrace_entry(dp); |
2138 | xfs_itrace_entry(ip); | ||
2143 | 2139 | ||
2144 | if (XFS_FORCED_SHUTDOWN(mp)) | 2140 | if (XFS_FORCED_SHUTDOWN(mp)) |
2145 | return XFS_ERROR(EIO); | 2141 | return XFS_ERROR(EIO); |
@@ -2152,19 +2148,23 @@ xfs_remove( | |||
2152 | return error; | 2148 | return error; |
2153 | } | 2149 | } |
2154 | 2150 | ||
2155 | xfs_itrace_entry(ip); | ||
2156 | xfs_itrace_ref(ip); | ||
2157 | |||
2158 | error = XFS_QM_DQATTACH(mp, dp, 0); | 2151 | error = XFS_QM_DQATTACH(mp, dp, 0); |
2159 | if (!error) | 2152 | if (error) |
2160 | error = XFS_QM_DQATTACH(mp, ip, 0); | 2153 | goto std_return; |
2161 | if (error) { | 2154 | |
2162 | REMOVE_DEBUG_TRACE(__LINE__); | 2155 | error = XFS_QM_DQATTACH(mp, ip, 0); |
2156 | if (error) | ||
2163 | goto std_return; | 2157 | goto std_return; |
2164 | } | ||
2165 | 2158 | ||
2166 | tp = xfs_trans_alloc(mp, XFS_TRANS_REMOVE); | 2159 | if (is_dir) { |
2160 | tp = xfs_trans_alloc(mp, XFS_TRANS_RMDIR); | ||
2161 | log_count = XFS_DEFAULT_LOG_COUNT; | ||
2162 | } else { | ||
2163 | tp = xfs_trans_alloc(mp, XFS_TRANS_REMOVE); | ||
2164 | log_count = XFS_REMOVE_LOG_COUNT; | ||
2165 | } | ||
2167 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; | 2166 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; |
2167 | |||
2168 | /* | 2168 | /* |
2169 | * We try to get the real space reservation first, | 2169 | * We try to get the real space reservation first, |
2170 | * allowing for directory btree deletion(s) implying | 2170 | * allowing for directory btree deletion(s) implying |
@@ -2176,25 +2176,21 @@ xfs_remove( | |||
2176 | */ | 2176 | */ |
2177 | resblks = XFS_REMOVE_SPACE_RES(mp); | 2177 | resblks = XFS_REMOVE_SPACE_RES(mp); |
2178 | error = xfs_trans_reserve(tp, resblks, XFS_REMOVE_LOG_RES(mp), 0, | 2178 | error = xfs_trans_reserve(tp, resblks, XFS_REMOVE_LOG_RES(mp), 0, |
2179 | XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT); | 2179 | XFS_TRANS_PERM_LOG_RES, log_count); |
2180 | if (error == ENOSPC) { | 2180 | if (error == ENOSPC) { |
2181 | resblks = 0; | 2181 | resblks = 0; |
2182 | error = xfs_trans_reserve(tp, 0, XFS_REMOVE_LOG_RES(mp), 0, | 2182 | error = xfs_trans_reserve(tp, 0, XFS_REMOVE_LOG_RES(mp), 0, |
2183 | XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT); | 2183 | XFS_TRANS_PERM_LOG_RES, log_count); |
2184 | } | 2184 | } |
2185 | if (error) { | 2185 | if (error) { |
2186 | ASSERT(error != ENOSPC); | 2186 | ASSERT(error != ENOSPC); |
2187 | REMOVE_DEBUG_TRACE(__LINE__); | 2187 | cancel_flags = 0; |
2188 | xfs_trans_cancel(tp, 0); | 2188 | goto out_trans_cancel; |
2189 | return error; | ||
2190 | } | 2189 | } |
2191 | 2190 | ||
2192 | error = xfs_lock_dir_and_entry(dp, ip); | 2191 | error = xfs_lock_dir_and_entry(dp, ip); |
2193 | if (error) { | 2192 | if (error) |
2194 | REMOVE_DEBUG_TRACE(__LINE__); | 2193 | goto out_trans_cancel; |
2195 | xfs_trans_cancel(tp, cancel_flags); | ||
2196 | goto std_return; | ||
2197 | } | ||
2198 | 2194 | ||
2199 | /* | 2195 | /* |
2200 | * At this point, we've gotten both the directory and the entry | 2196 | * At this point, we've gotten both the directory and the entry |
@@ -2207,6 +2203,21 @@ xfs_remove( | |||
2207 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); | 2203 | xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); |
2208 | 2204 | ||
2209 | /* | 2205 | /* |
2206 | * If we're removing a directory perform some additional validation. | ||
2207 | */ | ||
2208 | if (is_dir) { | ||
2209 | ASSERT(ip->i_d.di_nlink >= 2); | ||
2210 | if (ip->i_d.di_nlink != 2) { | ||
2211 | error = XFS_ERROR(ENOTEMPTY); | ||
2212 | goto out_trans_cancel; | ||
2213 | } | ||
2214 | if (!xfs_dir_isempty(ip)) { | ||
2215 | error = XFS_ERROR(ENOTEMPTY); | ||
2216 | goto out_trans_cancel; | ||
2217 | } | ||
2218 | } | ||
2219 | |||
2220 | /* | ||
2210 | * Entry must exist since we did a lookup in xfs_lock_dir_and_entry. | 2221 | * Entry must exist since we did a lookup in xfs_lock_dir_and_entry. |
2211 | */ | 2222 | */ |
2212 | XFS_BMAP_INIT(&free_list, &first_block); | 2223 | XFS_BMAP_INIT(&free_list, &first_block); |
@@ -2214,39 +2225,64 @@ xfs_remove( | |||
2214 | &first_block, &free_list, resblks); | 2225 | &first_block, &free_list, resblks); |
2215 | if (error) { | 2226 | if (error) { |
2216 | ASSERT(error != ENOENT); | 2227 | ASSERT(error != ENOENT); |
2217 | REMOVE_DEBUG_TRACE(__LINE__); | 2228 | goto out_bmap_cancel; |
2218 | goto error1; | ||
2219 | } | 2229 | } |
2220 | xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | 2230 | xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); |
2221 | 2231 | ||
2232 | /* | ||
2233 | * Bump the in memory generation count on the parent | ||
2234 | * directory so that other can know that it has changed. | ||
2235 | */ | ||
2222 | dp->i_gen++; | 2236 | dp->i_gen++; |
2223 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); | 2237 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); |
2224 | 2238 | ||
2225 | error = xfs_droplink(tp, ip); | 2239 | if (is_dir) { |
2226 | if (error) { | 2240 | /* |
2227 | REMOVE_DEBUG_TRACE(__LINE__); | 2241 | * Drop the link from ip's "..". |
2228 | goto error1; | 2242 | */ |
2243 | error = xfs_droplink(tp, dp); | ||
2244 | if (error) | ||
2245 | goto out_bmap_cancel; | ||
2246 | |||
2247 | /* | ||
2248 | * Drop the link from dp to ip. | ||
2249 | */ | ||
2250 | error = xfs_droplink(tp, ip); | ||
2251 | if (error) | ||
2252 | goto out_bmap_cancel; | ||
2253 | } else { | ||
2254 | /* | ||
2255 | * When removing a non-directory we need to log the parent | ||
2256 | * inode here for the i_gen update. For a directory this is | ||
2257 | * done implicitly by the xfs_droplink call for the ".." entry. | ||
2258 | */ | ||
2259 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); | ||
2229 | } | 2260 | } |
2230 | 2261 | ||
2231 | /* Determine if this is the last link while | 2262 | /* |
2263 | * Drop the "." link from ip to self. | ||
2264 | */ | ||
2265 | error = xfs_droplink(tp, ip); | ||
2266 | if (error) | ||
2267 | goto out_bmap_cancel; | ||
2268 | |||
2269 | /* | ||
2270 | * Determine if this is the last link while | ||
2232 | * we are in the transaction. | 2271 | * we are in the transaction. |
2233 | */ | 2272 | */ |
2234 | link_zero = (ip)->i_d.di_nlink==0; | 2273 | link_zero = (ip->i_d.di_nlink == 0); |
2235 | 2274 | ||
2236 | /* | 2275 | /* |
2237 | * If this is a synchronous mount, make sure that the | 2276 | * If this is a synchronous mount, make sure that the |
2238 | * remove transaction goes to disk before returning to | 2277 | * remove transaction goes to disk before returning to |
2239 | * the user. | 2278 | * the user. |
2240 | */ | 2279 | */ |
2241 | if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) { | 2280 | if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) |
2242 | xfs_trans_set_sync(tp); | 2281 | xfs_trans_set_sync(tp); |
2243 | } | ||
2244 | 2282 | ||
2245 | error = xfs_bmap_finish(&tp, &free_list, &committed); | 2283 | error = xfs_bmap_finish(&tp, &free_list, &committed); |
2246 | if (error) { | 2284 | if (error) |
2247 | REMOVE_DEBUG_TRACE(__LINE__); | 2285 | goto out_bmap_cancel; |
2248 | goto error_rele; | ||
2249 | } | ||
2250 | 2286 | ||
2251 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | 2287 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); |
2252 | if (error) | 2288 | if (error) |
@@ -2258,38 +2294,26 @@ xfs_remove( | |||
2258 | * will get killed on last close in xfs_close() so we don't | 2294 | * will get killed on last close in xfs_close() so we don't |
2259 | * have to worry about that. | 2295 | * have to worry about that. |
2260 | */ | 2296 | */ |
2261 | if (link_zero && xfs_inode_is_filestream(ip)) | 2297 | if (!is_dir && link_zero && xfs_inode_is_filestream(ip)) |
2262 | xfs_filestream_deassociate(ip); | 2298 | xfs_filestream_deassociate(ip); |
2263 | 2299 | ||
2264 | xfs_itrace_exit(ip); | 2300 | xfs_itrace_exit(ip); |
2301 | xfs_itrace_exit(dp); | ||
2265 | 2302 | ||
2266 | /* Fall through to std_return with error = 0 */ | ||
2267 | std_return: | 2303 | std_return: |
2268 | if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTREMOVE)) { | 2304 | if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTREMOVE)) { |
2269 | (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE, | 2305 | XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE, dp, DM_RIGHT_NULL, |
2270 | dp, DM_RIGHT_NULL, | 2306 | NULL, DM_RIGHT_NULL, name->name, NULL, |
2271 | NULL, DM_RIGHT_NULL, | 2307 | ip->i_d.di_mode, error, 0); |
2272 | name->name, NULL, ip->i_d.di_mode, error, 0); | ||
2273 | } | 2308 | } |
2274 | return error; | ||
2275 | 2309 | ||
2276 | error1: | 2310 | return error; |
2277 | xfs_bmap_cancel(&free_list); | ||
2278 | cancel_flags |= XFS_TRANS_ABORT; | ||
2279 | xfs_trans_cancel(tp, cancel_flags); | ||
2280 | goto std_return; | ||
2281 | 2311 | ||
2282 | error_rele: | 2312 | out_bmap_cancel: |
2283 | /* | ||
2284 | * In this case make sure to not release the inode until after | ||
2285 | * the current transaction is aborted. Releasing it beforehand | ||
2286 | * can cause us to go to xfs_inactive and start a recursive | ||
2287 | * transaction which can easily deadlock with the current one. | ||
2288 | */ | ||
2289 | xfs_bmap_cancel(&free_list); | 2313 | xfs_bmap_cancel(&free_list); |
2290 | cancel_flags |= XFS_TRANS_ABORT; | 2314 | cancel_flags |= XFS_TRANS_ABORT; |
2315 | out_trans_cancel: | ||
2291 | xfs_trans_cancel(tp, cancel_flags); | 2316 | xfs_trans_cancel(tp, cancel_flags); |
2292 | |||
2293 | goto std_return; | 2317 | goto std_return; |
2294 | } | 2318 | } |
2295 | 2319 | ||
@@ -2656,186 +2680,6 @@ std_return: | |||
2656 | } | 2680 | } |
2657 | 2681 | ||
2658 | int | 2682 | int |
2659 | xfs_rmdir( | ||
2660 | xfs_inode_t *dp, | ||
2661 | struct xfs_name *name, | ||
2662 | xfs_inode_t *cdp) | ||
2663 | { | ||
2664 | xfs_mount_t *mp = dp->i_mount; | ||
2665 | xfs_trans_t *tp; | ||
2666 | int error; | ||
2667 | xfs_bmap_free_t free_list; | ||
2668 | xfs_fsblock_t first_block; | ||
2669 | int cancel_flags; | ||
2670 | int committed; | ||
2671 | int last_cdp_link; | ||
2672 | uint resblks; | ||
2673 | |||
2674 | xfs_itrace_entry(dp); | ||
2675 | |||
2676 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
2677 | return XFS_ERROR(EIO); | ||
2678 | |||
2679 | if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) { | ||
2680 | error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE, | ||
2681 | dp, DM_RIGHT_NULL, | ||
2682 | NULL, DM_RIGHT_NULL, name->name, | ||
2683 | NULL, cdp->i_d.di_mode, 0, 0); | ||
2684 | if (error) | ||
2685 | return XFS_ERROR(error); | ||
2686 | } | ||
2687 | |||
2688 | /* | ||
2689 | * Get the dquots for the inodes. | ||
2690 | */ | ||
2691 | error = XFS_QM_DQATTACH(mp, dp, 0); | ||
2692 | if (!error) | ||
2693 | error = XFS_QM_DQATTACH(mp, cdp, 0); | ||
2694 | if (error) { | ||
2695 | REMOVE_DEBUG_TRACE(__LINE__); | ||
2696 | goto std_return; | ||
2697 | } | ||
2698 | |||
2699 | tp = xfs_trans_alloc(mp, XFS_TRANS_RMDIR); | ||
2700 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; | ||
2701 | /* | ||
2702 | * We try to get the real space reservation first, | ||
2703 | * allowing for directory btree deletion(s) implying | ||
2704 | * possible bmap insert(s). If we can't get the space | ||
2705 | * reservation then we use 0 instead, and avoid the bmap | ||
2706 | * btree insert(s) in the directory code by, if the bmap | ||
2707 | * insert tries to happen, instead trimming the LAST | ||
2708 | * block from the directory. | ||
2709 | */ | ||
2710 | resblks = XFS_REMOVE_SPACE_RES(mp); | ||
2711 | error = xfs_trans_reserve(tp, resblks, XFS_REMOVE_LOG_RES(mp), 0, | ||
2712 | XFS_TRANS_PERM_LOG_RES, XFS_DEFAULT_LOG_COUNT); | ||
2713 | if (error == ENOSPC) { | ||
2714 | resblks = 0; | ||
2715 | error = xfs_trans_reserve(tp, 0, XFS_REMOVE_LOG_RES(mp), 0, | ||
2716 | XFS_TRANS_PERM_LOG_RES, XFS_DEFAULT_LOG_COUNT); | ||
2717 | } | ||
2718 | if (error) { | ||
2719 | ASSERT(error != ENOSPC); | ||
2720 | cancel_flags = 0; | ||
2721 | goto error_return; | ||
2722 | } | ||
2723 | XFS_BMAP_INIT(&free_list, &first_block); | ||
2724 | |||
2725 | /* | ||
2726 | * Now lock the child directory inode and the parent directory | ||
2727 | * inode in the proper order. This will take care of validating | ||
2728 | * that the directory entry for the child directory inode has | ||
2729 | * not changed while we were obtaining a log reservation. | ||
2730 | */ | ||
2731 | error = xfs_lock_dir_and_entry(dp, cdp); | ||
2732 | if (error) { | ||
2733 | xfs_trans_cancel(tp, cancel_flags); | ||
2734 | goto std_return; | ||
2735 | } | ||
2736 | |||
2737 | IHOLD(dp); | ||
2738 | xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); | ||
2739 | |||
2740 | IHOLD(cdp); | ||
2741 | xfs_trans_ijoin(tp, cdp, XFS_ILOCK_EXCL); | ||
2742 | |||
2743 | ASSERT(cdp->i_d.di_nlink >= 2); | ||
2744 | if (cdp->i_d.di_nlink != 2) { | ||
2745 | error = XFS_ERROR(ENOTEMPTY); | ||
2746 | goto error_return; | ||
2747 | } | ||
2748 | if (!xfs_dir_isempty(cdp)) { | ||
2749 | error = XFS_ERROR(ENOTEMPTY); | ||
2750 | goto error_return; | ||
2751 | } | ||
2752 | |||
2753 | error = xfs_dir_removename(tp, dp, name, cdp->i_ino, | ||
2754 | &first_block, &free_list, resblks); | ||
2755 | if (error) | ||
2756 | goto error1; | ||
2757 | |||
2758 | xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
2759 | |||
2760 | /* | ||
2761 | * Bump the in memory generation count on the parent | ||
2762 | * directory so that other can know that it has changed. | ||
2763 | */ | ||
2764 | dp->i_gen++; | ||
2765 | |||
2766 | /* | ||
2767 | * Drop the link from cdp's "..". | ||
2768 | */ | ||
2769 | error = xfs_droplink(tp, dp); | ||
2770 | if (error) { | ||
2771 | goto error1; | ||
2772 | } | ||
2773 | |||
2774 | /* | ||
2775 | * Drop the link from dp to cdp. | ||
2776 | */ | ||
2777 | error = xfs_droplink(tp, cdp); | ||
2778 | if (error) { | ||
2779 | goto error1; | ||
2780 | } | ||
2781 | |||
2782 | /* | ||
2783 | * Drop the "." link from cdp to self. | ||
2784 | */ | ||
2785 | error = xfs_droplink(tp, cdp); | ||
2786 | if (error) { | ||
2787 | goto error1; | ||
2788 | } | ||
2789 | |||
2790 | /* Determine these before committing transaction */ | ||
2791 | last_cdp_link = (cdp)->i_d.di_nlink==0; | ||
2792 | |||
2793 | /* | ||
2794 | * If this is a synchronous mount, make sure that the | ||
2795 | * rmdir transaction goes to disk before returning to | ||
2796 | * the user. | ||
2797 | */ | ||
2798 | if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) { | ||
2799 | xfs_trans_set_sync(tp); | ||
2800 | } | ||
2801 | |||
2802 | error = xfs_bmap_finish (&tp, &free_list, &committed); | ||
2803 | if (error) { | ||
2804 | xfs_bmap_cancel(&free_list); | ||
2805 | xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | | ||
2806 | XFS_TRANS_ABORT)); | ||
2807 | goto std_return; | ||
2808 | } | ||
2809 | |||
2810 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | ||
2811 | if (error) { | ||
2812 | goto std_return; | ||
2813 | } | ||
2814 | |||
2815 | |||
2816 | /* Fall through to std_return with error = 0 or the errno | ||
2817 | * from xfs_trans_commit. */ | ||
2818 | std_return: | ||
2819 | if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTREMOVE)) { | ||
2820 | (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE, | ||
2821 | dp, DM_RIGHT_NULL, | ||
2822 | NULL, DM_RIGHT_NULL, | ||
2823 | name->name, NULL, cdp->i_d.di_mode, | ||
2824 | error, 0); | ||
2825 | } | ||
2826 | return error; | ||
2827 | |||
2828 | error1: | ||
2829 | xfs_bmap_cancel(&free_list); | ||
2830 | cancel_flags |= XFS_TRANS_ABORT; | ||
2831 | /* FALLTHROUGH */ | ||
2832 | |||
2833 | error_return: | ||
2834 | xfs_trans_cancel(tp, cancel_flags); | ||
2835 | goto std_return; | ||
2836 | } | ||
2837 | |||
2838 | int | ||
2839 | xfs_symlink( | 2683 | xfs_symlink( |
2840 | xfs_inode_t *dp, | 2684 | xfs_inode_t *dp, |
2841 | struct xfs_name *link_name, | 2685 | struct xfs_name *link_name, |