diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/xfs/xfs_rename.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/xfs/xfs_rename.c')
-rw-r--r-- | fs/xfs/xfs_rename.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c new file mode 100644 index 000000000000..cb13f9a1d45b --- /dev/null +++ b/fs/xfs/xfs_rename.c | |||
@@ -0,0 +1,673 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_macros.h" | ||
35 | #include "xfs_types.h" | ||
36 | #include "xfs_inum.h" | ||
37 | #include "xfs_log.h" | ||
38 | #include "xfs_trans.h" | ||
39 | #include "xfs_sb.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_dmapi.h" | ||
43 | #include "xfs_mount.h" | ||
44 | #include "xfs_bmap_btree.h" | ||
45 | #include "xfs_attr_sf.h" | ||
46 | #include "xfs_dir_sf.h" | ||
47 | #include "xfs_dir2_sf.h" | ||
48 | #include "xfs_dinode.h" | ||
49 | #include "xfs_inode_item.h" | ||
50 | #include "xfs_inode.h" | ||
51 | #include "xfs_bmap.h" | ||
52 | #include "xfs_error.h" | ||
53 | #include "xfs_quota.h" | ||
54 | #include "xfs_refcache.h" | ||
55 | #include "xfs_utils.h" | ||
56 | #include "xfs_trans_space.h" | ||
57 | #include "xfs_da_btree.h" | ||
58 | #include "xfs_dir_leaf.h" | ||
59 | |||
60 | |||
61 | /* | ||
62 | * Given an array of up to 4 inode pointers, unlock the pointed to inodes. | ||
63 | * If there are fewer than 4 entries in the array, the empty entries will | ||
64 | * be at the end and will have NULL pointers in them. | ||
65 | */ | ||
66 | STATIC void | ||
67 | xfs_rename_unlock4( | ||
68 | xfs_inode_t **i_tab, | ||
69 | uint lock_mode) | ||
70 | { | ||
71 | int i; | ||
72 | |||
73 | xfs_iunlock(i_tab[0], lock_mode); | ||
74 | for (i = 1; i < 4; i++) { | ||
75 | if (i_tab[i] == NULL) { | ||
76 | break; | ||
77 | } | ||
78 | /* | ||
79 | * Watch out for duplicate entries in the table. | ||
80 | */ | ||
81 | if (i_tab[i] != i_tab[i-1]) { | ||
82 | xfs_iunlock(i_tab[i], lock_mode); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | #ifdef DEBUG | ||
88 | int xfs_rename_skip, xfs_rename_nskip; | ||
89 | #endif | ||
90 | |||
91 | /* | ||
92 | * The following routine will acquire the locks required for a rename | ||
93 | * operation. The code understands the semantics of renames and will | ||
94 | * validate that name1 exists under dp1 & that name2 may or may not | ||
95 | * exist under dp2. | ||
96 | * | ||
97 | * We are renaming dp1/name1 to dp2/name2. | ||
98 | * | ||
99 | * Return ENOENT if dp1 does not exist, other lookup errors, or 0 for success. | ||
100 | */ | ||
101 | STATIC int | ||
102 | xfs_lock_for_rename( | ||
103 | xfs_inode_t *dp1, /* old (source) directory inode */ | ||
104 | xfs_inode_t *dp2, /* new (target) directory inode */ | ||
105 | vname_t *vname1,/* old entry name */ | ||
106 | vname_t *vname2,/* new entry name */ | ||
107 | xfs_inode_t **ipp1, /* inode of old entry */ | ||
108 | xfs_inode_t **ipp2, /* inode of new entry, if it | ||
109 | already exists, NULL otherwise. */ | ||
110 | xfs_inode_t **i_tab,/* array of inode returned, sorted */ | ||
111 | int *num_inodes) /* number of inodes in array */ | ||
112 | { | ||
113 | xfs_inode_t *ip1, *ip2, *temp; | ||
114 | xfs_ino_t inum1, inum2; | ||
115 | int error; | ||
116 | int i, j; | ||
117 | uint lock_mode; | ||
118 | int diff_dirs = (dp1 != dp2); | ||
119 | |||
120 | ip2 = NULL; | ||
121 | |||
122 | /* | ||
123 | * First, find out the current inums of the entries so that we | ||
124 | * can determine the initial locking order. We'll have to | ||
125 | * sanity check stuff after all the locks have been acquired | ||
126 | * to see if we still have the right inodes, directories, etc. | ||
127 | */ | ||
128 | lock_mode = xfs_ilock_map_shared(dp1); | ||
129 | error = xfs_get_dir_entry(vname1, &ip1); | ||
130 | if (error) { | ||
131 | xfs_iunlock_map_shared(dp1, lock_mode); | ||
132 | return error; | ||
133 | } | ||
134 | |||
135 | inum1 = ip1->i_ino; | ||
136 | |||
137 | ASSERT(ip1); | ||
138 | ITRACE(ip1); | ||
139 | |||
140 | /* | ||
141 | * Unlock dp1 and lock dp2 if they are different. | ||
142 | */ | ||
143 | |||
144 | if (diff_dirs) { | ||
145 | xfs_iunlock_map_shared(dp1, lock_mode); | ||
146 | lock_mode = xfs_ilock_map_shared(dp2); | ||
147 | } | ||
148 | |||
149 | error = xfs_dir_lookup_int(XFS_ITOBHV(dp2), lock_mode, | ||
150 | vname2, &inum2, &ip2); | ||
151 | if (error == ENOENT) { /* target does not need to exist. */ | ||
152 | inum2 = 0; | ||
153 | } else if (error) { | ||
154 | /* | ||
155 | * If dp2 and dp1 are the same, the next line unlocks dp1. | ||
156 | * Got it? | ||
157 | */ | ||
158 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
159 | IRELE (ip1); | ||
160 | return error; | ||
161 | } else { | ||
162 | ITRACE(ip2); | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * i_tab contains a list of pointers to inodes. We initialize | ||
167 | * the table here & we'll sort it. We will then use it to | ||
168 | * order the acquisition of the inode locks. | ||
169 | * | ||
170 | * Note that the table may contain duplicates. e.g., dp1 == dp2. | ||
171 | */ | ||
172 | i_tab[0] = dp1; | ||
173 | i_tab[1] = dp2; | ||
174 | i_tab[2] = ip1; | ||
175 | if (inum2 == 0) { | ||
176 | *num_inodes = 3; | ||
177 | i_tab[3] = NULL; | ||
178 | } else { | ||
179 | *num_inodes = 4; | ||
180 | i_tab[3] = ip2; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Sort the elements via bubble sort. (Remember, there are at | ||
185 | * most 4 elements to sort, so this is adequate.) | ||
186 | */ | ||
187 | for (i=0; i < *num_inodes; i++) { | ||
188 | for (j=1; j < *num_inodes; j++) { | ||
189 | if (i_tab[j]->i_ino < i_tab[j-1]->i_ino) { | ||
190 | temp = i_tab[j]; | ||
191 | i_tab[j] = i_tab[j-1]; | ||
192 | i_tab[j-1] = temp; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * We have dp2 locked. If it isn't first, unlock it. | ||
199 | * If it is first, tell xfs_lock_inodes so it can skip it | ||
200 | * when locking. if dp1 == dp2, xfs_lock_inodes will skip both | ||
201 | * since they are equal. xfs_lock_inodes needs all these inodes | ||
202 | * so that it can unlock and retry if there might be a dead-lock | ||
203 | * potential with the log. | ||
204 | */ | ||
205 | |||
206 | if (i_tab[0] == dp2 && lock_mode == XFS_ILOCK_SHARED) { | ||
207 | #ifdef DEBUG | ||
208 | xfs_rename_skip++; | ||
209 | #endif | ||
210 | xfs_lock_inodes(i_tab, *num_inodes, 1, XFS_ILOCK_SHARED); | ||
211 | } else { | ||
212 | #ifdef DEBUG | ||
213 | xfs_rename_nskip++; | ||
214 | #endif | ||
215 | xfs_iunlock_map_shared(dp2, lock_mode); | ||
216 | xfs_lock_inodes(i_tab, *num_inodes, 0, XFS_ILOCK_SHARED); | ||
217 | } | ||
218 | |||
219 | /* | ||
220 | * Set the return value. Null out any unused entries in i_tab. | ||
221 | */ | ||
222 | *ipp1 = *ipp2 = NULL; | ||
223 | for (i=0; i < *num_inodes; i++) { | ||
224 | if (i_tab[i]->i_ino == inum1) { | ||
225 | *ipp1 = i_tab[i]; | ||
226 | } | ||
227 | if (i_tab[i]->i_ino == inum2) { | ||
228 | *ipp2 = i_tab[i]; | ||
229 | } | ||
230 | } | ||
231 | for (;i < 4; i++) { | ||
232 | i_tab[i] = NULL; | ||
233 | } | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | |||
238 | int rename_which_error_return = 0; | ||
239 | |||
240 | /* | ||
241 | * xfs_rename | ||
242 | */ | ||
243 | int | ||
244 | xfs_rename( | ||
245 | bhv_desc_t *src_dir_bdp, | ||
246 | vname_t *src_vname, | ||
247 | vnode_t *target_dir_vp, | ||
248 | vname_t *target_vname, | ||
249 | cred_t *credp) | ||
250 | { | ||
251 | xfs_trans_t *tp; | ||
252 | xfs_inode_t *src_dp, *target_dp, *src_ip, *target_ip; | ||
253 | xfs_mount_t *mp; | ||
254 | int new_parent; /* moving to a new dir */ | ||
255 | int src_is_directory; /* src_name is a directory */ | ||
256 | int error; | ||
257 | xfs_bmap_free_t free_list; | ||
258 | xfs_fsblock_t first_block; | ||
259 | int cancel_flags; | ||
260 | int committed; | ||
261 | xfs_inode_t *inodes[4]; | ||
262 | int target_ip_dropped = 0; /* dropped target_ip link? */ | ||
263 | vnode_t *src_dir_vp; | ||
264 | bhv_desc_t *target_dir_bdp; | ||
265 | int spaceres; | ||
266 | int target_link_zero = 0; | ||
267 | int num_inodes; | ||
268 | char *src_name = VNAME(src_vname); | ||
269 | char *target_name = VNAME(target_vname); | ||
270 | int src_namelen = VNAMELEN(src_vname); | ||
271 | int target_namelen = VNAMELEN(target_vname); | ||
272 | |||
273 | src_dir_vp = BHV_TO_VNODE(src_dir_bdp); | ||
274 | vn_trace_entry(src_dir_vp, "xfs_rename", (inst_t *)__return_address); | ||
275 | vn_trace_entry(target_dir_vp, "xfs_rename", (inst_t *)__return_address); | ||
276 | |||
277 | /* | ||
278 | * Find the XFS behavior descriptor for the target directory | ||
279 | * vnode since it was not handed to us. | ||
280 | */ | ||
281 | target_dir_bdp = vn_bhv_lookup_unlocked(VN_BHV_HEAD(target_dir_vp), | ||
282 | &xfs_vnodeops); | ||
283 | if (target_dir_bdp == NULL) { | ||
284 | return XFS_ERROR(EXDEV); | ||
285 | } | ||
286 | |||
287 | src_dp = XFS_BHVTOI(src_dir_bdp); | ||
288 | target_dp = XFS_BHVTOI(target_dir_bdp); | ||
289 | mp = src_dp->i_mount; | ||
290 | |||
291 | if (DM_EVENT_ENABLED(src_dir_vp->v_vfsp, src_dp, DM_EVENT_RENAME) || | ||
292 | DM_EVENT_ENABLED(target_dir_vp->v_vfsp, | ||
293 | target_dp, DM_EVENT_RENAME)) { | ||
294 | error = XFS_SEND_NAMESP(mp, DM_EVENT_RENAME, | ||
295 | src_dir_vp, DM_RIGHT_NULL, | ||
296 | target_dir_vp, DM_RIGHT_NULL, | ||
297 | src_name, target_name, | ||
298 | 0, 0, 0); | ||
299 | if (error) { | ||
300 | return error; | ||
301 | } | ||
302 | } | ||
303 | /* Return through std_return after this point. */ | ||
304 | |||
305 | /* | ||
306 | * Lock all the participating inodes. Depending upon whether | ||
307 | * the target_name exists in the target directory, and | ||
308 | * whether the target directory is the same as the source | ||
309 | * directory, we can lock from 2 to 4 inodes. | ||
310 | * xfs_lock_for_rename() will return ENOENT if src_name | ||
311 | * does not exist in the source directory. | ||
312 | */ | ||
313 | tp = NULL; | ||
314 | error = xfs_lock_for_rename(src_dp, target_dp, src_vname, | ||
315 | target_vname, &src_ip, &target_ip, inodes, | ||
316 | &num_inodes); | ||
317 | |||
318 | if (error) { | ||
319 | rename_which_error_return = __LINE__; | ||
320 | /* | ||
321 | * We have nothing locked, no inode references, and | ||
322 | * no transaction, so just get out. | ||
323 | */ | ||
324 | goto std_return; | ||
325 | } | ||
326 | |||
327 | ASSERT(src_ip != NULL); | ||
328 | |||
329 | if ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR) { | ||
330 | /* | ||
331 | * Check for link count overflow on target_dp | ||
332 | */ | ||
333 | if (target_ip == NULL && (src_dp != target_dp) && | ||
334 | target_dp->i_d.di_nlink >= XFS_MAXLINK) { | ||
335 | rename_which_error_return = __LINE__; | ||
336 | error = XFS_ERROR(EMLINK); | ||
337 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
338 | goto rele_return; | ||
339 | } | ||
340 | } | ||
341 | |||
342 | new_parent = (src_dp != target_dp); | ||
343 | src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
344 | |||
345 | /* | ||
346 | * Drop the locks on our inodes so that we can start the transaction. | ||
347 | */ | ||
348 | xfs_rename_unlock4(inodes, XFS_ILOCK_SHARED); | ||
349 | |||
350 | XFS_BMAP_INIT(&free_list, &first_block); | ||
351 | tp = xfs_trans_alloc(mp, XFS_TRANS_RENAME); | ||
352 | cancel_flags = XFS_TRANS_RELEASE_LOG_RES; | ||
353 | spaceres = XFS_RENAME_SPACE_RES(mp, target_namelen); | ||
354 | error = xfs_trans_reserve(tp, spaceres, XFS_RENAME_LOG_RES(mp), 0, | ||
355 | XFS_TRANS_PERM_LOG_RES, XFS_RENAME_LOG_COUNT); | ||
356 | if (error == ENOSPC) { | ||
357 | spaceres = 0; | ||
358 | error = xfs_trans_reserve(tp, 0, XFS_RENAME_LOG_RES(mp), 0, | ||
359 | XFS_TRANS_PERM_LOG_RES, XFS_RENAME_LOG_COUNT); | ||
360 | } | ||
361 | if (error) { | ||
362 | rename_which_error_return = __LINE__; | ||
363 | xfs_trans_cancel(tp, 0); | ||
364 | goto rele_return; | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * Attach the dquots to the inodes | ||
369 | */ | ||
370 | if ((error = XFS_QM_DQVOPRENAME(mp, inodes))) { | ||
371 | xfs_trans_cancel(tp, cancel_flags); | ||
372 | rename_which_error_return = __LINE__; | ||
373 | goto rele_return; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Reacquire the inode locks we dropped above. | ||
378 | */ | ||
379 | xfs_lock_inodes(inodes, num_inodes, 0, XFS_ILOCK_EXCL); | ||
380 | |||
381 | /* | ||
382 | * Join all the inodes to the transaction. From this point on, | ||
383 | * we can rely on either trans_commit or trans_cancel to unlock | ||
384 | * them. Note that we need to add a vnode reference to the | ||
385 | * directories since trans_commit & trans_cancel will decrement | ||
386 | * them when they unlock the inodes. Also, we need to be careful | ||
387 | * not to add an inode to the transaction more than once. | ||
388 | */ | ||
389 | VN_HOLD(src_dir_vp); | ||
390 | xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); | ||
391 | if (new_parent) { | ||
392 | VN_HOLD(target_dir_vp); | ||
393 | xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); | ||
394 | } | ||
395 | if ((src_ip != src_dp) && (src_ip != target_dp)) { | ||
396 | xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); | ||
397 | } | ||
398 | if ((target_ip != NULL) && | ||
399 | (target_ip != src_ip) && | ||
400 | (target_ip != src_dp) && | ||
401 | (target_ip != target_dp)) { | ||
402 | xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | * Set up the target. | ||
407 | */ | ||
408 | if (target_ip == NULL) { | ||
409 | /* | ||
410 | * If there's no space reservation, check the entry will | ||
411 | * fit before actually inserting it. | ||
412 | */ | ||
413 | if (spaceres == 0 && | ||
414 | (error = XFS_DIR_CANENTER(mp, tp, target_dp, target_name, | ||
415 | target_namelen))) { | ||
416 | rename_which_error_return = __LINE__; | ||
417 | goto error_return; | ||
418 | } | ||
419 | /* | ||
420 | * If target does not exist and the rename crosses | ||
421 | * directories, adjust the target directory link count | ||
422 | * to account for the ".." reference from the new entry. | ||
423 | */ | ||
424 | error = XFS_DIR_CREATENAME(mp, tp, target_dp, target_name, | ||
425 | target_namelen, src_ip->i_ino, | ||
426 | &first_block, &free_list, spaceres); | ||
427 | if (error == ENOSPC) { | ||
428 | rename_which_error_return = __LINE__; | ||
429 | goto error_return; | ||
430 | } | ||
431 | if (error) { | ||
432 | rename_which_error_return = __LINE__; | ||
433 | goto abort_return; | ||
434 | } | ||
435 | xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
436 | |||
437 | if (new_parent && src_is_directory) { | ||
438 | error = xfs_bumplink(tp, target_dp); | ||
439 | if (error) { | ||
440 | rename_which_error_return = __LINE__; | ||
441 | goto abort_return; | ||
442 | } | ||
443 | } | ||
444 | } else { /* target_ip != NULL */ | ||
445 | |||
446 | /* | ||
447 | * If target exists and it's a directory, check that both | ||
448 | * target and source are directories and that target can be | ||
449 | * destroyed, or that neither is a directory. | ||
450 | */ | ||
451 | if ((target_ip->i_d.di_mode & S_IFMT) == S_IFDIR) { | ||
452 | /* | ||
453 | * Make sure target dir is empty. | ||
454 | */ | ||
455 | if (!(XFS_DIR_ISEMPTY(target_ip->i_mount, target_ip)) || | ||
456 | (target_ip->i_d.di_nlink > 2)) { | ||
457 | error = XFS_ERROR(EEXIST); | ||
458 | rename_which_error_return = __LINE__; | ||
459 | goto error_return; | ||
460 | } | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * Link the source inode under the target name. | ||
465 | * If the source inode is a directory and we are moving | ||
466 | * it across directories, its ".." entry will be | ||
467 | * inconsistent until we replace that down below. | ||
468 | * | ||
469 | * In case there is already an entry with the same | ||
470 | * name at the destination directory, remove it first. | ||
471 | */ | ||
472 | error = XFS_DIR_REPLACE(mp, tp, target_dp, target_name, | ||
473 | target_namelen, src_ip->i_ino, &first_block, | ||
474 | &free_list, spaceres); | ||
475 | if (error) { | ||
476 | rename_which_error_return = __LINE__; | ||
477 | goto abort_return; | ||
478 | } | ||
479 | xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
480 | |||
481 | /* | ||
482 | * Decrement the link count on the target since the target | ||
483 | * dir no longer points to it. | ||
484 | */ | ||
485 | error = xfs_droplink(tp, target_ip); | ||
486 | if (error) { | ||
487 | rename_which_error_return = __LINE__; | ||
488 | goto abort_return; | ||
489 | } | ||
490 | target_ip_dropped = 1; | ||
491 | |||
492 | if (src_is_directory) { | ||
493 | /* | ||
494 | * Drop the link from the old "." entry. | ||
495 | */ | ||
496 | error = xfs_droplink(tp, target_ip); | ||
497 | if (error) { | ||
498 | rename_which_error_return = __LINE__; | ||
499 | goto abort_return; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | /* Do this test while we still hold the locks */ | ||
504 | target_link_zero = (target_ip)->i_d.di_nlink==0; | ||
505 | |||
506 | } /* target_ip != NULL */ | ||
507 | |||
508 | /* | ||
509 | * Remove the source. | ||
510 | */ | ||
511 | if (new_parent && src_is_directory) { | ||
512 | |||
513 | /* | ||
514 | * Rewrite the ".." entry to point to the new | ||
515 | * directory. | ||
516 | */ | ||
517 | error = XFS_DIR_REPLACE(mp, tp, src_ip, "..", 2, | ||
518 | target_dp->i_ino, &first_block, | ||
519 | &free_list, spaceres); | ||
520 | ASSERT(error != EEXIST); | ||
521 | if (error) { | ||
522 | rename_which_error_return = __LINE__; | ||
523 | goto abort_return; | ||
524 | } | ||
525 | xfs_ichgtime(src_ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
526 | |||
527 | } else { | ||
528 | /* | ||
529 | * We always want to hit the ctime on the source inode. | ||
530 | * We do it in the if clause above for the 'new_parent && | ||
531 | * src_is_directory' case, and here we get all the other | ||
532 | * cases. This isn't strictly required by the standards | ||
533 | * since the source inode isn't really being changed, | ||
534 | * but old unix file systems did it and some incremental | ||
535 | * backup programs won't work without it. | ||
536 | */ | ||
537 | xfs_ichgtime(src_ip, XFS_ICHGTIME_CHG); | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Adjust the link count on src_dp. This is necessary when | ||
542 | * renaming a directory, either within one parent when | ||
543 | * the target existed, or across two parent directories. | ||
544 | */ | ||
545 | if (src_is_directory && (new_parent || target_ip != NULL)) { | ||
546 | |||
547 | /* | ||
548 | * Decrement link count on src_directory since the | ||
549 | * entry that's moved no longer points to it. | ||
550 | */ | ||
551 | error = xfs_droplink(tp, src_dp); | ||
552 | if (error) { | ||
553 | rename_which_error_return = __LINE__; | ||
554 | goto abort_return; | ||
555 | } | ||
556 | } | ||
557 | |||
558 | error = XFS_DIR_REMOVENAME(mp, tp, src_dp, src_name, src_namelen, | ||
559 | src_ip->i_ino, &first_block, &free_list, spaceres); | ||
560 | if (error) { | ||
561 | rename_which_error_return = __LINE__; | ||
562 | goto abort_return; | ||
563 | } | ||
564 | xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); | ||
565 | |||
566 | /* | ||
567 | * Update the generation counts on all the directory inodes | ||
568 | * that we're modifying. | ||
569 | */ | ||
570 | src_dp->i_gen++; | ||
571 | xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE); | ||
572 | |||
573 | if (new_parent) { | ||
574 | target_dp->i_gen++; | ||
575 | xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE); | ||
576 | } | ||
577 | |||
578 | /* | ||
579 | * If there was a target inode, take an extra reference on | ||
580 | * it here so that it doesn't go to xfs_inactive() from | ||
581 | * within the commit. | ||
582 | */ | ||
583 | if (target_ip != NULL) { | ||
584 | IHOLD(target_ip); | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * If this is a synchronous mount, make sure that the | ||
589 | * rename transaction goes to disk before returning to | ||
590 | * the user. | ||
591 | */ | ||
592 | if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) { | ||
593 | xfs_trans_set_sync(tp); | ||
594 | } | ||
595 | |||
596 | /* | ||
597 | * Take refs. for vop_link_removed calls below. No need to worry | ||
598 | * about directory refs. because the caller holds them. | ||
599 | * | ||
600 | * Do holds before the xfs_bmap_finish since it might rele them down | ||
601 | * to zero. | ||
602 | */ | ||
603 | |||
604 | if (target_ip_dropped) | ||
605 | IHOLD(target_ip); | ||
606 | IHOLD(src_ip); | ||
607 | |||
608 | error = xfs_bmap_finish(&tp, &free_list, first_block, &committed); | ||
609 | if (error) { | ||
610 | xfs_bmap_cancel(&free_list); | ||
611 | xfs_trans_cancel(tp, (XFS_TRANS_RELEASE_LOG_RES | | ||
612 | XFS_TRANS_ABORT)); | ||
613 | if (target_ip != NULL) { | ||
614 | IRELE(target_ip); | ||
615 | } | ||
616 | if (target_ip_dropped) { | ||
617 | IRELE(target_ip); | ||
618 | } | ||
619 | IRELE(src_ip); | ||
620 | goto std_return; | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * trans_commit will unlock src_ip, target_ip & decrement | ||
625 | * the vnode references. | ||
626 | */ | ||
627 | error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL); | ||
628 | if (target_ip != NULL) { | ||
629 | xfs_refcache_purge_ip(target_ip); | ||
630 | IRELE(target_ip); | ||
631 | } | ||
632 | /* | ||
633 | * Let interposed file systems know about removed links. | ||
634 | */ | ||
635 | if (target_ip_dropped) { | ||
636 | VOP_LINK_REMOVED(XFS_ITOV(target_ip), target_dir_vp, | ||
637 | target_link_zero); | ||
638 | IRELE(target_ip); | ||
639 | } | ||
640 | |||
641 | FSC_NOTIFY_NAME_CHANGED(XFS_ITOV(src_ip)); | ||
642 | |||
643 | IRELE(src_ip); | ||
644 | |||
645 | /* Fall through to std_return with error = 0 or errno from | ||
646 | * xfs_trans_commit */ | ||
647 | std_return: | ||
648 | if (DM_EVENT_ENABLED(src_dir_vp->v_vfsp, src_dp, DM_EVENT_POSTRENAME) || | ||
649 | DM_EVENT_ENABLED(target_dir_vp->v_vfsp, | ||
650 | target_dp, DM_EVENT_POSTRENAME)) { | ||
651 | (void) XFS_SEND_NAMESP (mp, DM_EVENT_POSTRENAME, | ||
652 | src_dir_vp, DM_RIGHT_NULL, | ||
653 | target_dir_vp, DM_RIGHT_NULL, | ||
654 | src_name, target_name, | ||
655 | 0, error, 0); | ||
656 | } | ||
657 | return error; | ||
658 | |||
659 | abort_return: | ||
660 | cancel_flags |= XFS_TRANS_ABORT; | ||
661 | /* FALLTHROUGH */ | ||
662 | error_return: | ||
663 | xfs_bmap_cancel(&free_list); | ||
664 | xfs_trans_cancel(tp, cancel_flags); | ||
665 | goto std_return; | ||
666 | |||
667 | rele_return: | ||
668 | IRELE(src_ip); | ||
669 | if (target_ip != NULL) { | ||
670 | IRELE(target_ip); | ||
671 | } | ||
672 | goto std_return; | ||
673 | } | ||