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