1 /* $NetBSD: pass2.c,v 1.53 2023/07/04 20:40:53 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1986, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)pass2.c 8.9 (Berkeley) 4/28/95"; 36 #else 37 __RCSID("$NetBSD: pass2.c,v 1.53 2023/07/04 20:40:53 riastradh Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include <sys/param.h> 42 #include <sys/time.h> 43 44 #include <ufs/ufs/dinode.h> 45 #include <ufs/ufs/dir.h> 46 #include <ufs/ffs/fs.h> 47 48 #include <err.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 53 #include "fsck.h" 54 #include "fsutil.h" 55 #include "extern.h" 56 #include "exitvalues.h" 57 58 #define MINDIRSIZE (sizeof (struct dirtemplate)) 59 60 static int blksort(const void *, const void *); 61 static int pass2check(struct inodesc *); 62 63 void 64 pass2(void) 65 { 66 union dinode *dp; 67 struct inoinfo **inpp, *inp, *pinp; 68 struct inoinfo **inpend; 69 struct inostat *rinfo, *info; 70 struct inodesc curino; 71 union dinode dino; 72 int i, maxblk; 73 #ifndef NO_FFS_EI 74 unsigned ii; 75 #endif 76 char pathbuf[MAXPATHLEN + 1]; 77 78 rinfo = inoinfo(UFS_ROOTINO); 79 switch (rinfo->ino_state) { 80 81 case USTATE: 82 pfatal("ROOT INODE UNALLOCATED"); 83 if (reply("ALLOCATE") == 0) { 84 markclean = 0; 85 ckfini(1); 86 exit(FSCK_EXIT_CHECK_FAILED); 87 } 88 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) 89 errexit("CANNOT ALLOCATE ROOT INODE"); 90 break; 91 92 case DCLEAR: 93 pfatal("DUPS/BAD IN ROOT INODE"); 94 if (reply("REALLOCATE")) { 95 freeino(UFS_ROOTINO); 96 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) 97 errexit("CANNOT ALLOCATE ROOT INODE"); 98 break; 99 } 100 markclean = 0; 101 if (reply("CONTINUE") == 0) { 102 ckfini(1); 103 exit(FSCK_EXIT_CHECK_FAILED); 104 } 105 break; 106 107 case FSTATE: 108 case FCLEAR: 109 pfatal("ROOT INODE NOT DIRECTORY"); 110 if (reply("REALLOCATE")) { 111 freeino(UFS_ROOTINO); 112 if (allocdir(UFS_ROOTINO, UFS_ROOTINO, 0755) != UFS_ROOTINO) 113 errexit("CANNOT ALLOCATE ROOT INODE"); 114 break; 115 } 116 if (reply("FIX") == 0) { 117 markclean = 0; 118 ckfini(1); 119 exit(FSCK_EXIT_CHECK_FAILED); 120 } 121 dp = ginode(UFS_ROOTINO); 122 DIP_SET(dp, mode, 123 iswap16((iswap16(DIP(dp, mode)) & ~IFMT) | IFDIR)); 124 inodirty(); 125 break; 126 127 case DSTATE: 128 break; 129 130 default: 131 errexit("BAD STATE %d FOR ROOT INODE", rinfo->ino_state); 132 } 133 if (newinofmt) { 134 info = inoinfo(UFS_WINO); 135 info->ino_state = FSTATE; 136 info->ino_type = DT_WHT; 137 } 138 /* 139 * Sort the directory list into disk block order. 140 */ 141 qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort); 142 /* 143 * Check the integrity of each directory. 144 */ 145 memset(&curino, 0, sizeof(struct inodesc)); 146 curino.id_type = DATA; 147 curino.id_func = pass2check; 148 inpend = &inpsort[inplast]; 149 for (inpp = inpsort; inpp < inpend; inpp++) { 150 if (got_siginfo) { 151 fprintf(stderr, 152 "%s: phase 2: dir %ld of %d (%d%%)\n", cdevname(), 153 (long)(inpp - inpsort), (int)inplast, 154 (int)((inpp - inpsort) * 100 / inplast)); 155 got_siginfo = 0; 156 } 157 #ifdef PROGRESS 158 progress_bar(cdevname(), preen ? NULL : "phase 2", 159 (inpp - inpsort), inplast); 160 #endif /* PROGRESS */ 161 inp = *inpp; 162 if (inp->i_isize == 0) 163 continue; 164 if (inp->i_isize < MINDIRSIZE) { 165 direrror(inp->i_number, "DIRECTORY TOO SHORT"); 166 inp->i_isize = roundup(MINDIRSIZE, dirblksiz); 167 if (reply("FIX") == 1) { 168 dp = ginode(inp->i_number); 169 DIP_SET(dp, size, iswap64(inp->i_isize)); 170 inodirty(); 171 } else 172 markclean = 0; 173 } else if ((inp->i_isize & (dirblksiz - 1)) != 0) { 174 getpathname(pathbuf, sizeof(pathbuf), inp->i_number, 175 inp->i_number); 176 if (usedsoftdep) 177 pfatal("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 178 "DIRECTORY", pathbuf, 179 (long long)inp->i_isize, dirblksiz); 180 else 181 pwarn("%s %s: LENGTH %lld NOT MULTIPLE OF %d", 182 "DIRECTORY", pathbuf, 183 (long long)inp->i_isize, dirblksiz); 184 if (preen) 185 printf(" (ADJUSTED)\n"); 186 inp->i_isize = roundup(inp->i_isize, dirblksiz); 187 if (preen || reply("ADJUST") == 1) { 188 dp = ginode(inp->i_number); 189 DIP_SET(dp, size, iswap64(inp->i_isize)); 190 inodirty(); 191 } else 192 markclean = 0; 193 } 194 memset(&dino, 0, sizeof dino); 195 dp = &dino; 196 if (!is_ufs2) { 197 dp->dp1.di_mode = iswap16(IFDIR); 198 dp->dp1.di_size = iswap64(inp->i_isize); 199 maxblk = inp->i_numblks < UFS_NDADDR ? inp->i_numblks : 200 UFS_NDADDR; 201 for (i = 0; i < maxblk; i++) 202 dp->dp1.di_db[i] = inp->i_blks[i]; 203 if (inp->i_numblks > UFS_NDADDR) { 204 for (i = 0; i < UFS_NIADDR; i++) 205 dp->dp1.di_ib[i] = 206 inp->i_blks[UFS_NDADDR + i]; 207 } 208 } else { 209 dp->dp2.di_mode = iswap16(IFDIR); 210 dp->dp2.di_size = iswap64(inp->i_isize); 211 maxblk = inp->i_numblks < UFS_NDADDR ? inp->i_numblks : 212 UFS_NDADDR; 213 for (i = 0; i < maxblk; i++) 214 dp->dp2.di_db[i] = inp->i_blks[i]; 215 if (inp->i_numblks > UFS_NDADDR) { 216 for (i = 0; i < UFS_NIADDR; i++) 217 dp->dp2.di_ib[i] = 218 inp->i_blks[UFS_NDADDR + i]; 219 } 220 } 221 curino.id_number = inp->i_number; 222 curino.id_parent = inp->i_parent; 223 curino.id_uid = iswap32(DIP(dp, uid)); 224 curino.id_gid = iswap32(DIP(dp, gid)); 225 (void)ckinode(&dino, &curino); 226 } 227 228 #ifndef NO_FFS_EI 229 /* 230 * Byte swapping in directory entries, if needed, has been done. 231 * Now rescan dirs for pass2check() 232 */ 233 if (do_dirswap) { 234 do_dirswap = 0; 235 for (inpp = inpsort; inpp < inpend; inpp++) { 236 inp = *inpp; 237 if (inp->i_isize == 0) 238 continue; 239 memset(&dino, 0, sizeof dino); 240 if (!is_ufs2) { 241 dino.dp1.di_mode = iswap16(IFDIR); 242 dino.dp1.di_size = iswap64(inp->i_isize); 243 for (ii = 0; ii < inp->i_numblks; ii++) 244 dino.dp1.di_db[ii] = inp->i_blks[ii]; 245 } else { 246 dino.dp2.di_mode = iswap16(IFDIR); 247 dino.dp2.di_size = iswap64(inp->i_isize); 248 for (ii = 0; ii < inp->i_numblks; ii++) 249 dino.dp2.di_db[ii] = inp->i_blks[ii]; 250 } 251 curino.id_number = inp->i_number; 252 curino.id_parent = inp->i_parent; 253 curino.id_uid = iswap32(DIP(&dino, uid)); 254 curino.id_gid = iswap32(DIP(&dino, gid)); 255 (void)ckinode(&dino, &curino); 256 } 257 } 258 #endif /* !NO_FFS_EI */ 259 260 /* 261 * Now that the parents of all directories have been found, 262 * make another pass to verify the value of `..' 263 */ 264 for (inpp = inpsort; inpp < inpend; inpp++) { 265 inp = *inpp; 266 if (inp->i_parent == 0 || inp->i_isize == 0) 267 continue; 268 if (inp->i_dotdot == inp->i_parent || 269 inp->i_dotdot == (ino_t)-1) 270 continue; 271 info = inoinfo(inp->i_parent); 272 if (inp->i_dotdot == 0) { 273 inp->i_dotdot = inp->i_parent; 274 if (debug) 275 fileerror(inp->i_parent, inp->i_number, 276 "DEFERRED MISSING '..' FIX"); 277 (void)makeentry(inp->i_number, inp->i_parent, ".."); 278 info->ino_linkcnt--; 279 continue; 280 } 281 fileerror(inp->i_parent, inp->i_number, 282 "BAD INODE NUMBER FOR '..'"); 283 if (reply("FIX") == 0) { 284 markclean = 0; 285 continue; 286 } 287 inoinfo(inp->i_dotdot)->ino_linkcnt++; 288 info->ino_linkcnt--; 289 inp->i_dotdot = inp->i_parent; 290 (void)changeino(inp->i_number, "..", inp->i_parent); 291 } 292 /* 293 * Create a list of children for each directory. 294 */ 295 inpend = &inpsort[inplast]; 296 for (inpp = inpsort; inpp < inpend; inpp++) { 297 inp = *inpp; 298 info = inoinfo(inp->i_number); 299 inp->i_child = inp->i_sibling = 0; 300 if (info->ino_state == DFOUND) 301 info->ino_state = DSTATE; 302 } 303 for (inpp = inpsort; inpp < inpend; inpp++) { 304 inp = *inpp; 305 if (inp->i_parent == 0 || 306 inp->i_number == UFS_ROOTINO) 307 continue; 308 pinp = getinoinfo(inp->i_parent); 309 inp->i_sibling = pinp->i_child; 310 pinp->i_child = inp; 311 } 312 /* 313 * Mark all the directories that can be found from the root. 314 */ 315 propagate(UFS_ROOTINO); 316 317 #ifdef PROGRESS 318 if (!preen) 319 progress_done(); 320 #endif /* PROGRESS */ 321 } 322 323 static int 324 pass2check(struct inodesc *idesc) 325 { 326 struct direct *dirp = idesc->id_dirp; 327 struct inoinfo *inp; 328 struct inostat *info; 329 int n, entrysize, ret = 0; 330 union dinode *dp; 331 const char *errmsg; 332 struct direct proto, *newdirp; 333 char namebuf[MAXPATHLEN + 1]; 334 char pathbuf[MAXPATHLEN + 1]; 335 336 /* 337 * If converting, set directory entry type. 338 */ 339 if (!is_ufs2 && doinglevel2 && iswap32(dirp->d_ino) > 0 && 340 iswap32(dirp->d_ino) < maxino) { 341 dirp->d_type = inoinfo(iswap32(dirp->d_ino))->ino_type; 342 ret |= ALTERED; 343 } 344 /* 345 * check for "." 346 */ 347 if (idesc->id_entryno != 0) 348 goto chk1; 349 if (dirp->d_ino != 0 && strcmp(dirp->d_name, ".") == 0) { 350 if (iswap32(dirp->d_ino) != idesc->id_number) { 351 direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'"); 352 if (reply("FIX") == 1) { 353 dirp->d_ino = iswap32(idesc->id_number); 354 ret |= ALTERED; 355 } else 356 markclean = 0; 357 } 358 if (newinofmt && dirp->d_type != DT_DIR) { 359 direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'"); 360 if (reply("FIX") == 1) { 361 dirp->d_type = DT_DIR; 362 ret |= ALTERED; 363 } else 364 markclean = 0; 365 } 366 goto chk1; 367 } 368 proto.d_ino = iswap32(idesc->id_number); 369 if (newinofmt) 370 proto.d_type = DT_DIR; 371 else 372 proto.d_type = 0; 373 proto.d_namlen = 1; 374 (void)strlcpy(proto.d_name, ".", sizeof(proto.d_name)); 375 # if BYTE_ORDER == LITTLE_ENDIAN 376 if (!newinofmt && !needswap) { 377 # else 378 if (!newinofmt && needswap) { 379 # endif 380 u_char tmp; 381 382 tmp = proto.d_type; 383 proto.d_type = proto.d_namlen; 384 proto.d_namlen = tmp; 385 } 386 entrysize = UFS_DIRSIZ(0, &proto, 0); 387 direrror(idesc->id_number, "MISSING '.'"); 388 errmsg = "ADD '.' ENTRY"; 389 if (iswap16(dirp->d_reclen) < entrysize + UFS_DIRSIZ(0, dirp, 0)) { 390 /* Not enough space to add '.', replace first entry with '.' */ 391 if (dirp->d_ino != 0) { 392 pwarn("\nFIRST ENTRY IN DIRECTORY CONTAINS %s\n", 393 dirp->d_name); 394 errmsg = "REPLACE WITH '.'"; 395 } 396 if (reply(errmsg) == 0) 397 goto chk1; 398 proto.d_reclen = dirp->d_reclen; 399 memmove(dirp, &proto, (size_t)entrysize); 400 ret |= ALTERED; 401 } else { 402 /* Move over first entry and add '.' entry */ 403 if (reply(errmsg) == 0) 404 goto chk1; 405 newdirp = (struct direct *)((char *)(dirp) + entrysize); 406 dirp->d_reclen = iswap16(iswap16(dirp->d_reclen) - entrysize); 407 memmove(newdirp, dirp, iswap16(dirp->d_reclen)); 408 proto.d_reclen = iswap16(entrysize); 409 memmove(dirp, &proto, (size_t)entrysize); 410 idesc->id_entryno++; 411 inoinfo(idesc->id_number)->ino_linkcnt--; 412 dirp = newdirp; 413 ret |= ALTERED; 414 } 415 chk1: 416 if (idesc->id_entryno > 1) 417 goto chk2; 418 inp = getinoinfo(idesc->id_number); 419 proto.d_ino = iswap32(inp->i_parent); 420 if (newinofmt) 421 proto.d_type = DT_DIR; 422 else 423 proto.d_type = 0; 424 proto.d_namlen = 2; 425 (void)strlcpy(proto.d_name, "..", sizeof(proto.d_name)); 426 #if BYTE_ORDER == LITTLE_ENDIAN 427 if (!newinofmt && !needswap) { 428 #else 429 if (!newinofmt && needswap) { 430 #endif 431 u_char tmp; 432 433 tmp = proto.d_type; 434 proto.d_type = proto.d_namlen; 435 proto.d_namlen = tmp; 436 } 437 entrysize = UFS_DIRSIZ(0, &proto, 0); 438 if (idesc->id_entryno == 0) { 439 n = UFS_DIRSIZ(0, dirp, 0); 440 if (iswap16(dirp->d_reclen) < n + entrysize) 441 goto chk2; 442 proto.d_reclen = iswap16(iswap16(dirp->d_reclen) - n); 443 dirp->d_reclen = iswap16(n); 444 idesc->id_entryno++; 445 inoinfo(iswap32(dirp->d_ino))->ino_linkcnt--; 446 dirp = (struct direct *)((char *)(dirp) + n); 447 memset(dirp, 0, (size_t)iswap16(proto.d_reclen)); 448 dirp->d_reclen = proto.d_reclen; 449 } 450 if (dirp->d_ino != 0 && strcmp(dirp->d_name, "..") == 0) { 451 inp->i_dotdot = iswap32(dirp->d_ino); 452 if (newinofmt && dirp->d_type != DT_DIR) { 453 direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'"); 454 dirp->d_type = DT_DIR; 455 if (reply("FIX") == 1) 456 ret |= ALTERED; 457 else 458 markclean = 0; 459 } 460 goto chk2; 461 } 462 fileerror(inp->i_parent != 0 ? inp->i_parent : idesc->id_number, 463 idesc->id_number, "MISSING '..'"); 464 errmsg = "ADD '..' ENTRY"; 465 if (dirp->d_reclen < entrysize + UFS_DIRSIZ(0, dirp, 0)) { 466 /* No space to add '..', replace second entry with '..' */ 467 if (dirp->d_ino != 0) { 468 pfatal("SECOND ENTRY IN DIRECTORY CONTAINS %s\n", 469 dirp->d_name); 470 errmsg = "REPLACE WITH '..'"; 471 } 472 if (reply(errmsg) == 0) { 473 inp->i_dotdot = (ino_t)-1; 474 goto chk2; 475 } 476 if (proto.d_ino == 0) { 477 /* Defer processing until parent known */ 478 idesc->id_entryno++; 479 if (debug) 480 printf("(FIX DEFERRED)\n"); 481 } 482 inp->i_dotdot = proto.d_ino; 483 proto.d_reclen = dirp->d_reclen; 484 memmove(dirp, &proto, (size_t)entrysize); 485 ret |= ALTERED; 486 } else { 487 /* Move over second entry and add '..' entry */ 488 if (reply(errmsg) == 0) { 489 inp->i_dotdot = (ino_t)-1; 490 goto chk2; 491 } 492 if (proto.d_ino == 0) { 493 /* Defer processing until parent known */ 494 idesc->id_entryno++; 495 if (debug) 496 printf("(FIX DEFERRED)\n"); 497 } 498 inp->i_dotdot = proto.d_ino; 499 if (dirp->d_ino == 0) { 500 proto.d_reclen = dirp->d_reclen; 501 memmove(dirp, &proto, (size_t)entrysize); 502 } else { 503 newdirp = (struct direct *)((char *)(dirp) + entrysize); 504 dirp->d_reclen -= entrysize; 505 memmove(newdirp, dirp, dirp->d_reclen); 506 proto.d_reclen = entrysize; 507 memmove(dirp, &proto, (size_t)entrysize); 508 if (dirp->d_ino != 0) { 509 idesc->id_entryno++; 510 inoinfo(dirp->d_ino)->ino_linkcnt--; 511 } 512 dirp = newdirp; 513 } 514 ret |= ALTERED; 515 } 516 chk2: 517 if (dirp->d_ino == 0) 518 return (ret|KEEPON); 519 if (dirp->d_namlen <= 2 && 520 dirp->d_name[0] == '.' && 521 idesc->id_entryno >= 2) { 522 if (dirp->d_namlen == 1) { 523 direrror(idesc->id_number, "EXTRA '.' ENTRY"); 524 dirp->d_ino = 0; 525 if (reply("FIX") == 1) 526 ret |= ALTERED; 527 else 528 markclean = 0; 529 return (KEEPON | ret); 530 } 531 if (dirp->d_name[1] == '.') { 532 direrror(idesc->id_number, "EXTRA '..' ENTRY"); 533 dirp->d_ino = 0; 534 if (reply("FIX") == 1) 535 ret |= ALTERED; 536 else 537 markclean = 0; 538 return (KEEPON | ret); 539 } 540 } 541 idesc->id_entryno++; 542 n = 0; 543 if (iswap32(dirp->d_ino) > maxino) { 544 fileerror(idesc->id_number, dirp->d_ino, "I OUT OF RANGE"); 545 n = reply("REMOVE"); 546 if (n == 0) 547 markclean = 0; 548 } else if (newinofmt && 549 ((iswap32(dirp->d_ino) == UFS_WINO && dirp->d_type != DT_WHT) || 550 (iswap32(dirp->d_ino) != UFS_WINO && dirp->d_type == DT_WHT))) { 551 fileerror(idesc->id_number, iswap32(dirp->d_ino), "BAD WHITEOUT ENTRY"); 552 dirp->d_ino = iswap32(UFS_WINO); 553 dirp->d_type = DT_WHT; 554 if (reply("FIX") == 1) 555 ret |= ALTERED; 556 else 557 markclean = 0; 558 } else { 559 again: 560 info = inoinfo(iswap32(dirp->d_ino)); 561 switch (info->ino_state) { 562 case USTATE: 563 if (idesc->id_entryno <= 2) 564 break; 565 fileerror(idesc->id_number, iswap32(dirp->d_ino), "UNALLOCATED"); 566 n = reply("REMOVE"); 567 if (n == 0) 568 markclean = 0; 569 break; 570 571 case DCLEAR: 572 case FCLEAR: 573 if (idesc->id_entryno <= 2) 574 break; 575 if (info->ino_state == FCLEAR) 576 errmsg = "DUP/BAD"; 577 else if (!preen && !usedsoftdep) 578 errmsg = "ZERO LENGTH DIRECTORY"; 579 else { 580 n = 1; 581 break; 582 } 583 fileerror(idesc->id_number, iswap32(dirp->d_ino), errmsg); 584 if ((n = reply("REMOVE")) == 1) 585 break; 586 dp = ginode(iswap32(dirp->d_ino)); 587 info->ino_state = 588 (iswap16(DIP(dp, mode)) & IFMT) == IFDIR ? DSTATE : FSTATE; 589 info->ino_linkcnt = iswap16(DIP(dp, nlink)); 590 goto again; 591 592 case DSTATE: 593 case DFOUND: 594 inp = getinoinfo(iswap32(dirp->d_ino)); 595 if (inp->i_parent != 0 && idesc->id_entryno > 2) { 596 getpathname(pathbuf, sizeof(pathbuf), 597 idesc->id_number, idesc->id_number); 598 getpathname(namebuf, sizeof(namebuf), 599 iswap32(dirp->d_ino), iswap32(dirp->d_ino)); 600 pwarn("%s %s %s\n", pathbuf, 601 "IS AN EXTRANEOUS HARD LINK TO DIRECTORY", 602 namebuf); 603 if (preen) 604 printf(" (IGNORED)\n"); 605 else if ((n = reply("REMOVE")) == 1) 606 break; 607 } 608 if (idesc->id_entryno > 2) 609 inp->i_parent = idesc->id_number; 610 /* fall through */ 611 612 case FSTATE: 613 if (newinofmt && dirp->d_type != info->ino_type) { 614 fileerror(idesc->id_number, iswap32(dirp->d_ino), 615 "BAD TYPE VALUE"); 616 dirp->d_type = info->ino_type; 617 if (reply("FIX") == 1) 618 ret |= ALTERED; 619 else 620 markclean = 0; 621 } 622 info->ino_linkcnt--; 623 break; 624 625 default: 626 errexit("BAD STATE %d FOR INODE I=%d", 627 info->ino_state, iswap32(dirp->d_ino)); 628 } 629 } 630 if (n == 0) 631 return (ret|KEEPON); 632 dirp->d_ino = 0; 633 return (ret|KEEPON|ALTERED); 634 } 635 636 /* 637 * Routine to sort disk blocks. 638 */ 639 static int 640 blksort(const void *arg1, const void *arg2) 641 { 642 643 return ((*(const struct inoinfo *const *)arg1)->i_blks[0] - 644 (*(const struct inoinfo *const *)arg2)->i_blks[0]); 645 } 646