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