1 /* $NetBSD: inode.c,v 1.70 2020/04/03 19:36:33 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Konrad E. Schroder <perseant@hhhh.org>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1980, 1986, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/types.h> 62 #include <sys/param.h> 63 #include <sys/time.h> 64 #include <sys/buf.h> 65 #include <sys/mount.h> 66 67 #define vnode uvnode 68 #include <ufs/lfs/lfs.h> 69 #include <ufs/lfs/lfs_accessors.h> 70 #include <ufs/lfs/lfs_inode.h> 71 #undef vnode 72 73 #include <err.h> 74 #ifndef SMALL 75 #include <pwd.h> 76 #endif 77 #include <stdio.h> 78 #include <stdlib.h> 79 #include <string.h> 80 #include <util.h> 81 82 #include "bufcache.h" 83 #include "lfs_user.h" 84 85 #include "fsck.h" 86 #include "fsutil.h" 87 #include "extern.h" 88 89 static int iblock(struct inodesc *, long, u_int64_t); 90 int blksreqd(struct lfs *, int); 91 int lfs_maxino(void); 92 93 /* 94 * Get a dinode of a given inum. 95 * XXX combine this function with vget. 96 */ 97 union lfs_dinode * 98 ginode(ino_t ino) 99 { 100 struct uvnode *vp; 101 struct ubuf *bp; 102 IFILE *ifp; 103 daddr_t daddr; 104 unsigned segno; 105 106 vp = vget(fs, ino); 107 if (vp == NULL) 108 return NULL; 109 110 if (din_table[ino] == 0x0) { 111 LFS_IENTRY(ifp, fs, ino, bp); 112 daddr = lfs_if_getdaddr(fs, ifp); 113 segno = lfs_dtosn(fs, daddr); 114 din_table[ino] = daddr; 115 seg_table[segno].su_nbytes += DINOSIZE(fs); 116 brelse(bp, 0); 117 } 118 return VTOI(vp)->i_din; 119 } 120 121 /* 122 * Check validity of held blocks in an inode, recursing through all blocks. 123 */ 124 int 125 ckinode(union lfs_dinode *dp, struct inodesc *idesc) 126 { 127 daddr_t lbn, pbn; 128 long ret, n, ndb, offset; 129 union lfs_dinode dino; 130 u_int64_t remsize, sizepb; 131 mode_t mode; 132 char pathbuf[MAXPATHLEN + 1]; 133 struct uvnode *vp, *thisvp; 134 135 if (idesc->id_fix != IGNORE) 136 idesc->id_fix = DONTKNOW; 137 idesc->id_entryno = 0; 138 idesc->id_filesize = lfs_dino_getsize(fs, dp); 139 mode = lfs_dino_getmode(fs, dp) & LFS_IFMT; 140 if (mode == LFS_IFBLK || mode == LFS_IFCHR || 141 (mode == LFS_IFLNK && (lfs_dino_getsize(fs, dp) < lfs_sb_getmaxsymlinklen(fs) || 142 (lfs_sb_getmaxsymlinklen(fs) == 0 && 143 lfs_dino_getblocks(fs, dp) == 0)))) 144 return (KEEPON); 145 /* XXX is this safe if we're 32-bit? */ 146 dino = *dp; 147 ndb = howmany(lfs_dino_getsize(fs, &dino), lfs_sb_getbsize(fs)); 148 149 thisvp = vget(fs, idesc->id_number); 150 for (lbn = 0; lbn < ULFS_NDADDR; lbn++) { 151 pbn = lfs_dino_getdb(fs, &dino, lbn); 152 if (thisvp) 153 idesc->id_numfrags = 154 lfs_numfrags(fs, VTOI(thisvp)->i_lfs_fragsize[lbn]); 155 else { 156 if (--ndb == 0 && (offset = lfs_blkoff(fs, lfs_dino_getsize(fs, &dino))) != 0) { 157 idesc->id_numfrags = 158 lfs_numfrags(fs, lfs_fragroundup(fs, offset)); 159 } else 160 idesc->id_numfrags = lfs_sb_getfrag(fs); 161 } 162 if (pbn == 0) { 163 if (idesc->id_type == DATA && ndb >= 0) { 164 /* An empty block in a directory XXX */ 165 getpathname(pathbuf, sizeof(pathbuf), 166 idesc->id_number, idesc->id_number); 167 pfatal("DIRECTORY %s INO %lld: CONTAINS EMPTY BLOCKS [1]", 168 pathbuf, (long long)idesc->id_number); 169 if (reply("ADJUST LENGTH") == 1) { 170 vp = vget(fs, idesc->id_number); 171 dp = VTOD(vp); 172 lfs_dino_setsize(fs, dp, 173 lbn * lfs_sb_getbsize(fs)); 174 printf( 175 "YOU MUST RERUN FSCK AFTERWARDS\n"); 176 rerun = 1; 177 inodirty(VTOI(vp)); 178 } else 179 break; 180 } 181 continue; 182 } 183 idesc->id_blkno = pbn; 184 idesc->id_lblkno = lbn; 185 if (idesc->id_type == ADDR) { 186 ret = (*idesc->id_func) (idesc); 187 } else 188 ret = dirscan(idesc); 189 if (ret & STOP) 190 return (ret); 191 } 192 idesc->id_numfrags = lfs_sb_getfrag(fs); 193 remsize = lfs_dino_getsize(fs, &dino) - lfs_sb_getbsize(fs) * ULFS_NDADDR; 194 sizepb = lfs_sb_getbsize(fs); 195 for (n = 1; n <= ULFS_NIADDR; n++) { 196 pbn = lfs_dino_getib(fs, &dino, n-1); 197 if (pbn) { 198 idesc->id_blkno = pbn; 199 ret = iblock(idesc, n, remsize); 200 if (ret & STOP) 201 return (ret); 202 } else { 203 if (idesc->id_type == DATA && remsize > 0) { 204 /* An empty block in a directory XXX */ 205 getpathname(pathbuf, sizeof(pathbuf), 206 idesc->id_number, idesc->id_number); 207 pfatal("DIRECTORY %s INO %lld: CONTAINS EMPTY BLOCKS [2]", 208 pathbuf, (long long)idesc->id_number); 209 if (reply("ADJUST LENGTH") == 1) { 210 vp = vget(fs, idesc->id_number); 211 dp = VTOD(vp); 212 lfs_dino_setsize(fs, dp, 213 lfs_dino_getsize(fs, dp) - remsize); 214 remsize = 0; 215 printf( 216 "YOU MUST RERUN FSCK AFTERWARDS\n"); 217 rerun = 1; 218 inodirty(VTOI(vp)); 219 break; 220 } else 221 break; 222 } 223 } 224 sizepb *= LFS_NINDIR(fs); 225 remsize -= sizepb; 226 } 227 return (KEEPON); 228 } 229 230 static int 231 iblock(struct inodesc *idesc, long ilevel, u_int64_t isize) 232 { 233 unsigned j, maxindir; 234 daddr_t found; 235 struct ubuf *bp; 236 int i, n, (*func) (struct inodesc *), nif; 237 u_int64_t sizepb; 238 char pathbuf[MAXPATHLEN + 1], buf[BUFSIZ]; 239 struct uvnode *devvp, *vp; 240 int diddirty = 0; 241 242 if (idesc->id_type == ADDR) { 243 func = idesc->id_func; 244 n = (*func) (idesc); 245 if ((n & KEEPON) == 0) 246 return (n); 247 } else 248 func = dirscan; 249 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) 250 return (SKIP); 251 252 devvp = fs->lfs_devvp; 253 bread(devvp, LFS_FSBTODB(fs, idesc->id_blkno), lfs_sb_getbsize(fs), 254 0, &bp); 255 ilevel--; 256 for (sizepb = lfs_sb_getbsize(fs), i = 0; i < ilevel; i++) 257 sizepb *= LFS_NINDIR(fs); 258 if (isize > sizepb * LFS_NINDIR(fs)) 259 nif = LFS_NINDIR(fs); 260 else 261 nif = howmany(isize, sizepb); 262 if (idesc->id_func == pass1check && nif < LFS_NINDIR(fs)) { 263 maxindir = LFS_NINDIR(fs); 264 for (j = nif; j < maxindir; j++) { 265 found = lfs_iblock_get(fs, bp->b_data, j); 266 if (found == 0) 267 continue; 268 (void)snprintf(buf, sizeof(buf), 269 "PARTIALLY TRUNCATED INODE I=%llu", 270 (unsigned long long)idesc->id_number); 271 if (dofix(idesc, buf)) { 272 lfs_iblock_set(fs, bp->b_data, j, 0); 273 ++diddirty; 274 } 275 } 276 } 277 maxindir = nif; 278 for (j = 0; j < maxindir; j++) { 279 found = lfs_iblock_get(fs, bp->b_data, j); 280 if (found) { 281 idesc->id_blkno = found; 282 if (ilevel == 0) { 283 /* 284 * dirscan needs lfs_lblkno. 285 */ 286 idesc->id_lblkno++; 287 n = (*func) (idesc); 288 } else { 289 n = iblock(idesc, ilevel, isize); 290 } 291 if (n & STOP) { 292 if (diddirty) 293 VOP_BWRITE(bp); 294 else 295 brelse(bp, 0); 296 return (n); 297 } 298 } else { 299 if (idesc->id_type == DATA && isize > 0) { 300 /* An empty block in a directory XXX */ 301 getpathname(pathbuf, sizeof(pathbuf), 302 idesc->id_number, idesc->id_number); 303 pfatal("DIRECTORY %s INO %lld: CONTAINS EMPTY BLOCKS [3]", 304 pathbuf, (long long)idesc->id_number); 305 if (reply("ADJUST LENGTH") == 1) { 306 vp = vget(fs, idesc->id_number); 307 lfs_dino_setsize(fs, VTOI(vp)->i_din, 308 lfs_dino_getsize(fs, 309 VTOI(vp)->i_din) 310 - isize); 311 isize = 0; 312 printf( 313 "YOU MUST RERUN FSCK AFTERWARDS\n"); 314 rerun = 1; 315 inodirty(VTOI(vp)); 316 if (diddirty) 317 VOP_BWRITE(bp); 318 else 319 brelse(bp, 0); 320 return (STOP); 321 } 322 } 323 } 324 isize -= sizepb; 325 } 326 if (diddirty) 327 VOP_BWRITE(bp); 328 else 329 brelse(bp, 0); 330 return (KEEPON); 331 } 332 333 /* 334 * Check that a block in a legal block number. 335 * Return 0 if in range, 1 if out of range. 336 */ 337 int 338 chkrange(daddr_t blk, int cnt) 339 { 340 if (blk < lfs_sntod(fs, 0)) { 341 return (1); 342 } 343 if (blk > maxfsblock) { 344 return (1); 345 } 346 if (blk + cnt < lfs_sntod(fs, 0)) { 347 return (1); 348 } 349 if (blk + cnt > maxfsblock) { 350 return (1); 351 } 352 return (0); 353 } 354 355 /* 356 * Routines to maintain information about directory inodes. 357 * This is built during the first pass and used during the 358 * second and third passes. 359 * 360 * Enter inodes into the cache. 361 */ 362 void 363 cacheino(union lfs_dinode *dp, ino_t inumber) 364 { 365 struct inoinfo *inp; 366 struct inoinfo **inpp, **ninpsort; 367 unsigned int blks, i; 368 369 blks = howmany(lfs_dino_getsize(fs, dp), lfs_sb_getbsize(fs)); 370 if (blks > ULFS_NDADDR) 371 blks = ULFS_NDADDR + ULFS_NIADDR; 372 inp = emalloc(sizeof(*inp) + (blks - 1) * sizeof(inp->i_blks[0])); 373 inpp = &inphead[inumber % numdirs]; 374 inp->i_nexthash = *inpp; 375 *inpp = inp; 376 inp->i_child = inp->i_sibling = inp->i_parentp = 0; 377 if (inumber == ULFS_ROOTINO) 378 inp->i_parent = ULFS_ROOTINO; 379 else 380 inp->i_parent = (ino_t) 0; 381 inp->i_dotdot = (ino_t) 0; 382 inp->i_number = inumber; 383 inp->i_isize = lfs_dino_getsize(fs, dp); 384 385 inp->i_numblks = blks * sizeof(inp->i_blks[0]); 386 for (i=0; i<blks && i<ULFS_NDADDR; i++) { 387 inp->i_blks[i] = lfs_dino_getdb(fs, dp, i); 388 } 389 for (; i<blks; i++) { 390 inp->i_blks[i] = lfs_dino_getib(fs, dp, i - ULFS_NDADDR); 391 } 392 if (inplast == listmax) { 393 ninpsort = erealloc(inpsort, 394 (listmax + 100) * sizeof(struct inoinfo *)); 395 inpsort = ninpsort; 396 listmax += 100; 397 } 398 inpsort[inplast++] = inp; 399 } 400 401 /* 402 * Look up an inode cache structure. 403 */ 404 struct inoinfo * 405 getinoinfo(ino_t inumber) 406 { 407 struct inoinfo *inp; 408 409 for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { 410 if (inp->i_number != inumber) 411 continue; 412 return (inp); 413 } 414 err(EEXIT, "cannot find inode %llu", (unsigned long long)inumber); 415 return ((struct inoinfo *) 0); 416 } 417 418 /* 419 * Clean up all the inode cache structure. 420 */ 421 void 422 inocleanup(void) 423 { 424 struct inoinfo **inpp; 425 426 if (inphead == NULL) 427 return; 428 for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) 429 free((char *) (*inpp)); 430 free((char *) inphead); 431 free((char *) inpsort); 432 inphead = inpsort = NULL; 433 } 434 435 void 436 inodirty(struct inode *ip) 437 { 438 ip->i_state |= IN_MODIFIED; 439 } 440 441 void 442 clri(struct inodesc * idesc, const char *type, int flag) 443 { 444 struct uvnode *vp; 445 446 vp = vget(fs, idesc->id_number); 447 if (flag & 0x1) { 448 pwarn("%s %s", type, 449 (lfs_dino_getmode(fs, VTOI(vp)->i_din) & LFS_IFMT) == LFS_IFDIR ? "DIR" : "FILE"); 450 pinode(idesc->id_number); 451 } 452 if ((flag & 0x2) || preen || reply("CLEAR") == 1) { 453 if (preen && flag != 2) 454 printf(" (CLEARED)\n"); 455 n_files--; 456 (void) ckinode(VTOD(vp), idesc); 457 clearinode(idesc->id_number); 458 statemap[idesc->id_number] = USTATE; 459 vnode_destroy(vp); 460 return; 461 } 462 return; 463 } 464 465 void 466 clearinode(ino_t inumber) 467 { 468 struct ubuf *bp; 469 IFILE *ifp; 470 daddr_t daddr; 471 472 /* Send cleared inode to the free list */ 473 474 LFS_IENTRY(ifp, fs, inumber, bp); 475 daddr = lfs_if_getdaddr(fs, ifp); 476 if (daddr == LFS_UNUSED_DADDR) { 477 brelse(bp, 0); 478 return; 479 } 480 lfs_if_setdaddr(fs, ifp, LFS_UNUSED_DADDR); 481 lfs_if_setnextfree(fs, ifp, lfs_sb_getfreehd(fs)); 482 lfs_sb_setfreehd(fs, inumber); 483 sbdirty(); 484 VOP_BWRITE(bp); 485 486 /* 487 * update segment usage. 488 */ 489 if (daddr != LFS_UNUSED_DADDR) { 490 SEGUSE *sup; 491 u_int32_t oldsn = lfs_dtosn(fs, daddr); 492 493 seg_table[oldsn].su_nbytes -= DINOSIZE(fs); 494 LFS_SEGENTRY(sup, fs, oldsn, bp); 495 sup->su_nbytes -= DINOSIZE(fs); 496 LFS_WRITESEGENTRY(sup, fs, oldsn, bp); /* Ifile */ 497 } 498 } 499 500 int 501 findname(struct inodesc * idesc) 502 { 503 LFS_DIRHEADER *dirp = idesc->id_dirp; 504 size_t len; 505 char *buf; 506 507 if (lfs_dir_getino(fs, dirp) != idesc->id_parent) 508 return (KEEPON); 509 len = lfs_dir_getnamlen(fs, dirp) + 1; 510 if (len > MAXPATHLEN) { 511 /* Truncate it but don't overflow the buffer */ 512 /* XXX: this case doesn't null-terminate the result */ 513 len = MAXPATHLEN; 514 } 515 /* this is namebuf with utils.h */ 516 buf = __UNCONST(idesc->id_name); 517 (void)memcpy(buf, lfs_dir_nameptr(fs, dirp), len); 518 return (STOP | FOUND); 519 } 520 521 int 522 findino(struct inodesc * idesc) 523 { 524 LFS_DIRHEADER *dirp = idesc->id_dirp; 525 ino_t ino; 526 527 ino = lfs_dir_getino(fs, dirp); 528 if (ino == 0) 529 return (KEEPON); 530 if (strcmp(lfs_dir_nameptr(fs, dirp), idesc->id_name) == 0 && 531 ino >= ULFS_ROOTINO && ino < maxino) { 532 idesc->id_parent = ino; 533 return (STOP | FOUND); 534 } 535 return (KEEPON); 536 } 537 538 void 539 pinode(ino_t ino) 540 { 541 union lfs_dinode *dp; 542 struct passwd *pw; 543 544 printf(" I=%llu ", (unsigned long long)ino); 545 if (ino < ULFS_ROOTINO || ino >= maxino) 546 return; 547 dp = ginode(ino); 548 if (dp) { 549 printf(" OWNER="); 550 #ifndef SMALL 551 if (Uflag && (pw = getpwuid(lfs_dino_getuid(fs, dp))) != 0) 552 printf("%s ", pw->pw_name); 553 else 554 #endif 555 printf("%u ", (unsigned)lfs_dino_getuid(fs, dp)); 556 printf("MODE=%o\n", lfs_dino_getmode(fs, dp)); 557 if (preen) 558 printf("%s: ", cdevname()); 559 printf("SIZE=%ju ", (uintmax_t) lfs_dino_getsize(fs, dp)); 560 printf("MTIME=%s ", print_mtime(lfs_dino_getmtime(fs, dp))); 561 } 562 } 563 564 void 565 blkerror(ino_t ino, const char *type, daddr_t blk) 566 { 567 568 pfatal("%lld %s I=%llu", (long long) blk, type, 569 (unsigned long long)ino); 570 printf("\n"); 571 if (exitonfail) 572 exit(1); 573 switch (statemap[ino]) { 574 575 case FSTATE: 576 statemap[ino] = FCLEAR; 577 return; 578 579 case DSTATE: 580 statemap[ino] = DCLEAR; 581 return; 582 583 case FCLEAR: 584 case DCLEAR: 585 return; 586 587 default: 588 err(EEXIT, "BAD STATE %d TO BLKERR", statemap[ino]); 589 /* NOTREACHED */ 590 } 591 } 592 593 /* 594 * allocate an unused inode 595 */ 596 ino_t 597 allocino(ino_t request, int type) 598 { 599 ino_t ino; 600 union lfs_dinode *dp; 601 time_t t; 602 struct uvnode *vp; 603 struct ubuf *bp; 604 605 if (request == 0) 606 request = ULFS_ROOTINO; 607 else if (statemap[request] != USTATE) 608 return (0); 609 for (ino = request; ino < maxino; ino++) 610 if (statemap[ino] == USTATE) 611 break; 612 if (ino == maxino) 613 extend_ifile(fs); 614 615 switch (type & LFS_IFMT) { 616 case LFS_IFDIR: 617 statemap[ino] = DSTATE; 618 break; 619 case LFS_IFREG: 620 case LFS_IFLNK: 621 statemap[ino] = FSTATE; 622 break; 623 default: 624 return (0); 625 } 626 vp = lfs_valloc(fs, ino); 627 if (vp == NULL) 628 return (0); 629 dp = VTOI(vp)->i_din; 630 bp = getblk(vp, 0, lfs_sb_getfsize(fs)); 631 VOP_BWRITE(bp); 632 lfs_dino_setmode(fs, dp, type); 633 (void) time(&t); 634 lfs_dino_setatime(fs, dp, t); 635 lfs_dino_setctime(fs, dp, t); 636 lfs_dino_setmtime(fs, dp, t); 637 lfs_dino_setsize(fs, dp, lfs_sb_getfsize(fs)); 638 lfs_dino_setblocks(fs, dp, lfs_btofsb(fs, lfs_sb_getfsize(fs))); 639 n_files++; 640 inodirty(VTOI(vp)); 641 typemap[ino] = LFS_IFTODT(type); 642 return (ino); 643 } 644 645 /* 646 * deallocate an inode 647 */ 648 void 649 freeino(ino_t ino) 650 { 651 struct inodesc idesc; 652 struct uvnode *vp; 653 654 memset(&idesc, 0, sizeof(struct inodesc)); 655 idesc.id_type = ADDR; 656 idesc.id_func = pass4check; 657 idesc.id_number = ino; 658 vp = vget(fs, ino); 659 (void) ckinode(VTOD(vp), &idesc); 660 clearinode(ino); 661 statemap[ino] = USTATE; 662 vnode_destroy(vp); 663 664 n_files--; 665 } 666