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