1 /* $OpenBSD: inode.c,v 1.17 2011/03/12 17:50:47 deraadt Exp $ */ 2 /* $NetBSD: inode.c,v 1.8 2000/01/28 16:01:46 bouyer Exp $ */ 3 4 /* 5 * Copyright (c) 1997 Manuel Bouyer. 6 * Copyright (c) 1980, 1986, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <ufs/ext2fs/ext2fs_dinode.h> 37 #include <ufs/ext2fs/ext2fs_dir.h> 38 #include <ufs/ext2fs/ext2fs.h> 39 40 #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 41 #ifndef SMALL 42 #include <pwd.h> 43 #endif 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 49 #include "fsck.h" 50 #include "fsutil.h" 51 #include "extern.h" 52 53 /* 54 * CG is stored in fs byte order in memory, so we can't use ino_to_fsba 55 * here. 56 */ 57 58 #define fsck_ino_to_fsba(fs, x) \ 59 (fs2h32((fs)->e2fs_gd[ino_to_cg(fs, x)].ext2bgd_i_tables) + \ 60 (((x)-1) % (fs)->e2fs.e2fs_ipg)/(fs)->e2fs_ipb) 61 62 static ino_t startinum; 63 64 static int iblock(struct inodesc *, long, u_int64_t); 65 static int setlarge(void); 66 67 static int 68 setlarge(void) 69 { 70 if (sblock.e2fs.e2fs_rev < E2FS_REV1) { 71 pfatal("LARGE FILES UNSUPPORTED ON REVISION 0 FILESYSTEMS"); 72 return 0; 73 } 74 if (!(sblock.e2fs.e2fs_features_rocompat & EXT2F_ROCOMPAT_LARGEFILE)) { 75 if (preen) 76 pwarn("SETTING LARGE FILE INDICATOR\n"); 77 else if (!reply("SET LARGE FILE INDICATOR")) 78 return 0; 79 sblock.e2fs.e2fs_features_rocompat |= EXT2F_ROCOMPAT_LARGEFILE; 80 sbdirty(); 81 } 82 return 1; 83 } 84 85 u_int64_t 86 inosize(struct ext2fs_dinode *dp) 87 { 88 u_int64_t size = fs2h32(dp->e2di_size); 89 90 if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG) 91 size |= (u_int64_t)fs2h32(dp->e2di_dacl) << 32; 92 if (size >= 0x80000000U) 93 (void)setlarge(); 94 return size; 95 } 96 97 void 98 inossize(struct ext2fs_dinode *dp, u_int64_t size) 99 { 100 if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG) { 101 dp->e2di_dacl = h2fs32(size >> 32); 102 if (size >= 0x80000000U) 103 if (!setlarge()) 104 return; 105 } else if (size >= 0x80000000U) { 106 pfatal("TRYING TO SET FILESIZE TO %llu ON MODE %x FILE\n", 107 (unsigned long long)size, fs2h16(dp->e2di_mode) & IFMT); 108 return; 109 } 110 dp->e2di_size = h2fs32(size); 111 } 112 113 int 114 ckinode(struct ext2fs_dinode *dp, struct inodesc *idesc) 115 { 116 u_int32_t *ap; 117 long ret, n, ndb; 118 struct ext2fs_dinode dino; 119 u_int64_t remsize, sizepb; 120 mode_t mode; 121 char pathbuf[MAXPATHLEN + 1]; 122 123 if (idesc->id_fix != IGNORE) 124 idesc->id_fix = DONTKNOW; 125 idesc->id_entryno = 0; 126 idesc->id_filesize = inosize(dp); 127 mode = fs2h16(dp->e2di_mode) & IFMT; 128 if (mode == IFBLK || mode == IFCHR || mode == IFIFO || 129 (mode == IFLNK && (inosize(dp) < EXT2_MAXSYMLINKLEN))) 130 return (KEEPON); 131 dino = *dp; 132 ndb = howmany(inosize(&dino), sblock.e2fs_bsize); 133 for (ap = &dino.e2di_blocks[0]; ap < &dino.e2di_blocks[NDADDR]; 134 ap++,ndb--) { 135 idesc->id_numfrags = 1; 136 if (*ap == 0) { 137 if (idesc->id_type == DATA && ndb > 0) { 138 /* An empty block in a directory XXX */ 139 getpathname(pathbuf, sizeof pathbuf, 140 idesc->id_number, idesc->id_number); 141 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 142 pathbuf); 143 if (reply("ADJUST LENGTH") == 1) { 144 dp = ginode(idesc->id_number); 145 inossize(dp, 146 (ap - &dino.e2di_blocks[0]) * 147 sblock.e2fs_bsize); 148 printf( 149 "YOU MUST RERUN FSCK AFTERWARDS\n"); 150 rerun = 1; 151 inodirty(); 152 } 153 } 154 continue; 155 } 156 idesc->id_blkno = fs2h32(*ap); 157 if (idesc->id_type == ADDR) 158 ret = (*idesc->id_func)(idesc); 159 else 160 ret = dirscan(idesc); 161 if (ret & STOP) 162 return (ret); 163 } 164 idesc->id_numfrags = 1; 165 remsize = inosize(&dino) - sblock.e2fs_bsize * NDADDR; 166 sizepb = sblock.e2fs_bsize; 167 for (ap = &dino.e2di_blocks[NDADDR], n = 1; n <= NIADDR; ap++, n++) { 168 if (*ap) { 169 idesc->id_blkno = fs2h32(*ap); 170 ret = iblock(idesc, n, remsize); 171 if (ret & STOP) 172 return (ret); 173 } else { 174 if (idesc->id_type == DATA && remsize > 0) { 175 /* An empty block in a directory XXX */ 176 getpathname(pathbuf, sizeof pathbuf, 177 idesc->id_number, idesc->id_number); 178 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 179 pathbuf); 180 if (reply("ADJUST LENGTH") == 1) { 181 dp = ginode(idesc->id_number); 182 inossize(dp, inosize(dp) - remsize); 183 remsize = 0; 184 printf( 185 "YOU MUST RERUN FSCK AFTERWARDS\n"); 186 rerun = 1; 187 inodirty(); 188 break; 189 } 190 } 191 } 192 sizepb *= NINDIR(&sblock); 193 remsize -= sizepb; 194 } 195 return (KEEPON); 196 } 197 198 static int 199 iblock(struct inodesc *idesc, long ilevel, u_int64_t isize) 200 { 201 daddr32_t *ap; 202 daddr32_t *aplim; 203 struct bufarea *bp; 204 int i, n, (*func)(struct inodesc *), nif; 205 u_int64_t sizepb; 206 char buf[BUFSIZ]; 207 char pathbuf[MAXPATHLEN + 1]; 208 struct ext2fs_dinode *dp; 209 210 if (idesc->id_type == ADDR) { 211 func = idesc->id_func; 212 if (((n = (*func)(idesc)) & KEEPON) == 0) 213 return (n); 214 } else 215 func = dirscan; 216 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) 217 return (SKIP); 218 bp = getdatablk(idesc->id_blkno, sblock.e2fs_bsize); 219 ilevel--; 220 for (sizepb = sblock.e2fs_bsize, i = 0; i < ilevel; i++) 221 sizepb *= NINDIR(&sblock); 222 if (isize > sizepb * NINDIR(&sblock)) 223 nif = NINDIR(&sblock); 224 else 225 nif = howmany(isize, sizepb); 226 if (idesc->id_func == pass1check && 227 nif < NINDIR(&sblock)) { 228 aplim = &bp->b_un.b_indir[NINDIR(&sblock)]; 229 for (ap = &bp->b_un.b_indir[nif]; ap < aplim; ap++) { 230 if (*ap == 0) 231 continue; 232 (void)snprintf(buf, sizeof(buf), 233 "PARTIALLY TRUNCATED INODE I=%u", idesc->id_number); 234 if (dofix(idesc, buf)) { 235 *ap = 0; 236 dirty(bp); 237 } 238 } 239 flush(fswritefd, bp); 240 } 241 aplim = &bp->b_un.b_indir[nif]; 242 for (ap = bp->b_un.b_indir; ap < aplim; ap++) { 243 if (*ap) { 244 idesc->id_blkno = fs2h32(*ap); 245 if (ilevel == 0) 246 n = (*func)(idesc); 247 else 248 n = iblock(idesc, ilevel, isize); 249 if (n & STOP) { 250 bp->b_flags &= ~B_INUSE; 251 return (n); 252 } 253 } else { 254 if (idesc->id_type == DATA && isize > 0) { 255 /* An empty block in a directory XXX */ 256 getpathname(pathbuf, sizeof pathbuf, 257 idesc->id_number, idesc->id_number); 258 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 259 pathbuf); 260 if (reply("ADJUST LENGTH") == 1) { 261 dp = ginode(idesc->id_number); 262 inossize(dp, inosize(dp) - isize); 263 isize = 0; 264 printf( 265 "YOU MUST RERUN FSCK AFTERWARDS\n"); 266 rerun = 1; 267 inodirty(); 268 bp->b_flags &= ~B_INUSE; 269 return(STOP); 270 } 271 } 272 } 273 isize -= sizepb; 274 } 275 bp->b_flags &= ~B_INUSE; 276 return (KEEPON); 277 } 278 279 /* 280 * Check that a block in a legal block number. 281 * Return 0 if in range, 1 if out of range. 282 */ 283 int 284 chkrange(daddr32_t blk, int cnt) 285 { 286 int c, overh; 287 288 if ((unsigned)(blk + cnt) > maxfsblock) 289 return (1); 290 c = dtog(&sblock, blk); 291 overh = cgoverhead(c); 292 if (blk < sblock.e2fs.e2fs_bpg * c + overh + 293 sblock.e2fs.e2fs_first_dblock) { 294 if ((blk + cnt) > sblock.e2fs.e2fs_bpg * c + overh + 295 sblock.e2fs.e2fs_first_dblock) { 296 if (debug) { 297 printf("blk %d < cgdmin %d;", 298 blk, sblock.e2fs.e2fs_bpg * c + overh + 299 sblock.e2fs.e2fs_first_dblock); 300 printf(" blk + cnt %d > cgsbase %d\n", 301 blk + cnt, sblock.e2fs.e2fs_bpg * c + 302 overh + sblock.e2fs.e2fs_first_dblock); 303 } 304 return (1); 305 } 306 } else { 307 if ((blk + cnt) > sblock.e2fs.e2fs_bpg * (c + 1) + overh + 308 sblock.e2fs.e2fs_first_dblock) { 309 if (debug) { 310 printf("blk %d >= cgdmin %d;", 311 blk, sblock.e2fs.e2fs_bpg * c + overh + 312 sblock.e2fs.e2fs_first_dblock); 313 printf(" blk + cnt %d > cgdmax %d\n", 314 blk+cnt, sblock.e2fs.e2fs_bpg * (c + 1) + 315 overh + sblock.e2fs.e2fs_first_dblock); 316 } 317 return (1); 318 } 319 } 320 return (0); 321 } 322 323 /* 324 * General purpose interface for reading inodes. 325 */ 326 struct ext2fs_dinode * 327 ginode(ino_t inumber) 328 { 329 daddr32_t iblk; 330 331 if ((inumber < EXT2_FIRSTINO && inumber != EXT2_ROOTINO) 332 || inumber > maxino) 333 errexit("bad inode number %d to ginode\n", inumber); 334 if (startinum == 0 || 335 inumber < startinum || inumber >= startinum + sblock.e2fs_ipb) { 336 iblk = fsck_ino_to_fsba(&sblock, inumber); 337 if (pbp != 0) 338 pbp->b_flags &= ~B_INUSE; 339 pbp = getdatablk(iblk, sblock.e2fs_bsize); 340 startinum = ((inumber -1) / sblock.e2fs_ipb) * sblock.e2fs_ipb + 1; 341 } 342 return (&pbp->b_un.b_dinode[(inumber-1) % sblock.e2fs_ipb]); 343 } 344 345 /* 346 * Special purpose version of ginode used to optimize first pass 347 * over all the inodes in numerical order. 348 */ 349 ino_t nextino, lastinum; 350 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 351 struct ext2fs_dinode *inodebuf; 352 353 struct ext2fs_dinode * 354 getnextinode(ino_t inumber) 355 { 356 long size; 357 daddr32_t dblk; 358 static struct ext2fs_dinode *dp; 359 360 if (inumber != nextino++ || inumber > maxino) 361 errexit("bad inode number %d to nextinode\n", inumber); 362 if (inumber >= lastinum) { 363 readcnt++; 364 dblk = fsbtodb(&sblock, fsck_ino_to_fsba(&sblock, lastinum)); 365 if (readcnt % readpercg == 0) { 366 size = partialsize; 367 lastinum += partialcnt; 368 } else { 369 size = inobufsize; 370 lastinum += fullcnt; 371 } 372 (void)bread(fsreadfd, (char *)inodebuf, dblk, size); 373 dp = inodebuf; 374 } 375 return (dp++); 376 } 377 378 void 379 resetinodebuf(void) 380 { 381 382 startinum = 0; 383 nextino = 1; 384 lastinum = 1; 385 readcnt = 0; 386 inobufsize = blkroundup(&sblock, INOBUFSIZE); 387 fullcnt = inobufsize / sizeof(struct ext2fs_dinode); 388 readpercg = sblock.e2fs.e2fs_ipg / fullcnt; 389 partialcnt = sblock.e2fs.e2fs_ipg % fullcnt; 390 partialsize = partialcnt * sizeof(struct ext2fs_dinode); 391 if (partialcnt != 0) { 392 readpercg++; 393 } else { 394 partialcnt = fullcnt; 395 partialsize = inobufsize; 396 } 397 if (inodebuf == NULL && 398 (inodebuf = (struct ext2fs_dinode *)malloc((unsigned)inobufsize)) == 399 NULL) 400 errexit("Cannot allocate space for inode buffer\n"); 401 while (nextino < EXT2_ROOTINO) 402 (void)getnextinode(nextino); 403 } 404 405 void 406 freeinodebuf(void) 407 { 408 409 if (inodebuf != NULL) 410 free((char *)inodebuf); 411 inodebuf = NULL; 412 } 413 414 /* 415 * Routines to maintain information about directory inodes. 416 * This is built during the first pass and used during the 417 * second and third passes. 418 * 419 * Enter inodes into the cache. 420 */ 421 void 422 cacheino(struct ext2fs_dinode *dp, ino_t inumber) 423 { 424 struct inoinfo *inp; 425 struct inoinfo **inpp; 426 unsigned int blks; 427 428 blks = howmany(inosize(dp), sblock.e2fs_bsize); 429 if (blks > NDADDR) 430 blks = NDADDR + NIADDR; 431 inp = (struct inoinfo *) 432 malloc(sizeof(*inp) + (blks - 1) * sizeof(daddr32_t)); 433 if (inp == NULL) 434 return; 435 inpp = &inphead[inumber % numdirs]; 436 inp->i_nexthash = *inpp; 437 *inpp = inp; 438 inp->i_child = inp->i_sibling = inp->i_parentp = 0; 439 if (inumber == EXT2_ROOTINO) 440 inp->i_parent = EXT2_ROOTINO; 441 else 442 inp->i_parent = (ino_t)0; 443 inp->i_dotdot = (ino_t)0; 444 inp->i_number = inumber; 445 inp->i_isize = inosize(dp); 446 inp->i_numblks = blks * sizeof(daddr32_t); 447 memcpy(&inp->i_blks[0], &dp->e2di_blocks[0], (size_t)inp->i_numblks); 448 if (inplast == listmax) { 449 listmax += 100; 450 inpsort = (struct inoinfo **)realloc((char *)inpsort, 451 (unsigned)listmax * sizeof(struct inoinfo *)); 452 if (inpsort == NULL) 453 errexit("cannot increase directory list\n"); 454 } 455 inpsort[inplast++] = inp; 456 } 457 458 /* 459 * Look up an inode cache structure. 460 */ 461 struct inoinfo * 462 getinoinfo(ino_t inumber) 463 { 464 struct inoinfo *inp; 465 466 for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { 467 if (inp->i_number != inumber) 468 continue; 469 return (inp); 470 } 471 errexit("cannot find inode %d\n", inumber); 472 return (NULL); 473 } 474 475 /* 476 * Clean up all the inode cache structure. 477 */ 478 void 479 inocleanup(void) 480 { 481 struct inoinfo **inpp; 482 483 if (inphead == NULL) 484 return; 485 for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) 486 free((char *)(*inpp)); 487 free((char *)inphead); 488 free((char *)inpsort); 489 inphead = inpsort = NULL; 490 } 491 492 void 493 inodirty(void) 494 { 495 496 dirty(pbp); 497 } 498 499 void 500 clri(struct inodesc *idesc, char *type, int flag) 501 { 502 struct ext2fs_dinode *dp; 503 504 dp = ginode(idesc->id_number); 505 if (flag == 1) { 506 pwarn("%s %s", type, 507 (dp->e2di_mode & IFMT) == IFDIR ? "DIR" : "FILE"); 508 pinode(idesc->id_number); 509 } 510 if (preen || reply("CLEAR") == 1) { 511 if (preen) 512 printf(" (CLEARED)\n"); 513 n_files--; 514 (void)ckinode(dp, idesc); 515 clearinode(dp); 516 statemap[idesc->id_number] = USTATE; 517 inodirty(); 518 } 519 } 520 521 int 522 findname(struct inodesc *idesc) 523 { 524 struct ext2fs_direct *dirp = idesc->id_dirp; 525 u_int16_t namlen = dirp->e2d_namlen; 526 527 if (fs2h32(dirp->e2d_ino) != idesc->id_parent) 528 return (KEEPON); 529 memcpy(idesc->id_name, dirp->e2d_name, (size_t)namlen); 530 idesc->id_name[namlen] = '\0'; 531 return (STOP|FOUND); 532 } 533 534 int 535 findino(struct inodesc *idesc) 536 { 537 struct ext2fs_direct *dirp = idesc->id_dirp; 538 u_int32_t ino = fs2h32(dirp->e2d_ino); 539 540 if (ino == 0) 541 return (KEEPON); 542 if (strcmp(dirp->e2d_name, idesc->id_name) == 0 && 543 (ino == EXT2_ROOTINO || ino >= EXT2_FIRSTINO) 544 && ino <= maxino) { 545 idesc->id_parent = ino; 546 return (STOP|FOUND); 547 } 548 return (KEEPON); 549 } 550 551 void 552 pinode(ino_t ino) 553 { 554 struct ext2fs_dinode *dp; 555 char *p; 556 struct passwd *pw; 557 time_t t; 558 u_int32_t uid; 559 560 printf(" I=%u ", ino); 561 if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) 562 return; 563 dp = ginode(ino); 564 printf(" OWNER="); 565 uid = fs2h16(dp->e2di_uid_low) | (fs2h16(dp->e2di_uid_high) << 16); 566 #ifndef SMALL 567 if ((pw = getpwuid((int)uid)) != 0) 568 printf("%s ", pw->pw_name); 569 else 570 #endif 571 printf("%u ", (unsigned)uid); 572 printf("MODE=%o\n", fs2h16(dp->e2di_mode)); 573 if (preen) 574 printf("%s: ", cdevname()); 575 printf("SIZE=%llu ", (long long)inosize(dp)); 576 t = fs2h32(dp->e2di_mtime); 577 p = ctime(&t); 578 printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); 579 } 580 581 void 582 blkerror(ino_t ino, char *type, daddr32_t blk) 583 { 584 585 pfatal("%d %s I=%u", blk, type, ino); 586 printf("\n"); 587 switch (statemap[ino]) { 588 589 case FSTATE: 590 statemap[ino] = FCLEAR; 591 return; 592 593 case DSTATE: 594 statemap[ino] = DCLEAR; 595 return; 596 597 case FCLEAR: 598 case DCLEAR: 599 return; 600 601 default: 602 errexit("BAD STATE %d TO BLKERR\n", statemap[ino]); 603 /* NOTREACHED */ 604 } 605 } 606 607 /* 608 * allocate an unused inode 609 */ 610 ino_t 611 allocino(ino_t request, int type) 612 { 613 ino_t ino; 614 struct ext2fs_dinode *dp; 615 time_t t; 616 617 if (request == 0) 618 request = EXT2_ROOTINO; 619 else if (statemap[request] != USTATE) 620 return (0); 621 for (ino = request; ino < maxino; ino++) { 622 if ((ino > EXT2_ROOTINO) && (ino < EXT2_FIRSTINO)) 623 continue; 624 if (statemap[ino] == USTATE) 625 break; 626 } 627 if (ino == maxino) 628 return (0); 629 switch (type & IFMT) { 630 case IFDIR: 631 statemap[ino] = DSTATE; 632 break; 633 case IFREG: 634 case IFLNK: 635 statemap[ino] = FSTATE; 636 break; 637 default: 638 return (0); 639 } 640 dp = ginode(ino); 641 dp->e2di_blocks[0] = h2fs32(allocblk()); 642 if (dp->e2di_blocks[0] == 0) { 643 statemap[ino] = USTATE; 644 return (0); 645 } 646 dp->e2di_mode = h2fs16(type); 647 (void)time(&t); 648 dp->e2di_atime = h2fs32(t); 649 dp->e2di_mtime = dp->e2di_ctime = dp->e2di_atime; 650 dp->e2di_dtime = 0; 651 inossize(dp, sblock.e2fs_bsize); 652 dp->e2di_nblock = h2fs32(btodb(sblock.e2fs_bsize)); 653 n_files++; 654 inodirty(); 655 typemap[ino] = E2IFTODT(type); 656 return (ino); 657 } 658 659 /* 660 * deallocate an inode 661 */ 662 void 663 freeino(ino_t ino) 664 { 665 struct inodesc idesc; 666 struct ext2fs_dinode *dp; 667 668 memset(&idesc, 0, sizeof(struct inodesc)); 669 idesc.id_type = ADDR; 670 idesc.id_func = pass4check; 671 idesc.id_number = ino; 672 dp = ginode(ino); 673 (void)ckinode(dp, &idesc); 674 clearinode(dp); 675 inodirty(); 676 statemap[ino] = USTATE; 677 n_files--; 678 } 679