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