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