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 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, 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 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 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 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 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 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