xref: /minix/sys/fs/udf/udf_rename.c (revision ebfedea0)
1 /* $NetBSD: udf_rename.c,v 1.10 2013/07/16 10:49:36 reinoud 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.10 2013/07/16 10:49:36 reinoud 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
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
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
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
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
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
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
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
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)
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 
370 	/* create new directory entry for the node */
371 	error = udf_dir_attach(tdnode->ump, tdnode, fnode, &fvap, tcnp);
372 	if (error)
373 		return error;
374 
375 	/* unlink old directory entry for the node, if failing, unattach new */
376 	error = udf_dir_detach(tdnode->ump, fdnode, fnode, fcnp);
377 	if (error)
378 		goto rollback_attach;
379 
380 	if ((fdnode != tdnode) && (fvp->v_type == VDIR)) {
381 		/* update fnode's '..' entry */
382 		error = udf_dir_update_rootentry(fnode->ump, fnode, tdnode);
383 		if (error)
384 			goto rollback;
385 	}
386 
387 	VN_KNOTE(fvp, NOTE_RENAME);
388 	genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp);
389 	return 0;
390 
391 rollback:
392 	/* 'try' to recover from this situation */
393 	udf_dir_attach(tdnode->ump, fdnode, fnode, &fvap, fcnp);
394 rollback_attach:
395 	udf_dir_detach(tdnode->ump, tdnode, fnode, tcnp);
396 
397 	return error;
398 }
399 
400 
401 /*
402  * udf_gro_remove: rename an object over another link to itself, effectively
403  * removing just the original link.
404  */
405 static int
406 udf_gro_remove(struct mount *mp, kauth_cred_t cred,
407     struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp)
408 {
409 	struct udf_node *dir_node, *udf_node;
410 
411 	KASSERT(mp != NULL);
412 	KASSERT(dvp != NULL);
413 	KASSERT(cnp != NULL);
414 	KASSERT(vp != NULL);
415 	KASSERT(dvp != vp);
416 	KASSERT(dvp->v_mount == mp);
417 	KASSERT(vp->v_mount == mp);
418 	KASSERT(dvp->v_type == VDIR);
419 	KASSERT(vp->v_type != VDIR);
420 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
421 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
422 
423 	DPRINTF(CALL, ("udf_gro_remove called\n"));
424 
425 	dir_node = VTOI(dvp);
426 	udf_node = VTOI(vp);
427 	udf_dir_detach(dir_node->ump, dir_node, udf_node, cnp);
428 
429 	return 0;
430 }
431 
432 
433 /*
434  * udf_gro_lookup: look up and save the lookup results.
435  */
436 static int
437 udf_gro_lookup(struct mount *mp, struct vnode *dvp,
438     struct componentname *cnp, void *de_ret, struct vnode **vp_ret)
439 {
440 	struct udf_node *dir_node, *res_node;
441 	struct long_ad   icb_loc;
442 	const char *name;
443 	int namelen, error;
444 	int found;
445 
446 	(void)mp;
447 	KASSERT(mp != NULL);
448 	KASSERT(dvp != NULL);
449 	KASSERT(cnp != NULL);
450 	KASSERT(vp_ret != NULL);
451 	KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
452 
453 	dir_node = VTOI(dvp);
454 
455 	DPRINTF(CALL, ("udf_gro_lookup called\n"));
456 
457 	/* lookup filename in the directory; location icb_loc */
458 	name    = cnp->cn_nameptr;
459 	namelen = cnp->cn_namelen;
460 	error = udf_lookup_name_in_dir(dvp, name, namelen,
461 			&icb_loc, &found);
462 	if (error)
463 		return error;
464 	if (!found)
465 		return ENOENT;
466 
467 	DPRINTF(LOOKUP, ("udf_gro_lookup found '%s'\n", name));
468 	error = udf_get_node(dir_node->ump, &icb_loc, &res_node);
469 	if (error)
470 		return error;
471 	*vp_ret = res_node->vnode;
472 	VOP_UNLOCK(res_node->vnode);
473 
474 	return 0;
475 }
476 
477 
478 /*
479  * udf_rmdired_p: check whether the directory vp has been rmdired.
480  *
481  * vp must be locked and referenced.
482  */
483 static bool
484 udf_rmdired_p(struct vnode *vp)
485 {
486 	DPRINTF(CALL, ("udf_rmdired_p called\n"));
487 
488 	KASSERT(vp != NULL);
489 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
490 	KASSERT(vp->v_type == VDIR);
491 
492 	return (VTOI(vp)->i_flags & IN_DELETED);
493 }
494 
495 
496 /*
497  * udf_gro_genealogy: analyze the genealogy of the source and target
498  * directories.
499  */
500 static int
501 udf_gro_genealogy(struct mount *mp, kauth_cred_t cred,
502     struct vnode *fdvp, struct vnode *tdvp,
503     struct vnode **intermediate_node_ret)
504 {
505 	struct udf_mount *ump;
506 	struct udf_node *parent_node;
507 	struct vnode *vp, *dvp;
508 	struct long_ad parent_loc;
509 	const char *name;
510 	int namelen;
511 	int error, found;
512 
513 	(void)cred;
514 	KASSERT(mp != NULL);
515 	KASSERT(fdvp != NULL);
516 	KASSERT(tdvp != NULL);
517 	KASSERT(fdvp != tdvp);
518 	KASSERT(intermediate_node_ret != NULL);
519 	KASSERT(fdvp->v_mount == mp);
520 	KASSERT(tdvp->v_mount == mp);
521 	KASSERT(fdvp->v_type == VDIR);
522 	KASSERT(tdvp->v_type == VDIR);
523 
524 	DPRINTF(CALL, ("udf_gro_genealogy called\n"));
525 
526 	/*
527 	 * We need to provisionally lock tdvp to keep rmdir from deleting it
528 	 * -- or any ancestor -- at an inopportune moment.
529 	 *
530 	 * XXX WHY is this not in genfs's rename? XXX
531 	 */
532 	error = udf_gro_lock_directory(mp, tdvp);
533 	if (error)
534 		return error;
535 
536 	name     = "..";
537 	namelen  = 2;
538 	error    = 0;
539 
540 	ump = VTOI(tdvp)->ump;
541 
542 	/* if nodes are equal, it is no use looking */
543 	KASSERT(udf_compare_icb(&VTOI(fdvp)->loc, &VTOI(tdvp)->loc) != 0);
544 
545 	/* start at destination vnode and walk up the tree */
546 	vp = tdvp;
547 	vref(vp);
548 
549 	for (;;) {
550 		KASSERT(vp != NULL);
551 		KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
552 		KASSERT(vp->v_mount == mp);
553 		KASSERT(vp->v_type == VDIR);
554 		KASSERT(!udf_rmdired_p(vp));
555 
556 		DPRINTF(NODE, ("udf_gro_genealogy : "
557 			"fdvp %p, looking at vp %p\n",
558 			fdvp, vp));
559 
560 		/* sanity check */
561 		if (vp->v_type != VDIR) {
562 			vput(vp);
563 			return ENOTDIR;
564 		}
565 
566 		/* go down one level */
567 		error = udf_lookup_name_in_dir(vp, name, namelen,
568 			&parent_loc, &found);
569 		DPRINTF(NODE, ("\tlookup of parent '..' resulted in error %d, "
570 			"found %d\n", error, found));
571 		if (!found)
572 			error = ENOENT;
573 		if (error) {
574 			vput(vp);
575 			return error;
576 		}
577 
578 		/* did we encounter the root node? i.e. loop back */
579 		if (udf_compare_icb(&parent_loc, &VTOI(vp)->loc) == 0) {
580 			DPRINTF(NODE, ("ROOT found!\n"));
581 			vput(vp);
582 			*intermediate_node_ret = NULL;
583 			return 0;
584 		}
585 
586 		/* Did we find that fdvp is an ancestor of tdvp? */
587 		if (udf_compare_icb(&parent_loc, &VTOI(fdvp)->loc) == 0) {
588 			DPRINTF(NODE, ("fdvp is ancestor of tdvp\n"));
589 			*intermediate_node_ret = vp;
590 			VOP_UNLOCK(vp);
591 			return 0;
592 		}
593 
594 		/*
595 		 * Unlock vp so that we can lock the parent, but keep child vp
596 		 * referenced until after we have found the parent, so that
597 		 * parent_node will not be recycled.
598 		 */
599 		DPRINTF(NODE, ("\tgetting the parent node\n"));
600 		VOP_UNLOCK(vp);
601 		error = udf_get_node(ump, &parent_loc, &parent_node);
602 		vrele(vp);
603 		if (error)
604 			return error;
605 
606 		dvp = parent_node->vnode;
607 
608 		/* switch */
609 		KASSERT(dvp != NULL);
610 		KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE);
611 		vp  = dvp;
612 
613 		/* sanity check */
614 		if (vp->v_type != VDIR) {
615 			/*
616 			 * Odd, but can happen if we loose the race and the
617 			 * '..' node has been recycled.
618 			 */
619 			vput(vp);
620 			return ENOTDIR;
621 		}
622 
623 		if (udf_rmdired_p(vp)) {
624 			vput(vp);
625 			return ENOENT;
626 		}
627 	}
628 }
629 
630 
631 /*
632  * udf_gro_lock_directory: lock the directory vp, but fail if it has been
633  * rmdir'd.
634  */
635 static int
636 udf_gro_lock_directory(struct mount *mp, struct vnode *vp)
637 {
638 
639 	(void)mp;
640 	KASSERT(mp != NULL);
641 	KASSERT(vp != NULL);
642 	KASSERT(vp->v_mount == mp);
643 
644 	DPRINTF(CALL, ("udf_gro_lock_directory called\n"));
645 	DPRINTF(LOCKING, ("udf_gro_lock_directory called\n"));
646 
647 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
648 
649 	if (udf_rmdired_p(vp)) {
650 		VOP_UNLOCK(vp);
651 		return ENOENT;
652 	}
653 
654 	return 0;
655 }
656 
657 
658 static const struct genfs_rename_ops udf_genfs_rename_ops = {
659 	.gro_directory_empty_p		= udf_gro_directory_empty_p,
660 	.gro_rename_check_possible	= udf_gro_rename_check_possible,
661 	.gro_rename_check_permitted	= udf_gro_rename_check_permitted,
662 	.gro_remove_check_possible	= udf_gro_remove_check_possible,
663 	.gro_remove_check_permitted	= udf_gro_remove_check_permitted,
664 	.gro_rename			= udf_gro_rename,
665 	.gro_remove			= udf_gro_remove,
666 	.gro_lookup			= udf_gro_lookup,
667 	.gro_genealogy			= udf_gro_genealogy,
668 	.gro_lock_directory		= udf_gro_lock_directory,
669 };
670