1 /* $NetBSD: ufs_rename.c,v 1.16 2026/01/22 03:24:19 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * UFS Rename 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: ufs_rename.c,v 1.16 2026/01/22 03:24:19 riastradh Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 42 #include <sys/buf.h> 43 #include <sys/errno.h> 44 #include <sys/kauth.h> 45 #include <sys/mount.h> 46 #include <sys/namei.h> 47 #include <sys/pool.h> 48 #include <sys/sdt.h> 49 #include <sys/vnode.h> 50 #include <sys/vnode_if.h> 51 #include <sys/wapbl.h> 52 53 #include <miscfs/genfs/genfs.h> 54 55 #include <ufs/ufs/dir.h> 56 #include <ufs/ufs/inode.h> 57 #include <ufs/ufs/ufs_bswap.h> 58 #include <ufs/ufs/ufs_extern.h> 59 #include <ufs/ufs/ufs_wapbl.h> 60 #include <ufs/ufs/ufsmount.h> 61 62 /* 63 * Forward declarations 64 */ 65 66 static int ufs_sane_rename(struct vnode *, struct componentname *, 67 struct vnode *, struct componentname *, 68 kauth_cred_t, bool); 69 static bool ufs_rename_ulr_overlap_p(const struct ufs_lookup_results *, 70 const struct ufs_lookup_results *); 71 static int ufs_rename_recalculate_fulr(struct vnode *, 72 struct ufs_lookup_results *, const struct ufs_lookup_results *, 73 const struct componentname *); 74 static int ufs_direct_namlen(const struct direct *, const struct vnode *); 75 static int ufs_read_dotdot(struct vnode *, kauth_cred_t, ino_t *); 76 static int ufs_dirbuf_dotdot_namlen(const struct dirtemplate *, 77 const struct vnode *); 78 79 static const struct genfs_rename_ops ufs_genfs_rename_ops; 80 81 /* 82 * ufs_sane_rename: The hairiest vop, with the saner API. 83 * 84 * Arguments: 85 * 86 * . fdvp (from directory vnode), 87 * . fcnp (from component name), 88 * . tdvp (to directory vnode), 89 * . tcnp (to component name), 90 * . cred (credentials structure), and 91 * . posixly_correct (flag for behaviour if target & source link same file). 92 * 93 * fdvp and tdvp may be the same, and must be referenced and unlocked. 94 */ 95 static int 96 ufs_sane_rename( 97 struct vnode *fdvp, struct componentname *fcnp, 98 struct vnode *tdvp, struct componentname *tcnp, 99 kauth_cred_t cred, bool posixly_correct) 100 { 101 struct ufs_lookup_results fulr, tulr; 102 103 return genfs_sane_rename(&ufs_genfs_rename_ops, 104 fdvp, fcnp, &fulr, tdvp, tcnp, &tulr, 105 cred, posixly_correct); 106 } 107 108 /* 109 * ufs_rename: The hairiest vop, with the insanest API. Defer to 110 * genfs_insane_rename immediately. 111 */ 112 int 113 ufs_rename(void *v) 114 { 115 116 return genfs_insane_rename(v, &ufs_sane_rename); 117 } 118 119 /* 120 * ufs_gro_directory_empty_p: Return true if the directory vp is 121 * empty. dvp is its parent. 122 * 123 * vp and dvp must be locked and referenced. 124 */ 125 bool 126 ufs_gro_directory_empty_p(struct mount *mp, kauth_cred_t cred, 127 struct vnode *vp, struct vnode *dvp) 128 { 129 130 (void)mp; 131 KASSERT(mp != NULL); 132 KASSERT(vp != NULL); 133 KASSERT(dvp != NULL); 134 KASSERT(vp != dvp); 135 KASSERT(vp->v_mount == mp); 136 KASSERT(dvp->v_mount == mp); 137 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 138 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 139 140 return ufs_dirempty(VTOI(vp), VTOI(dvp)->i_number, cred); 141 } 142 143 /* 144 * ufs_gro_rename_check_possible: Check whether a rename is possible 145 * independent of credentials. 146 */ 147 int 148 ufs_gro_rename_check_possible(struct mount *mp, 149 struct vnode *fdvp, struct vnode *fvp, 150 struct vnode *tdvp, struct vnode *tvp) 151 { 152 153 (void)mp; 154 KASSERT(mp != NULL); 155 KASSERT(fdvp != NULL); 156 KASSERT(fvp != NULL); 157 KASSERT(tdvp != NULL); 158 KASSERT(fdvp != fvp); 159 KASSERT(fdvp != tvp); 160 KASSERT(tdvp != fvp); 161 KASSERT(tdvp != tvp); 162 KASSERT(fvp != tvp); 163 KASSERT(fdvp->v_type == VDIR); 164 KASSERT(tdvp->v_type == VDIR); 165 KASSERT(fdvp->v_mount == mp); 166 KASSERT(fvp->v_mount == mp); 167 KASSERT(tdvp->v_mount == mp); 168 KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 169 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 170 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 171 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 172 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 173 174 return genfs_ufslike_rename_check_possible( 175 VTOI(fdvp)->i_flags, VTOI(fvp)->i_flags, 176 VTOI(tdvp)->i_flags, (tvp? VTOI(tvp)->i_flags : 0), 177 (tvp != NULL), 178 IMMUTABLE, APPEND); 179 } 180 181 /* 182 * ufs_gro_rename_check_permitted: Check whether a rename is permitted 183 * given our credentials. 184 */ 185 int 186 ufs_gro_rename_check_permitted(struct mount *mp, kauth_cred_t cred, 187 struct vnode *fdvp, struct vnode *fvp, 188 struct vnode *tdvp, struct vnode *tvp) 189 { 190 191 (void)mp; 192 KASSERT(mp != NULL); 193 KASSERT(fdvp != NULL); 194 KASSERT(fvp != NULL); 195 KASSERT(tdvp != NULL); 196 KASSERT(fdvp != fvp); 197 KASSERT(fdvp != tvp); 198 KASSERT(tdvp != fvp); 199 KASSERT(tdvp != tvp); 200 KASSERT(fvp != tvp); 201 KASSERT(fdvp->v_type == VDIR); 202 KASSERT(tdvp->v_type == VDIR); 203 KASSERT(fdvp->v_mount == mp); 204 KASSERT(fvp->v_mount == mp); 205 KASSERT(tdvp->v_mount == mp); 206 KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 207 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 208 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 209 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 210 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 211 212 return genfs_ufslike_rename_check_permitted(cred, 213 fdvp, VTOI(fdvp)->i_mode, VTOI(fdvp)->i_uid, 214 fvp, VTOI(fvp)->i_uid, 215 tdvp, VTOI(tdvp)->i_mode, VTOI(tdvp)->i_uid, 216 tvp, (tvp? VTOI(tvp)->i_uid : 0)); 217 } 218 219 /* 220 * ufs_gro_remove_check_possible: Check whether a remove is possible 221 * independent of credentials. 222 */ 223 int 224 ufs_gro_remove_check_possible(struct mount *mp, 225 struct vnode *dvp, struct vnode *vp) 226 { 227 228 (void)mp; 229 KASSERT(mp != NULL); 230 KASSERT(dvp != NULL); 231 KASSERT(vp != NULL); 232 KASSERT(dvp != vp); 233 KASSERT(dvp->v_type == VDIR); 234 KASSERT(vp->v_type != VDIR); 235 KASSERT(dvp->v_mount == mp); 236 KASSERT(vp->v_mount == mp); 237 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 238 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 239 240 return genfs_ufslike_remove_check_possible( 241 VTOI(dvp)->i_flags, VTOI(vp)->i_flags, 242 IMMUTABLE, APPEND); 243 } 244 245 /* 246 * ufs_gro_remove_check_permitted: Check whether a remove is permitted 247 * given our credentials. 248 */ 249 int 250 ufs_gro_remove_check_permitted(struct mount *mp, kauth_cred_t cred, 251 struct vnode *dvp, struct vnode *vp) 252 { 253 254 (void)mp; 255 KASSERT(mp != NULL); 256 KASSERT(dvp != NULL); 257 KASSERT(vp != NULL); 258 KASSERT(dvp != vp); 259 KASSERT(dvp->v_type == VDIR); 260 KASSERT(vp->v_type != VDIR); 261 KASSERT(dvp->v_mount == mp); 262 KASSERT(vp->v_mount == mp); 263 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 264 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 265 266 return genfs_ufslike_remove_check_permitted(cred, 267 dvp, VTOI(dvp)->i_mode, VTOI(dvp)->i_uid, vp, VTOI(vp)->i_uid); 268 } 269 270 /* 271 * A virgin directory (no blushing please). 272 * 273 * XXX Copypasta from ufs_vnops.c. Kill! 274 */ 275 static const struct dirtemplate mastertemplate = { 276 0, 12, DT_DIR, 1, ".", 277 0, UFS_DIRBLKSIZ - 12, DT_DIR, 2, ".." 278 }; 279 280 /* 281 * ufs_gro_rename: Actually perform the rename operation. 282 */ 283 int 284 ufs_gro_rename(struct mount *mp, kauth_cred_t cred, 285 struct vnode *fdvp, struct componentname *fcnp, 286 void *fde, struct vnode *fvp, 287 struct vnode *tdvp, struct componentname *tcnp, 288 void *tde, struct vnode *tvp, nlink_t *tvp_nlinkp) 289 { 290 struct ufs_lookup_results *fulr = fde; 291 struct ufs_lookup_results *tulr = tde; 292 bool directory_p, reparent_p; 293 struct direct *newdir; 294 int error; 295 296 KASSERT(mp != NULL); 297 KASSERT(fdvp != NULL); 298 KASSERT(fcnp != NULL); 299 KASSERT(fulr != NULL); 300 KASSERT(fvp != NULL); 301 KASSERT(tdvp != NULL); 302 KASSERT(tcnp != NULL); 303 KASSERT(tulr != NULL); 304 KASSERT(fulr != tulr); 305 KASSERT(fdvp != fvp); 306 KASSERT(fdvp != tvp); 307 KASSERT(tdvp != fvp); 308 KASSERT(tdvp != tvp); 309 KASSERT(fvp != tvp); 310 KASSERT(fdvp->v_mount == mp); 311 KASSERT(fvp->v_mount == mp); 312 KASSERT(tdvp->v_mount == mp); 313 KASSERT((tvp == NULL) || (tvp->v_mount == mp)); 314 KASSERT(VOP_ISLOCKED(fdvp) == LK_EXCLUSIVE); 315 KASSERT(VOP_ISLOCKED(fvp) == LK_EXCLUSIVE); 316 KASSERT(VOP_ISLOCKED(tdvp) == LK_EXCLUSIVE); 317 KASSERT((tvp == NULL) || (VOP_ISLOCKED(tvp) == LK_EXCLUSIVE)); 318 319 /* 320 * We shall need to temporarily bump the link count, so make 321 * sure there is room to do so. 322 */ 323 if ((nlink_t)VTOI(fvp)->i_nlink >= LINK_MAX) 324 return SET_ERROR(EMLINK); 325 326 directory_p = (fvp->v_type == VDIR); 327 KASSERT(directory_p == ((VTOI(fvp)->i_mode & IFMT) == IFDIR)); 328 KASSERT((tvp == NULL) || (directory_p == (tvp->v_type == VDIR))); 329 KASSERT((tvp == NULL) || (directory_p == 330 ((VTOI(tvp)->i_mode & IFMT) == IFDIR))); 331 332 reparent_p = (fdvp != tdvp); 333 KASSERT(reparent_p == (VTOI(fdvp)->i_number != VTOI(tdvp)->i_number)); 334 335 /* 336 * Commence hacking of the data on disk. 337 */ 338 339 error = UFS_WAPBL_BEGIN(mp); 340 if (error) 341 goto ihateyou; 342 343 /* 344 * 1) Bump link count while we're moving stuff 345 * around. If we crash somewhere before 346 * completing our work, the link count 347 * may be wrong, but correctable. 348 */ 349 350 KASSERT((nlink_t)VTOI(fvp)->i_nlink < LINK_MAX); 351 VTOI(fvp)->i_nlink++; 352 DIP_ASSIGN(VTOI(fvp), nlink, VTOI(fvp)->i_nlink); 353 VTOI(fvp)->i_flag |= IN_CHANGE; 354 error = UFS_UPDATE(fvp, NULL, NULL, UPDATE_DIROP); 355 if (error) 356 goto whymustithurtsomuch; 357 358 /* 359 * 2) If target doesn't exist, link the target 360 * to the source and unlink the source. 361 * Otherwise, rewrite the target directory 362 * entry to reference the source inode and 363 * expunge the original entry's existence. 364 */ 365 366 if (tvp == NULL) { 367 /* 368 * Account for ".." in new directory. 369 * When source and destination have the same 370 * parent we don't fool with the link count. 371 */ 372 if (directory_p && reparent_p) { 373 if ((nlink_t)VTOI(tdvp)->i_nlink >= LINK_MAX) { 374 error = SET_ERROR(EMLINK); 375 goto whymustithurtsomuch; 376 } 377 KASSERT((nlink_t)VTOI(tdvp)->i_nlink < LINK_MAX); 378 VTOI(tdvp)->i_nlink++; 379 DIP_ASSIGN(VTOI(tdvp), nlink, VTOI(tdvp)->i_nlink); 380 VTOI(tdvp)->i_flag |= IN_CHANGE; 381 error = UFS_UPDATE(tdvp, NULL, NULL, UPDATE_DIROP); 382 if (error) { 383 /* 384 * Link count update didn't take -- 385 * back out the in-memory link count. 386 */ 387 KASSERT(0 < VTOI(tdvp)->i_nlink); 388 VTOI(tdvp)->i_nlink--; 389 DIP_ASSIGN(VTOI(tdvp), nlink, 390 VTOI(tdvp)->i_nlink); 391 VTOI(tdvp)->i_flag |= IN_CHANGE; 392 goto whymustithurtsomuch; 393 } 394 } 395 396 newdir = pool_cache_get(ufs_direct_cache, PR_WAITOK); 397 ufs_makedirentry(VTOI(fvp), tcnp, newdir); 398 error = ufs_direnter(tdvp, tulr, NULL, newdir, tcnp, NULL); 399 pool_cache_put(ufs_direct_cache, newdir); 400 if (error) { 401 if (directory_p && reparent_p) { 402 /* 403 * Directory update didn't take, but 404 * the link count update did -- back 405 * out the in-memory link count and the 406 * on-disk link count. 407 */ 408 KASSERT(0 < VTOI(tdvp)->i_nlink); 409 VTOI(tdvp)->i_nlink--; 410 DIP_ASSIGN(VTOI(tdvp), nlink, 411 VTOI(tdvp)->i_nlink); 412 VTOI(tdvp)->i_flag |= IN_CHANGE; 413 (void)UFS_UPDATE(tdvp, NULL, NULL, 414 UPDATE_WAIT | UPDATE_DIROP); 415 } 416 goto whymustithurtsomuch; 417 } 418 } else { 419 if (directory_p) 420 /* XXX WTF? Why purge here? Why not purge others? */ 421 cache_purge(tdvp); 422 423 /* 424 * Make the target directory's entry for tcnp point at 425 * the source node. 426 * 427 * XXX ufs_dirrewrite decrements tvp's link count, but 428 * doesn't touch the link count of the new inode. Go 429 * figure. 430 */ 431 error = ufs_dirrewrite(VTOI(tdvp), tulr->ulr_offset, 432 VTOI(tvp), VTOI(fvp)->i_number, IFTODT(VTOI(fvp)->i_mode), 433 ((directory_p && reparent_p) ? reparent_p : directory_p), 434 IN_CHANGE | IN_UPDATE); 435 if (error) 436 goto whymustithurtsomuch; 437 438 /* 439 * If the source and target are directories, and the 440 * target is in the same directory as the source, 441 * decrement the link count of the common parent 442 * directory, since we are removing the target from 443 * that directory. 444 */ 445 if (directory_p && !reparent_p) { 446 KASSERT(fdvp == tdvp); 447 /* XXX check, don't kassert */ 448 KASSERT(0 < VTOI(tdvp)->i_nlink); 449 VTOI(tdvp)->i_nlink--; 450 DIP_ASSIGN(VTOI(tdvp), nlink, VTOI(tdvp)->i_nlink); 451 VTOI(tdvp)->i_flag |= IN_CHANGE; 452 UFS_WAPBL_UPDATE(tdvp, NULL, NULL, 0); 453 } 454 455 if (directory_p) { 456 /* 457 * XXX I don't understand the following comment 458 * from ufs_rename -- in particular, the part 459 * about `there may be other hard links'. 460 * 461 * Truncate inode. The only stuff left in the directory 462 * is "." and "..". The "." reference is inconsequential 463 * since we are quashing it. We have removed the "." 464 * reference and the reference in the parent directory, 465 * but there may be other hard links. 466 * 467 * XXX The ufs_dirempty call earlier does 468 * not guarantee anything about nlink. 469 */ 470 if (VTOI(tvp)->i_nlink != 1) 471 ufs_dirbad(VTOI(tvp), (doff_t)0, 472 "hard-linked directory"); 473 VTOI(tvp)->i_nlink = 0; 474 DIP_ASSIGN(VTOI(tvp), nlink, 0); 475 (void) UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC, cred); 476 } 477 } 478 479 /* 480 * If the source is a directory with a new parent, the link 481 * count of the old parent directory must be decremented and 482 * ".." set to point to the new parent. 483 * 484 * XXX ufs_dirrewrite updates the link count of fdvp, but not 485 * the link count of fvp or the link count of tdvp. Go figure. 486 */ 487 if (directory_p && reparent_p) { 488 error = ufs_dirrewrite(VTOI(fvp), mastertemplate.dot_reclen, 489 VTOI(fdvp), VTOI(tdvp)->i_number, DT_DIR, 0, IN_CHANGE); 490 #if 0 /* XXX This branch was not in ufs_rename! */ 491 if (error) 492 goto whymustithurtsomuch; 493 #endif 494 495 /* XXX WTF? Why purge here? Why not purge others? */ 496 cache_purge(fdvp); 497 } 498 499 /* 500 * 3) Unlink the source. 501 */ 502 503 /* 504 * ufs_direnter may compact the directory in the process of 505 * inserting a new entry. That may invalidate fulr, which we 506 * need in order to remove the old entry. In that case, we 507 * need to recalculate what fulr should be. 508 */ 509 if (!reparent_p && (tvp == NULL) && 510 ufs_rename_ulr_overlap_p(fulr, tulr)) { 511 error = ufs_rename_recalculate_fulr(fdvp, fulr, tulr, fcnp); 512 #if 0 /* XXX */ 513 if (error) /* XXX Try to back out changes? */ 514 goto whymustithurtsomuch; 515 #endif 516 } 517 518 /* 519 * XXX 0 means !isrmdir. But can't this be an rmdir? 520 * XXX Well, turns out that argument to ufs_dirremove is ignored... 521 * XXX And it turns out ufs_dirremove updates the link count of fvp. 522 * XXX But it doesn't update the link count of fdvp. Go figure. 523 * XXX fdvp's link count is updated in ufs_dirrewrite instead. 524 * XXX Actually, sometimes it doesn't update fvp's link count. 525 * XXX I hate the world. 526 */ 527 error = ufs_dirremove(fdvp, fulr, VTOI(fvp), fcnp->cn_flags, 0); 528 if (error) 529 #if 0 /* XXX */ 530 goto whymustithurtsomuch; 531 #endif 532 goto arghmybrainhurts; 533 534 if (tvp != NULL) { 535 *tvp_nlinkp = VTOI(tvp)->i_nlink; 536 } 537 #if 0 /* XXX */ 538 genfs_rename_cache_purge(fdvp, fvp, tdvp, tvp); 539 #endif 540 goto arghmybrainhurts; 541 542 whymustithurtsomuch: 543 KASSERT(0 < VTOI(fvp)->i_nlink); 544 VTOI(fvp)->i_nlink--; 545 DIP_ASSIGN(VTOI(fvp), nlink, VTOI(fvp)->i_nlink); 546 VTOI(fvp)->i_flag |= IN_CHANGE; 547 UFS_WAPBL_UPDATE(fvp, NULL, NULL, 0); 548 549 arghmybrainhurts: 550 UFS_WAPBL_END(mp); 551 552 ihateyou: 553 return error; 554 } 555 556 /* 557 * ufs_rename_ulr_overlap_p: True iff tulr overlaps with fulr so that 558 * entering a directory entry at tulr may move fulr. 559 */ 560 static bool 561 ufs_rename_ulr_overlap_p(const struct ufs_lookup_results *fulr, 562 const struct ufs_lookup_results *tulr) 563 { 564 doff_t from_prev_start, from_prev_end, to_start, to_end; 565 566 KASSERT(fulr != NULL); 567 KASSERT(tulr != NULL); 568 KASSERT(fulr != tulr); 569 570 /* 571 * fulr is from a DELETE lookup, so fulr->ulr_count is the size 572 * of the preceding entry (d_reclen). 573 */ 574 from_prev_end = fulr->ulr_offset; 575 KASSERT(fulr->ulr_count <= from_prev_end); 576 from_prev_start = (from_prev_end - fulr->ulr_count); 577 578 /* 579 * tulr is from a RENAME lookup, so tulr->ulr_count is the size 580 * of the free space for an entry that we are about to fill. 581 */ 582 to_start = tulr->ulr_offset; 583 KASSERT(tulr->ulr_count < (UFS_MAXDIRSIZE - to_start)); 584 to_end = (to_start + tulr->ulr_count); 585 586 return 587 (((to_start <= from_prev_start) && (from_prev_start < to_end)) || 588 ((to_start <= from_prev_end) && (from_prev_end < to_end))); 589 } 590 591 /* 592 * ufs_rename_recalculate_fulr: If we have just entered a directory into 593 * dvp at tulr, and we were about to remove one at fulr for an entry 594 * named fcnp, fulr may be invalid. So, if necessary, recalculate it. 595 */ 596 static int 597 ufs_rename_recalculate_fulr(struct vnode *dvp, 598 struct ufs_lookup_results *fulr, const struct ufs_lookup_results *tulr, 599 const struct componentname *fcnp) 600 { 601 struct mount *mp; 602 struct ufsmount *ump; 603 int needswap; 604 /* XXX int is a silly type for this; blame ufsmount::um_dirblksiz. */ 605 int dirblksiz; 606 doff_t search_start, search_end; 607 doff_t offset; /* Offset of entry we're examining. */ 608 struct buf *bp; /* I/O block we're examining. */ 609 char *dirbuf; /* Pointer into directory at search_start. */ 610 struct direct *ep; /* Pointer to the entry we're examining. */ 611 /* XXX direct::d_reclen is 16-bit; 612 * ufs_lookup_results::ulr_reclen is 32-bit. Blah. */ 613 uint32_t reclen; /* Length of the entry we're examining. */ 614 uint32_t prev_reclen; /* Length of the preceding entry. */ 615 int error; 616 617 KASSERT(dvp != NULL); 618 KASSERT(dvp->v_mount != NULL); 619 KASSERT(VTOI(dvp) != NULL); 620 KASSERT(fulr != NULL); 621 KASSERT(tulr != NULL); 622 KASSERT(fulr != tulr); 623 KASSERT(ufs_rename_ulr_overlap_p(fulr, tulr)); 624 625 mp = dvp->v_mount; 626 ump = VFSTOUFS(mp); 627 KASSERT(ump != NULL); 628 KASSERT(ump == VTOI(dvp)->i_ump); 629 630 needswap = UFS_MPNEEDSWAP(ump); 631 632 dirblksiz = ump->um_dirblksiz; 633 KASSERT(0 < dirblksiz); 634 KASSERT((dirblksiz & (dirblksiz - 1)) == 0); 635 636 /* A directory block may not span across multiple I/O blocks. */ 637 KASSERT(dirblksiz <= mp->mnt_stat.f_iosize); 638 639 /* Find the bounds of the search. */ 640 search_start = tulr->ulr_offset; 641 KASSERT(fulr->ulr_reclen < (UFS_MAXDIRSIZE - fulr->ulr_offset)); 642 search_end = (fulr->ulr_offset + fulr->ulr_reclen); 643 644 /* Compaction must happen only within a directory block. (*) */ 645 KASSERT(search_start <= search_end); 646 KASSERT((search_end - (search_start &~ (dirblksiz - 1))) <= dirblksiz); 647 648 dirbuf = NULL; 649 bp = NULL; 650 error = ufs_blkatoff(dvp, (off_t)search_start, &dirbuf, &bp, false); 651 if (error) 652 return error; 653 KASSERT(dirbuf != NULL); 654 KASSERT(bp != NULL); 655 656 /* 657 * Guarantee we sha'n't go past the end of the buffer we got. 658 * dirbuf is bp->b_data + (search_start & (iosize - 1)), and 659 * the valid range is [bp->b_data, bp->b_data + bp->b_bcount). 660 */ 661 KASSERT((search_end - search_start) <= 662 (bp->b_bcount - (search_start & (mp->mnt_stat.f_iosize - 1)))); 663 664 prev_reclen = fulr->ulr_count; 665 offset = search_start; 666 667 /* 668 * Search from search_start to search_end for the entry matching 669 * fcnp, which must be there because we found it before and it 670 * should only at most have moved earlier. 671 */ 672 for (;;) { 673 KASSERT(search_start <= offset); 674 KASSERT(offset < search_end); 675 676 /* 677 * Examine the directory entry at offset. 678 */ 679 ep = (struct direct *)(dirbuf + (offset - search_start)); 680 reclen = ufs_rw16(ep->d_reclen, needswap); 681 682 if (ep->d_ino == 0) 683 goto next; /* Entry is unused. */ 684 685 if (ufs_rw32(ep->d_ino, needswap) == UFS_WINO) 686 goto next; /* Entry is whiteout. */ 687 688 if (fcnp->cn_namelen != ufs_direct_namlen(ep, dvp)) 689 goto next; /* Wrong name length. */ 690 691 if (memcmp(ep->d_name, fcnp->cn_nameptr, fcnp->cn_namelen)) 692 goto next; /* Wrong name. */ 693 694 /* Got it! */ 695 break; 696 697 next: 698 if (! ((reclen < search_end) && 699 (offset < (search_end - reclen)))) { 700 brelse(bp, 0); 701 return SET_ERROR(EIO); /* XXX Panic? What? */ 702 } 703 704 /* We may not move past the search end. */ 705 KASSERT(reclen < search_end); 706 KASSERT(offset < (search_end - reclen)); 707 708 /* 709 * We may not move across a directory block boundary; 710 * see (*) above. 711 */ 712 KASSERT((offset &~ (dirblksiz - 1)) == 713 ((offset + reclen) &~ (dirblksiz - 1))); 714 715 prev_reclen = reclen; 716 offset += reclen; 717 } 718 719 /* 720 * Found the entry. Record where. 721 */ 722 fulr->ulr_offset = offset; 723 fulr->ulr_reclen = reclen; 724 725 /* 726 * Record the preceding record length, but not if we're at the 727 * start of a directory block. 728 */ 729 fulr->ulr_count = ((offset & (dirblksiz - 1))? prev_reclen : 0); 730 731 brelse(bp, 0); 732 return 0; 733 } 734 735 /* 736 * ufs_direct_namlen: Return the namlen of the directory entry ep from 737 * the directory vp. 738 */ 739 static int /* XXX int? uint8_t? */ 740 ufs_direct_namlen(const struct direct *ep, const struct vnode *vp) 741 { 742 bool swap; 743 744 KASSERT(ep != NULL); 745 KASSERT(vp != NULL); 746 KASSERT(VTOI(vp) != NULL); 747 KASSERT(VTOI(vp)->i_ump != NULL); 748 749 #if (BYTE_ORDER == LITTLE_ENDIAN) 750 swap = (UFS_MPNEEDSWAP(VTOI(vp)->i_ump) == 0); 751 #else 752 swap = (UFS_MPNEEDSWAP(VTOI(vp)->i_ump) != 0); 753 #endif 754 755 return ((FSFMT(vp) && swap)? ep->d_type : ep->d_namlen); 756 } 757 758 /* 759 * ufs_gro_remove: Rename an object over another link to itself, 760 * effectively removing just the original link. 761 */ 762 int 763 ufs_gro_remove(struct mount *mp, kauth_cred_t cred, 764 struct vnode *dvp, struct componentname *cnp, void *de, struct vnode *vp, 765 nlink_t *tvp_nlinkp) 766 { 767 struct ufs_lookup_results *ulr = de; 768 int error; 769 770 KASSERT(mp != NULL); 771 KASSERT(dvp != NULL); 772 KASSERT(cnp != NULL); 773 KASSERT(ulr != NULL); 774 KASSERT(vp != NULL); 775 KASSERT(dvp != vp); 776 KASSERT(dvp->v_mount == mp); 777 KASSERT(vp->v_mount == mp); 778 KASSERT(dvp->v_type == VDIR); 779 KASSERT(vp->v_type != VDIR); 780 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 781 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 782 KASSERT(cnp->cn_nameiop == DELETE); 783 784 error = UFS_WAPBL_BEGIN(mp); 785 if (error) 786 goto out; 787 788 /* XXX ufs_dirremove decrements vp's link count for us. */ 789 error = ufs_dirremove(dvp, ulr, VTOI(vp), cnp->cn_flags, 0); 790 UFS_WAPBL_END(mp); 791 792 *tvp_nlinkp = VTOI(vp)->i_nlink; 793 out: 794 return error; 795 } 796 797 /* 798 * ufs_gro_lookup: Look up and save the lookup results. 799 */ 800 int 801 ufs_gro_lookup(struct mount *mp, struct vnode *dvp, 802 struct componentname *cnp, void *de_ret, struct vnode **vp_ret) 803 { 804 struct ufs_lookup_results *ulr_ret = de_ret; 805 struct vnode *vp = NULL; 806 int error; 807 808 (void)mp; 809 KASSERT(mp != NULL); 810 KASSERT(dvp != NULL); 811 KASSERT(cnp != NULL); 812 KASSERT(ulr_ret != NULL); 813 KASSERT(vp_ret != NULL); 814 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 815 816 /* Kludge cargo-culted from dholland's ufs_rename. */ 817 cnp->cn_flags &=~ MODMASK; 818 cnp->cn_flags |= (LOCKPARENT | LOCKLEAF); 819 820 error = relookup(dvp, &vp, cnp, 0 /* dummy */); 821 if ((error == 0) && (vp == NULL)) { 822 error = SET_ERROR(ENOENT); 823 goto out; 824 } else if (error) { 825 return error; 826 } 827 828 /* 829 * Thanks to VFS insanity, relookup locks vp, which screws us 830 * in various ways. 831 */ 832 KASSERT(vp != NULL); 833 VOP_UNLOCK(vp); 834 835 out: *ulr_ret = VTOI(dvp)->i_crap; 836 *vp_ret = vp; 837 return error; 838 } 839 840 /* 841 * ufs_rmdired_p: Check whether the directory vp has been rmdired. 842 * 843 * vp must be locked and referenced. 844 */ 845 static bool 846 ufs_rmdired_p(struct vnode *vp) 847 { 848 849 KASSERT(vp != NULL); 850 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 851 KASSERT(vp->v_type == VDIR); 852 853 /* XXX Is this correct? */ 854 return (VTOI(vp)->i_size == 0); 855 } 856 857 /* 858 * ufs_read_dotdot: Store in *ino_ret the inode number of the parent 859 * of the directory vp. 860 */ 861 static int 862 ufs_read_dotdot(struct vnode *vp, kauth_cred_t cred, ino_t *ino_ret) 863 { 864 struct dirtemplate dirbuf; 865 int error; 866 867 KASSERT(vp != NULL); 868 KASSERT(ino_ret != NULL); 869 KASSERT(vp->v_type == VDIR); 870 871 error = ufs_bufio(UIO_READ, vp, &dirbuf, sizeof dirbuf, (off_t)0, 872 IO_NODELOCKED, cred, NULL, NULL); 873 if (error) 874 return error; 875 876 if (ufs_dirbuf_dotdot_namlen(&dirbuf, vp) != 2 || 877 dirbuf.dotdot_name[0] != '.' || 878 dirbuf.dotdot_name[1] != '.') 879 /* XXX Panic? Print warning? */ 880 return SET_ERROR(ENOTDIR); 881 882 *ino_ret = ufs_rw32(dirbuf.dotdot_ino, 883 UFS_MPNEEDSWAP(VTOI(vp)->i_ump)); 884 return 0; 885 } 886 887 /* 888 * ufs_dirbuf_dotdot_namlen: Return the namlen of the directory buffer 889 * dirbuf that came from the directory vp. Swap byte order if 890 * necessary. 891 */ 892 static int /* XXX int? uint8_t? */ 893 ufs_dirbuf_dotdot_namlen(const struct dirtemplate *dirbuf, 894 const struct vnode *vp) 895 { 896 bool swap; 897 898 KASSERT(dirbuf != NULL); 899 KASSERT(vp != NULL); 900 KASSERT(VTOI(vp) != NULL); 901 KASSERT(VTOI(vp)->i_ump != NULL); 902 903 #if (BYTE_ORDER == LITTLE_ENDIAN) 904 swap = (UFS_MPNEEDSWAP(VTOI(vp)->i_ump) == 0); 905 #else 906 swap = (UFS_MPNEEDSWAP(VTOI(vp)->i_ump) != 0); 907 #endif 908 909 return ((FSFMT(vp) && swap)? 910 dirbuf->dotdot_type : dirbuf->dotdot_namlen); 911 } 912 913 /* 914 * ufs_gro_genealogy: Analyze the genealogy of the source and target 915 * directories. 916 */ 917 int 918 ufs_gro_genealogy(struct mount *mp, kauth_cred_t cred, 919 struct vnode *fdvp, struct vnode *tdvp, 920 struct vnode **intermediate_node_ret) 921 { 922 struct vnode *vp, *dvp; 923 ino_t dotdot_ino = 0; /* XXX: gcc */ 924 int error; 925 926 KASSERT(mp != NULL); 927 KASSERT(fdvp != NULL); 928 KASSERT(tdvp != NULL); 929 KASSERT(fdvp != tdvp); 930 KASSERT(intermediate_node_ret != NULL); 931 KASSERT(fdvp->v_mount == mp); 932 KASSERT(tdvp->v_mount == mp); 933 KASSERT(fdvp->v_type == VDIR); 934 KASSERT(tdvp->v_type == VDIR); 935 936 /* 937 * We need to provisionally lock tdvp to keep rmdir from 938 * deleting it -- or any ancestor -- at an inopportune moment. 939 */ 940 error = ufs_gro_lock_directory(mp, tdvp); 941 if (error) 942 return error; 943 944 vp = tdvp; 945 vref(vp); 946 947 for (;;) { 948 KASSERT(vp != NULL); 949 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 950 KASSERT(vp->v_mount == mp); 951 KASSERT(vp->v_type == VDIR); 952 KASSERT(!ufs_rmdired_p(vp)); 953 954 /* Did we hit the root without finding fdvp? */ 955 if (VTOI(vp)->i_number == UFS_ROOTINO) { 956 vput(vp); 957 *intermediate_node_ret = NULL; 958 return 0; 959 } 960 961 error = ufs_read_dotdot(vp, cred, &dotdot_ino); 962 if (error) { 963 vput(vp); 964 return error; 965 } 966 967 /* Did we find that fdvp is an ancestor of tdvp? */ 968 if (VTOI(fdvp)->i_number == dotdot_ino) { 969 /* Unlock vp, but keep it referenced. */ 970 VOP_UNLOCK(vp); 971 *intermediate_node_ret = vp; 972 return 0; 973 } 974 975 /* Neither -- keep ascending the family tree. */ 976 error = vcache_get(mp, &dotdot_ino, sizeof(dotdot_ino), &dvp); 977 vput(vp); 978 if (error) 979 return error; 980 error = vn_lock(dvp, LK_EXCLUSIVE); 981 if (error) { 982 vrele(dvp); 983 return error; 984 } 985 986 KASSERT(dvp != NULL); 987 KASSERT(VOP_ISLOCKED(dvp) == LK_EXCLUSIVE); 988 vp = dvp; 989 990 if (vp->v_type != VDIR) { 991 /* 992 * XXX Panic? Print a warning? Can this 993 * happen if we lose the race I suspect to 994 * exist above, and the `..' inode number has 995 * been recycled? 996 */ 997 vput(vp); 998 return SET_ERROR(ENOTDIR); 999 } 1000 1001 if (ufs_rmdired_p(vp)) { 1002 vput(vp); 1003 return SET_ERROR(ENOENT); 1004 } 1005 } 1006 } 1007 1008 /* 1009 * ufs_gro_lock_directory: Lock the directory vp, but fail if it has 1010 * been rmdir'd. 1011 */ 1012 int 1013 ufs_gro_lock_directory(struct mount *mp, struct vnode *vp) 1014 { 1015 1016 (void)mp; 1017 KASSERT(mp != NULL); 1018 KASSERT(vp != NULL); 1019 KASSERT(vp->v_mount == mp); 1020 1021 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 1022 1023 if (ufs_rmdired_p(vp)) { 1024 VOP_UNLOCK(vp); 1025 return SET_ERROR(ENOENT); 1026 } 1027 1028 return 0; 1029 } 1030 1031 static const struct genfs_rename_ops ufs_genfs_rename_ops = { 1032 .gro_directory_empty_p = ufs_gro_directory_empty_p, 1033 .gro_rename_check_possible = ufs_gro_rename_check_possible, 1034 .gro_rename_check_permitted = ufs_gro_rename_check_permitted, 1035 .gro_remove_check_possible = ufs_gro_remove_check_possible, 1036 .gro_remove_check_permitted = ufs_gro_remove_check_permitted, 1037 .gro_rename = ufs_gro_rename, 1038 .gro_remove = ufs_gro_remove, 1039 .gro_lookup = ufs_gro_lookup, 1040 .gro_genealogy = ufs_gro_genealogy, 1041 .gro_lock_directory = ufs_gro_lock_directory, 1042 }; 1043