1/* $NetBSD: ulfs_lookup.c,v 1.47 2022/08/06 18:26:42 andvar Exp $ */ 2/* from NetBSD: ufs_lookup.c,v 1.135 2015/07/11 11:04:48 mlelstv */ 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.47 2022/08/06 18:26:42 andvar 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/proc.h> 58#include <sys/kmem.h> 59 60#include <ufs/lfs/lfs.h> 61#include <ufs/lfs/lfs_accessors.h> 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 75int lfs_dirchk = 1; 76#else 77int 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 */ 113int 114ulfs_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 LFS_DIRHEADER *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 *tdp; /* returned by vcache_get */ 142 doff_t enduseful; /* pointer past last used dir slot. 143 used for directory truncation. */ 144 u_long bmask; /* block offset mask */ 145 int error; 146 struct vnode **vpp = ap->a_vpp; 147 struct componentname *cnp = ap->a_cnp; 148 kauth_cred_t cred = cnp->cn_cred; 149 int flags; 150 int nameiop = cnp->cn_nameiop; 151 struct lfs *fs = dp->i_lfs; 152 int dirblksiz = fs->um_dirblksiz; 153 ino_t foundino; 154 struct ulfs_lookup_results *results; 155 int iswhiteout; /* temp result from cache_lookup() */ 156 157 flags = cnp->cn_flags; 158 159 bp = NULL; 160 slotoffset = -1; 161 *vpp = NULL; 162 endsearch = 0; /* silence compiler warning */ 163 164 /* 165 * Check accessibility of directory. 166 */ 167 if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0) 168 return (error); 169 170 if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) && 171 (nameiop == DELETE || nameiop == RENAME)) 172 return (EROFS); 173 174 /* 175 * We now have a segment name to search for, and a directory to search. 176 * 177 * Before tediously performing a linear scan of the directory, 178 * check the name cache to see if the directory/name pair 179 * we are looking for is known already. 180 */ 181 if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen, 182 cnp->cn_nameiop, cnp->cn_flags, &iswhiteout, vpp)) { 183 if (iswhiteout) { 184 cnp->cn_flags |= ISWHITEOUT; 185 } 186 return *vpp == NULLVP ? ENOENT : 0; 187 } 188 189 /* May need to restart the lookup with an exclusive lock. */ 190 if (VOP_ISLOCKED(vdp) != LK_EXCLUSIVE) 191 return ENOLCK; 192 193 /* 194 * Produce the auxiliary lookup results into i_crap. Increment 195 * its serial number so elsewhere we can tell if we're using 196 * stale results. This should not be done this way. XXX. 197 */ 198 results = &dp->i_crap; 199 dp->i_crapcounter++; 200 201 if (iswhiteout) { 202 /* 203 * The namecache set iswhiteout without finding a 204 * cache entry. As of this writing (20121014), this 205 * can happen if there was a whiteout entry that has 206 * been invalidated by the lookup. It is not clear if 207 * it is correct to set ISWHITEOUT in this case or 208 * not; however, doing so retains the prior behavior, 209 * so we'll go with that until some clearer answer 210 * appears. XXX 211 */ 212 cnp->cn_flags |= ISWHITEOUT; 213 } 214 215 /* 216 * Suppress search for slots unless creating 217 * file and at end of pathname, in which case 218 * we watch for a place to put the new file in 219 * case it doesn't already exist. 220 */ 221 slotstatus = FOUND; 222 slotfreespace = slotsize = slotneeded = 0; 223 if ((nameiop == CREATE || nameiop == RENAME) && (flags & ISLASTCN)) { 224 slotstatus = NONE; 225 slotneeded = LFS_DIRECTSIZ(fs, cnp->cn_namelen); 226 } 227 228 /* 229 * If there is cached information on a previous search of 230 * this directory, pick up where we last left off. 231 * We cache only lookups as these are the most common 232 * and have the greatest payoff. Caching CREATE has little 233 * benefit as it usually must search the entire directory 234 * to determine that the entry does not exist. Caching the 235 * location of the last DELETE or RENAME has not reduced 236 * profiling time and hence has been removed in the interest 237 * of simplicity. 238 */ 239 bmask = vdp->v_mount->mnt_stat.f_iosize - 1; 240 241#ifdef LFS_DIRHASH 242 /* 243 * Use dirhash for fast operations on large directories. The logic 244 * to determine whether to hash the directory is contained within 245 * ulfsdirhash_build(); a zero return means that it decided to hash 246 * this directory and it successfully built up the hash table. 247 */ 248 if (ulfsdirhash_build(dp) == 0) { 249 /* Look for a free slot if needed. */ 250 enduseful = dp->i_size; 251 if (slotstatus != FOUND) { 252 slotoffset = ulfsdirhash_findfree(dp, slotneeded, 253 &slotsize); 254 if (slotoffset >= 0) { 255 slotstatus = COMPACT; 256 enduseful = ulfsdirhash_enduseful(dp); 257 if (enduseful < 0) 258 enduseful = dp->i_size; 259 } 260 } 261 /* Look up the component. */ 262 numdirpasses = 1; 263 entryoffsetinblock = 0; /* silence compiler warning */ 264 switch (ulfsdirhash_lookup(dp, cnp->cn_nameptr, cnp->cn_namelen, 265 &results->ulr_offset, &bp, nameiop == DELETE ? &prevoff : NULL)) { 266 case 0: 267 ep = (LFS_DIRHEADER *)((char *)bp->b_data + 268 (results->ulr_offset & bmask)); 269 goto foundentry; 270 case ENOENT: 271 results->ulr_offset = roundup(dp->i_size, dirblksiz); 272 goto notfound; 273 default: 274 /* Something failed; just do a linear search. */ 275 break; 276 } 277 } 278#endif /* LFS_DIRHASH */ 279 280 if (nameiop != LOOKUP || results->ulr_diroff == 0 || 281 results->ulr_diroff >= dp->i_size) { 282 entryoffsetinblock = 0; 283 results->ulr_offset = 0; 284 numdirpasses = 1; 285 } else { 286 results->ulr_offset = results->ulr_diroff; 287 if ((entryoffsetinblock = results->ulr_offset & bmask) && 288 (error = ulfs_blkatoff(vdp, (off_t)results->ulr_offset, 289 NULL, &bp, false))) 290 goto out; 291 numdirpasses = 2; 292 namecache_count_2passes(); 293 } 294 prevoff = results->ulr_offset; 295 endsearch = roundup(dp->i_size, dirblksiz); 296 enduseful = 0; 297 298searchloop: 299 while (results->ulr_offset < endsearch) { 300 preempt_point(); 301 302 /* 303 * If necessary, get the next directory block. 304 */ 305 if ((results->ulr_offset & bmask) == 0) { 306 if (bp != NULL) 307 brelse(bp, 0); 308 error = ulfs_blkatoff(vdp, (off_t)results->ulr_offset, 309 NULL, &bp, false); 310 if (error) 311 goto out; 312 entryoffsetinblock = 0; 313 } 314 /* 315 * If still looking for a slot, and at a DIRBLKSIZ 316 * boundary, have to start looking for free space again. 317 */ 318 if (slotstatus == NONE && 319 (entryoffsetinblock & (dirblksiz - 1)) == 0) { 320 slotoffset = -1; 321 slotfreespace = 0; 322 } 323 /* 324 * Get pointer to next entry. 325 * Full validation checks are slow, so we only check 326 * enough to insure forward progress through the 327 * directory. Complete checks can be run by patching 328 * "lfs_dirchk" to be true. 329 */ 330 KASSERT(bp != NULL); 331 ep = (LFS_DIRHEADER *)((char *)bp->b_data + entryoffsetinblock); 332 if (lfs_dir_getreclen(fs, ep) == 0 || 333 (lfs_dirchk && ulfs_dirbadentry(vdp, ep, entryoffsetinblock))) { 334 int i; 335 336 ulfs_dirbad(dp, results->ulr_offset, "mangled entry"); 337 i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1)); 338 results->ulr_offset += i; 339 entryoffsetinblock += i; 340 continue; 341 } 342 343 /* 344 * If an appropriate sized slot has not yet been found, 345 * check to see if one is available. Also accumulate space 346 * in the current block so that we can determine if 347 * compaction is viable. 348 */ 349 if (slotstatus != FOUND) { 350 int size = lfs_dir_getreclen(fs, ep); 351 352 if (lfs_dir_getino(fs, ep) != 0) 353 size -= LFS_DIRSIZ(fs, ep); 354 if (size > 0) { 355 if (size >= slotneeded) { 356 slotstatus = FOUND; 357 slotoffset = results->ulr_offset; 358 slotsize = lfs_dir_getreclen(fs, ep); 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 lfs_dir_getreclen(fs, ep) - 367 slotoffset; 368 } 369 } 370 } 371 } 372 373 /* 374 * Check for a name match. 375 */ 376 if (lfs_dir_getino(fs, ep)) { 377 int namlen; 378 379 namlen = lfs_dir_getnamlen(fs, ep); 380 if (namlen == cnp->cn_namelen && 381 !memcmp(cnp->cn_nameptr, lfs_dir_nameptr(fs, ep), 382 (unsigned)namlen)) { 383#ifdef LFS_DIRHASH 384foundentry: 385#endif 386 /* 387 * Save directory entry's inode number and 388 * reclen, and release directory buffer. 389 */ 390 if (!FSFMT(vdp) && lfs_dir_gettype(fs, ep) == LFS_DT_WHT) { 391 slotstatus = FOUND; 392 slotoffset = results->ulr_offset; 393 slotsize = lfs_dir_getreclen(fs, ep); 394 results->ulr_reclen = slotsize; 395 /* 396 * This is used to set 397 * results->ulr_endoff, 398 * which may be used by ulfs_direnter() 399 * as a length to truncate the 400 * directory to. Therefore, it must 401 * point past the end of the last 402 * non-empty directory entry. We don't 403 * know where that is in this case, so 404 * we effectively disable shrinking by 405 * using the existing size of the 406 * directory. 407 * 408 * Note that we wouldn't expect to 409 * shrink the directory while rewriting 410 * an existing entry anyway. 411 */ 412 enduseful = endsearch; 413 cnp->cn_flags |= ISWHITEOUT; 414 numdirpasses--; 415 goto notfound; 416 } 417 foundino = lfs_dir_getino(fs, ep); 418 results->ulr_reclen = lfs_dir_getreclen(fs, ep); 419 goto found; 420 } 421 } 422 prevoff = results->ulr_offset; 423 results->ulr_offset += lfs_dir_getreclen(fs, ep); 424 entryoffsetinblock += lfs_dir_getreclen(fs, ep); 425 if (lfs_dir_getino(fs, ep)) 426 enduseful = results->ulr_offset; 427 } 428notfound: 429 /* 430 * If we started in the middle of the directory and failed 431 * to find our target, we must check the beginning as well. 432 */ 433 if (numdirpasses == 2) { 434 numdirpasses--; 435 results->ulr_offset = 0; 436 endsearch = results->ulr_diroff; 437 goto searchloop; 438 } 439 if (bp != NULL) 440 brelse(bp, 0); 441 /* 442 * If creating, and at end of pathname and current 443 * directory has not been removed, then can consider 444 * allowing file to be created. 445 */ 446 if ((nameiop == CREATE || nameiop == RENAME || 447 (nameiop == DELETE && 448 (cnp->cn_flags & DOWHITEOUT) && 449 (cnp->cn_flags & ISWHITEOUT))) && 450 (flags & ISLASTCN) && dp->i_nlink != 0) { 451 /* 452 * Access for write is interpreted as allowing 453 * creation of files in the directory. 454 */ 455 error = VOP_ACCESS(vdp, VWRITE, cred); 456 if (error) 457 goto out; 458 /* 459 * Return an indication of where the new directory 460 * entry should be put. If we didn't find a slot, 461 * then set results->ulr_count to 0 indicating 462 * that the new slot belongs at the end of the 463 * directory. If we found a slot, then the new entry 464 * can be put in the range from results->ulr_offset to 465 * results->ulr_offset + results->ulr_count. 466 */ 467 if (slotstatus == NONE) { 468 results->ulr_offset = roundup(dp->i_size, dirblksiz); 469 results->ulr_count = 0; 470 enduseful = results->ulr_offset; 471 } else if (nameiop == DELETE) { 472 results->ulr_offset = slotoffset; 473 if ((results->ulr_offset & (dirblksiz - 1)) == 0) 474 results->ulr_count = 0; 475 else 476 results->ulr_count = 477 results->ulr_offset - prevoff; 478 } else { 479 results->ulr_offset = slotoffset; 480 results->ulr_count = slotsize; 481 if (enduseful < slotoffset + slotsize) 482 enduseful = slotoffset + slotsize; 483 } 484 results->ulr_endoff = roundup(enduseful, dirblksiz); 485#if 0 /* commented out by dbj. none of the on disk fields changed */ 486 dp->i_state |= IN_CHANGE | IN_UPDATE; 487#endif 488 /* 489 * We return with the directory locked, so that 490 * the parameters we set up above will still be 491 * valid if we actually decide to do a direnter(). 492 * We return ni_vp == NULL to indicate that the entry 493 * does not currently exist; we leave a pointer to 494 * the (locked) directory inode in ndp->ni_dvp. 495 * 496 * NB - if the directory is unlocked, then this 497 * information cannot be used. 498 */ 499 error = EJUSTRETURN; 500 goto out; 501 } 502 /* 503 * Insert name into cache (as non-existent) if appropriate. 504 */ 505 if (nameiop != CREATE) { 506 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, 507 cnp->cn_flags); 508 } 509 error = ENOENT; 510 goto out; 511 512found: 513 if (numdirpasses == 2) 514 namecache_count_pass2(); 515 /* 516 * Check that directory length properly reflects presence 517 * of this entry. 518 */ 519 if (results->ulr_offset + LFS_DIRSIZ(fs, ep) > dp->i_size) { 520 ulfs_dirbad(dp, results->ulr_offset, "i_size too small"); 521 dp->i_size = 522 results->ulr_offset + LFS_DIRSIZ(fs, ep); 523 DIP_ASSIGN(dp, size, dp->i_size); 524 dp->i_state |= IN_CHANGE | IN_UPDATE; 525 } 526 brelse(bp, 0); 527 528 /* 529 * Found component in pathname. 530 * If the final component of path name, save information 531 * in the cache as to where the entry was found. 532 */ 533 if ((flags & ISLASTCN) && nameiop == LOOKUP) 534 results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1); 535 536 /* 537 * If deleting, and at end of pathname, return 538 * parameters which can be used to remove file. 539 * Lock the inode, being careful with ".". 540 */ 541 if (nameiop == DELETE && (flags & ISLASTCN)) { 542 /* 543 * Return pointer to current entry in results->ulr_offset, 544 * and distance past previous entry (if there 545 * is a previous entry in this block) in results->ulr_count. 546 * Save directory inode pointer in ndp->ni_dvp for dirremove(). 547 */ 548 if ((results->ulr_offset & (dirblksiz - 1)) == 0) 549 results->ulr_count = 0; 550 else 551 results->ulr_count = results->ulr_offset - prevoff; 552 if (dp->i_number == foundino) { 553 vref(vdp); 554 tdp = vdp; 555 } else { 556 error = vcache_get(vdp->v_mount, 557 &foundino, sizeof(foundino), &tdp); 558 if (error) 559 goto out; 560 } 561 /* 562 * Write access to directory required to delete files. 563 */ 564 error = VOP_ACCESS(vdp, VWRITE, cred); 565 if (error) { 566 vrele(tdp); 567 goto out; 568 } 569 /* 570 * If directory is "sticky", then user must own 571 * the directory, or the file in it, else she 572 * may not delete it (unless she's root). This 573 * implements append-only directories. 574 */ 575 if (dp->i_mode & ISVTX) { 576 error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE, 577 tdp, vdp, genfs_can_sticky(vdp, cred, dp->i_uid, 578 VTOI(tdp)->i_uid)); 579 if (error) { 580 vrele(tdp); 581 error = EPERM; 582 goto out; 583 } 584 } 585 *vpp = tdp; 586 error = 0; 587 goto out; 588 } 589 590 /* 591 * If rewriting (RENAME), return the inode and the 592 * information required to rewrite the present directory 593 * Must get inode of directory entry to verify it's a 594 * regular file, or empty directory. 595 */ 596 if (nameiop == RENAME && (flags & ISLASTCN)) { 597 error = VOP_ACCESS(vdp, VWRITE, cred); 598 if (error) 599 goto out; 600 /* 601 * Careful about locking second inode. 602 * This can only occur if the target is ".". 603 */ 604 if (dp->i_number == foundino) { 605 error = EISDIR; 606 goto out; 607 } 608 error = vcache_get(vdp->v_mount, 609 &foundino, sizeof(foundino), &tdp); 610 if (error) 611 goto out; 612 *vpp = tdp; 613 error = 0; 614 goto out; 615 } 616 617 if (dp->i_number == foundino) { 618 vref(vdp); /* we want ourself, ie "." */ 619 *vpp = vdp; 620 } else { 621 error = vcache_get(vdp->v_mount, 622 &foundino, sizeof(foundino), &tdp); 623 if (error) 624 goto out; 625 *vpp = tdp; 626 } 627 628 /* 629 * Insert name into cache if appropriate. 630 */ 631 cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags); 632 error = 0; 633 634out: 635 return error; 636} 637 638void 639ulfs_dirbad(struct inode *ip, doff_t offset, const char *how) 640{ 641 struct mount *mp; 642 643 mp = ITOV(ip)->v_mount; 644 printf("%s: bad dir ino %llu at offset %d: %s\n", 645 mp->mnt_stat.f_mntonname, (unsigned long long)ip->i_number, 646 offset, how); 647 if ((mp->mnt_flag & MNT_RDONLY) == 0) 648 panic("bad dir"); 649} 650 651/* 652 * Do consistency checking on a directory entry: 653 * record length must be multiple of 4 654 * entry must fit in rest of its DIRBLKSIZ block 655 * record must be large enough to contain entry 656 * name is not longer than LFS_MAXNAMLEN 657 * name must be as long as advertised, and null terminated 658 */ 659int 660ulfs_dirbadentry(struct vnode *dp, LFS_DIRHEADER *ep, int entryoffsetinblock) 661{ 662 int i; 663 int namlen; 664 unsigned reclen; 665 struct ulfsmount *ump = VFSTOULFS(dp->v_mount); 666 struct lfs *fs = ump->um_lfs; 667 int dirblksiz = fs->um_dirblksiz; 668 const char *name; 669 670 namlen = lfs_dir_getnamlen(fs, ep); 671 reclen = lfs_dir_getreclen(fs, ep); 672 if ((reclen & 0x3) != 0 || 673 reclen > dirblksiz - (entryoffsetinblock & (dirblksiz - 1)) || 674 reclen < LFS_DIRSIZ(fs, ep) || namlen > LFS_MAXNAMLEN) { 675 /*return (1); */ 676 printf("First bad, reclen=%#x, DIRSIZ=%lu, namlen=%d, " 677 "flags=%#x, entryoffsetinblock=%d, dirblksiz = %d\n", 678 lfs_dir_getreclen(fs, ep), 679 (u_long)LFS_DIRSIZ(fs, ep), 680 namlen, dp->v_mount->mnt_flag, entryoffsetinblock, 681 dirblksiz); 682 goto bad; 683 } 684 if (lfs_dir_getino(fs, ep) == 0) 685 return (0); 686 name = lfs_dir_nameptr(fs, ep); 687 for (i = 0; i < namlen; i++) 688 if (name[i] == '\0') { 689 /*return (1); */ 690 printf("Second bad\n"); 691 goto bad; 692 } 693 if (name[i]) 694 goto bad; 695 return (0); 696bad: 697 return (1); 698} 699 700/* 701 * Assign the contents of directory entry DIRP, on volume FS. 702 * 703 * NAME/NAMLEN is the name, which is not necessarily null terminated. 704 * INUM is the inode number, and DTYPE is the type code (LFS_DT_*). 705 * 706 * Note that these values typically come from: 707 * cnp->cn_nameptr 708 * cnp->cn_namelen 709 * ip->i_number 710 * LFS_IFTODT(ip->i_mode) 711 * 712 * Does not set d_reclen. 713 */ 714static void 715ulfs_direntry_assign(struct lfs *fs, LFS_DIRHEADER *dirp, 716 const char *name, size_t namlen, 717 ino_t inum, unsigned dtype) 718{ 719 lfs_dir_setino(fs, dirp, inum); 720 lfs_dir_setnamlen(fs, dirp, namlen); 721 lfs_dir_settype(fs, dirp, dtype); 722 memcpy(lfs_dir_nameptr(fs, dirp), name, namlen); 723 lfs_dir_nameptr(fs, dirp)[namlen] = '\0'; 724} 725 726/* 727 * Write a directory entry after a call to namei, using the parameters 728 * that ulfs_lookup left in nameidata and in the ulfs_lookup_results. 729 * 730 * DVP is the directory to be updated. It must be locked. 731 * ULR is the ulfs_lookup_results structure from the final lookup step. 732 * TVP is not used. (XXX: why is it here? remove it) 733 * CNP is the componentname from the final lookup step. 734 * INUM is the inode number to insert into the new directory entry. 735 * DTYPE is the type code (LFS_DT_*) to insert into the new directory entry. 736 * NEWDIRBP is not used and (XXX) should be removed. The previous 737 * comment here said it was used by the now-removed softupdates code. 738 * 739 * The link count of the target inode is *not* incremented; the 740 * caller does that. 741 * 742 * If ulr->ulr_count is 0, ulfs_lookup did not find space to insert the 743 * directory entry. ulr_offset, which is the place to put the entry, 744 * should be on a block boundary (and should be at the end of the 745 * directory AFAIK) and a fresh block is allocated to put the new 746 * directory entry in. 747 * 748 * If ulr->ulr_count is not zero, ulfs_lookup found a slot to insert 749 * the entry into. This slot ranges from ulr_offset to ulr_offset + 750 * ulr_count. However, this slot may already be partially populated 751 * requiring compaction. See notes below. 752 * 753 * Furthermore, if ulr_count is not zero and ulr_endoff is not the 754 * same as i_size, the directory is truncated to size ulr_endoff. 755 */ 756int 757ulfs_direnter(struct vnode *dvp, const struct ulfs_lookup_results *ulr, 758 struct vnode *tvp, 759 struct componentname *cnp, ino_t inum, unsigned dtype, 760 struct buf *newdirbp) 761{ 762 kauth_cred_t cr; 763 int newentrysize; 764 struct inode *dp; 765 struct buf *bp; 766 u_int dsize; 767 LFS_DIRHEADER *ep, *nep; 768 int error, ret, lfs_blkoff, loc, spacefree; 769 char *dirbuf; 770 struct timespec ts; 771 struct ulfsmount *ump = VFSTOULFS(dvp->v_mount); 772 struct lfs *fs = ump->um_lfs; 773 int dirblksiz = fs->um_dirblksiz; 774 const char *name; 775 unsigned namlen, reclen; 776#ifdef LFS_DIRHASH 777 int dohashadd; 778#endif 779 780 error = 0; 781 name = cnp->cn_nameptr; /* note: not null-terminated */ 782 namlen = cnp->cn_namelen; 783 cr = cnp->cn_cred; 784 785 dp = VTOI(dvp); 786 newentrysize = LFS_DIRECTSIZ(fs, namlen); 787 788 if (ulr->ulr_count == 0) { 789 /* 790 * If ulr_count is 0, then namei could find no 791 * space in the directory. Here, ulr_offset will 792 * be on a directory block boundary and we will write the 793 * new entry into a fresh block. 794 */ 795 if (ulr->ulr_offset & (dirblksiz - 1)) 796 panic("ulfs_direnter: newblk"); 797 if ((error = lfs_balloc(dvp, (off_t)ulr->ulr_offset, dirblksiz, 798 cr, B_CLRBUF | B_SYNC, &bp)) != 0) { 799 return (error); 800 } 801 dp->i_size = ulr->ulr_offset + dirblksiz; 802 DIP_ASSIGN(dp, size, dp->i_size); 803 dp->i_state |= IN_CHANGE | IN_UPDATE; 804 uvm_vnp_setsize(dvp, dp->i_size); 805 lfs_blkoff = ulr->ulr_offset & (ump->um_mountp->mnt_stat.f_iosize - 1); 806 ep = (LFS_DIRHEADER *)((char *)bp->b_data + lfs_blkoff); 807 ulfs_direntry_assign(fs, ep, name, namlen, inum, dtype); 808 lfs_dir_setreclen(fs, ep, dirblksiz); 809#ifdef LFS_DIRHASH 810 if (dp->i_dirhash != NULL) { 811 ulfsdirhash_newblk(dp, ulr->ulr_offset); 812 ulfsdirhash_add(dp, ep, ulr->ulr_offset); 813 ulfsdirhash_checkblock(dp, (char *)bp->b_data + lfs_blkoff, 814 ulr->ulr_offset); 815 } 816#endif 817 error = VOP_BWRITE(bp->b_vp, bp); 818 vfs_timestamp(&ts); 819 ret = lfs_update(dvp, &ts, &ts, UPDATE_DIROP); 820 if (error == 0) 821 return (ret); 822 return (error); 823 } 824 825 /* 826 * If ulr_count is non-zero, then namei found space for the new 827 * entry in the range ulr_offset to ulr_offset + ulr_count 828 * in the directory. To use this space, we may have to compact 829 * the entries located there, by copying them together towards the 830 * beginning of the block, leaving the free space in one usable 831 * chunk at the end. 832 */ 833 834 /* 835 * Increase size of directory if entry eats into new space. 836 * This should never push the size past a new multiple of 837 * DIRBLKSIZ. 838 * 839 * N.B. - THIS IS AN ARTIFACT OF 4.2 AND SHOULD NEVER HAPPEN. 840 */ 841 if (ulr->ulr_offset + ulr->ulr_count > dp->i_size) { 842#ifdef DIAGNOSTIC 843 printf("ulfs_direnter: reached 4.2-only block, " 844 "not supposed to happen\n"); 845#endif 846 dp->i_size = ulr->ulr_offset + ulr->ulr_count; 847 DIP_ASSIGN(dp, size, dp->i_size); 848 dp->i_state |= IN_CHANGE | IN_UPDATE; 849 } 850 /* 851 * Get the block containing the space for the new directory entry. 852 */ 853 error = ulfs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp, true); 854 if (error) { 855 return (error); 856 } 857 /* 858 * Find space for the new entry. In the simple case, the entry at 859 * offset base will have the space. If it does not, then namei 860 * arranged that compacting the region ulr_offset to 861 * ulr_offset + ulr_count would yield the space. 862 */ 863 ep = (LFS_DIRHEADER *)dirbuf; 864 dsize = (lfs_dir_getino(fs, ep) != 0) ? LFS_DIRSIZ(fs, ep) : 0; 865 spacefree = lfs_dir_getreclen(fs, ep) - dsize; 866 for (loc = lfs_dir_getreclen(fs, ep); loc < ulr->ulr_count; ) { 867 nep = (LFS_DIRHEADER *)(dirbuf + loc); 868 869 /* Trim the existing slot (NB: dsize may be zero). */ 870 lfs_dir_setreclen(fs, ep, dsize); 871 ep = LFS_NEXTDIR(fs, ep); 872 873 reclen = lfs_dir_getreclen(fs, nep); 874 loc += reclen; 875 if (lfs_dir_getino(fs, nep) == 0) { 876 /* 877 * A mid-block unused entry. Such entries are 878 * never created by the kernel, but fsck_ffs 879 * can create them (and it doesn't fix them). 880 * 881 * Add up the free space, and initialise the 882 * relocated entry since we don't memcpy it. 883 */ 884 spacefree += reclen; 885 lfs_dir_setino(fs, ep, 0); 886 dsize = 0; 887 continue; 888 } 889 dsize = LFS_DIRSIZ(fs, nep); 890 spacefree += reclen - dsize; 891#ifdef LFS_DIRHASH 892 if (dp->i_dirhash != NULL) 893 ulfsdirhash_move(dp, nep, 894 ulr->ulr_offset + ((char *)nep - dirbuf), 895 ulr->ulr_offset + ((char *)ep - dirbuf)); 896#endif 897 memcpy((void *)ep, (void *)nep, dsize); 898 } 899 /* 900 * Here, `ep' points to a directory entry containing `dsize' in-use 901 * bytes followed by `spacefree' unused bytes. If ep->d_ino == 0, 902 * then the entry is completely unused (dsize == 0). The value 903 * of ep->d_reclen is always indeterminate. 904 * 905 * Update the pointer fields in the previous entry (if any), 906 * copy in the new entry, and write out the block. 907 */ 908 if (lfs_dir_getino(fs, ep) == 0 || 909 (lfs_dir_getino(fs, ep) == ULFS_WINO && 910 memcmp(lfs_dir_nameptr(fs, ep), name, namlen) == 0)) { 911 if (spacefree + dsize < newentrysize) 912 panic("ulfs_direnter: compact1"); 913 reclen = spacefree + dsize; 914#ifdef LFS_DIRHASH 915 dohashadd = (lfs_dir_getino(fs, ep) == 0); 916#endif 917 } else { 918 if (spacefree < newentrysize) 919 panic("ulfs_direnter: compact2"); 920 reclen = spacefree; 921 lfs_dir_setreclen(fs, ep, dsize); 922 ep = LFS_NEXTDIR(fs, ep); 923#ifdef LFS_DIRHASH 924 dohashadd = 1; 925#endif 926 } 927 928 ulfs_direntry_assign(fs, ep, name, namlen, inum, dtype); 929 lfs_dir_setreclen(fs, ep, reclen); 930#ifdef LFS_DIRHASH 931 if (dp->i_dirhash != NULL && dohashadd) 932 ulfsdirhash_add(dp, ep, ulr->ulr_offset + ((char *)ep - dirbuf)); 933 if (dp->i_dirhash != NULL) 934 ulfsdirhash_checkblock(dp, dirbuf - 935 (ulr->ulr_offset & (dirblksiz - 1)), 936 ulr->ulr_offset & ~(dirblksiz - 1)); 937#endif 938 error = VOP_BWRITE(bp->b_vp, bp); 939 dp->i_state |= IN_CHANGE | IN_UPDATE; 940 /* 941 * If all went well, and the directory can be shortened, proceed 942 * with the truncation. Note that we have to unlock the inode for 943 * the entry that we just entered, as the truncation may need to 944 * lock other inodes which can lead to deadlock if we also hold a 945 * lock on the newly entered node. 946 */ 947 if (error == 0 && ulr->ulr_endoff && ulr->ulr_endoff < dp->i_size) { 948#ifdef LFS_DIRHASH 949 if (dp->i_dirhash != NULL) 950 ulfsdirhash_dirtrunc(dp, ulr->ulr_endoff); 951#endif 952 (void) lfs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC, cr); 953 } 954 return (error); 955} 956 957/* 958 * Remove a directory entry after a call to namei, using the 959 * parameters that ulfs_lookup left in nameidata and in the 960 * ulfs_lookup_results. 961 * 962 * DVP is the directory to be updated. It must be locked. 963 * ULR is the ulfs_lookup_results structure from the final lookup step. 964 * IP, if not null, is the inode being unlinked. 965 * FLAGS may contain DOWHITEOUT. 966 * ISRMDIR is not used and (XXX) should be removed. 967 * 968 * If FLAGS contains DOWHITEOUT the entry is replaced with a whiteout 969 * instead of being cleared. 970 * 971 * ulr->ulr_offset contains the position of the directory entry 972 * to be removed. 973 * 974 * ulr->ulr_reclen contains the size of the directory entry to be 975 * removed. 976 * 977 * ulr->ulr_count contains the size of the *previous* directory 978 * entry. This allows finding it, for free space management. If 979 * ulr_count is 0, the target entry is at the beginning of the 980 * directory. (Does this ever happen? The first entry should be ".", 981 * which should only be removed at rmdir time. Does rmdir come here 982 * to clear out the "." and ".." entries? Perhaps, but I doubt it.) 983 * 984 * The space is marked free by adding it to the record length (not 985 * name length) of the preceding entry. If the first entry becomes 986 * free, it is marked free by setting the inode number to 0. 987 * 988 * The link count of IP is decremented. Note that this is not the 989 * inverse behavior of ulfs_direnter, which does not adjust link 990 * counts. Sigh. 991 */ 992int 993ulfs_dirremove(struct vnode *dvp, const struct ulfs_lookup_results *ulr, 994 struct inode *ip, int flags, int isrmdir) 995{ 996 struct inode *dp = VTOI(dvp); 997 struct lfs *fs = dp->i_lfs; 998 LFS_DIRHEADER *ep; 999 struct buf *bp; 1000 int error; 1001 1002 if (flags & DOWHITEOUT) { 1003 /* 1004 * Whiteout entry: set d_ino to ULFS_WINO. 1005 */ 1006 error = ulfs_blkatoff(dvp, (off_t)ulr->ulr_offset, (void *)&ep, 1007 &bp, true); 1008 if (error) 1009 return (error); 1010 lfs_dir_setino(fs, ep, ULFS_WINO); 1011 lfs_dir_settype(fs, ep, LFS_DT_WHT); 1012 goto out; 1013 } 1014 1015 if ((error = ulfs_blkatoff(dvp, 1016 (off_t)(ulr->ulr_offset - ulr->ulr_count), (void *)&ep, &bp, true)) != 0) 1017 return (error); 1018 1019#ifdef LFS_DIRHASH 1020 /* 1021 * Remove the dirhash entry. This is complicated by the fact 1022 * that `ep' is the previous entry when ulr_count != 0. 1023 */ 1024 if (dp->i_dirhash != NULL) 1025 ulfsdirhash_remove(dp, (ulr->ulr_count == 0) ? ep : 1026 LFS_NEXTDIR(fs, ep), ulr->ulr_offset); 1027#endif 1028 1029 if (ulr->ulr_count == 0) { 1030 /* 1031 * First entry in block: set d_ino to zero. 1032 */ 1033 lfs_dir_setino(fs, ep, 0); 1034 } else { 1035 /* 1036 * Collapse new free space into previous entry. 1037 */ 1038 lfs_dir_setreclen(fs, ep, 1039 lfs_dir_getreclen(fs, ep) + ulr->ulr_reclen); 1040 } 1041 1042#ifdef LFS_DIRHASH 1043 if (dp->i_dirhash != NULL) { 1044 int dirblksiz = ip->i_lfs->um_dirblksiz; 1045 ulfsdirhash_checkblock(dp, (char *)ep - 1046 ((ulr->ulr_offset - ulr->ulr_count) & (dirblksiz - 1)), 1047 ulr->ulr_offset & ~(dirblksiz - 1)); 1048 } 1049#endif 1050 1051out: 1052 if (ip) { 1053 ip->i_nlink--; 1054 DIP_ASSIGN(ip, nlink, ip->i_nlink); 1055 ip->i_state |= IN_CHANGE; 1056 } 1057 /* 1058 * XXX did it ever occur to anyone that it might be a good 1059 * idea to restore ip->i_nlink if this fails? Or something? 1060 * Currently on error return from this function the state of 1061 * ip->i_nlink depends on what happened, and callers 1062 * definitely do not take this into account. 1063 */ 1064 error = VOP_BWRITE(bp->b_vp, bp); 1065 dp->i_state |= IN_CHANGE | IN_UPDATE; 1066 /* 1067 * If the last named reference to a snapshot goes away, 1068 * drop its snapshot reference so that it will be reclaimed 1069 * when last open reference goes away. 1070 */ 1071 if (ip != 0 && (ip->i_flags & SF_SNAPSHOT) != 0 && 1072 ip->i_nlink == 0) 1073 ulfs_snapgone(ip); 1074 return (error); 1075} 1076 1077/* 1078 * Rewrite an existing directory entry to point at the inode supplied. 1079 * 1080 * DP is the directory to update. 1081 * OFFSET is the position of the entry in question. It may come 1082 * from ulr_offset of a ulfs_lookup_results. 1083 * OIP is the old inode the directory previously pointed to. 1084 * NEWINUM is the number of the new inode. 1085 * NEWTYPE is the new value for the type field of the directory entry. 1086 * (This is ignored if the fs doesn't support that.) 1087 * ISRMDIR is not used and (XXX) should be removed. 1088 * IFLAGS are added to DP's inode flags. 1089 * 1090 * The link count of OIP is decremented. Note that the link count of 1091 * the new inode is *not* incremented. Yay for symmetry. 1092 */ 1093int 1094ulfs_dirrewrite(struct inode *dp, off_t offset, 1095 struct inode *oip, ino_t newinum, int newtype, 1096 int isrmdir, int iflags) 1097{ 1098 struct lfs *fs = dp->i_lfs; 1099 struct buf *bp; 1100 LFS_DIRHEADER *ep; 1101 struct vnode *vdp = ITOV(dp); 1102 int error; 1103 1104 error = ulfs_blkatoff(vdp, offset, (void *)&ep, &bp, true); 1105 if (error) 1106 return (error); 1107 lfs_dir_setino(fs, ep, newinum); 1108 lfs_dir_settype(fs, ep, newtype); 1109 oip->i_nlink--; 1110 DIP_ASSIGN(oip, nlink, oip->i_nlink); 1111 oip->i_state |= IN_CHANGE; 1112 error = VOP_BWRITE(bp->b_vp, bp); 1113 dp->i_state |= iflags; 1114 /* 1115 * If the last named reference to a snapshot goes away, 1116 * drop its snapshot reference so that it will be reclaimed 1117 * when last open reference goes away. 1118 */ 1119 if ((oip->i_flags & SF_SNAPSHOT) != 0 && oip->i_nlink == 0) 1120 ulfs_snapgone(oip); 1121 return (error); 1122} 1123 1124/* 1125 * Check if a directory is empty or not. 1126 * Inode supplied must be locked. 1127 * 1128 * Using a struct lfs_dirtemplate here is not precisely 1129 * what we want, but better than using a struct lfs_direct. 1130 * 1131 * NB: does not handle corrupted directories. 1132 */ 1133int 1134ulfs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred) 1135{ 1136 struct lfs *fs = ip->i_lfs; 1137 doff_t off; 1138 union lfs_dirtemplate dbuf; 1139 LFS_DIRHEADER *dp = (LFS_DIRHEADER *)&dbuf; 1140 int error, namlen; 1141 const char *name; 1142 size_t count; 1143/* XXX this should probably use LFS_DIRECTSIZ(fs, 2) */ 1144#define MINDIRSIZ (sizeof (struct lfs_dirtemplate64) / 2) 1145 1146 for (off = 0; off < ip->i_size; off += lfs_dir_getreclen(fs, dp)) { 1147 error = ulfs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ, 1148 off, IO_NODELOCKED, cred, &count, NULL); 1149 /* 1150 * Since we read MINDIRSIZ, residual must 1151 * be 0 unless we're at end of file. 1152 */ 1153 if (error || count != 0) 1154 return (0); 1155 /* avoid infinite loops */ 1156 if (lfs_dir_getreclen(fs, dp) == 0) 1157 return (0); 1158 /* skip empty entries */ 1159 if (lfs_dir_getino(fs, dp) == 0 || 1160 lfs_dir_getino(fs, dp) == ULFS_WINO) 1161 continue; 1162 /* accept only "." and ".." */ 1163 namlen = lfs_dir_getnamlen(fs, dp); 1164 name = lfs_dir_nameptr(fs, dp); 1165 if (namlen > 2) 1166 return (0); 1167 if (name[0] != '.') 1168 return (0); 1169 /* 1170 * At this point namlen must be 1 or 2. 1171 * 1 implies ".", 2 implies ".." if second 1172 * char is also "." 1173 */ 1174 if (namlen == 1 && lfs_dir_getino(fs, dp) == ip->i_number) 1175 continue; 1176 if (name[1] == '.' && lfs_dir_getino(fs, dp) == parentino) 1177 continue; 1178 return (0); 1179 } 1180 return (1); 1181} 1182 1183#define ULFS_DIRRABLKS 0 1184int ulfs_dirrablks = ULFS_DIRRABLKS; 1185 1186/* 1187 * ulfs_blkatoff: Return buffer with the contents of block "offset" from 1188 * the beginning of directory "vp". If "res" is non-NULL, fill it in with 1189 * a pointer to the remaining space in the directory. If the caller intends 1190 * to modify the buffer returned, "modify" must be true. 1191 */ 1192 1193int 1194ulfs_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp, 1195 bool modify) 1196{ 1197 struct inode *ip __diagused; 1198 struct buf *bp; 1199 daddr_t lbn; 1200 const int dirrablks = ulfs_dirrablks; 1201 daddr_t *blks; 1202 int *blksizes; 1203 int run, error; 1204 struct mount *mp = vp->v_mount; 1205 const int bshift = mp->mnt_fs_bshift; 1206 const int bsize = 1 << bshift; 1207 off_t eof; 1208 1209 blks = kmem_alloc((1 + dirrablks) * sizeof(daddr_t), KM_SLEEP); 1210 blksizes = kmem_alloc((1 + dirrablks) * sizeof(int), KM_SLEEP); 1211 ip = VTOI(vp); 1212 KASSERT(vp->v_size == ip->i_size); 1213 GOP_SIZE(vp, vp->v_size, &eof, 0); 1214 lbn = offset >> bshift; 1215 1216 for (run = 0; run <= dirrablks;) { 1217 const off_t curoff = lbn << bshift; 1218 const int size = MIN(eof - curoff, bsize); 1219 1220 if (size == 0) { 1221 break; 1222 } 1223 KASSERT(curoff < eof); 1224 blks[run] = lbn; 1225 blksizes[run] = size; 1226 lbn++; 1227 run++; 1228 if (size != bsize) { 1229 break; 1230 } 1231 } 1232 KASSERT(run >= 1); 1233 error = breadn(vp, blks[0], blksizes[0], &blks[1], &blksizes[1], 1234 run - 1, (modify ? B_MODIFY : 0), &bp); 1235 if (error != 0) { 1236 *bpp = NULL; 1237 goto out; 1238 } 1239 if (res) { 1240 *res = (char *)bp->b_data + (offset & (bsize - 1)); 1241 } 1242 *bpp = bp; 1243 1244 out: 1245 kmem_free(blks, (1 + dirrablks) * sizeof(daddr_t)); 1246 kmem_free(blksizes, (1 + dirrablks) * sizeof(int)); 1247 return error; 1248} 1249