xref: /netbsd-src/sys/fs/udf/udf_rename.c (revision 002d97bff0c8a01a572600e42b4958b0d5a71708)
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