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