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