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