1 /* $NetBSD: ufs_lookup.c,v 1.161 2026/01/22 03:24:19 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)ufs_lookup.c 8.9 (Berkeley) 8/11/94 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: ufs_lookup.c,v 1.161 2026/01/22 03:24:19 riastradh Exp $"); 41 42 #ifdef _KERNEL_OPT 43 #include "opt_ffs.h" 44 #endif 45 46 #include <sys/param.h> 47 #include <sys/types.h> 48 49 #include <sys/buf.h> 50 #include <sys/file.h> 51 #include <sys/kauth.h> 52 #include <sys/kernel.h> 53 #include <sys/kmem.h> 54 #include <sys/mount.h> 55 #include <sys/namei.h> 56 #include <sys/proc.h> 57 #include <sys/sdt.h> 58 #include <sys/stat.h> 59 #include <sys/systm.h> 60 #include <sys/vnode.h> 61 #include <sys/wapbl.h> 62 63 #include <ufs/ufs/dir.h> 64 #ifdef UFS_DIRHASH 65 #include <ufs/ufs/dirhash.h> 66 #endif 67 #include <ufs/ufs/inode.h> 68 #include <ufs/ufs/ufs_bswap.h> 69 #include <ufs/ufs/ufs_extern.h> 70 #include <ufs/ufs/ufs_wapbl.h> 71 #include <ufs/ufs/ufsmount.h> 72 73 #include <miscfs/genfs/genfs.h> 74 75 #ifdef DIAGNOSTIC 76 int dirchk = 1; 77 #else 78 int dirchk = 0; 79 #endif 80 81 #if BYTE_ORDER == LITTLE_ENDIAN 82 # define ENDIANSWAP(needswap) ((needswap) == 0) 83 #else 84 # define ENDIANSWAP(needswap) ((needswap) != 0) 85 #endif 86 87 #define NAMLEN(fsfmt, needswap, dp) \ 88 ((fsfmt) && ENDIANSWAP(needswap) ? (dp)->d_type : (dp)->d_namlen) 89 90 static void 91 ufs_dirswap(struct direct *dirp) 92 { 93 uint8_t tmp = dirp->d_namlen; 94 dirp->d_namlen = dirp->d_type; 95 dirp->d_type = tmp; 96 } 97 98 struct slotinfo { 99 enum { 100 NONE, /* need to search a slot for our new entry */ 101 COMPACT, /* a compaction can make a slot in the current 102 DIRBLKSIZ block */ 103 FOUND, /* found a slot (or no need to search) */ 104 } status; 105 doff_t offset; /* offset of area with free space. 106 a special value -1 for invalid */ 107 int size; /* size of area at slotoffset */ 108 int freespace; /* accumulated amount of space free in 109 the current DIRBLKSIZ block */ 110 int needed; /* size of the entry we're seeking */ 111 }; 112 113 static void 114 calc_count(struct ufs_lookup_results *results, int dirblksiz, doff_t prevoff) 115 { 116 if ((results->ulr_offset & (dirblksiz - 1)) == 0) 117 results->ulr_count = 0; 118 else 119 results->ulr_count = results->ulr_offset - prevoff; 120 } 121 122 static void 123 slot_init(struct slotinfo *slot) 124 { 125 slot->status = FOUND; 126 slot->offset = -1; 127 slot->freespace = slot->size = slot->needed = 0; 128 } 129 130 #ifdef UFS_DIRHASH 131 static doff_t 132 slot_findfree(struct slotinfo *slot, struct inode *dp) 133 { 134 if (slot->status == FOUND) 135 return dp->i_size; 136 137 slot->offset = ufsdirhash_findfree(dp, slot->needed, &slot->size); 138 if (slot->offset < 0) 139 return dp->i_size; 140 141 slot->status = COMPACT; 142 doff_t enduseful = ufsdirhash_enduseful(dp); 143 if (enduseful < 0) 144 return dp->i_size; 145 return enduseful; 146 } 147 #endif 148 149 static void 150 slot_white(struct slotinfo *slot, uint16_t reclen, 151 struct ufs_lookup_results *results) 152 { 153 slot->status = FOUND; 154 slot->offset = results->ulr_offset; 155 slot->size = reclen; 156 results->ulr_reclen = slot->size; 157 } 158 159 static void 160 slot_update(struct slotinfo *slot, int size, uint16_t reclen, doff_t offset) 161 { 162 if (size >= slot->needed) { 163 slot->status = FOUND; 164 slot->offset = offset; 165 slot->size = reclen; 166 } else if (slot->status == NONE) { 167 slot->freespace += size; 168 if (slot->offset == -1) 169 slot->offset = offset; 170 if (slot->freespace >= slot->needed) { 171 slot->status = COMPACT; 172 slot->size = offset + reclen - slot->offset; 173 } 174 } 175 } 176 177 /* 178 * Return an indication of where the new directory entry should be put. 179 * If we didn't find a slot, then set results->ulr_count to 0 indicating 180 * that the new slot belongs at the end of the directory. If we found a slot, 181 * then the new entry can be put in the range from results->ulr_offset to 182 * results->ulr_offset + results->ulr_count. 183 */ 184 static int 185 slot_estimate(const struct slotinfo *slot, int dirblksiz, int nameiop, 186 doff_t prevoff, doff_t enduseful, const struct inode *ip, 187 struct ufs_lookup_results *results) 188 { 189 if (slot->status == NONE) { 190 results->ulr_offset = roundup(ip->i_size, dirblksiz); 191 results->ulr_count = 0; 192 enduseful = results->ulr_offset; 193 } else if (nameiop == DELETE) { 194 results->ulr_offset = slot->offset; 195 calc_count(results, dirblksiz, prevoff); 196 } else { 197 results->ulr_offset = slot->offset; 198 results->ulr_count = slot->size; 199 if (enduseful < slot->offset + slot->size) 200 enduseful = slot->offset + slot->size; 201 } 202 results->ulr_endoff = roundup(enduseful, dirblksiz); 203 #if 0 /* commented out by dbj. none of the on disk fields changed */ 204 ip->i_flag |= IN_CHANGE | IN_UPDATE; 205 #endif 206 return SET_ERROR(EJUSTRETURN); 207 } 208 209 /* 210 * Check if we can delete inode tdp in directory vdp with inode ip and creds. 211 */ 212 static int 213 ufs_can_delete(struct vnode *tdp, struct vnode *vdp, struct inode *ip, 214 kauth_cred_t cred) 215 { 216 int error; 217 218 #ifdef UFS_ACL 219 /* 220 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 221 * 222 * 3.16.2.1. ACE4_DELETE vs. ACE4_DELETE_CHILD 223 */ 224 225 /* 226 * XXX: Is this check required? 227 */ 228 error = VOP_ACCESS(vdp, VEXEC, cred); 229 if (error) 230 goto out; 231 232 #if 0 233 /* Moved to ufs_remove, ufs_rmdir because they hold the lock */ 234 error = VOP_ACCESSX(tdp, VDELETE, cred); 235 if (error == 0) 236 return 0; 237 #endif 238 239 error = VOP_ACCESSX(vdp, VDELETE_CHILD, cred); 240 if (error == 0) 241 return 0; 242 243 error = VOP_ACCESSX(vdp, VEXPLICIT_DENY | VDELETE_CHILD, cred); 244 if (error) 245 goto out; 246 247 #endif /* !UFS_ACL */ 248 249 /* 250 * Write access to directory required to delete files. 251 */ 252 error = VOP_ACCESS(vdp, VWRITE, cred); 253 if (error) 254 goto out; 255 256 if (!(ip->i_mode & ISVTX)) 257 return 0; 258 259 /* 260 * If directory is "sticky", then user must own 261 * the directory, or the file in it, else she 262 * may not delete it (unless she's root). This 263 * implements append-only directories. 264 */ 265 error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, tdp, vdp, 266 genfs_can_sticky(vdp, cred, ip->i_uid, VTOI(tdp)->i_uid)); 267 if (error) { 268 error = SET_ERROR(EPERM); // Why override? 269 goto out; 270 } 271 return 0; 272 out: 273 vrele(tdp); 274 return error; 275 } 276 277 static int 278 ufs_getino(struct vnode *vdp, struct inode *ip, ino_t foundino, 279 struct vnode **tdp, bool same) 280 { 281 if (ip->i_number == foundino) { 282 if (same) 283 return SET_ERROR(EISDIR); 284 vref(vdp); 285 *tdp = vdp; 286 return 0; 287 } 288 return vcache_get(vdp->v_mount, &foundino, sizeof(foundino), tdp); 289 } 290 291 292 /* 293 * Convert a component of a pathname into a pointer to a locked inode. 294 * This is a very central and rather complicated routine. 295 * If the file system is not maintained in a strict tree hierarchy, 296 * this can result in a deadlock situation (see comments in code below). 297 * 298 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending 299 * on whether the name is to be looked up, created, renamed, or deleted. 300 * When CREATE, RENAME, or DELETE is specified, information usable in 301 * creating, renaming, or deleting a directory entry may be calculated. 302 * If flag has LOCKPARENT or'ed into it and the target of the pathname 303 * exists, lookup returns both the target and its parent directory locked. 304 * When creating or renaming and LOCKPARENT is specified, the target may 305 * not be ".". When deleting and LOCKPARENT is specified, the target may 306 * be ".", but the caller must check to ensure it does a vrele and vput 307 * instead of two vputs. 308 * 309 * Overall outline of ufs_lookup: 310 * 311 * check accessibility of directory 312 * look for name in cache, if found, then if at end of path 313 * and deleting or creating, drop it, else return name 314 * search for name in directory, to found or notfound 315 * notfound: 316 * if creating, return locked directory, leaving info on available slots 317 * else return error 318 * found: 319 * if at end of path and deleting, return information to allow delete 320 * if at end of path and rewriting (RENAME and LOCKPARENT), lock target 321 * inode and return info to allow rewrite 322 * if not at end, add name to cache; if at end and neither creating 323 * nor deleting, add name to cache 324 */ 325 int 326 ufs_lookup(void *v) 327 { 328 struct vop_lookup_v2_args /* { 329 struct vnode *a_dvp; 330 struct vnode **a_vpp; 331 struct componentname *a_cnp; 332 } */ *ap = v; 333 struct vnode *vdp = ap->a_dvp; /* vnode for directory being searched */ 334 struct inode *dp = VTOI(vdp); /* inode for directory being searched */ 335 struct buf *bp; /* a buffer of directory entries */ 336 struct direct *ep; /* the current directory entry */ 337 int entryoffsetinblock; /* offset of ep in bp's buffer */ 338 struct slotinfo slot; 339 int numdirpasses; /* strategy for directory search */ 340 doff_t endsearch; /* offset to end directory search */ 341 doff_t prevoff; /* previous value of ulr_offset */ 342 struct vnode *tdp; /* returned by vcache_get */ 343 doff_t enduseful; /* pointer past last used dir slot. 344 used for directory truncation. */ 345 u_long bmask; /* block offset mask */ 346 int error; 347 struct vnode **vpp = ap->a_vpp; 348 struct componentname *cnp = ap->a_cnp; 349 kauth_cred_t cred = cnp->cn_cred; 350 int flags; 351 int nameiop = cnp->cn_nameiop; 352 struct ufsmount *ump = dp->i_ump; 353 const int needswap = UFS_MPNEEDSWAP(ump); 354 int dirblksiz = ump->um_dirblksiz; 355 ino_t foundino; 356 struct ufs_lookup_results *results; 357 int iswhiteout; /* temp result from cache_lookup() */ 358 const int fsfmt = FSFMT(vdp); 359 uint16_t reclen; 360 361 flags = cnp->cn_flags; 362 363 bp = NULL; 364 *vpp = NULL; 365 endsearch = 0; /* silence compiler warning */ 366 367 /* 368 * Check accessibility of directory. 369 */ 370 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) 371 return error; 372 373 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 374 (nameiop == DELETE || nameiop == RENAME)) 375 return SET_ERROR(EROFS); 376 377 /* 378 * We now have a segment name to search for, and a directory to search. 379 * 380 * Before tediously performing a linear scan of the directory, 381 * check the name cache to see if the directory/name pair 382 * we are looking for is known already. 383 */ 384 if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen, 385 cnp->cn_nameiop, cnp->cn_flags, &iswhiteout, vpp)) { 386 if (iswhiteout) { 387 cnp->cn_flags |= ISWHITEOUT; 388 } 389 return *vpp == NULLVP ? SET_ERROR(ENOENT) : 0; 390 } 391 392 /* May need to restart the lookup with an exclusive lock. */ 393 if (VOP_ISLOCKED(vdp) != LK_EXCLUSIVE) { 394 return SET_ERROR(ENOLCK); 395 } 396 397 /* 398 * Produce the auxiliary lookup results into i_crap. Increment 399 * its serial number so elsewhere we can tell if we're using 400 * stale results. This should not be done this way. XXX. 401 */ 402 results = &dp->i_crap; 403 dp->i_crapcounter++; 404 405 if (iswhiteout) { 406 /* 407 * The namecache set iswhiteout without finding a 408 * cache entry. As of this writing (20121014), this 409 * can happen if there was a whiteout entry that has 410 * been invalidated by the lookup. It is not clear if 411 * it is correct to set ISWHITEOUT in this case or 412 * not; however, doing so retains the prior behavior, 413 * so we'll go with that until some clearer answer 414 * appears. XXX 415 */ 416 cnp->cn_flags |= ISWHITEOUT; 417 } 418 419 /* 420 * Suppress search for slots unless creating 421 * file and at end of pathname, in which case 422 * we watch for a place to put the new file in 423 * case it doesn't already exist. 424 */ 425 slot_init(&slot); 426 427 if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 428 slot.status = NONE; 429 slot.needed = UFS_DIRECTSIZ(cnp->cn_namelen); 430 } 431 432 /* 433 * If there is cached information on a previous search of 434 * this directory, pick up where we last left off. 435 * We cache only lookups as these are the most common 436 * and have the greatest payoff. Caching CREATE has little 437 * benefit as it usually must search the entire directory 438 * to determine that the entry does not exist. Caching the 439 * location of the last DELETE or RENAME has not reduced 440 * profiling time and hence has been removed in the interest 441 * of simplicity. 442 */ 443 bmask = vdp->v_mount->mnt_stat.f_iosize - 1; 444 445 #ifdef UFS_DIRHASH 446 /* 447 * Use dirhash for fast operations on large directories. The logic 448 * to determine whether to hash the directory is contained within 449 * ufsdirhash_build(); a zero return means that it decided to hash 450 * this directory and it successfully built up the hash table. 451 */ 452 if (ufsdirhash_build(dp) == 0) { 453 /* Look for a free slot if needed. */ 454 enduseful = slot_findfree(&slot, dp); 455 /* Look up the component. */ 456 numdirpasses = 1; 457 entryoffsetinblock = 0; /* silence compiler warning */ 458 switch (ufsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen, 459 &results->ulr_offset, &bp, 460 nameiop == DELETE ? &prevoff : NULL)) { 461 case 0: 462 ep = (void *)((char *)bp->b_data + 463 (results->ulr_offset & bmask)); 464 reclen = ufs_rw16(ep->d_reclen, needswap); 465 goto foundentry; 466 case ENOENT: 467 results->ulr_offset = roundup(dp->i_size, dirblksiz); 468 goto notfound; 469 default: 470 /* Something failed; just do a linear search. */ 471 break; 472 } 473 } 474 #endif /* UFS_DIRHASH */ 475 476 if (nameiop != LOOKUP || results->ulr_diroff == 0 || 477 results->ulr_diroff >= dp->i_size) { 478 entryoffsetinblock = 0; 479 results->ulr_offset = 0; 480 numdirpasses = 1; 481 } else { 482 results->ulr_offset = results->ulr_diroff; 483 entryoffsetinblock = results->ulr_offset & bmask; 484 if (entryoffsetinblock != 0 && 485 (error = ufs_blkatoff(vdp, (off_t)results->ulr_offset, 486 NULL, &bp, false))) 487 goto out; 488 numdirpasses = 2; 489 namecache_count_2passes(); 490 } 491 prevoff = results->ulr_offset; 492 endsearch = roundup(dp->i_size, dirblksiz); 493 enduseful = 0; 494 495 searchloop: 496 while (results->ulr_offset < endsearch) { 497 preempt_point(); 498 499 /* 500 * If necessary, get the next directory block. 501 */ 502 if ((results->ulr_offset & bmask) == 0) { 503 if (bp != NULL) 504 brelse(bp, 0); 505 error = ufs_blkatoff(vdp, (off_t)results->ulr_offset, 506 NULL, &bp, false); 507 if (error) 508 goto out; 509 entryoffsetinblock = 0; 510 } 511 /* 512 * If still looking for a slot, and at a DIRBLKSIZ 513 * boundary, have to start looking for free space again. 514 */ 515 if (slot.status == NONE && 516 (entryoffsetinblock & (dirblksiz - 1)) == 0) { 517 slot.offset = -1; 518 slot.freespace = 0; 519 } 520 /* 521 * Get pointer to next entry. 522 * Full validation checks are slow, so we only check 523 * enough to insure forward progress through the 524 * directory. Complete checks can be run by patching 525 * "dirchk" to be true. 526 */ 527 KASSERT(bp != NULL); 528 ep = (void *)((char *)bp->b_data + entryoffsetinblock); 529 const char *msg; 530 reclen = ufs_rw16(ep->d_reclen, needswap); 531 if ((reclen == 0 && (msg = "null entry")) || (dirchk && 532 (msg = ufs_dirbadentry(vdp, ep, entryoffsetinblock)))) { 533 ufs_dirbad(dp, results->ulr_offset, msg); 534 reclen = dirblksiz - 535 (entryoffsetinblock & (dirblksiz - 1)); 536 goto next; 537 } 538 539 /* 540 * If an appropriate sized slot has not yet been found, 541 * check to see if one is available. Also accumulate space 542 * in the current block so that we can determine if 543 * compaction is viable. 544 */ 545 if (slot.status != FOUND) { 546 int size = reclen; 547 if (ep->d_ino != 0) 548 size -= UFS_DIRSIZ(fsfmt, ep, needswap); 549 if (size > 0) 550 slot_update(&slot, size, reclen, 551 results->ulr_offset); 552 } 553 554 if (ep->d_ino == 0) 555 goto next; 556 557 /* 558 * Check for a name match. 559 */ 560 const uint16_t namlen = NAMLEN(fsfmt, needswap, ep); 561 if (namlen != cnp->cn_namelen || 562 memcmp(cnp->cn_nameptr, ep->d_name, (size_t)namlen)) 563 goto next; 564 565 #ifdef UFS_DIRHASH 566 foundentry: 567 #endif 568 /* 569 * Save directory entry's inode number and 570 * reclen, and release directory buffer. 571 */ 572 if (!fsfmt && ep->d_type == DT_WHT) { 573 slot_white(&slot, reclen, results); 574 /* 575 * This is used to set results->ulr_endoff, which may 576 * be used by ufs_direnter() as a length to truncate 577 * the directory to. Therefore, it must point past the 578 * end of the last non-empty directory entry. We don't 579 * know where that is in this case, so we effectively 580 * disable shrinking by using the existing size of the 581 * directory. 582 * 583 * Note that we wouldn't expect to shrink the 584 * directory while rewriting an existing entry anyway. 585 */ 586 enduseful = endsearch; 587 cnp->cn_flags |= ISWHITEOUT; 588 numdirpasses--; 589 goto notfound; 590 } 591 foundino = ufs_rw32(ep->d_ino, needswap); 592 results->ulr_reclen = reclen; 593 goto found; 594 next: 595 prevoff = results->ulr_offset; 596 results->ulr_offset += reclen; 597 entryoffsetinblock += reclen; 598 if (ep->d_ino) 599 enduseful = results->ulr_offset; 600 } 601 notfound: 602 /* 603 * If we started in the middle of the directory and failed 604 * to find our target, we must check the beginning as well. 605 */ 606 if (numdirpasses == 2) { 607 numdirpasses--; 608 results->ulr_offset = 0; 609 endsearch = results->ulr_diroff; 610 goto searchloop; 611 } 612 if (bp != NULL) 613 brelse(bp, 0); 614 /* 615 * If creating, and at end of pathname and current 616 * directory has not been removed, then can consider 617 * allowing file to be created. 618 */ 619 if ((nameiop == CREATE || nameiop == RENAME || 620 (nameiop == DELETE && 621 (cnp->cn_flags & DOWHITEOUT) && 622 (cnp->cn_flags & ISWHITEOUT))) && 623 (flags & ISLASTCN) && dp->i_nlink != 0) { 624 /* 625 * Access for write is interpreted as allowing 626 * creation of files in the directory. 627 */ 628 if (flags & WILLBEDIR) 629 error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred); 630 else 631 error = VOP_ACCESS(vdp, VWRITE, cred); 632 if (error) 633 goto out; 634 error = slot_estimate(&slot, dirblksiz, nameiop, 635 prevoff, enduseful, dp, results); 636 /* 637 * We return with the directory locked, so that 638 * the parameters we set up above will still be 639 * valid if we actually decide to do a direnter(). 640 * We return ni_vp == NULL to indicate that the entry 641 * does not currently exist; we leave a pointer to 642 * the (locked) directory inode in ndp->ni_dvp. 643 * 644 * NB - if the directory is unlocked, then this 645 * information cannot be used. 646 */ 647 goto out; 648 } 649 /* 650 * Insert name into cache (as non-existent) if appropriate. 651 */ 652 if (nameiop != CREATE) { 653 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, 654 cnp->cn_flags); 655 } 656 error = SET_ERROR(ENOENT); 657 goto out; 658 659 found: 660 if (numdirpasses == 2) 661 namecache_count_pass2(); 662 /* 663 * Check that directory length properly reflects presence 664 * of this entry. 665 */ 666 const uint64_t newisize = 667 results->ulr_offset + UFS_DIRSIZ(fsfmt, ep, needswap); 668 if (newisize > dp->i_size) { 669 ufs_dirbad(dp, results->ulr_offset, "i_size too small"); 670 dp->i_size = newisize; 671 DIP_ASSIGN(dp, size, dp->i_size); 672 dp->i_flag |= IN_CHANGE | IN_UPDATE; 673 UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP); 674 } 675 brelse(bp, 0); 676 677 /* 678 * Found component in pathname. 679 * If the final component of path name, save information 680 * in the cache as to where the entry was found. 681 */ 682 if ((flags & ISLASTCN) && nameiop == LOOKUP) 683 results->ulr_diroff = results->ulr_offset & ~(dirblksiz - 1); 684 685 /* 686 * If deleting, and at end of pathname, return 687 * parameters which can be used to remove file. 688 * Lock the inode, being careful with ".". 689 */ 690 if (nameiop == DELETE && (flags & ISLASTCN)) { 691 /* 692 * Return pointer to current entry in results->ulr_offset, 693 * and distance past previous entry (if there 694 * is a previous entry in this block) in results->ulr_count. 695 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 696 */ 697 calc_count(results, dirblksiz, prevoff); 698 699 if ((error = ufs_getino(vdp, dp, foundino, &tdp, false)) != 0) 700 goto out; 701 702 if ((error = ufs_can_delete(tdp, vdp, dp, cred)) != 0) 703 goto out; 704 705 *vpp = tdp; 706 goto out; 707 } 708 709 /* 710 * If rewriting (RENAME), return the inode and the 711 * information required to rewrite the present directory 712 * Must get inode of directory entry to verify it's a 713 * regular file, or empty directory. 714 */ 715 if (nameiop == RENAME && (flags & ISLASTCN)) { 716 if (flags & WILLBEDIR) 717 error = VOP_ACCESSX(vdp, VWRITE | VAPPEND, cred); 718 else 719 error = VOP_ACCESS(vdp, VWRITE, cred); 720 if (error) 721 goto out; 722 /* 723 * Careful about locking second inode. 724 * This can only occur if the target is ".". 725 */ 726 if ((error = ufs_getino(vdp, dp, foundino, &tdp, true)) != 0) 727 goto out; 728 *vpp = tdp; 729 goto out; 730 } 731 732 if ((error = ufs_getino(vdp, dp, foundino, &tdp, false)) != 0) 733 goto out; 734 735 *vpp = tdp; 736 /* 737 * Insert name into cache if appropriate. 738 */ 739 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); 740 error = 0; 741 742 out: 743 return error; 744 } 745 746 void 747 ufs_dirbad(struct inode *ip, doff_t offset, const char *how) 748 { 749 struct mount *mp = ITOV(ip)->v_mount; 750 void (*p)(const char *, ...) __printflike(1, 2) = 751 (mp->mnt_flag & MNT_RDONLY) == 0 ? panic : printf; 752 753 (*p)("%s: bad dir ino %ju at offset %d: %s\n", 754 mp->mnt_stat.f_mntonname, (uintmax_t)ip->i_number, 755 offset, how); 756 } 757 758 /* 759 * Do consistency checking on a directory entry: 760 * record length must be multiple of 4 761 * entry must fit in rest of its DIRBLKSIZ block 762 * record must be large enough to contain entry 763 * name is not longer than FFS_MAXNAMLEN 764 * name must be as long as advertised, and null terminated 765 */ 766 const char * 767 ufs_dirbadentry(const struct vnode *dp, const struct direct *ep, 768 int entryoffsetinblock) 769 { 770 const struct ufsmount *ump = VFSTOUFS(dp->v_mount); 771 const int needswap = UFS_MPNEEDSWAP(ump); 772 const int dirblksiz = ump->um_dirblksiz; 773 const int maxsize = dirblksiz - (entryoffsetinblock & (dirblksiz - 1)); 774 const int fsfmt = FSFMT(dp); 775 const uint8_t namlen = NAMLEN(fsfmt, needswap, ep); 776 const uint16_t reclen = ufs_rw16(ep->d_reclen, needswap); 777 const int dirsiz = (int)UFS_DIRSIZ(fsfmt, ep, needswap); 778 const char *name = ep->d_name; 779 const char *str; 780 #ifdef DIAGNOSTIC 781 static char buf[512]; 782 #endif 783 784 if ((reclen & 0x3) != 0) 785 str = "not rounded"; 786 else if (reclen > maxsize) 787 str = "too big"; 788 else if (reclen < dirsiz) 789 str = "too small"; 790 #if FFS_MAXNAMLEN < 255 791 else if (namlen > FFS_MAXNAMLEN) 792 str = "long name"; 793 #endif 794 else 795 str = NULL; 796 797 if (str) { 798 #ifdef DIAGNOSTIC 799 snprintf(buf, sizeof(buf), "Bad dir (%s), reclen=%#x, " 800 "namlen=%d, dirsiz=%d <= reclen=%d <= maxsize=%d, " 801 "flags=%#x, entryoffsetinblock=%d, dirblksiz=%d", 802 str, reclen, namlen, dirsiz, reclen, maxsize, 803 dp->v_mount->mnt_flag, entryoffsetinblock, dirblksiz); 804 str = buf; 805 #endif 806 return str; 807 } 808 809 if (ep->d_ino == 0) 810 return NULL; 811 812 for (uint8_t i = 0; i < namlen; i++) 813 if (name[i] == '\0') { 814 str = "NUL in name"; 815 #ifdef DIAGNOSTIC 816 snprintf(buf, sizeof(buf), "%s [%s] i=%d, namlen=%d", 817 str, name, i, namlen); 818 str = buf; 819 #endif 820 return str; 821 } 822 823 if (name[namlen]) { 824 str = "missing NUL in name"; 825 #ifdef DIAGNOSTIC 826 snprintf(buf, sizeof(buf), "%s [%*.*s] namlen=%d", str, 827 namlen, namlen, name, namlen); 828 str = buf; 829 #endif 830 return str; 831 } 832 return NULL; 833 } 834 835 /* 836 * Construct a new directory entry after a call to namei, using the 837 * name in the componentname argument cnp. The argument ip is the 838 * inode to which the new directory entry will refer. 839 */ 840 void 841 ufs_makedirentry(struct inode *ip, struct componentname *cnp, 842 struct direct *newdirp) 843 { 844 size_t namelen = cnp->cn_namelen; 845 846 newdirp->d_ino = ip->i_number; 847 newdirp->d_namlen = namelen; 848 memcpy(newdirp->d_name, cnp->cn_nameptr, namelen); 849 850 /* NUL terminate and zero out padding */ 851 memset(&newdirp->d_name[namelen], 0, UFS_NAMEPAD(namelen)); 852 853 if (FSFMT(ITOV(ip))) 854 newdirp->d_type = 0; 855 else 856 newdirp->d_type = IFTODT(ip->i_mode); 857 } 858 859 860 static int 861 ufs_dirgrow(struct vnode *dvp, const struct ufs_lookup_results *ulr, 862 struct vnode *tvp, struct direct *dirp, 863 struct componentname *cnp, struct buf *newdirbp) 864 { 865 const kauth_cred_t cr = cnp->cn_cred; 866 const struct ufsmount *ump = VFSTOUFS(dvp->v_mount); 867 const int needswap = UFS_MPNEEDSWAP(ump); 868 const int dirblksiz = ump->um_dirblksiz; 869 const int fsfmt = FSFMT(dvp); 870 const u_int newentrysize = UFS_DIRSIZ(0, dirp, 0); 871 struct inode *dp = VTOI(dvp); 872 int error, ret, blkoff; 873 struct timespec ts; 874 struct buf *bp; 875 876 /* 877 * If ulr_count is 0, then namei could find no 878 * space in the directory. Here, ulr_offset will 879 * be on a directory block boundary and we will write the 880 * new entry into a fresh block. 881 */ 882 if (ulr->ulr_offset & (dirblksiz - 1)) 883 panic("%s: newblk", __func__); 884 if ((error = UFS_BALLOC(dvp, (off_t)ulr->ulr_offset, dirblksiz, 885 cr, B_CLRBUF | B_SYNC, &bp)) != 0) { 886 return error; 887 } 888 889 dp->i_size = ulr->ulr_offset + dirblksiz; 890 DIP_ASSIGN(dp, size, dp->i_size); 891 dp->i_flag |= IN_CHANGE | IN_UPDATE; 892 uvm_vnp_setsize(dvp, dp->i_size); 893 dirp->d_reclen = ufs_rw16(dirblksiz, needswap); 894 dirp->d_ino = ufs_rw32(dirp->d_ino, needswap); 895 if (fsfmt && ENDIANSWAP(needswap)) 896 ufs_dirswap(dirp); 897 blkoff = ulr->ulr_offset & (ump->um_mountp->mnt_stat.f_iosize - 1); 898 memcpy((char *)bp->b_data + blkoff, dirp, newentrysize); 899 #ifdef UFS_DIRHASH 900 if (dp->i_dirhash != NULL) { 901 ufsdirhash_newblk(dp, ulr->ulr_offset); 902 ufsdirhash_add(dp, dirp, ulr->ulr_offset); 903 ufsdirhash_checkblock(dp, (char *)bp->b_data + blkoff, 904 ulr->ulr_offset); 905 } 906 #endif 907 error = VOP_BWRITE(bp->b_vp, bp); 908 vfs_timestamp(&ts); 909 ret = UFS_UPDATE(dvp, &ts, &ts, UPDATE_DIROP); 910 if (error == 0) 911 return ret; 912 return error; 913 } 914 915 static int 916 #if __GNUC_PREREQ__(5, 3) 917 /* This gets miscompiled by gcc 5.3 PR/51094 */ 918 __attribute__((__optimize__("no-tree-vrp"))) 919 #endif 920 ufs_dircompact(struct vnode *dvp, const struct ufs_lookup_results *ulr, 921 struct vnode *tvp, struct direct *dirp, 922 struct componentname *cnp, struct buf *newdirbp) 923 { 924 const struct ufsmount *ump = VFSTOUFS(dvp->v_mount); 925 const int needswap = UFS_MPNEEDSWAP(ump); 926 const int fsfmt = FSFMT(dvp); 927 const u_int newentrysize = UFS_DIRSIZ(0, dirp, 0); 928 struct inode *dp = VTOI(dvp); 929 struct buf *bp; 930 u_int dsize; 931 struct direct *ep, *nep; 932 int error, loc, spacefree; 933 char *dirbuf; 934 uint16_t reclen; 935 936 UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount); 937 938 /* 939 * If ulr_count is non-zero, then namei found space for the new 940 * entry in the range ulr_offset to ulr_offset + ulr_count 941 * in the directory. To use this space, we may have to compact 942 * the entries located there, by copying them together towards the 943 * beginning of the block, leaving the free space in one usable 944 * chunk at the end. 945 */ 946 947 /* 948 * Increase size of directory if entry eats into new space. 949 * This should never push the size past a new multiple of 950 * DIRBLKSIZ. 951 * 952 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 953 */ 954 if (ulr->ulr_offset + ulr->ulr_count > dp->i_size) { 955 #ifdef DIAGNOSTIC 956 printf("%s: reached 4.2-only block, not supposed to happen\n", 957 __func__); 958 #endif 959 dp->i_size = ulr->ulr_offset + ulr->ulr_count; 960 DIP_ASSIGN(dp, size, dp->i_size); 961 dp->i_flag |= IN_CHANGE | IN_UPDATE; 962 UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); 963 } 964 /* 965 * Get the block containing the space for the new directory entry. 966 */ 967 error = ufs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp, true); 968 if (error) 969 return error; 970 971 /* 972 * Find space for the new entry. In the simple case, the entry at 973 * offset base will have the space. If it does not, then namei 974 * arranged that compacting the region ulr_offset to 975 * ulr_offset + ulr_count would yield the space. 976 */ 977 ep = (void *)dirbuf; 978 dsize = (ep->d_ino != 0) ? UFS_DIRSIZ(fsfmt, ep, needswap) : 0; 979 reclen = ufs_rw16(ep->d_reclen, needswap); 980 spacefree = reclen - dsize; 981 for (loc = reclen; loc < ulr->ulr_count; ) { 982 nep = (void *)(dirbuf + loc); 983 984 /* Trim the existing slot (NB: dsize may be zero). */ 985 ep->d_reclen = ufs_rw16(dsize, needswap); 986 ep = (void *)((char *)ep + dsize); 987 988 reclen = ufs_rw16(nep->d_reclen, needswap); 989 loc += reclen; 990 if (nep->d_ino == 0) { 991 /* 992 * A mid-block unused entry. Such entries are 993 * never created by the kernel, but fsck_ffs 994 * can create them (and it doesn't fix them). 995 * 996 * Add up the free space, and initialise the 997 * relocated entry since we don't memcpy it. 998 */ 999 spacefree += reclen; 1000 ep->d_ino = 0; 1001 dsize = 0; 1002 continue; 1003 } 1004 dsize = UFS_DIRSIZ(fsfmt, nep, needswap); 1005 spacefree += reclen - dsize; 1006 #ifdef UFS_DIRHASH 1007 if (dp->i_dirhash != NULL) 1008 ufsdirhash_move(dp, nep, 1009 ulr->ulr_offset + ((char *)nep - dirbuf), 1010 ulr->ulr_offset + ((char *)ep - dirbuf)); 1011 #endif 1012 memcpy(ep, nep, dsize); 1013 } 1014 /* 1015 * Here, `ep' points to a directory entry containing `dsize' in-use 1016 * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0, 1017 * then the entry is completely unused (dsize == 0). The value 1018 * of ep->d_reclen is always indeterminate. 1019 * 1020 * Update the pointer fields in the previous entry (if any), 1021 * copy in the new entry, and write out the block. 1022 */ 1023 if (ep->d_ino == 0 || 1024 (ufs_rw32(ep->d_ino, needswap) == UFS_WINO && 1025 memcmp(ep->d_name, dirp->d_name, dirp->d_namlen) == 0)) { 1026 if (spacefree + dsize < newentrysize) 1027 panic("%s: too big", __func__); 1028 dirp->d_reclen = spacefree + dsize; 1029 } else { 1030 if (spacefree < newentrysize) 1031 panic("%s: nospace", __func__); 1032 dirp->d_reclen = spacefree; 1033 ep->d_reclen = ufs_rw16(dsize, needswap); 1034 ep = (void *)((char *)ep + dsize); 1035 } 1036 1037 dirp->d_reclen = ufs_rw16(dirp->d_reclen, needswap); 1038 dirp->d_ino = ufs_rw32(dirp->d_ino, needswap); 1039 if (fsfmt && ENDIANSWAP(needswap)) 1040 ufs_dirswap(dirp); 1041 #ifdef UFS_DIRHASH 1042 if (dp->i_dirhash != NULL && (ep->d_ino == 0 || 1043 dirp->d_reclen == spacefree)) 1044 ufsdirhash_add(dp, dirp, ulr->ulr_offset + ((char *)ep - dirbuf)); 1045 #endif 1046 memcpy(ep, dirp, newentrysize); 1047 #ifdef UFS_DIRHASH 1048 if (dp->i_dirhash != NULL) { 1049 const int dirblkmsk = ump->um_dirblksiz - 1; 1050 ufsdirhash_checkblock(dp, dirbuf - 1051 (ulr->ulr_offset & dirblkmsk), 1052 ulr->ulr_offset & ~dirblkmsk); 1053 } 1054 #endif 1055 error = VOP_BWRITE(bp->b_vp, bp); 1056 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1057 /* 1058 * If all went well, and the directory can be shortened, proceed 1059 * with the truncation. Note that we have to unlock the inode for 1060 * the entry that we just entered, as the truncation may need to 1061 * lock other inodes which can lead to deadlock if we also hold a 1062 * lock on the newly entered node. 1063 */ 1064 if (error == 0 && ulr->ulr_endoff && ulr->ulr_endoff < dp->i_size) { 1065 const kauth_cred_t cr = cnp->cn_cred; 1066 #ifdef UFS_DIRHASH 1067 if (dp->i_dirhash != NULL) 1068 ufsdirhash_dirtrunc(dp, ulr->ulr_endoff); 1069 #endif 1070 (void) UFS_TRUNCATE(dvp, (off_t)ulr->ulr_endoff, IO_SYNC, cr); 1071 } 1072 UFS_WAPBL_UPDATE(dvp, NULL, NULL, UPDATE_DIROP); 1073 return error; 1074 } 1075 1076 /* 1077 * Write a directory entry after a call to namei, using the parameters 1078 * that ufs_lookup left in nameidata and in the ufs_lookup_results. 1079 * 1080 * DVP is the directory to be updated. It must be locked. 1081 * ULR is the ufs_lookup_results structure from the final lookup step. 1082 * TVP is not used. (XXX: why is it here? remove it) 1083 * DIRP is the new directory entry contents. 1084 * CNP is the componentname from the final lookup step. 1085 * NEWDIRBP is not used and (XXX) should be removed. The previous 1086 * comment here said it was used by the now-removed softupdates code. 1087 * 1088 * The link count of the target inode is *not* incremented; the 1089 * caller does that. 1090 * 1091 * If ulr->ulr_count is 0, ufs_lookup did not find space to insert the 1092 * directory entry. ulr_offset, which is the place to put the entry, 1093 * should be on a block boundary (and should be at the end of the 1094 * directory AFAIK) and a fresh block is allocated to put the new 1095 * directory entry in. 1096 * 1097 * If ulr->ulr_count is not zero, ufs_lookup found a slot to insert 1098 * the entry into. This slot ranges from ulr_offset to ulr_offset + 1099 * ulr_count. However, this slot may already be partially populated 1100 * requiring compaction. See notes below. 1101 * 1102 * Furthermore, if ulr_count is not zero and ulr_endoff is not the 1103 * same as i_size, the directory is truncated to size ulr_endoff. 1104 */ 1105 int 1106 ufs_direnter(struct vnode *dvp, const struct ufs_lookup_results *ulr, 1107 struct vnode *tvp, struct direct *dirp, 1108 struct componentname *cnp, struct buf *newdirbp) 1109 { 1110 if (ulr->ulr_count == 0) 1111 return ufs_dirgrow(dvp, ulr, tvp, dirp, cnp, newdirbp); 1112 else 1113 return ufs_dircompact(dvp, ulr, tvp, dirp, cnp, newdirbp); 1114 } 1115 1116 /* 1117 * Remove a directory entry after a call to namei, using the 1118 * parameters that ufs_lookup left in nameidata and in the 1119 * ufs_lookup_results. 1120 * 1121 * DVP is the directory to be updated. It must be locked. 1122 * ULR is the ufs_lookup_results structure from the final lookup step. 1123 * IP, if not null, is the inode being unlinked. 1124 * FLAGS may contain DOWHITEOUT. 1125 * ISRMDIR is not used and (XXX) should be removed. 1126 * 1127 * If FLAGS contains DOWHITEOUT the entry is replaced with a whiteout 1128 * instead of being cleared. 1129 * 1130 * ulr->ulr_offset contains the position of the directory entry 1131 * to be removed. 1132 * 1133 * ulr->ulr_reclen contains the size of the directory entry to be 1134 * removed. 1135 * 1136 * ulr->ulr_count contains the size of the *previous* directory 1137 * entry. This allows finding it, for free space management. If 1138 * ulr_count is 0, the target entry is at the beginning of the 1139 * directory. (Does this ever happen? The first entry should be ".", 1140 * which should only be removed at rmdir time. Does rmdir come here 1141 * to clear out the "." and ".." entries? Perhaps, but I doubt it.) 1142 * 1143 * The space is marked free by adding it to the record length (not 1144 * name length) of the preceding entry. If the first entry becomes 1145 * free, it is marked free by setting the inode number to 0. 1146 * 1147 * The link count of IP is decremented. Note that this is not the 1148 * inverse behavior of ufs_direnter, which does not adjust link 1149 * counts. Sigh. 1150 */ 1151 int 1152 ufs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr, 1153 struct inode *ip, int flags, int isrmdir) 1154 { 1155 struct inode *dp = VTOI(dvp); 1156 struct direct *ep; 1157 struct buf *bp; 1158 int error; 1159 const int needswap = UFS_MPNEEDSWAP(dp->i_ump); 1160 uint16_t reclen; 1161 1162 UFS_WAPBL_JLOCK_ASSERT(dvp->v_mount); 1163 1164 if (flags & DOWHITEOUT) { 1165 /* 1166 * Whiteout entry: set d_ino to UFS_WINO. 1167 */ 1168 error = ufs_blkatoff(dvp, (off_t)ulr->ulr_offset, &ep, 1169 &bp, true); 1170 if (error) 1171 return error; 1172 ep->d_ino = ufs_rw32(UFS_WINO, needswap); 1173 ep->d_type = DT_WHT; 1174 goto out; 1175 } 1176 1177 if ((error = ufs_blkatoff(dvp, 1178 (off_t)(ulr->ulr_offset - ulr->ulr_count), &ep, &bp, true)) != 0) 1179 return error; 1180 1181 reclen = ufs_rw16(ep->d_reclen, needswap); 1182 #ifdef UFS_DIRHASH 1183 /* 1184 * Remove the dirhash entry. This is complicated by the fact 1185 * that `ep' is the previous entry when ulr_count != 0. 1186 */ 1187 if (dp->i_dirhash != NULL) 1188 ufsdirhash_remove(dp, (ulr->ulr_count == 0) ? ep : 1189 (void *)((char *)ep + reclen), ulr->ulr_offset); 1190 #endif 1191 1192 if (ulr->ulr_count == 0) { 1193 /* 1194 * First entry in block: set d_ino to zero. 1195 */ 1196 ep->d_ino = 0; 1197 } else { 1198 /* 1199 * Collapse new free space into previous entry. 1200 */ 1201 ep->d_reclen = ufs_rw16(reclen + ulr->ulr_reclen, needswap); 1202 } 1203 1204 #ifdef UFS_DIRHASH 1205 if (dp->i_dirhash != NULL) { 1206 int dirblksiz = ip->i_ump->um_dirblksiz; 1207 ufsdirhash_checkblock(dp, (char *)ep - 1208 ((ulr->ulr_offset - ulr->ulr_count) & (dirblksiz - 1)), 1209 ulr->ulr_offset & ~(dirblksiz - 1)); 1210 } 1211 #endif 1212 1213 out: 1214 if (ip) { 1215 ip->i_nlink--; 1216 DIP_ASSIGN(ip, nlink, ip->i_nlink); 1217 ip->i_flag |= IN_CHANGE; 1218 UFS_WAPBL_UPDATE(ITOV(ip), NULL, NULL, 0); 1219 } 1220 /* 1221 * XXX did it ever occur to anyone that it might be a good 1222 * idea to restore ip->i_nlink if this fails? Or something? 1223 * Currently on error return from this function the state of 1224 * ip->i_nlink depends on what happened, and callers 1225 * definitely do not take this into account. 1226 */ 1227 error = VOP_BWRITE(bp->b_vp, bp); 1228 dp->i_flag |= IN_CHANGE | IN_UPDATE; 1229 /* 1230 * If the last named reference to a snapshot goes away, 1231 * drop its snapshot reference so that it will be reclaimed 1232 * when last open reference goes away. 1233 */ 1234 if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && 1235 ip->i_nlink == 0) 1236 UFS_SNAPGONE(ITOV(ip)); 1237 UFS_WAPBL_UPDATE(dvp, NULL, NULL, 0); 1238 return error; 1239 } 1240 1241 /* 1242 * Rewrite an existing directory entry to point at the inode supplied. 1243 * 1244 * DP is the directory to update. 1245 * OFFSET is the position of the entry in question. It may come 1246 * from ulr_offset of a ufs_lookup_results. 1247 * OIP is the old inode the directory previously pointed to. 1248 * NEWINUM is the number of the new inode. 1249 * NEWTYPE is the new value for the type field of the directory entry. 1250 * (This is ignored if the fs doesn't support that.) 1251 * ISRMDIR is not used and (XXX) should be removed. 1252 * IFLAGS are added to DP's inode flags. 1253 * 1254 * The link count of OIP is decremented. Note that the link count of 1255 * the new inode is *not* incremented. Yay for symmetry. 1256 */ 1257 int 1258 ufs_dirrewrite(struct inode *dp, off_t offset, 1259 struct inode *oip, ino_t newinum, int newtype, 1260 int isrmdir, int iflags) 1261 { 1262 struct buf *bp; 1263 struct direct *ep; 1264 struct vnode *vdp = ITOV(dp); 1265 int error; 1266 1267 error = ufs_blkatoff(vdp, offset, &ep, &bp, true); 1268 if (error) 1269 return error; 1270 ep->d_ino = ufs_rw32(newinum, UFS_MPNEEDSWAP(dp->i_ump)); 1271 if (!FSFMT(vdp)) 1272 ep->d_type = newtype; 1273 oip->i_nlink--; 1274 DIP_ASSIGN(oip, nlink, oip->i_nlink); 1275 oip->i_flag |= IN_CHANGE; 1276 UFS_WAPBL_UPDATE(ITOV(oip), NULL, NULL, UPDATE_DIROP); 1277 error = VOP_BWRITE(bp->b_vp, bp); 1278 dp->i_flag |= iflags; 1279 /* 1280 * If the last named reference to a snapshot goes away, 1281 * drop its snapshot reference so that it will be reclaimed 1282 * when last open reference goes away. 1283 */ 1284 if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_nlink == 0) 1285 UFS_SNAPGONE(ITOV(oip)); 1286 UFS_WAPBL_UPDATE(vdp, NULL, NULL, UPDATE_DIROP); 1287 return error; 1288 } 1289 1290 /* 1291 * Check if a directory is empty or not. 1292 * Inode supplied must be locked. 1293 * 1294 * Using a struct dirtemplate here is not precisely 1295 * what we want, but better than using a struct direct. 1296 * 1297 * NB: does not handle corrupted directories. 1298 */ 1299 int 1300 ufs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred) 1301 { 1302 doff_t off; 1303 struct direct dbuf; 1304 struct direct *dp = &dbuf; 1305 int error; 1306 size_t count; 1307 const int needswap = UFS_IPNEEDSWAP(ip); 1308 const int fsfmt = FSFMT(ITOV(ip)); 1309 #define MINDIRSIZ (sizeof (struct dirtemplate) / 2) 1310 1311 for (off = 0; off < ip->i_size; 1312 off += ufs_rw16(dp->d_reclen, needswap)) { 1313 error = ufs_bufio(UIO_READ, ITOV(ip), dp, MINDIRSIZ, 1314 off, IO_NODELOCKED, cred, &count, NULL); 1315 /* 1316 * Since we read MINDIRSIZ, residual must 1317 * be 0 unless we're at end of file. 1318 */ 1319 if (error || count != 0) 1320 return 0; 1321 /* avoid infinite loops */ 1322 if (dp->d_reclen == 0) 1323 return 0; 1324 /* skip empty entries */ 1325 ino_t ino = ufs_rw32(dp->d_ino, needswap); 1326 if (ino == 0 || ino == UFS_WINO) 1327 continue; 1328 /* accept only "." and ".." */ 1329 const uint8_t namlen = NAMLEN(fsfmt, needswap, dp); 1330 if (namlen > 2) 1331 return 0; 1332 if (dp->d_name[0] != '.') 1333 return 0; 1334 /* 1335 * At this point namlen must be 1 or 2. 1336 * 1 implies ".", 2 implies ".." if second 1337 * char is also "." 1338 */ 1339 if (namlen == 1 && ino == ip->i_number) 1340 continue; 1341 if (dp->d_name[1] == '.' && ino == parentino) 1342 continue; 1343 return 0; 1344 } 1345 return 1; 1346 } 1347 1348 #define UFS_DIRRABLKS 0 1349 int ufs_dirrablks = UFS_DIRRABLKS; 1350 1351 /* 1352 * ufs_blkatoff: Return buffer with the contents of block "offset" from 1353 * the beginning of directory "vp". If "res" is non-NULL, fill it in with 1354 * a pointer to the remaining space in the directory. If the caller intends 1355 * to modify the buffer returned, "modify" must be true. 1356 */ 1357 1358 int 1359 ufs_blkatoff(struct vnode *vp, off_t offset, void *v, struct buf **bpp, 1360 bool modify) 1361 { 1362 char **res = v; 1363 struct inode *ip __diagused; 1364 struct buf *bp; 1365 daddr_t lbn; 1366 const int dirrablks = ufs_dirrablks; 1367 daddr_t *blks; 1368 int *blksizes; 1369 int run, error; 1370 struct mount *mp = vp->v_mount; 1371 const int bshift = mp->mnt_fs_bshift; 1372 const int bsize = 1 << bshift; 1373 off_t eof; 1374 1375 blks = kmem_alloc((1 + dirrablks) * sizeof(daddr_t), KM_SLEEP); 1376 blksizes = kmem_alloc((1 + dirrablks) * sizeof(int), KM_SLEEP); 1377 ip = VTOI(vp); 1378 KASSERT(vp->v_size == ip->i_size); 1379 GOP_SIZE(vp, vp->v_size, &eof, 0); 1380 lbn = offset >> bshift; 1381 1382 for (run = 0; run <= dirrablks;) { 1383 const off_t curoff = lbn << bshift; 1384 const int size = MIN(eof - curoff, bsize); 1385 1386 if (size == 0) { 1387 break; 1388 } 1389 KASSERT(curoff < eof); 1390 blks[run] = lbn; 1391 blksizes[run] = size; 1392 lbn++; 1393 run++; 1394 if (size != bsize) { 1395 break; 1396 } 1397 } 1398 KASSERT(run >= 1); 1399 error = breadn(vp, blks[0], blksizes[0], &blks[1], &blksizes[1], 1400 run - 1, (modify ? B_MODIFY : 0), &bp); 1401 if (error != 0) { 1402 *bpp = NULL; 1403 goto out; 1404 } 1405 if (res) { 1406 *res = (char *)bp->b_data + (offset & (bsize - 1)); 1407 } 1408 *bpp = bp; 1409 1410 out: 1411 kmem_free(blks, (1 + dirrablks) * sizeof(daddr_t)); 1412 kmem_free(blksizes, (1 + dirrablks) * sizeof(int)); 1413 return error; 1414 } 1415