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