1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 /* All Rights Reserved */ 8 9 10 /* 11 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms are permitted 15 * provided that: (1) source distributions retain this entire copyright 16 * notice and comment, and (2) distributions including binaries display 17 * the following acknowledgement: ``This product includes software 18 * developed by the University of California, Berkeley and its contributors'' 19 * in the documentation or other materials provided with the distribution 20 * and in all advertising materials mentioning features or use of this 21 * software. Neither the name of the University nor the names of its 22 * contributors may be used to endorse or promote products derived 23 * from this software without specific prior written permission. 24 * THIS SOFTWARE IS PROVIDED '`AS IS'' AND WITHOUT ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 /* 32 * In-core structures: 33 * blockmap[] 34 * A bitmap of block usage very similar to what's on disk, but 35 * for the entire filesystem rather than just a cylinder group. 36 * Zero indicates free, one indicates allocated. Note that this 37 * is opposite the interpretation of a cylinder group's free block 38 * bitmap. 39 * 40 * statemap[] 41 * Tracks what is known about each inode in the filesystem. 42 * The fundamental state value is one of USTATE, FSTATE, DSTATE, 43 * or SSTATE (unallocated, file, directory, shadow/acl). 44 * 45 * There are optional modifying attributes as well: INZLINK, 46 * INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix 47 * stands for inode. INZLINK declares that no links (di_nlink == 48 * 0) to the inode have been found. It is used instead of 49 * examining di_nlink because we've always got the statemap[] in 50 * memory, and on average the odds are against having any given 51 * inode in the cache. INFOUND flags that an inode was 52 * encountered during the descent of the filesystem. In other 53 * words, it's reachable, either by name or by being an acl or 54 * attribute. INCLEAR declares an intent to call clri() on an 55 * inode. The INCLEAR and INZLINK attributes are treated in a 56 * mutually exclusive manner with INCLEAR taking higher precedence 57 * as the intent is to clear the inode. 58 * 59 * INORPHAN indicates that the inode has already been seen once 60 * in pass3 and determined to be an orphan, so any additional 61 * encounters don't need to waste cycles redetermining that status. 62 * It also means we don't ask the user about doing something to the 63 * inode N times. 64 * 65 * INDELAYD marks inodes that pass1 determined needed to be truncated. 66 * They can't be truncated during that pass, because it depends on 67 * having a stable world for building the block and inode tables from. 68 * 69 * The IN flags rarely used directly, but instead are 70 * pre-combined through the {D,F,S}ZLINK, DFOUND, and 71 * {D,F,S}CLEAR convenience macros. This mainly matters when 72 * trying to use grep on the source. 73 * 74 * Three state-test macros are provided: S_IS_DUNFOUND(), 75 * S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an 76 * inode's state indicates that it is either a simple directory 77 * (DSTATE without the INFOUND or INCLEAR modifiers) or a 78 * directory with the INZLINK modifier set. By definition, if a 79 * directory has zero links, then it can't be found. As for 80 * S_IS_DVALID(), it decides if a directory inode is alive. 81 * Effectively, this translates to whether or not it's been 82 * flagged for clearing. If not, then it's valid for current 83 * purposes. This is true even if INZLINK is set, as we may find 84 * a reference to it later. Finally, S_IS_ZLINK() just picks out 85 * the INZLINK flag from the state. 86 * 87 * The S_*() macros all work on a state value. To simplify a 88 * bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode 89 * number argument. The inode is looked up in the statemap[] and 90 * the result handed off to the corresponding S_*() macro. This 91 * is partly a holdover from working with different data 92 * structures (with the same net intent) in the BSD fsck. 93 * 94 * lncntp 95 * Each entry is initialized to the di_link from the on-disk 96 * inode. Each time we find one of those links, we decrement it. 97 * Once all the traversing is done, we should have a zero. If we 98 * have a positive value, then some reference disappeared 99 * (probably from a directory that got nuked); deal with it by 100 * fixing the count. If we have a negative value, then we found 101 * an extra reference. This is a can't-happen, except in the 102 * special case of when we reconnect a directory to its parent or 103 * to lost+found. An exact match between lncntp[] and the on-disk 104 * inode means it's completely unreferenced. 105 * 106 * aclphead 107 * This is a hash table of the acl inodes in the filesystem. 108 * 109 * aclpsort 110 * The same acls as in aclphead, but as a simple linear array. 111 * It is used to hold the acl pointers for sorting and scanning 112 * in pass3b. 113 */ 114 115 #include <stdio.h> 116 #include <stdlib.h> 117 #include <unistd.h> 118 #include <sys/types.h> 119 #include <sys/param.h> 120 #include <sys/int_types.h> 121 #include <sys/mntent.h> 122 #include <sys/fs/ufs_fs.h> 123 #include <sys/vnode.h> 124 #include <sys/fs/ufs_inode.h> 125 #include <sys/stat.h> 126 #include <fcntl.h> 127 #include <sys/wait.h> 128 #include <sys/mnttab.h> 129 #include <signal.h> 130 #include <string.h> 131 #include <sys/vfstab.h> 132 #include <sys/statvfs.h> 133 #include <sys/filio.h> 134 #include <ustat.h> 135 #include <errno.h> 136 #include "fsck.h" 137 138 static void usage(void); 139 static long argtol(int, char *, char *, int); 140 static void checkfilesys(char *); 141 static void check_sanity(char *); 142 static void report_limbo(const void *, VISIT, int); 143 144 #define QUICK_CHECK 'm' /* are things ok according to superblock? */ 145 #define ALL_no 'n' /* auto-answer interactive questions `no' */ 146 #define ALL_NO 'N' /* auto-answer interactive questions `no' */ 147 #define UFS_OPTS 'o' /* ufs-specific options, see subopts[] */ 148 #define ECHO_CMD 'V' /* echo the command line */ 149 #define ALL_yes 'y' /* auto-answer interactive questions `yes' */ 150 #define ALL_YES 'Y' /* auto-answer interactive questions `yes' */ 151 #define VERBOSE 'v' /* be chatty */ 152 153 static char *subopts[] = { 154 #define PREEN 0 /* non-interactive mode (parent is parallel) */ 155 "p", 156 #define BLOCK 1 /* alternate superblock */ 157 "b", 158 #define DEBUG 2 /* yammer */ 159 "d", 160 #define ONLY_WRITES 3 /* check all writable filesystems */ 161 "w", 162 #define FORCE 4 /* force checking, even if clean */ 163 "f", 164 NULL 165 }; 166 167 /* 168 * Filesystems that are `magical' - if they exist in vfstab, 169 * then they have to be mounted for the system to have gotten 170 * far enough to be able to run fsck. Thus, don't get all 171 * bent out of shape if we're asked to check it and it is mounted. 172 */ 173 char *magic_fs[] = { 174 "", /* MAGIC_NONE, for normal filesystems */ 175 "/", /* MAGIC_ROOT */ 176 "/usr", /* MAGIC_USR */ 177 NULL /* MAGIC_LIMIT */ 178 }; 179 180 int 181 main(int argc, char *argv[]) 182 { 183 int c; 184 int wflag = 0; 185 char *suboptions, *value; 186 struct rlimit rlimit; 187 extern int optind; 188 extern char *optarg; 189 190 while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) { 191 switch (c) { 192 193 case QUICK_CHECK: 194 mflag++; 195 break; 196 197 case ALL_no: 198 case ALL_NO: 199 nflag++; 200 yflag = 0; 201 break; 202 203 case VERBOSE: 204 verbose++; 205 break; 206 207 case UFS_OPTS: 208 /* 209 * ufs specific options. 210 */ 211 if (optarg == NULL) { 212 usage(); 213 /* 214 * lint does not believe this, nor does it 215 * believe #pragma does_not_return(usage) 216 */ 217 /* NOTREACHED */ 218 } 219 suboptions = optarg; 220 while (*suboptions != '\0') { 221 switch (getsubopt(&suboptions, subopts, 222 &value)) { 223 224 case PREEN: 225 preen++; 226 break; 227 228 case BLOCK: 229 bflag = argtol(BLOCK, "block", 230 value, 10); 231 (void) printf("Alternate super block " 232 "location: %ld.\n", 233 (long)bflag); 234 break; 235 236 case DEBUG: 237 debug++; 238 verbose++; 239 break; 240 241 case ONLY_WRITES: 242 /* check only writable filesystems */ 243 wflag++; 244 break; 245 246 case FORCE: 247 fflag++; 248 break; 249 250 default: 251 usage(); 252 } 253 } 254 break; 255 256 case ECHO_CMD: 257 { 258 int opt_count; 259 char *opt_text; 260 261 (void) printf("fsck -F ufs "); 262 for (opt_count = 1; opt_count < argc; 263 opt_count++) { 264 opt_text = argv[opt_count]; 265 if (opt_text) 266 (void) printf("%s ", opt_text); 267 } 268 (void) printf("\n"); 269 } 270 break; 271 272 case ALL_yes: 273 case ALL_YES: 274 yflag++; 275 nflag = 0; 276 break; 277 278 default: 279 usage(); 280 } 281 } 282 argc -= optind; 283 argv += optind; 284 285 if (argc == 0) 286 usage(); 287 288 rflag++; /* check raw devices where we can */ 289 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 290 (void) signal(SIGINT, catch); 291 if (preen) 292 (void) signal(SIGQUIT, catchquit); 293 294 /* 295 * Push up our allowed memory limit so we can cope 296 * with huge file systems. 297 */ 298 if (getrlimit(RLIMIT_DATA, &rlimit) == 0) { 299 rlimit.rlim_cur = rlimit.rlim_max; 300 (void) setrlimit(RLIMIT_DATA, &rlimit); 301 } 302 303 /* 304 * There are a lot of places where we just exit if a problem is 305 * found. This means that we won't necessarily check everything 306 * we were asked to. It would be nice to do everything, and 307 * then provide a summary when we're done. However, the 308 * interface doesn't really allow us to do that in any useful 309 * way. So, we'll just bail on the first unrecoverable 310 * problem encountered. If we've been run by the generic 311 * wrapper, we were only given one filesystem to check, so the 312 * multi-fs case implies being run manually; that means the 313 * user can rerun us on the remaining filesystems when it's 314 * convenient for them. 315 */ 316 while (argc-- > 0) { 317 if (wflag && !writable(*argv)) { 318 (void) fprintf(stderr, "not writeable '%s'\n", *argv); 319 argv++; 320 if (exitstat == 0) 321 exitstat = EXBADPARM; 322 } else { 323 checkfilesys(*argv++); 324 } 325 } 326 if (interrupted) 327 exitstat = EXSIGNAL; 328 exit(exitstat); 329 } 330 331 /* 332 * A relatively intelligent strtol(). Note that if str is NULL, we'll 333 * exit, so ret does not actually need to be pre-initialized. Lint 334 * doesn't believe this, and it's harmless enough to make lint happy here. 335 */ 336 static long 337 argtol(int flag, char *req, char *str, int base) 338 { 339 char *cp = str; 340 long ret = -1; 341 342 errno = 0; 343 if (str != NULL) 344 ret = strtol(str, &cp, base); 345 if (cp == str || *cp) { 346 (void) fprintf(stderr, "-%c flag requires a %s\n", flag, req); 347 exit(EXBADPARM); 348 } 349 if (errno != 0) { 350 (void) fprintf(stderr, "-%c %s value out of range\n", 351 flag, req); 352 } 353 354 return (ret); 355 } 356 357 /* 358 * Check the specified file system. 359 */ 360 static void 361 checkfilesys(char *filesys) 362 { 363 daddr32_t n_ffree, n_bfree; 364 char *devstr; 365 fsck_ino_t files; 366 daddr32_t blks; 367 fsck_ino_t inumber; 368 int zlinks_printed; 369 fsck_ino_t limbo_victim; 370 double dbl_nffree, dbl_dsize; 371 int quiet_dups; 372 373 mountfd = -1; 374 hotroot = 0; 375 mountedfs = M_NOMNT; 376 reattached_dir = 0; 377 broke_dir_link = 0; 378 iscorrupt = 1; /* assume failure in setup() */ 379 islog = 0; 380 islogok = 0; 381 overflowed_lf = 0; 382 errorlocked = is_errorlocked(filesys); 383 limbo_dirs = NULL; 384 385 if ((devstr = setup(filesys)) == NULL) { 386 if (!iscorrupt) { 387 return; 388 } 389 390 if (preen) 391 pfatal("CAN'T CHECK FILE SYSTEM."); 392 if ((exitstat == 0) && (mflag)) 393 exitstat = EXUMNTCHK; 394 exit(exitstat); 395 } else { 396 devname = devstr; 397 } 398 399 if (mflag) { 400 check_sanity(filesys); 401 /* NOTREACHED */ 402 } 403 404 if (debug) 405 printclean(); 406 407 iscorrupt = 0; /* setup() succeeded, assume good filesystem */ 408 409 /* 410 * 1: scan inodes tallying blocks used 411 */ 412 if (!preen) { 413 /* hotroot is reported as such in setup() if debug is on */ 414 if (mountedfs != M_NOMNT) 415 (void) printf("** Currently Mounted on %s\n", 416 sblock.fs_fsmnt); 417 else 418 (void) printf("** Last Mounted on %s\n", 419 sblock.fs_fsmnt); 420 (void) printf("** Phase 1 - Check Blocks and Sizes\n"); 421 } 422 pass1(); 423 424 /* 425 * 1b: locate first references to duplicates, if any 426 */ 427 if (have_dups()) { 428 if (preen) 429 pfatal("INTERNAL ERROR: dups with -o p"); 430 (void) printf("** Phase 1b - Rescan For More DUPS\n"); 431 pass1b(); 432 } 433 434 /* 435 * 2: traverse directories from root to mark all connected directories 436 */ 437 if (!preen) 438 (void) printf("** Phase 2 - Check Pathnames\n"); 439 pass2(); 440 441 /* 442 * 3a: scan inodes looking for disconnected directories. 443 */ 444 if (!preen) 445 (void) printf("** Phase 3a - Check Connectivity\n"); 446 pass3a(); 447 448 /* 449 * 3b: check acls 450 */ 451 if (!preen) 452 (void) printf("** Phase 3b - Verify Shadows/ACLs\n"); 453 pass3b(); 454 455 /* 456 * 4: scan inodes looking for disconnected files; check reference counts 457 */ 458 if (!preen) 459 (void) printf("** Phase 4 - Check Reference Counts\n"); 460 pass4(); 461 462 /* 463 * 5: check and repair resource counts in cylinder groups 464 */ 465 if (!preen) 466 (void) printf("** Phase 5 - Check Cylinder Groups\n"); 467 recount: 468 pass5(); 469 470 if (overflowed_lf) { 471 iscorrupt = 1; 472 } 473 474 if (!nflag && mountedfs == M_RW) { 475 (void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n"); 476 rerun = 1; 477 } 478 479 if (have_dups()) { 480 quiet_dups = (reply("LIST REMAINING DUPS") == 0); 481 if (report_dups(quiet_dups) > 0) 482 iscorrupt = 1; 483 484 (void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO " 485 "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n"); 486 } 487 488 if (limbo_dirs != NULL) { 489 /* 490 * Don't force iscorrupt, as this is sufficiently 491 * harmless that the filesystem can be mounted and 492 * used. We just leak some inodes and/or blocks. 493 */ 494 pwarn("Orphan directories not cleared or reconnected:\n"); 495 496 twalk(limbo_dirs, report_limbo); 497 498 while (limbo_dirs != NULL) { 499 limbo_victim = *(fsck_ino_t *)limbo_dirs; 500 if (limbo_victim != NULL) { 501 (void) tdelete((void *)limbo_victim, 502 &limbo_dirs, 503 ino_t_cmp); 504 } 505 } 506 507 rerun = 1; 508 } 509 510 if (iscorrupt) { 511 if (mountedfs == M_RW) 512 (void) printf("FS IS MOUNTED R/W AND" 513 " FSCK DID ITS BEST TO FIX" 514 " INCONSISTENCIES.\n"); 515 else 516 (void) printf("FILESYSTEM MAY STILL BE" 517 " INCONSISTENT.\n"); 518 rerun = 1; 519 } 520 521 /* 522 * iscorrupt must be stable at this point. 523 * updateclean() returns true when it had to discard the log. 524 * This can only happen once, since sblock.fs_logbno gets 525 * cleared as part of that operation. 526 */ 527 if (updateclean()) { 528 if (!preen) 529 (void) printf( 530 "Log was discarded, updating cyl groups\n"); 531 goto recount; 532 } 533 534 if (debug) 535 printclean(); 536 537 ckfini(); 538 539 /* 540 * print out summary statistics 541 */ 542 n_ffree = sblock.fs_cstotal.cs_nffree; 543 n_bfree = sblock.fs_cstotal.cs_nbfree; 544 files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files; 545 blks = n_blks + 546 sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); 547 blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); 548 blks += howmany(sblock.fs_cssize, sblock.fs_fsize); 549 blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks; 550 if (debug && (files > 0 || blks > 0)) { 551 countdirs = sblock.fs_cstotal.cs_ndir - countdirs; 552 pwarn("Reclaimed: %d directories, %d files, %lld fragments\n", 553 countdirs, files - countdirs, 554 (longlong_t)blks); 555 } 556 557 dbl_nffree = (double)n_ffree; 558 dbl_dsize = (double)sblock.fs_dsize; 559 560 if (!verbose) { 561 /* 562 * Done as one big string to try for a single write, 563 * so the output doesn't get interleaved with other 564 * preening fscks. 565 */ 566 pwarn("%ld files, %lld used, %lld free " 567 "(%lld frags, %lld blocks, %.1f%% fragmentation)\n", 568 (long)n_files, (longlong_t)n_blks, 569 (longlong_t)n_ffree + sblock.fs_frag * n_bfree, 570 (longlong_t)n_ffree, (longlong_t)n_bfree, 571 (dbl_nffree * 100.0) / dbl_dsize); 572 } else { 573 pwarn("\nFilesystem summary:\n"); 574 pwarn("Inodes in use: %ld\n", (long)n_files); 575 pwarn("Blocks in use: %lld\n", (longlong_t)n_blks); 576 pwarn("Total free fragments: %lld\n", 577 (longlong_t)n_ffree + sblock.fs_frag * n_bfree); 578 pwarn("Free fragments not in blocks: %lld\n", 579 (longlong_t)n_ffree); 580 pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree); 581 pwarn("Fragment/block fragmentation: %.1f%%\n", 582 (dbl_nffree * 100.0) / dbl_dsize); 583 pwarn(""); 584 585 if (files < 0) 586 pwarn("%d inodes missing\n", -files); 587 if (blks < 0) 588 pwarn("%lld blocks missing\n", -(longlong_t)blks); 589 590 zlinks_printed = 0; 591 for (inumber = UFSROOTINO; inumber < maxino; inumber++) { 592 if (S_IS_ZLINK(statemap[inumber])) { 593 if (zlinks_printed == 0) { 594 pwarn("The following zero " 595 "link count inodes remain:"); 596 } 597 if (zlinks_printed) { 598 if ((zlinks_printed % 9) == 0) 599 (void) puts(",\n"); 600 else 601 (void) puts(", "); 602 } 603 (void) printf("%u", inumber); 604 zlinks_printed++; 605 } 606 } 607 if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0)) 608 (void) putchar('\n'); 609 } 610 611 /* 612 * Clean up after ourselves, so we can do the next filesystem. 613 */ 614 free_dup_state(); 615 inocleanup(); 616 free(blockmap); 617 free(statemap); 618 free((void *)lncntp); 619 lncntp = NULL; 620 blockmap = NULL; 621 statemap = NULL; 622 if (iscorrupt && exitstat == 0) 623 exitstat = EXFNDERRS; 624 if (fsmodified) 625 (void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 626 if (overflowed_lf) 627 (void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n", 628 lfname); 629 if (reattached_dir) { 630 (void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK " 631 "COUNTS MAY NOT BE CORRECT.\n"); 632 rerun = 1; 633 } 634 if (broke_dir_link) { 635 (void) printf( 636 "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n"); 637 rerun = 1; 638 } 639 if (iscorrupt) 640 (void) printf("***** FILE SYSTEM IS BAD *****\n"); 641 642 if (rerun) { 643 if (mountedfs == M_RW) 644 (void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED" 645 " FILE SYSTEM *****\n"); 646 else 647 (void) printf("\n***** PLEASE RERUN FSCK *****\n"); 648 } 649 650 if ((exitstat == 0) && 651 (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) { 652 exitstat = EXROOTOKAY; 653 } 654 655 if ((exitstat == 0) && rerun) 656 exitstat = EXFNDERRS; 657 658 if (mountedfs != M_NOMNT) { 659 if (!fsmodified) 660 return; 661 /* 662 * _FIOFFS is much more effective than a simple sync(). 663 * Note that the original fswritefd was discarded in 664 * ckfini(). 665 */ 666 fswritefd = open(devstr, O_RDWR, 0); 667 if (fswritefd != -1) { 668 (void) ioctl(fswritefd, _FIOFFS, NULL); 669 (void) close(fswritefd); 670 } 671 672 if (!preen) 673 (void) printf("\n***** REBOOT NOW *****\n"); 674 675 exitstat = EXREBOOTNOW; 676 } 677 } 678 679 /* 680 * fsck -m: does the filesystem pass cursory examination 681 * 682 * XXX This is very redundant with setup(). The right thing would be 683 * for setup() to modify its behaviour when mflag is set (less 684 * chatty, exit instead of return, etc). 685 */ 686 void 687 check_sanity(char *filename) 688 { 689 struct stat64 stbd, stbr; 690 char *devname; 691 struct ustat usb; 692 char vfsfilename[MAXPATHLEN]; 693 struct vfstab vfsbuf; 694 FILE *vfstab; 695 struct statvfs vfs_stat; 696 int found_magic[MAGIC_LIMIT]; 697 int magic_cnt; 698 int is_magic = 0; 699 int is_block; 700 701 (void) memset((void *)found_magic, 0, sizeof (found_magic)); 702 703 if (stat64(filename, &stbd) < 0) { 704 (void) fprintf(stderr, 705 "ufs fsck: sanity check failed : cannot stat %s\n", filename); 706 exit(EXNOSTAT); 707 } 708 709 if ((stbd.st_mode & S_IFMT) == S_IFBLK) { 710 is_block = 1; 711 } else if ((stbd.st_mode & S_IFMT) == S_IFCHR) { 712 is_block = 0; 713 } else { 714 /* 715 * In !mflag mode, we allow checking the contents 716 * of a file. Since this is intended primarily for 717 * speeding up boot-time checks and allowing for a 718 * file complicates the ok-input tests, we'll disallow 719 * that option. 720 */ 721 (void) fprintf(stderr, 722 "ufs fsck: sanity check failed: " 723 "%s not block or character device\n", filename); 724 exit(EXNOSTAT); 725 } 726 727 /* 728 * Determine if this is the root file system via vfstab. Give up 729 * silently on failures. The whole point of this is to be tolerant 730 * of the magic file systems being already mounted. 731 */ 732 if ((vfstab = fopen(VFSTAB, "r")) != 0) { 733 for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) { 734 if (magic_cnt == MAGIC_NONE) 735 continue; 736 if (getvfsfile(vfstab, &vfsbuf, 737 magic_fs[magic_cnt]) == 0) { 738 if (is_block) 739 devname = vfsbuf.vfs_special; 740 else 741 devname = vfsbuf.vfs_fsckdev; 742 if (stat64(devname, &stbr) == 0) { 743 if (stbr.st_rdev == stbd.st_rdev) { 744 found_magic[magic_cnt] = 1; 745 is_magic = magic_cnt; 746 break; 747 } 748 } 749 } 750 } 751 } 752 753 /* 754 * Only works if filename is a block device or if 755 * character and block device has the same dev_t value. 756 * This is currently true, but nothing really forces it. 757 */ 758 if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) { 759 (void) fprintf(stderr, 760 "ufs fsck: sanity check: %s already mounted\n", filename); 761 exit(EXMOUNTED); 762 } 763 764 if (is_magic) { 765 (void) strcpy(vfsfilename, magic_fs[is_magic]); 766 if (statvfs(vfsfilename, &vfs_stat) != 0) { 767 (void) fprintf(stderr, "ufs fsck: Cannot stat %s\n", 768 vfsfilename); 769 exit(EXNOSTAT); 770 } 771 772 if (!(vfs_stat.f_flag & ST_RDONLY)) { 773 /* 774 * The file system is mounted read/write 775 * We need to exit saying this. If it's only 776 * mounted readonly, we can continue. 777 */ 778 779 (void) fprintf(stderr, 780 "ufs fsck: sanity check:" 781 "%s already mounted read/write\n", filename); 782 exit(EXMOUNTED); 783 } 784 } 785 786 /* 787 * We know that at boot, the ufs root file system is mounted 788 * read-only first. After fsck runs, it is remounted as 789 * read-write. Therefore, we do not need to check for different 790 * values for fs_state between the root file system and the 791 * rest of the file systems. 792 */ 793 if (islog && !islogok) { 794 (void) fprintf(stderr, 795 "ufs fsck: sanity check: %s needs checking\n", filename); 796 exit(EXUMNTCHK); 797 } 798 if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) && 799 (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE || 800 (sblock.fs_clean == FSLOG && islog))) { 801 (void) fprintf(stderr, 802 "ufs fsck: sanity check: %s okay\n", filename); 803 } else { 804 (void) fprintf(stderr, 805 "ufs fsck: sanity check: %s needs checking\n", filename); 806 exit(EXUMNTCHK); 807 } 808 exit(EXOKAY); 809 } 810 811 caddr_t 812 hasvfsopt(struct vfstab *vfs, char *opt) 813 { 814 struct mnttab mtab; 815 816 if (vfs->vfs_mntopts == NULL) 817 return (NULL); 818 mtab.mnt_mntopts = vfs->vfs_mntopts; 819 return (hasmntopt(&mtab, opt)); 820 } 821 822 void 823 usage(void) 824 { 825 (void) fprintf(stderr, 826 "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] " 827 "[-o p,b=#,w,f] [special ....]\n"); 828 829 exit(EXBADPARM); 830 } 831 832 /*ARGSUSED*/ 833 static void 834 report_limbo(const void *node, VISIT order, int level) 835 { 836 fsck_ino_t ino = *(fsck_ino_t *)node; 837 838 if ((order == postorder) || (order == leaf)) { 839 (void) printf(" Inode %d\n", ino); 840 } 841 } 842