1 /* 2 * Copyright (c) 1980, 1986, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. 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 #ifndef lint 35 #if 0 36 static const char sccsid[] = "@(#)inode.c 8.8 (Berkeley) 4/28/95"; 37 #endif 38 static const char rcsid[] = 39 "$FreeBSD$"; 40 #endif /* not lint */ 41 42 #include <sys/param.h> 43 #include <sys/stdint.h> 44 #include <sys/time.h> 45 #include <sys/sysctl.h> 46 47 #include <ufs/ufs/dinode.h> 48 #include <ufs/ufs/dir.h> 49 #include <ufs/ffs/fs.h> 50 51 #include <err.h> 52 #include <pwd.h> 53 #include <string.h> 54 55 #include "fsck.h" 56 57 static ino_t startinum; 58 59 static int iblock(struct inodesc *, long ilevel, off_t isize); 60 61 int 62 ckinode(union dinode *dp, struct inodesc *idesc) 63 { 64 off_t remsize, sizepb; 65 int i, offset, ret; 66 union dinode dino; 67 ufs2_daddr_t ndb; 68 mode_t mode; 69 char pathbuf[MAXPATHLEN + 1]; 70 71 if (idesc->id_fix != IGNORE) 72 idesc->id_fix = DONTKNOW; 73 idesc->id_lbn = -1; 74 idesc->id_entryno = 0; 75 idesc->id_filesize = DIP(dp, di_size); 76 mode = DIP(dp, di_mode) & IFMT; 77 if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && 78 DIP(dp, di_size) < (unsigned)sblock.fs_maxsymlinklen)) 79 return (KEEPON); 80 if (sblock.fs_magic == FS_UFS1_MAGIC) 81 dino.dp1 = dp->dp1; 82 else 83 dino.dp2 = dp->dp2; 84 ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize); 85 for (i = 0; i < NDADDR; i++) { 86 idesc->id_lbn++; 87 if (--ndb == 0 && 88 (offset = blkoff(&sblock, DIP(&dino, di_size))) != 0) 89 idesc->id_numfrags = 90 numfrags(&sblock, fragroundup(&sblock, offset)); 91 else 92 idesc->id_numfrags = sblock.fs_frag; 93 if (DIP(&dino, di_db[i]) == 0) { 94 if (idesc->id_type == DATA && ndb >= 0) { 95 /* An empty block in a directory XXX */ 96 getpathname(pathbuf, idesc->id_number, 97 idesc->id_number); 98 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 99 pathbuf); 100 if (reply("ADJUST LENGTH") == 1) { 101 dp = ginode(idesc->id_number); 102 DIP(dp, di_size) = i * sblock.fs_bsize; 103 printf( 104 "YOU MUST RERUN FSCK AFTERWARDS\n"); 105 rerun = 1; 106 inodirty(); 107 108 } 109 } 110 continue; 111 } 112 idesc->id_blkno = DIP(&dino, di_db[i]); 113 if (idesc->id_type != DATA) 114 ret = (*idesc->id_func)(idesc); 115 else 116 ret = dirscan(idesc); 117 if (ret & STOP) 118 return (ret); 119 } 120 idesc->id_numfrags = sblock.fs_frag; 121 remsize = DIP(&dino, di_size) - sblock.fs_bsize * NDADDR; 122 sizepb = sblock.fs_bsize; 123 for (i = 0; i < NIADDR; i++) { 124 sizepb *= NINDIR(&sblock); 125 if (DIP(&dino, di_ib[i])) { 126 idesc->id_blkno = DIP(&dino, di_ib[i]); 127 ret = iblock(idesc, i + 1, remsize); 128 if (ret & STOP) 129 return (ret); 130 } else { 131 idesc->id_lbn += sizepb / sblock.fs_bsize; 132 if (idesc->id_type == DATA && remsize > 0) { 133 /* An empty block in a directory XXX */ 134 getpathname(pathbuf, idesc->id_number, 135 idesc->id_number); 136 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 137 pathbuf); 138 if (reply("ADJUST LENGTH") == 1) { 139 dp = ginode(idesc->id_number); 140 DIP(dp, di_size) -= remsize; 141 remsize = 0; 142 printf( 143 "YOU MUST RERUN FSCK AFTERWARDS\n"); 144 rerun = 1; 145 inodirty(); 146 break; 147 } 148 } 149 } 150 remsize -= sizepb; 151 } 152 return (KEEPON); 153 } 154 155 static int 156 iblock(struct inodesc *idesc, long ilevel, off_t isize) 157 { 158 struct bufarea *bp; 159 int i, n, (*func)(struct inodesc *), nif; 160 off_t sizepb; 161 char buf[BUFSIZ]; 162 char pathbuf[MAXPATHLEN + 1]; 163 union dinode *dp; 164 165 if (idesc->id_type != DATA) { 166 func = idesc->id_func; 167 if (((n = (*func)(idesc)) & KEEPON) == 0) 168 return (n); 169 } else 170 func = dirscan; 171 if (chkrange(idesc->id_blkno, idesc->id_numfrags)) 172 return (SKIP); 173 bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); 174 ilevel--; 175 for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) 176 sizepb *= NINDIR(&sblock); 177 if (howmany(isize, sizepb) > NINDIR(&sblock)) 178 nif = NINDIR(&sblock); 179 else 180 nif = howmany(isize, sizepb); 181 if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { 182 for (i = nif; i < NINDIR(&sblock); i++) { 183 if (IBLK(bp, i) == 0) 184 continue; 185 (void)sprintf(buf, "PARTIALLY TRUNCATED INODE I=%lu", 186 (u_long)idesc->id_number); 187 if (preen) { 188 pfatal("%s", buf); 189 } else if (dofix(idesc, buf)) { 190 IBLK(bp, i) = 0; 191 dirty(bp); 192 } 193 } 194 flush(fswritefd, bp); 195 } 196 for (i = 0; i < nif; i++) { 197 if (ilevel == 0) 198 idesc->id_lbn++; 199 if (IBLK(bp, i)) { 200 idesc->id_blkno = IBLK(bp, i); 201 if (ilevel == 0) 202 n = (*func)(idesc); 203 else 204 n = iblock(idesc, ilevel, isize); 205 if (n & STOP) { 206 bp->b_flags &= ~B_INUSE; 207 return (n); 208 } 209 } else { 210 if (idesc->id_type == DATA && isize > 0) { 211 /* An empty block in a directory XXX */ 212 getpathname(pathbuf, idesc->id_number, 213 idesc->id_number); 214 pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", 215 pathbuf); 216 if (reply("ADJUST LENGTH") == 1) { 217 dp = ginode(idesc->id_number); 218 DIP(dp, di_size) -= isize; 219 isize = 0; 220 printf( 221 "YOU MUST RERUN FSCK AFTERWARDS\n"); 222 rerun = 1; 223 inodirty(); 224 bp->b_flags &= ~B_INUSE; 225 return(STOP); 226 } 227 } 228 } 229 isize -= sizepb; 230 } 231 bp->b_flags &= ~B_INUSE; 232 return (KEEPON); 233 } 234 235 /* 236 * Check that a block in a legal block number. 237 * Return 0 if in range, 1 if out of range. 238 */ 239 int 240 chkrange(ufs2_daddr_t blk, int cnt) 241 { 242 int c; 243 244 if (cnt <= 0 || blk <= 0 || blk > maxfsblock || 245 cnt - 1 > maxfsblock - blk) 246 return (1); 247 if (cnt > sblock.fs_frag || 248 fragnum(&sblock, blk) + cnt > sblock.fs_frag) { 249 if (debug) 250 printf("bad size: blk %ld, offset %i, size %d\n", 251 (long)blk, (int)fragnum(&sblock, blk), cnt); 252 return (1); 253 } 254 c = dtog(&sblock, blk); 255 if (blk < cgdmin(&sblock, c)) { 256 if ((blk + cnt) > cgsblock(&sblock, c)) { 257 if (debug) { 258 printf("blk %ld < cgdmin %ld;", 259 (long)blk, (long)cgdmin(&sblock, c)); 260 printf(" blk + cnt %ld > cgsbase %ld\n", 261 (long)(blk + cnt), 262 (long)cgsblock(&sblock, c)); 263 } 264 return (1); 265 } 266 } else { 267 if ((blk + cnt) > cgbase(&sblock, c+1)) { 268 if (debug) { 269 printf("blk %ld >= cgdmin %ld;", 270 (long)blk, (long)cgdmin(&sblock, c)); 271 printf(" blk + cnt %ld > sblock.fs_fpg %ld\n", 272 (long)(blk + cnt), (long)sblock.fs_fpg); 273 } 274 return (1); 275 } 276 } 277 return (0); 278 } 279 280 /* 281 * General purpose interface for reading inodes. 282 */ 283 union dinode * 284 ginode(ino_t inumber) 285 { 286 ufs2_daddr_t iblk; 287 288 if (inumber < ROOTINO || inumber > maxino) 289 errx(EEXIT, "bad inode number %d to ginode", inumber); 290 if (startinum == 0 || 291 inumber < startinum || inumber >= startinum + INOPB(&sblock)) { 292 iblk = ino_to_fsba(&sblock, inumber); 293 if (pbp != 0) 294 pbp->b_flags &= ~B_INUSE; 295 pbp = getdatablk(iblk, sblock.fs_bsize); 296 startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); 297 } 298 if (sblock.fs_magic == FS_UFS1_MAGIC) 299 return ((union dinode *) 300 &pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]); 301 return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]); 302 } 303 304 /* 305 * Special purpose version of ginode used to optimize first pass 306 * over all the inodes in numerical order. 307 */ 308 static ino_t nextino, lastinum, lastvalidinum; 309 static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 310 static caddr_t inodebuf; 311 312 union dinode * 313 getnextinode(ino_t inumber) 314 { 315 long size; 316 ufs2_daddr_t dblk; 317 union dinode *dp; 318 static caddr_t nextinop; 319 320 if (inumber != nextino++ || inumber > lastvalidinum) 321 errx(EEXIT, "bad inode number %d to nextinode", inumber); 322 if (inumber >= lastinum) { 323 readcnt++; 324 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 325 if (readcnt % readpercg == 0) { 326 size = partialsize; 327 lastinum += partialcnt; 328 } else { 329 size = inobufsize; 330 lastinum += fullcnt; 331 } 332 /* 333 * If bread returns an error, it will already have zeroed 334 * out the buffer, so we do not need to do so here. 335 */ 336 (void)bread(fsreadfd, inodebuf, dblk, size); 337 nextinop = inodebuf; 338 } 339 dp = (union dinode *)nextinop; 340 if (sblock.fs_magic == FS_UFS1_MAGIC) 341 nextinop += sizeof(struct ufs1_dinode); 342 else 343 nextinop += sizeof(struct ufs2_dinode); 344 return (dp); 345 } 346 347 void 348 setinodebuf(ino_t inum) 349 { 350 351 if (inum % sblock.fs_ipg != 0) 352 errx(EEXIT, "bad inode number %d to setinodebuf", inum); 353 lastvalidinum = inum + sblock.fs_ipg - 1; 354 startinum = 0; 355 nextino = inum; 356 lastinum = inum; 357 readcnt = 0; 358 if (inodebuf != NULL) 359 return; 360 inobufsize = blkroundup(&sblock, INOBUFSIZE); 361 fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 362 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 363 readpercg = sblock.fs_ipg / fullcnt; 364 partialcnt = sblock.fs_ipg % fullcnt; 365 partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 366 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 367 if (partialcnt != 0) { 368 readpercg++; 369 } else { 370 partialcnt = fullcnt; 371 partialsize = inobufsize; 372 } 373 if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) 374 errx(EEXIT, "cannot allocate space for inode buffer"); 375 } 376 377 void 378 freeinodebuf(void) 379 { 380 381 if (inodebuf != NULL) 382 free((char *)inodebuf); 383 inodebuf = NULL; 384 } 385 386 /* 387 * Routines to maintain information about directory inodes. 388 * This is built during the first pass and used during the 389 * second and third passes. 390 * 391 * Enter inodes into the cache. 392 */ 393 void 394 cacheino(union dinode *dp, ino_t inumber) 395 { 396 struct inoinfo *inp, **inpp; 397 int i, blks; 398 399 if (howmany(DIP(dp, di_size), sblock.fs_bsize) > NDADDR) 400 blks = NDADDR + NIADDR; 401 else 402 blks = howmany(DIP(dp, di_size), sblock.fs_bsize); 403 inp = (struct inoinfo *) 404 malloc(sizeof(*inp) + (blks - 1) * sizeof(ufs2_daddr_t)); 405 if (inp == NULL) 406 errx(EEXIT, "cannot increase directory list"); 407 inpp = &inphead[inumber % dirhash]; 408 inp->i_nexthash = *inpp; 409 *inpp = inp; 410 inp->i_parent = inumber == ROOTINO ? ROOTINO : (ino_t)0; 411 inp->i_dotdot = (ino_t)0; 412 inp->i_number = inumber; 413 inp->i_isize = DIP(dp, di_size); 414 inp->i_numblks = blks; 415 for (i = 0; i < (blks < NDADDR ? blks : NDADDR); i++) 416 inp->i_blks[i] = DIP(dp, di_db[i]); 417 if (blks > NDADDR) 418 for (i = 0; i < NIADDR; i++) 419 inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]); 420 if (inplast == listmax) { 421 listmax += 100; 422 inpsort = (struct inoinfo **)realloc((char *)inpsort, 423 (unsigned)listmax * sizeof(struct inoinfo *)); 424 if (inpsort == NULL) 425 errx(EEXIT, "cannot increase directory list"); 426 } 427 inpsort[inplast++] = inp; 428 } 429 430 /* 431 * Look up an inode cache structure. 432 */ 433 struct inoinfo * 434 getinoinfo(ino_t inumber) 435 { 436 struct inoinfo *inp; 437 438 for (inp = inphead[inumber % dirhash]; inp; inp = inp->i_nexthash) { 439 if (inp->i_number != inumber) 440 continue; 441 return (inp); 442 } 443 errx(EEXIT, "cannot find inode %d", inumber); 444 return ((struct inoinfo *)0); 445 } 446 447 /* 448 * Clean up all the inode cache structure. 449 */ 450 void 451 inocleanup(void) 452 { 453 struct inoinfo **inpp; 454 455 if (inphead == NULL) 456 return; 457 for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) 458 free((char *)(*inpp)); 459 free((char *)inphead); 460 free((char *)inpsort); 461 inphead = inpsort = NULL; 462 } 463 464 void 465 inodirty(void) 466 { 467 468 dirty(pbp); 469 } 470 471 void 472 clri(struct inodesc *idesc, const char *type, int flag) 473 { 474 union dinode *dp; 475 476 dp = ginode(idesc->id_number); 477 if (flag == 1) { 478 pwarn("%s %s", type, 479 (DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"); 480 pinode(idesc->id_number); 481 } 482 if (preen || reply("CLEAR") == 1) { 483 if (preen) 484 printf(" (CLEARED)\n"); 485 n_files--; 486 if (bkgrdflag == 0) { 487 (void)ckinode(dp, idesc); 488 inoinfo(idesc->id_number)->ino_state = USTATE; 489 clearinode(dp); 490 inodirty(); 491 } else { 492 cmd.value = idesc->id_number; 493 cmd.size = -DIP(dp, di_nlink); 494 if (debug) 495 printf("adjrefcnt ino %ld amt %lld\n", 496 (long)cmd.value, (long long)cmd.size); 497 if (sysctl(adjrefcnt, MIBSIZE, 0, 0, 498 &cmd, sizeof cmd) == -1) 499 rwerror("ADJUST INODE", cmd.value); 500 } 501 } 502 } 503 504 int 505 findname(struct inodesc *idesc) 506 { 507 struct direct *dirp = idesc->id_dirp; 508 509 if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) { 510 idesc->id_entryno++; 511 return (KEEPON); 512 } 513 memmove(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); 514 return (STOP|FOUND); 515 } 516 517 int 518 findino(struct inodesc *idesc) 519 { 520 struct direct *dirp = idesc->id_dirp; 521 522 if (dirp->d_ino == 0) 523 return (KEEPON); 524 if (strcmp(dirp->d_name, idesc->id_name) == 0 && 525 dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { 526 idesc->id_parent = dirp->d_ino; 527 return (STOP|FOUND); 528 } 529 return (KEEPON); 530 } 531 532 int 533 clearentry(struct inodesc *idesc) 534 { 535 struct direct *dirp = idesc->id_dirp; 536 537 if (dirp->d_ino != idesc->id_parent || idesc->id_entryno < 2) { 538 idesc->id_entryno++; 539 return (KEEPON); 540 } 541 dirp->d_ino = 0; 542 return (STOP|FOUND|ALTERED); 543 } 544 545 void 546 pinode(ino_t ino) 547 { 548 union dinode *dp; 549 char *p; 550 struct passwd *pw; 551 time_t t; 552 553 printf(" I=%lu ", (u_long)ino); 554 if (ino < ROOTINO || ino > maxino) 555 return; 556 dp = ginode(ino); 557 printf(" OWNER="); 558 if ((pw = getpwuid((int)DIP(dp, di_uid))) != 0) 559 printf("%s ", pw->pw_name); 560 else 561 printf("%u ", (unsigned)DIP(dp, di_uid)); 562 printf("MODE=%o\n", DIP(dp, di_mode)); 563 if (preen) 564 printf("%s: ", cdevname); 565 printf("SIZE=%ju ", (uintmax_t)DIP(dp, di_size)); 566 t = DIP(dp, di_mtime); 567 p = ctime(&t); 568 printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); 569 } 570 571 void 572 blkerror(ino_t ino, const char *type, ufs2_daddr_t blk) 573 { 574 575 pfatal("%jd %s I=%ju", (intmax_t)blk, type, (uintmax_t)ino); 576 printf("\n"); 577 switch (inoinfo(ino)->ino_state) { 578 579 case FSTATE: 580 inoinfo(ino)->ino_state = FCLEAR; 581 return; 582 583 case DSTATE: 584 inoinfo(ino)->ino_state = DCLEAR; 585 return; 586 587 case FCLEAR: 588 case DCLEAR: 589 return; 590 591 default: 592 errx(EEXIT, "BAD STATE %d TO BLKERR", inoinfo(ino)->ino_state); 593 /* NOTREACHED */ 594 } 595 } 596 597 /* 598 * allocate an unused inode 599 */ 600 ino_t 601 allocino(ino_t request, int type) 602 { 603 ino_t ino; 604 union dinode *dp; 605 struct cg *cgp = &cgrp; 606 int cg; 607 608 if (request == 0) 609 request = ROOTINO; 610 else if (inoinfo(request)->ino_state != USTATE) 611 return (0); 612 for (ino = request; ino < maxino; ino++) 613 if (inoinfo(ino)->ino_state == USTATE) 614 break; 615 if (ino == maxino) 616 return (0); 617 cg = ino_to_cg(&sblock, ino); 618 getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); 619 if (!cg_chkmagic(cgp)) 620 pfatal("CG %d: BAD MAGIC NUMBER\n", cg); 621 setbit(cg_inosused(cgp), ino % sblock.fs_ipg); 622 cgp->cg_cs.cs_nifree--; 623 switch (type & IFMT) { 624 case IFDIR: 625 inoinfo(ino)->ino_state = DSTATE; 626 cgp->cg_cs.cs_ndir++; 627 break; 628 case IFREG: 629 case IFLNK: 630 inoinfo(ino)->ino_state = FSTATE; 631 break; 632 default: 633 return (0); 634 } 635 cgdirty(); 636 dp = ginode(ino); 637 DIP(dp, di_db[0]) = allocblk((long)1); 638 if (DIP(dp, di_db[0]) == 0) { 639 inoinfo(ino)->ino_state = USTATE; 640 return (0); 641 } 642 DIP(dp, di_mode) = type; 643 DIP(dp, di_flags) = 0; 644 DIP(dp, di_atime) = time(NULL); 645 DIP(dp, di_mtime) = DIP(dp, di_ctime) = DIP(dp, di_atime); 646 DIP(dp, di_mtimensec) = 0; 647 DIP(dp, di_ctimensec) = 0; 648 DIP(dp, di_atimensec) = 0; 649 DIP(dp, di_size) = sblock.fs_fsize; 650 DIP(dp, di_blocks) = btodb(sblock.fs_fsize); 651 n_files++; 652 inodirty(); 653 inoinfo(ino)->ino_type = IFTODT(type); 654 return (ino); 655 } 656 657 /* 658 * deallocate an inode 659 */ 660 void 661 freeino(ino_t ino) 662 { 663 struct inodesc idesc; 664 union dinode *dp; 665 666 memset(&idesc, 0, sizeof(struct inodesc)); 667 idesc.id_type = ADDR; 668 idesc.id_func = pass4check; 669 idesc.id_number = ino; 670 dp = ginode(ino); 671 (void)ckinode(dp, &idesc); 672 clearinode(dp); 673 inodirty(); 674 inoinfo(ino)->ino_state = USTATE; 675 n_files--; 676 } 677