1 /* $NetBSD: udf_rename.c,v 1.16 2024/05/18 00:04:01 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2013 Reinoud Zandijk
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Comments and trivial code from the reference implementation in tmpfs.
28 */
29
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: udf_rename.c,v 1.16 2024/05/18 00:04:01 thorpej Exp $");
32
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/kauth.h>
36 #include <sys/mount.h>
37 #include <sys/namei.h>
38 #include <sys/stat.h>
39 #include <sys/dirent.h>
40 #include <sys/vnode.h>
41 #include <sys/vnode_if.h>
42
43 #include <miscfs/genfs/genfs.h>
44
45 #include <fs/udf/ecma167-udf.h>
46 #include <fs/udf/udf_mount.h>
47 #include <sys/dirhash.h>
48
49 #include "udf.h"
50 #include "udf_subr.h"
51 #include "udf_bswap.h"
52
53
54 /* forwards */
55 static int udf_sane_rename( struct vnode *, struct componentname *,
56 struct vnode *, struct componentname *,
57 kauth_cred_t, bool);
58 static bool udf_rmdired_p(struct vnode *);
59 static int udf_gro_lock_directory(struct mount *, struct vnode *);
60
61 static const struct genfs_rename_ops udf_genfs_rename_ops;
62
63
64 #define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
65
66
67 /*
68 * udf_sane_rename: The hairiest vop, with the saner API.
69 *
70 * Arguments:
71 *
72 * . fdvp (from directory vnode),
73 * . fcnp (from component name),
74 * . tdvp (to directory vnode),
75 * . tcnp (to component name),
76 * . cred (credentials structure), and
77 * . posixly_correct (flag for behaviour if target & source link same file).
78 *
79 * fdvp and tdvp may be the same, and must be referenced and unlocked.
80 */
81 static int
udf_sane_rename(struct vnode * fdvp,struct componentname * fcnp,struct vnode * tdvp,struct componentname * tcnp,kauth_cred_t cred,bool posixly_correct)82 udf_sane_rename( struct vnode *fdvp, struct componentname *fcnp,
83 struct vnode *tdvp, struct componentname *tcnp,
84 kauth_cred_t cred, bool posixly_correct)
85 {
86 DPRINTF(CALL, ("udf_sane_rename '%s' -> '%s'\n",
87 fcnp->cn_nameptr, tcnp->cn_nameptr));
88 return genfs_sane_rename(&udf_genfs_rename_ops,
89 fdvp, fcnp, NULL, tdvp, tcnp, NULL,
90 cred, posixly_correct);
91 }
92
93
94 /*
95 * udf_rename: the hairiest vop, with the insanest API. Pass to
96 * genfs_insane_rename immediately.
97 */
98 int
udf_rename(void * v)99 udf_rename(void *v)
100 {
101 struct vop_rename_args /* {
102 struct vnode *a_fdvp;
103 struct vnode *a_fvp;
104 struct componentname *a_fcnp;
105 struct vnode *a_tdvp;
106 struct vnode *a_tvp;
107 struct componentname *a_tcnp;
108 } */ *ap = v;
109 DPRINTF(CALL, ("udf_rename called\n"));
110 return genfs_insane_rename(ap, &udf_sane_rename);
111 }
112
113
114 /*
115 * udf_gro_directory_empty_p: return true if the directory vp is empty. dvp is
116 * its parent.
117 *
118 * vp and dvp must be locked and referenced.
119 */
120 static bool
udf_gro_directory_empty_p(struct mount * mp,kauth_cred_t cred,struct vnode * vp,struct vnode * dvp)121 udf_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred,
122 struct vnode *vp, struct vnode *dvp)
123 {
124 struct udf_node *udf_node = VTOI(vp);
125 int error, isempty;
126
127 KASSERT(mp != NULL);
128 KASSERT(vp != NULL);
129 KASSERT(dvp != NULL);
130 KASSERT(vp != dvp);
131 KASSERT(vp->v_mount == mp);
132 KASSERT(dvp->v_mount == mp);
133 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
134 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
135
136 DPRINTF(CALL, ("udf_gro_directory_empty_p called\n"));
137 /* make sure our `leaf' node's hash is populated */
138 dirhash_get(&udf_node->dir_hash);
139 error = udf_dirhash_fill(udf_node);
140 if (error) {
141 dirhash_put(udf_node->dir_hash);
142 /* VERY unlikely, answer its not empty */
143 return 0;
144 }
145
146 /* check to see if the directory is empty */
147 isempty = dirhash_dir_isempty(udf_node->dir_hash);
148 dirhash_put(udf_node->dir_hash);
149
150 return isempty;
151 }
152
153
154 /*
155 * udf_gro_rename_check_possible: check whether a rename is possible
156 * independent of credentials.
157 */
158 static int
udf_gro_rename_check_possible(struct mount * mp,struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)159 udf_gro_rename_check_possible(struct mount *mp,
160 struct vnode *fdvp, struct vnode *fvp,
161 struct vnode *tdvp, struct vnode *tvp)
162 {
163 (void)mp;
164 KASSERT(mp != NULL);
165 KASSERT(fdvp != NULL);
166 KASSERT(fvp != NULL);
167 KASSERT(tdvp != NULL);
168 KASSERT(fdvp != fvp);
169 KASSERT(fdvp != tvp);
170 KASSERT(tdvp != fvp);
171 KASSERT(tdvp != tvp);
172 KASSERT(fvp != tvp);
173 KASSERT(fdvp->v_type == VDIR);
174 KASSERT(tdvp->v_type == VDIR);
175 KASSERT(fdvp->v_mount == mp);
176 KASSERT(fvp->v_mount == mp);
177 KASSERT(tdvp->v_mount == mp);
178 KASSERT((tvp == NULL) || (tvp->v_mount == mp));
179 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
180 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
181 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
182 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
183
184 DPRINTF(CALL, ("udf_gro_rename_check_possible called\n"));
185
186 /* flags not implemented since they are not defined (yet) in UDF */
187 return 0;
188 }
189
190
191 /*
192 * udf_gro_rename_check_permitted: check whether a rename is permitted given
193 * our credentials.
194 */
195 static int
udf_gro_rename_check_permitted(struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct vnode * fvp,struct vnode * tdvp,struct vnode * tvp)196 udf_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred,
197 struct vnode *fdvp, struct vnode *fvp,
198 struct vnode *tdvp, struct vnode *tvp)
199 {
200 struct udf_node *fdir_node = VTOI(fdvp);
201 struct udf_node *tdir_node = VTOI(tdvp);
202 struct udf_node *f_node = VTOI(fvp);
203 struct udf_node *t_node = (tvp? VTOI(tvp): NULL);
204 mode_t fdmode, tdmode;
205 uid_t fduid, tduid, fuid, tuid;
206 gid_t gdummy;
207
208 (void)mp;
209 KASSERT(mp != NULL);
210 KASSERT(fdvp != NULL);
211 KASSERT(fvp != NULL);
212 KASSERT(tdvp != NULL);
213 KASSERT(fdvp != fvp);
214 KASSERT(fdvp != tvp);
215 KASSERT(tdvp != fvp);
216 KASSERT(tdvp != tvp);
217 KASSERT(fvp != tvp);
218 KASSERT(fdvp->v_type == VDIR);
219 KASSERT(tdvp->v_type == VDIR);
220 KASSERT(fdvp->v_mount == mp);
221 KASSERT(fvp->v_mount == mp);
222 KASSERT(tdvp->v_mount == mp);
223 KASSERT((tvp == NULL) || (tvp->v_mount == mp));
224 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
225 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
226 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
227 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
228
229 DPRINTF(CALL, ("udf_gro_rename_check_permitted called\n"));
230 fdmode = udf_getaccessmode(fdir_node);
231 tdmode = udf_getaccessmode(tdir_node);
232
233 udf_getownership(fdir_node, &fduid, &gdummy);
234 udf_getownership(tdir_node, &tduid, &gdummy);
235 udf_getownership(f_node, &fuid, &gdummy);
236
237 tuid = 0;
238 if (t_node)
239 udf_getownership(t_node, &tuid, &gdummy);
240
241 return genfs_ufslike_rename_check_permitted(cred,
242 fdvp, fdmode, fduid,
243 fvp, fuid,
244 tdvp, tdmode, tduid,
245 tvp, tuid);
246 }
247
248
249 /*
250 * udf_gro_remove_check_possible: check whether a remove is possible
251 * independent of credentials.
252 *
253 * XXX could check for special attributes?
254 */
255 static int
udf_gro_remove_check_possible(struct mount * mp,struct vnode * dvp,struct vnode * vp)256 udf_gro_remove_check_possible(struct mount *mp,
257 struct vnode *dvp, struct vnode *vp)
258 {
259 (void)mp;
260 KASSERT(mp != NULL);
261 KASSERT(dvp != NULL);
262 KASSERT(vp != NULL);
263 KASSERT(dvp != vp);
264 KASSERT(dvp->v_type == VDIR);
265 KASSERT(vp->v_type != VDIR);
266 KASSERT(dvp->v_mount == mp);
267 KASSERT(vp->v_mount == mp);
268 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
269 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
270
271 DPRINTF(CALL, ("udf_gro_remove_check_possible called\n"));
272
273 /* flags not implemented since they are not defined (yet) in UDF */
274 return 0;
275 }
276
277
278 /*
279 * udf_gro_remove_check_permitted: check whether a remove is permitted given
280 * our credentials.
281 */
282 static int
udf_gro_remove_check_permitted(struct mount * mp,kauth_cred_t cred,struct vnode * dvp,struct vnode * vp)283 udf_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred,
284 struct vnode *dvp, struct vnode *vp)
285 {
286 struct udf_node *dir_node = VTOI(dvp);
287 struct udf_node *udf_node = VTOI(vp);
288 mode_t dmode;
289 uid_t duid, uid;
290 gid_t gdummy;
291
292 (void)mp;
293 KASSERT(mp != NULL);
294 KASSERT(dvp != NULL);
295 KASSERT(vp != NULL);
296 KASSERT(dvp != vp);
297 KASSERT(dvp->v_type == VDIR);
298 KASSERT(vp->v_type != VDIR);
299 KASSERT(dvp->v_mount == mp);
300 KASSERT(vp->v_mount == mp);
301 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
302 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
303
304 DPRINTF(CALL, ("udf_gro_remove_check_permitted called\n"));
305 dmode = udf_getaccessmode(dir_node);
306
307 udf_getownership(dir_node, &duid, &gdummy);
308 udf_getownership(udf_node, &uid, &gdummy);
309
310 return genfs_ufslike_remove_check_permitted(cred,
311 dvp, dmode, duid,
312 vp, uid);
313 }
314
315
316 /*
317 * udf_gro_rename: actually perform the rename operation.
318 */
319 static int
udf_gro_rename(struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct componentname * fcnp,void * fde,struct vnode * fvp,struct vnode * tdvp,struct componentname * tcnp,void * tde,struct vnode * tvp,nlink_t * tvp_nlinkp)320 udf_gro_rename(struct mount *mp, kauth_cred_t cred,
321 struct vnode *fdvp, struct componentname *fcnp,
322 void *fde, struct vnode *fvp,
323 struct vnode *tdvp, struct componentname *tcnp,
324 void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp)
325 {
326 struct udf_node *fnode, *fdnode, *tnode, *tdnode;
327 struct vattr fvap;
328 int error;
329
330 (void)cred;
331 KASSERT(mp != NULL);
332 KASSERT(fdvp != NULL);
333 KASSERT(fcnp != NULL);
334 KASSERT(fvp != NULL);
335 KASSERT(tdvp != NULL);
336 KASSERT(tcnp != NULL);
337 KASSERT(fdvp != fvp);
338 KASSERT(fdvp != tvp);
339 KASSERT(tdvp != fvp);
340 KASSERT(tdvp != tvp);
341 KASSERT(fvp != tvp);
342 KASSERT(fdvp->v_mount == mp);
343 KASSERT(fvp->v_mount == mp);
344 KASSERT(tdvp->v_mount == mp);
345 KASSERT((tvp == NULL) || (tvp->v_mount == mp));
346 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE);
347 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE);
348 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE);
349 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE));
350
351 DPRINTF(CALL, ("udf_gro_rename called\n"));
352 DPRINTF(NODE, ("udf_gro_rename called : %s -> %s\n",
353 fcnp->cn_nameptr, tcnp->cn_nameptr));
354
355 fnode = VTOI(fvp);
356 fdnode = VTOI(fdvp);
357 tnode = (tvp == NULL) ? NULL : VTOI(tvp);
358 tdnode = VTOI(tdvp);
359
360 /* get attribute information */
361 error = VOP_GETATTR(fvp, &fvap, NULL);
362 if (error)
363 return error;
364
365 /* remove existing entry if present */
366 if (tvp) {
367 udf_dir_detach(tdnode->ump, tdnode, tnode, tcnp);
368 if (tnode->fe) {
369 *tvp_nlinkp = udf_rw16(tnode->fe->link_cnt);
370 } else {
371 KASSERT(tnode->efe != NULL);
372 *tvp_nlinkp = udf_rw16(tnode->efe->link_cnt);
373 }
374 }
375
376 /* create new directory entry for the node */
377 error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp);
378 if (error)
379 return error;
380
381 /* unlink old directory entry for the node, if failing, unattach new */
382 error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
383 if (error)
384 goto rollback_attach;
385
386 if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
387 /* update fnode's '..' entry */
388 error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode);
389 if (error)
390 goto rollback;
391 }
392
393 genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
394 return 0;
395
396 rollback:
397 /* 'try' to recover from this situation */
398 udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
399 rollback_attach:
400 udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
401
402 return error;
403 }
404
405
406 /*
407 * udf_gro_remove: rename an object over another link to itself, effectively
408 * removing just the original link.
409 */
410 static int
udf_gro_remove(struct mount * mp,kauth_cred_t cred,struct vnode * dvp,struct componentname * cnp,void * de,struct vnode * vp,nlink_t * tvp_nlinkp)411 udf_gro_remove(struct mount *mp, kauth_cred_t cred,
412 struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp,
413 nlink_t *tvp_nlinkp)
414 {
415 struct udf_node *dir_node, *udf_node;
416
417 KASSERT(mp != NULL);
418 KASSERT(dvp != NULL);
419 KASSERT(cnp != NULL);
420 KASSERT(vp != NULL);
421 KASSERT(dvp != vp);
422 KASSERT(dvp->v_mount == mp);
423 KASSERT(vp->v_mount == mp);
424 KASSERT(dvp->v_type == VDIR);
425 KASSERT(vp->v_type != VDIR);
426 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
427 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
428
429 DPRINTF(CALL, ("udf_gro_remove called\n"));
430
431 dir_node = VTOI(dvp);
432 udf_node = VTOI(vp);
433 udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp);
434
435 if (udf_node->fe) {
436 *tvp_nlinkp = udf_rw16(udf_node->fe->link_cnt);
437 } else {
438 KASSERT(udf_node->efe != NULL);
439 *tvp_nlinkp = udf_rw16(udf_node->efe->link_cnt);
440 }
441
442 return 0;
443 }
444
445
446 /*
447 * udf_gro_lookup: look up and save the lookup results.
448 */
449 static int
udf_gro_lookup(struct mount * mp,struct vnode * dvp,struct componentname * cnp,void * de_ret,struct vnode ** vp_ret)450 udf_gro_lookup(struct mount *mp, struct vnode *dvp,
451 struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
452 {
453 struct udf_node *dir_node, *res_node;
454 struct long_ad icb_loc;
455 const char *name;
456 int namelen, error;
457 int found;
458
459 (void)mp;
460 KASSERT(mp != NULL);
461 KASSERT(dvp != NULL);
462 KASSERT(cnp != NULL);
463 KASSERT(vp_ret != NULL);
464 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
465
466 dir_node = VTOI(dvp);
467
468 DPRINTF(CALL, ("udf_gro_lookup called\n"));
469
470 /* lookup filename in the directory; location icb_loc */
471 name = cnp->cn_nameptr;
472 namelen = cnp->cn_namelen;
473 error = udf_lookup_name_in_dir(dvp, name, namelen,
474 &icb_loc, &found);
475 if (error)
476 return error;
477 if (!found)
478 return ENOENT;
479
480 DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name));
481 error = udf_get_node(dir_node->ump, &icb_loc, &res_node, LK_EXCLUSIVE);
482 if (error)
483 return error;
484 *vp_ret = res_node->vnode;
485 VOP_UNLOCK(res_node->vnode);
486
487 return 0;
488 }
489
490
491 /*
492 * udf_rmdired_p: check whether the directory vp has been rmdired.
493 *
494 * vp must be locked and referenced.
495 */
496 static bool
udf_rmdired_p(struct vnode * vp)497 udf_rmdired_p(struct vnode *vp)
498 {
499 DPRINTF(CALL, ("udf_rmdired_p called\n"));
500
501 KASSERT(vp != NULL);
502 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
503 KASSERT(vp->v_type == VDIR);
504
505 return (VTOI(vp)->i_flags & IN_DELETED);
506 }
507
508
509 /*
510 * udf_gro_genealogy: analyze the genealogy of the source and target
511 * directories.
512 */
513 static int
udf_gro_genealogy(struct mount * mp,kauth_cred_t cred,struct vnode * fdvp,struct vnode * tdvp,struct vnode ** intermediate_node_ret)514 udf_gro_genealogy(struct mount *mp, kauth_cred_t cred,
515 struct vnode *fdvp, struct vnode *tdvp,
516 struct vnode **intermediate_node_ret)
517 {
518 struct udf_mount *ump;
519 struct udf_node *parent_node;
520 struct vnode *vp, *dvp;
521 struct long_ad parent_loc;
522 const char *name;
523 int namelen;
524 int error, found;
525
526 (void)cred;
527 KASSERT(mp != NULL);
528 KASSERT(fdvp != NULL);
529 KASSERT(tdvp != NULL);
530 KASSERT(fdvp != tdvp);
531 KASSERT(intermediate_node_ret != NULL);
532 KASSERT(fdvp->v_mount == mp);
533 KASSERT(tdvp->v_mount == mp);
534 KASSERT(fdvp->v_type == VDIR);
535 KASSERT(tdvp->v_type == VDIR);
536
537 DPRINTF(CALL, ("udf_gro_genealogy called\n"));
538
539 /*
540 * We need to provisionally lock tdvp to keep rmdir from deleting it
541 * -- or any ancestor -- at an inopportune moment.
542 *
543 * XXX WHY is this not in genfs's rename? XXX
544 */
545 error = udf_gro_lock_directory(mp, tdvp);
546 if (error)
547 return error;
548
549 name = "..";
550 namelen = 2;
551 error = 0;
552
553 ump = VTOI(tdvp)->ump;
554
555 /* if nodes are equal, it is no use looking */
556 KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0);
557
558 /* start at destination vnode and walk up the tree */
559 vp = tdvp;
560 vref(vp);
561
562 for (;;) {
563 KASSERT(vp != NULL);
564 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
565 KASSERT(vp->v_mount == mp);
566 KASSERT(vp->v_type == VDIR);
567 KASSERT(!udf_rmdired_p(vp));
568
569 DPRINTF(NODE, ("udf_gro_genealogy : "
570 "fdvp %p, looking at vp %p\n",
571 fdvp, vp));
572
573 /* sanity check */
574 if (vp->v_type != VDIR) {
575 vput(vp);
576 return ENOTDIR;
577 }
578
579 /* go down one level */
580 error = udf_lookup_name_in_dir(vp, name, namelen,
581 &parent_loc, &found);
582 DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, "
583 "found %d\n", error, found));
584 if (!found)
585 error = ENOENT;
586 if (error) {
587 vput(vp);
588 return error;
589 }
590
591 /* did we encounter the root node? i.e. loop back */
592 if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) {
593 DPRINTF(NODE, ("ROOT found!\n"));
594 vput(vp);
595 *intermediate_node_ret = NULL;
596 return 0;
597 }
598
599 /* Did we find that fdvp is an ancestor of tdvp? */
600 if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) {
601 DPRINTF(NODE, ("fdvp is ancestor of tdvp\n"));
602 *intermediate_node_ret = vp;
603 VOP_UNLOCK(vp);
604 return 0;
605 }
606
607 /*
608 * Unlock vp so that we can lock the parent, but keep child vp
609 * referenced until after we have found the parent, so that
610 * parent_node will not be recycled.
611 */
612 DPRINTF(NODE, ("\tgetting the parent node\n"));
613 VOP_UNLOCK(vp);
614 error = udf_get_node(ump, &parent_loc, &parent_node,
615 LK_EXCLUSIVE);
616 vrele(vp);
617 if (error)
618 return error;
619
620 dvp = parent_node->vnode;
621
622 /* switch */
623 KASSERT(dvp != NULL);
624 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
625 vp = dvp;
626
627 /* sanity check */
628 if (vp->v_type != VDIR) {
629 /*
630 * Odd, but can happen if we lose the race and the
631 * '..' node has been recycled.
632 */
633 vput(vp);
634 return ENOTDIR;
635 }
636
637 if (udf_rmdired_p(vp)) {
638 vput(vp);
639 return ENOENT;
640 }
641 }
642 }
643
644
645 /*
646 * udf_gro_lock_directory: lock the directory vp, but fail if it has been
647 * rmdir'd.
648 */
649 static int
udf_gro_lock_directory(struct mount * mp,struct vnode * vp)650 udf_gro_lock_directory(struct mount *mp, struct vnode *vp)
651 {
652
653 (void)mp;
654 KASSERT(mp != NULL);
655 KASSERT(vp != NULL);
656 KASSERT(vp->v_mount == mp);
657
658 DPRINTF(CALL, ("udf_gro_lock_directory called\n"));
659 DPRINTF(LOCKING, ("udf_gro_lock_directory called\n"));
660
661 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
662
663 if (udf_rmdired_p(vp)) {
664 VOP_UNLOCK(vp);
665 return ENOENT;
666 }
667
668 return 0;
669 }
670
671
672 static const struct genfs_rename_ops udf_genfs_rename_ops = {
673 .gro_directory_empty_p = udf_gro_directory_empty_p,
674 .gro_rename_check_possible = udf_gro_rename_check_possible,
675 .gro_rename_check_permitted = udf_gro_rename_check_permitted,
676 .gro_remove_check_possible = udf_gro_remove_check_possible,
677 .gro_remove_check_permitted = udf_gro_remove_check_permitted,
678 .gro_rename = udf_gro_rename,
679 .gro_remove = udf_gro_remove,
680 .gro_lookup = udf_gro_lookup,
681 .gro_genealogy = udf_gro_genealogy,
682 .gro_lock_directory = udf_gro_lock_directory,
683 };
684