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