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   "@(#)main.c     1.54    08/02/06 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 }