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