diff options
Diffstat (limited to 'fs/xfs/xfs_rename.c')
-rw-r--r-- | fs/xfs/xfs_rename.c | 252 |
1 files changed, 51 insertions, 201 deletions
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index ee371890d85d..d8063e1ad298 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c | |||
@@ -55,85 +55,32 @@ xfs_rename_unlock4( | |||
55 | 55 | ||
56 | xfs_iunlock(i_tab[0], lock_mode); | 56 | xfs_iunlock(i_tab[0], lock_mode); |
57 | for (i = 1; i < 4; i++) { | 57 | for (i = 1; i < 4; i++) { |
58 | if (i_tab[i] == NULL) { | 58 | if (i_tab[i] == NULL) |
59 | break; | 59 | break; |
60 | } | 60 | |
61 | /* | 61 | /* |
62 | * Watch out for duplicate entries in the table. | 62 | * Watch out for duplicate entries in the table. |
63 | */ | 63 | */ |
64 | if (i_tab[i] != i_tab[i-1]) { | 64 | if (i_tab[i] != i_tab[i-1]) |
65 | xfs_iunlock(i_tab[i], lock_mode); | 65 | xfs_iunlock(i_tab[i], lock_mode); |
66 | } | ||
67 | } | 66 | } |
68 | } | 67 | } |
69 | 68 | ||
70 | #ifdef DEBUG | ||
71 | int xfs_rename_skip, xfs_rename_nskip; | ||
72 | #endif | ||
73 | |||
74 | /* | 69 | /* |
75 | * The following routine will acquire the locks required for a rename | 70 | * Enter all inodes for a rename transaction into a sorted array. |
76 | * operation. The code understands the semantics of renames and will | ||
77 | * validate that name1 exists under dp1 & that name2 may or may not | ||
78 | * exist under dp2. | ||
79 | * | ||
80 | * We are renaming dp1/name1 to dp2/name2. | ||
81 | * | ||
82 | * Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success. | ||
83 | */ | 71 | */ |
84 | STATIC int | 72 | STATIC void |
85 | xfs_lock_for_rename( | 73 | xfs_sort_for_rename( |
86 | xfs_inode_t *dp1, /* in: old (source) directory inode */ | 74 | xfs_inode_t *dp1, /* in: old (source) directory inode */ |
87 | xfs_inode_t *dp2, /* in: new (target) directory inode */ | 75 | xfs_inode_t *dp2, /* in: new (target) directory inode */ |
88 | xfs_inode_t *ip1, /* in: inode of old entry */ | 76 | xfs_inode_t *ip1, /* in: inode of old entry */ |
89 | struct xfs_name *name2, /* in: new entry name */ | 77 | xfs_inode_t *ip2, /* in: inode of new entry, if it |
90 | xfs_inode_t **ipp2, /* out: inode of new entry, if it | ||
91 | already exists, NULL otherwise. */ | 78 | already exists, NULL otherwise. */ |
92 | xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ | 79 | xfs_inode_t **i_tab,/* out: array of inode returned, sorted */ |
93 | int *num_inodes) /* out: number of inodes in array */ | 80 | int *num_inodes) /* out: number of inodes in array */ |
94 | { | 81 | { |
95 | xfs_inode_t *ip2 = NULL; | ||
96 | xfs_inode_t *temp; | 82 | xfs_inode_t *temp; |
97 | xfs_ino_t inum1, inum2; | ||
98 | int error; | ||
99 | int i, j; | 83 | int i, j; |
100 | uint lock_mode; | ||
101 | int diff_dirs = (dp1 != dp2); | ||
102 | |||
103 | /* | ||
104 | * First, find out the current inums of the entries so that we | ||
105 | * can determine the initial locking order. We'll have to | ||
106 | * sanity check stuff after all the locks have been acquired | ||
107 | * to see if we still have the right inodes, directories, etc. | ||
108 | */ | ||
109 | lock_mode = xfs_ilock_map_shared(dp1); | ||
110 | IHOLD(ip1); | ||
111 | xfs_itrace_ref(ip1); | ||
112 | |||
113 | inum1 = ip1->i_ino; | ||
114 | |||
115 | /* | ||
116 | * Unlock dp1 and lock dp2 if they are different. | ||
117 | */ | ||
118 | if (diff_dirs) { | ||
119 | xfs_iunlock_map_shared(dp1, lock_mode); | ||
120 | lock_mode = xfs_ilock_map_shared(dp2); | ||
121 | } | ||
122 | |||
123 | error = xfs_dir_lookup_int(dp2, lock_mode, name2, &inum2, &ip2); | ||
124 | if (error == ENOENT) { /* target does not need to exist. */ | ||
125 | inum2 = 0; | ||
126 | } else if (error) { | ||
127 | /* | ||
128 | * If dp2 and dp1 are the same, the next line unlocks dp1. | ||
129 | * Got it? | ||
130 | */ | ||
131 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
132 | IRELE (ip1); | ||
133 | return error; | ||
134 | } else { | ||
135 | xfs_itrace_ref(ip2); | ||
136 | } | ||
137 | 84 | ||
138 | /* | 85 | /* |
139 | * i_tab contains a list of pointers to inodes. We initialize | 86 | * i_tab contains a list of pointers to inodes. We initialize |
@@ -145,21 +92,20 @@ xfs_lock_for_rename( | |||
145 | i_tab[0] = dp1; | 92 | i_tab[0] = dp1; |
146 | i_tab[1] = dp2; | 93 | i_tab[1] = dp2; |
147 | i_tab[2] = ip1; | 94 | i_tab[2] = ip1; |
148 | if (inum2 == 0) { | 95 | if (ip2) { |
149 | *num_inodes = 3; | ||
150 | i_tab[3] = NULL; | ||
151 | } else { | ||
152 | *num_inodes = 4; | 96 | *num_inodes = 4; |
153 | i_tab[3] = ip2; | 97 | i_tab[3] = ip2; |
98 | } else { | ||
99 | *num_inodes = 3; | ||
100 | i_tab[3] = NULL; | ||
154 | } | 101 | } |
155 | *ipp2 = i_tab[3]; | ||
156 | 102 | ||
157 | /* | 103 | /* |
158 | * Sort the elements via bubble sort. (Remember, there are at | 104 | * Sort the elements via bubble sort. (Remember, there are at |
159 | * most 4 elements to sort, so this is adequate.) | 105 | * most 4 elements to sort, so this is adequate.) |
160 | */ | 106 | */ |
161 | for (i=0; i < *num_inodes; i++) { | 107 | for (i = 0; i < *num_inodes; i++) { |
162 | for (j=1; j < *num_inodes; j++) { | 108 | for (j = 1; j < *num_inodes; j++) { |
163 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { | 109 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { |
164 | temp = i_tab[j]; | 110 | temp = i_tab[j]; |
165 | i_tab[j] = i_tab[j-1]; | 111 | i_tab[j] = i_tab[j-1]; |
@@ -167,30 +113,6 @@ xfs_lock_for_rename( | |||
167 | } | 113 | } |
168 | } | 114 | } |
169 | } | 115 | } |
170 | |||
171 | /* | ||
172 | * We have dp2 locked. If it isn't first, unlock it. | ||
173 | * If it is first, tell xfs_lock_inodes so it can skip it | ||
174 | * when locking. if dp1 == dp2, xfs_lock_inodes will skip both | ||
175 | * since they are equal. xfs_lock_inodes needs all these inodes | ||
176 | * so that it can unlock and retry if there might be a dead-lock | ||
177 | * potential with the log. | ||
178 | */ | ||
179 | |||
180 | if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) { | ||
181 | #ifdef DEBUG | ||
182 | xfs_rename_skip++; | ||
183 | #endif | ||
184 | xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED); | ||
185 | } else { | ||
186 | #ifdef DEBUG | ||
187 | xfs_rename_nskip++; | ||
188 | #endif | ||
189 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
190 | xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED); | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | 116 | } |
195 | 117 | ||
196 | /* | 118 | /* |
@@ -202,10 +124,10 @@ xfs_rename( | |||
202 | struct xfs_name *src_name, | 124 | struct xfs_name *src_name, |
203 | xfs_inode_t *src_ip, | 125 | xfs_inode_t *src_ip, |
204 | xfs_inode_t *target_dp, | 126 | xfs_inode_t *target_dp, |
205 | struct xfs_name *target_name) | 127 | struct xfs_name *target_name, |
128 | xfs_inode_t *target_ip) | ||
206 | { | 129 | { |
207 | xfs_trans_t *tp; | 130 | xfs_trans_t *tp = NULL; |
208 | xfs_inode_t *target_ip; | ||
209 | xfs_mount_t *mp = src_dp->i_mount; | 131 | xfs_mount_t *mp = src_dp->i_mount; |
210 | int new_parent; /* moving to a new dir */ | 132 | int new_parent; /* moving to a new dir */ |
211 | int src_is_directory; /* src_name is a directory */ | 133 | int src_is_directory; /* src_name is a directory */ |
@@ -215,9 +137,7 @@ xfs_rename( | |||
215 | int cancel_flags; | 137 | int cancel_flags; |
216 | int committed; | 138 | int committed; |
217 | xfs_inode_t *inodes[4]; | 139 | xfs_inode_t *inodes[4]; |
218 | int target_ip_dropped = 0; /* dropped target_ip link? */ | ||
219 | int spaceres; | 140 | int spaceres; |
220 | int target_link_zero = 0; | ||
221 | int num_inodes; | 141 | int num_inodes; |
222 | 142 | ||
223 | xfs_itrace_entry(src_dp); | 143 | xfs_itrace_entry(src_dp); |
@@ -230,64 +150,27 @@ xfs_rename( | |||
230 | target_dp, DM_RIGHT_NULL, | 150 | target_dp, DM_RIGHT_NULL, |
231 | src_name->name, target_name->name, | 151 | src_name->name, target_name->name, |
232 | 0, 0, 0); | 152 | 0, 0, 0); |
233 | if (error) { | 153 | if (error) |
234 | return error; | 154 | return error; |
235 | } | ||
236 | } | 155 | } |
237 | /* Return through std_return after this point. */ | 156 | /* Return through std_return after this point. */ |
238 | 157 | ||
239 | /* | 158 | new_parent = (src_dp != target_dp); |
240 | * Lock all the participating inodes. Depending upon whether | 159 | src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); |
241 | * the target_name exists in the target directory, and | ||
242 | * whether the target directory is the same as the source | ||
243 | * directory, we can lock from 2 to 4 inodes. | ||
244 | * xfs_lock_for_rename() will return ENOENT if src_name | ||
245 | * does not exist in the source directory. | ||
246 | */ | ||
247 | tp = NULL; | ||
248 | error = xfs_lock_for_rename(src_dp, target_dp, src_ip, target_name, | ||
249 | &target_ip, inodes, &num_inodes); | ||
250 | if (error) { | ||
251 | /* | ||
252 | * We have nothing locked, no inode references, and | ||
253 | * no transaction, so just get out. | ||
254 | */ | ||
255 | goto std_return; | ||
256 | } | ||
257 | |||
258 | ASSERT(src_ip != NULL); | ||
259 | 160 | ||
260 | if ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR) { | 161 | if (src_is_directory) { |
261 | /* | 162 | /* |
262 | * Check for link count overflow on target_dp | 163 | * Check for link count overflow on target_dp |
263 | */ | 164 | */ |
264 | if (target_ip == NULL && (src_dp != target_dp) && | 165 | if (target_ip == NULL && new_parent && |
265 | target_dp->i_d.di_nlink >= XFS_MAXLINK) { | 166 | target_dp->i_d.di_nlink >= XFS_MAXLINK) { |
266 | error = XFS_ERROR(EMLINK); | 167 | error = XFS_ERROR(EMLINK); |
267 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | 168 | goto std_return; |
268 | goto rele_return; | ||
269 | } | 169 | } |
270 | } | 170 | } |
271 | 171 | ||
272 | /* | 172 | xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, |
273 | * If we are using project inheritance, we only allow renames | 173 | inodes, &num_inodes); |
274 | * into our tree when the project IDs are the same; else the | ||
275 | * tree quota mechanism would be circumvented. | ||
276 | */ | ||
277 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && | ||
278 | (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) { | ||
279 | error = XFS_ERROR(EXDEV); | ||
280 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
281 | goto rele_return; | ||
282 | } | ||
283 | |||
284 | new_parent = (src_dp != target_dp); | ||
285 | src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
286 | |||
287 | /* | ||
288 | * Drop the locks on our inodes so that we can start the transaction. | ||
289 | */ | ||
290 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
291 | 174 | ||
292 | XFS_BMAP_INIT(&free_list, &first_block); | 175 | XFS_BMAP_INIT(&free_list, &first_block); |
293 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); | 176 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); |
@@ -302,7 +185,7 @@ xfs_rename( | |||
302 | } | 185 | } |
303 | if (error) { | 186 | if (error) { |
304 | xfs_trans_cancel(tp, 0); | 187 | xfs_trans_cancel(tp, 0); |
305 | goto rele_return; | 188 | goto std_return; |
306 | } | 189 | } |
307 | 190 | ||
308 | /* | 191 | /* |
@@ -310,13 +193,29 @@ xfs_rename( | |||
310 | */ | 193 | */ |
311 | if ((error = XFS_QM_DQVOPRENAME(mp, inodes))) { | 194 | if ((error = XFS_QM_DQVOPRENAME(mp, inodes))) { |
312 | xfs_trans_cancel(tp, cancel_flags); | 195 | xfs_trans_cancel(tp, cancel_flags); |
313 | goto rele_return; | 196 | goto std_return; |
314 | } | 197 | } |
315 | 198 | ||
316 | /* | 199 | /* |
317 | * Reacquire the inode locks we dropped above. | 200 | * Lock all the participating inodes. Depending upon whether |
201 | * the target_name exists in the target directory, and | ||
202 | * whether the target directory is the same as the source | ||
203 | * directory, we can lock from 2 to 4 inodes. | ||
204 | */ | ||
205 | xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL); | ||
206 | |||
207 | /* | ||
208 | * If we are using project inheritance, we only allow renames | ||
209 | * into our tree when the project IDs are the same; else the | ||
210 | * tree quota mechanism would be circumvented. | ||
318 | */ | 211 | */ |
319 | xfs_lock_inodes(inodes, num_inodes, 0, XFS_ILOCK_EXCL); | 212 | if (unlikely((target_dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) && |
213 | (target_dp->i_d.di_projid != src_ip->i_d.di_projid))) { | ||
214 | error = XFS_ERROR(EXDEV); | ||
215 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
216 | xfs_trans_cancel(tp, cancel_flags); | ||
217 | goto std_return; | ||
218 | } | ||
320 | 219 | ||
321 | /* | 220 | /* |
322 | * Join all the inodes to the transaction. From this point on, | 221 | * Join all the inodes to the transaction. From this point on, |
@@ -328,17 +227,17 @@ xfs_rename( | |||
328 | */ | 227 | */ |
329 | IHOLD(src_dp); | 228 | IHOLD(src_dp); |
330 | xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); | 229 | xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); |
230 | |||
331 | if (new_parent) { | 231 | if (new_parent) { |
332 | IHOLD(target_dp); | 232 | IHOLD(target_dp); |
333 | xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); | 233 | xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); |
334 | } | 234 | } |
335 | if ((src_ip != src_dp) && (src_ip != target_dp)) { | 235 | |
336 | xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); | 236 | IHOLD(src_ip); |
337 | } | 237 | xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); |
338 | if ((target_ip != NULL) && | 238 | |
339 | (target_ip != src_ip) && | 239 | if (target_ip) { |
340 | (target_ip != src_dp) && | 240 | IHOLD(target_ip); |
341 | (target_ip != target_dp)) { | ||
342 | xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); | 241 | xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); |
343 | } | 242 | } |
344 | 243 | ||
@@ -412,7 +311,6 @@ xfs_rename( | |||
412 | error = xfs_droplink(tp, target_ip); | 311 | error = xfs_droplink(tp, target_ip); |
413 | if (error) | 312 | if (error) |
414 | goto abort_return; | 313 | goto abort_return; |
415 | target_ip_dropped = 1; | ||
416 | 314 | ||
417 | if (src_is_directory) { | 315 | if (src_is_directory) { |
418 | /* | 316 | /* |
@@ -422,10 +320,6 @@ xfs_rename( | |||
422 | if (error) | 320 | if (error) |
423 | goto abort_return; | 321 | goto abort_return; |
424 | } | 322 | } |
425 | |||
426 | /* Do this test while we still hold the locks */ | ||
427 | target_link_zero = (target_ip)->i_d.di_nlink==0; | ||
428 | |||
429 | } /* target_ip != NULL */ | 323 | } /* target_ip != NULL */ |
430 | 324 | ||
431 | /* | 325 | /* |
@@ -492,15 +386,6 @@ xfs_rename( | |||
492 | } | 386 | } |
493 | 387 | ||
494 | /* | 388 | /* |
495 | * If there was a target inode, take an extra reference on | ||
496 | * it here so that it doesn't go to xfs_inactive() from | ||
497 | * within the commit. | ||
498 | */ | ||
499 | if (target_ip != NULL) { | ||
500 | IHOLD(target_ip); | ||
501 | } | ||
502 | |||
503 | /* | ||
504 | * If this is a synchronous mount, make sure that the | 389 | * If this is a synchronous mount, make sure that the |
505 | * rename transaction goes to disk before returning to | 390 | * rename transaction goes to disk before returning to |
506 | * the user. | 391 | * the user. |
@@ -509,30 +394,11 @@ xfs_rename( | |||
509 | xfs_trans_set_sync(tp); | 394 | xfs_trans_set_sync(tp); |
510 | } | 395 | } |
511 | 396 | ||
512 | /* | ||
513 | * Take refs. for vop_link_removed calls below. No need to worry | ||
514 | * about directory refs. because the caller holds them. | ||
515 | * | ||
516 | * Do holds before the xfs_bmap_finish since it might rele them down | ||
517 | * to zero. | ||
518 | */ | ||
519 | |||
520 | if (target_ip_dropped) | ||
521 | IHOLD(target_ip); | ||
522 | IHOLD(src_ip); | ||
523 | |||
524 | error = xfs_bmap_finish(&tp, &free_list, &committed); | 397 | error = xfs_bmap_finish(&tp, &free_list, &committed); |
525 | if (error) { | 398 | if (error) { |
526 | xfs_bmap_cancel(&free_list); | 399 | xfs_bmap_cancel(&free_list); |
527 | xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | | 400 | xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | |
528 | XFS_TRANS_ABORT)); | 401 | XFS_TRANS_ABORT)); |
529 | if (target_ip != NULL) { | ||
530 | IRELE(target_ip); | ||
531 | } | ||
532 | if (target_ip_dropped) { | ||
533 | IRELE(target_ip); | ||
534 | } | ||
535 | IRELE(src_ip); | ||
536 | goto std_return; | 402 | goto std_return; |
537 | } | 403 | } |
538 | 404 | ||
@@ -541,15 +407,6 @@ xfs_rename( | |||
541 | * the vnode references. | 407 | * the vnode references. |
542 | */ | 408 | */ |
543 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); | 409 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); |
544 | if (target_ip != NULL) | ||
545 | IRELE(target_ip); | ||
546 | /* | ||
547 | * Let interposed file systems know about removed links. | ||
548 | */ | ||
549 | if (target_ip_dropped) | ||
550 | IRELE(target_ip); | ||
551 | |||
552 | IRELE(src_ip); | ||
553 | 410 | ||
554 | /* Fall through to std_return with error = 0 or errno from | 411 | /* Fall through to std_return with error = 0 or errno from |
555 | * xfs_trans_commit */ | 412 | * xfs_trans_commit */ |
@@ -571,11 +428,4 @@ std_return: | |||
571 | xfs_bmap_cancel(&free_list); | 428 | xfs_bmap_cancel(&free_list); |
572 | xfs_trans_cancel(tp, cancel_flags); | 429 | xfs_trans_cancel(tp, cancel_flags); |
573 | goto std_return; | 430 | goto std_return; |
574 | |||
575 | rele_return: | ||
576 | IRELE(src_ip); | ||
577 | if (target_ip != NULL) { | ||
578 | IRELE(target_ip); | ||
579 | } | ||
580 | goto std_return; | ||
581 | } | 431 | } |