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