17c478bd9Sstevel@tonic-gate /* 2*ce37393aSowenr * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 77c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 87c478bd9Sstevel@tonic-gate 97c478bd9Sstevel@tonic-gate 107c478bd9Sstevel@tonic-gate /* 117c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 127c478bd9Sstevel@tonic-gate * All rights reserved. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 157c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright 167c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display 177c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software 187c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors'' 197c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution 207c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this 217c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its 227c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived 237c478bd9Sstevel@tonic-gate * from this software without specific prior written permission. 24355d6bb5Sswilcox * THIS SOFTWARE IS PROVIDED '`AS IS'' AND WITHOUT ANY EXPRESS OR 257c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 267c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 307c478bd9Sstevel@tonic-gate 31355d6bb5Sswilcox /* 32355d6bb5Sswilcox * In-core structures: 33355d6bb5Sswilcox * blockmap[] 34355d6bb5Sswilcox * A bitmap of block usage very similar to what's on disk, but 35355d6bb5Sswilcox * for the entire filesystem rather than just a cylinder group. 36355d6bb5Sswilcox * Zero indicates free, one indicates allocated. Note that this 37355d6bb5Sswilcox * is opposite the interpretation of a cylinder group's free block 38355d6bb5Sswilcox * bitmap. 39355d6bb5Sswilcox * 40355d6bb5Sswilcox * statemap[] 41355d6bb5Sswilcox * Tracks what is known about each inode in the filesystem. 42355d6bb5Sswilcox * The fundamental state value is one of USTATE, FSTATE, DSTATE, 43355d6bb5Sswilcox * or SSTATE (unallocated, file, directory, shadow/acl). 44355d6bb5Sswilcox * 45355d6bb5Sswilcox * There are optional modifying attributes as well: INZLINK, 46355d6bb5Sswilcox * INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix 47355d6bb5Sswilcox * stands for inode. INZLINK declares that no links (di_nlink == 48355d6bb5Sswilcox * 0) to the inode have been found. It is used instead of 49355d6bb5Sswilcox * examining di_nlink because we've always got the statemap[] in 50355d6bb5Sswilcox * memory, and on average the odds are against having any given 51355d6bb5Sswilcox * inode in the cache. INFOUND flags that an inode was 52355d6bb5Sswilcox * encountered during the descent of the filesystem. In other 53355d6bb5Sswilcox * words, it's reachable, either by name or by being an acl or 54355d6bb5Sswilcox * attribute. INCLEAR declares an intent to call clri() on an 55*ce37393aSowenr * inode. The INCLEAR and INZLINK attributes are treated in a 56*ce37393aSowenr * mutually exclusive manner with INCLEAR taking higher precedence 57*ce37393aSowenr * as the intent is to clear the inode. 58355d6bb5Sswilcox * 59355d6bb5Sswilcox * INORPHAN indicates that the inode has already been seen once 60355d6bb5Sswilcox * in pass3 and determined to be an orphan, so any additional 61355d6bb5Sswilcox * encounters don't need to waste cycles redetermining that status. 62355d6bb5Sswilcox * It also means we don't ask the user about doing something to the 63355d6bb5Sswilcox * inode N times. 64355d6bb5Sswilcox * 65355d6bb5Sswilcox * INDELAYD marks inodes that pass1 determined needed to be truncated. 66355d6bb5Sswilcox * They can't be truncated during that pass, because it depends on 67355d6bb5Sswilcox * having a stable world for building the block and inode tables from. 68355d6bb5Sswilcox * 69355d6bb5Sswilcox * The IN flags rarely used directly, but instead are 70355d6bb5Sswilcox * pre-combined through the {D,F,S}ZLINK, DFOUND, and 71355d6bb5Sswilcox * {D,F,S}CLEAR convenience macros. This mainly matters when 72355d6bb5Sswilcox * trying to use grep on the source. 73355d6bb5Sswilcox * 74355d6bb5Sswilcox * Three state-test macros are provided: S_IS_DUNFOUND(), 75355d6bb5Sswilcox * S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an 76355d6bb5Sswilcox * inode's state indicates that it is either a simple directory 77355d6bb5Sswilcox * (DSTATE without the INFOUND or INCLEAR modifiers) or a 78355d6bb5Sswilcox * directory with the INZLINK modifier set. By definition, if a 79355d6bb5Sswilcox * directory has zero links, then it can't be found. As for 80355d6bb5Sswilcox * S_IS_DVALID(), it decides if a directory inode is alive. 81355d6bb5Sswilcox * Effectively, this translates to whether or not it's been 82355d6bb5Sswilcox * flagged for clearing. If not, then it's valid for current 83355d6bb5Sswilcox * purposes. This is true even if INZLINK is set, as we may find 84355d6bb5Sswilcox * a reference to it later. Finally, S_IS_ZLINK() just picks out 85355d6bb5Sswilcox * the INZLINK flag from the state. 86355d6bb5Sswilcox * 87355d6bb5Sswilcox * The S_*() macros all work on a state value. To simplify a 88355d6bb5Sswilcox * bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode 89355d6bb5Sswilcox * number argument. The inode is looked up in the statemap[] and 90355d6bb5Sswilcox * the result handed off to the corresponding S_*() macro. This 91355d6bb5Sswilcox * is partly a holdover from working with different data 92355d6bb5Sswilcox * structures (with the same net intent) in the BSD fsck. 93355d6bb5Sswilcox * 94355d6bb5Sswilcox * lncntp 95355d6bb5Sswilcox * Each entry is initialized to the di_link from the on-disk 96355d6bb5Sswilcox * inode. Each time we find one of those links, we decrement it. 97355d6bb5Sswilcox * Once all the traversing is done, we should have a zero. If we 98355d6bb5Sswilcox * have a positive value, then some reference disappeared 99355d6bb5Sswilcox * (probably from a directory that got nuked); deal with it by 100355d6bb5Sswilcox * fixing the count. If we have a negative value, then we found 101355d6bb5Sswilcox * an extra reference. This is a can't-happen, except in the 102355d6bb5Sswilcox * special case of when we reconnect a directory to its parent or 103355d6bb5Sswilcox * to lost+found. An exact match between lncntp[] and the on-disk 104355d6bb5Sswilcox * inode means it's completely unreferenced. 105355d6bb5Sswilcox * 106355d6bb5Sswilcox * aclphead 107355d6bb5Sswilcox * This is a hash table of the acl inodes in the filesystem. 108355d6bb5Sswilcox * 109355d6bb5Sswilcox * aclpsort 110355d6bb5Sswilcox * The same acls as in aclphead, but as a simple linear array. 111355d6bb5Sswilcox * It is used to hold the acl pointers for sorting and scanning 112355d6bb5Sswilcox * in pass3b. 113355d6bb5Sswilcox */ 114355d6bb5Sswilcox 1157c478bd9Sstevel@tonic-gate #include <stdio.h> 116355d6bb5Sswilcox #include <stdlib.h> 117355d6bb5Sswilcox #include <unistd.h> 1187c478bd9Sstevel@tonic-gate #include <sys/types.h> 119355d6bb5Sswilcox #include <sys/param.h> 1207c478bd9Sstevel@tonic-gate #include <sys/int_types.h> 1217c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 1227c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 1237c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 1247c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 1257c478bd9Sstevel@tonic-gate #include <sys/stat.h> 126355d6bb5Sswilcox #include <fcntl.h> 1277c478bd9Sstevel@tonic-gate #include <sys/wait.h> 1287c478bd9Sstevel@tonic-gate #include <sys/mnttab.h> 129355d6bb5Sswilcox #include <signal.h> 1307c478bd9Sstevel@tonic-gate #include <string.h> 1317c478bd9Sstevel@tonic-gate #include <sys/vfstab.h> 1327c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 133355d6bb5Sswilcox #include <sys/filio.h> 134355d6bb5Sswilcox #include <ustat.h> 1357c478bd9Sstevel@tonic-gate #include <errno.h> 136355d6bb5Sswilcox #include "fsck.h" 1377c478bd9Sstevel@tonic-gate 138355d6bb5Sswilcox static void usage(void); 139355d6bb5Sswilcox static long argtol(int, char *, char *, int); 140355d6bb5Sswilcox static void checkfilesys(char *); 141355d6bb5Sswilcox static void check_sanity(char *); 142355d6bb5Sswilcox static void report_limbo(const void *, VISIT, int); 143355d6bb5Sswilcox 144355d6bb5Sswilcox #define QUICK_CHECK 'm' /* are things ok according to superblock? */ 145355d6bb5Sswilcox #define ALL_no 'n' /* auto-answer interactive questions `no' */ 146355d6bb5Sswilcox #define ALL_NO 'N' /* auto-answer interactive questions `no' */ 147355d6bb5Sswilcox #define UFS_OPTS 'o' /* ufs-specific options, see subopts[] */ 148355d6bb5Sswilcox #define ECHO_CMD 'V' /* echo the command line */ 149355d6bb5Sswilcox #define ALL_yes 'y' /* auto-answer interactive questions `yes' */ 150355d6bb5Sswilcox #define ALL_YES 'Y' /* auto-answer interactive questions `yes' */ 151355d6bb5Sswilcox #define VERBOSE 'v' /* be chatty */ 152355d6bb5Sswilcox 153355d6bb5Sswilcox static char *subopts[] = { 154355d6bb5Sswilcox #define PREEN 0 /* non-interactive mode (parent is parallel) */ 1557c478bd9Sstevel@tonic-gate "p", 156355d6bb5Sswilcox #define BLOCK 1 /* alternate superblock */ 1577c478bd9Sstevel@tonic-gate "b", 158355d6bb5Sswilcox #define DEBUG 2 /* yammer */ 1597c478bd9Sstevel@tonic-gate "d", 160355d6bb5Sswilcox #define ONLY_WRITES 3 /* check all writable filesystems */ 1617c478bd9Sstevel@tonic-gate "w", 162355d6bb5Sswilcox #define FORCE 4 /* force checking, even if clean */ 1637c478bd9Sstevel@tonic-gate "f", 1647c478bd9Sstevel@tonic-gate NULL 1657c478bd9Sstevel@tonic-gate }; 1667c478bd9Sstevel@tonic-gate 167355d6bb5Sswilcox /* 168355d6bb5Sswilcox * Filesystems that are `magical' - if they exist in vfstab, 169355d6bb5Sswilcox * then they have to be mounted for the system to have gotten 170355d6bb5Sswilcox * far enough to be able to run fsck. Thus, don't get all 171355d6bb5Sswilcox * bent out of shape if we're asked to check it and it is mounted. 172355d6bb5Sswilcox */ 173355d6bb5Sswilcox char *magic_fs[] = { 174355d6bb5Sswilcox "", /* MAGIC_NONE, for normal filesystems */ 175355d6bb5Sswilcox "/", /* MAGIC_ROOT */ 176355d6bb5Sswilcox "/usr", /* MAGIC_USR */ 177355d6bb5Sswilcox NULL /* MAGIC_LIMIT */ 178355d6bb5Sswilcox }; 179355d6bb5Sswilcox 180d1a180b0Smaheshvs int 181355d6bb5Sswilcox main(int argc, char *argv[]) 1827c478bd9Sstevel@tonic-gate { 183355d6bb5Sswilcox int c; 184355d6bb5Sswilcox int wflag = 0; 185355d6bb5Sswilcox char *suboptions, *value; 186355d6bb5Sswilcox struct rlimit rlimit; 187355d6bb5Sswilcox extern int optind; 188355d6bb5Sswilcox extern char *optarg; 189355d6bb5Sswilcox 190355d6bb5Sswilcox while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) { 1917c478bd9Sstevel@tonic-gate switch (c) { 1927c478bd9Sstevel@tonic-gate 193355d6bb5Sswilcox case QUICK_CHECK: 1947c478bd9Sstevel@tonic-gate mflag++; 1957c478bd9Sstevel@tonic-gate break; 1967c478bd9Sstevel@tonic-gate 197355d6bb5Sswilcox case ALL_no: 198355d6bb5Sswilcox case ALL_NO: 1997c478bd9Sstevel@tonic-gate nflag++; 2007c478bd9Sstevel@tonic-gate yflag = 0; 2017c478bd9Sstevel@tonic-gate break; 2027c478bd9Sstevel@tonic-gate 203355d6bb5Sswilcox case VERBOSE: 204355d6bb5Sswilcox verbose++; 205355d6bb5Sswilcox break; 206355d6bb5Sswilcox 207355d6bb5Sswilcox case UFS_OPTS: 2087c478bd9Sstevel@tonic-gate /* 2097c478bd9Sstevel@tonic-gate * ufs specific options. 2107c478bd9Sstevel@tonic-gate */ 211355d6bb5Sswilcox if (optarg == NULL) { 212355d6bb5Sswilcox usage(); 213355d6bb5Sswilcox /* 214355d6bb5Sswilcox * lint does not believe this, nor does it 215355d6bb5Sswilcox * believe #pragma does_not_return(usage) 216355d6bb5Sswilcox */ 217355d6bb5Sswilcox /* NOTREACHED */ 218355d6bb5Sswilcox } 2197c478bd9Sstevel@tonic-gate suboptions = optarg; 2207c478bd9Sstevel@tonic-gate while (*suboptions != '\0') { 221355d6bb5Sswilcox switch (getsubopt(&suboptions, subopts, 222355d6bb5Sswilcox &value)) { 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate case PREEN: 2257c478bd9Sstevel@tonic-gate preen++; 2267c478bd9Sstevel@tonic-gate break; 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate case BLOCK: 229355d6bb5Sswilcox bflag = argtol(BLOCK, "block", 230355d6bb5Sswilcox value, 10); 231355d6bb5Sswilcox (void) printf("Alternate super block " 232355d6bb5Sswilcox "location: %ld.\n", 233355d6bb5Sswilcox (long)bflag); 2347c478bd9Sstevel@tonic-gate break; 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate case DEBUG: 2377c478bd9Sstevel@tonic-gate debug++; 238355d6bb5Sswilcox verbose++; 2397c478bd9Sstevel@tonic-gate break; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate case ONLY_WRITES: 2427c478bd9Sstevel@tonic-gate /* check only writable filesystems */ 2437c478bd9Sstevel@tonic-gate wflag++; 2447c478bd9Sstevel@tonic-gate break; 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate case FORCE: 2477c478bd9Sstevel@tonic-gate fflag++; 2487c478bd9Sstevel@tonic-gate break; 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate default: 2517c478bd9Sstevel@tonic-gate usage(); 2527c478bd9Sstevel@tonic-gate } 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate break; 2557c478bd9Sstevel@tonic-gate 256355d6bb5Sswilcox case ECHO_CMD: 2577c478bd9Sstevel@tonic-gate { 2587c478bd9Sstevel@tonic-gate int opt_count; 2597c478bd9Sstevel@tonic-gate char *opt_text; 2607c478bd9Sstevel@tonic-gate 261355d6bb5Sswilcox (void) printf("fsck -F ufs "); 2627c478bd9Sstevel@tonic-gate for (opt_count = 1; opt_count < argc; 263*ce37393aSowenr opt_count++) { 2647c478bd9Sstevel@tonic-gate opt_text = argv[opt_count]; 2657c478bd9Sstevel@tonic-gate if (opt_text) 266355d6bb5Sswilcox (void) printf("%s ", opt_text); 2677c478bd9Sstevel@tonic-gate } 268355d6bb5Sswilcox (void) printf("\n"); 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate break; 2717c478bd9Sstevel@tonic-gate 272355d6bb5Sswilcox case ALL_yes: 273355d6bb5Sswilcox case ALL_YES: 2747c478bd9Sstevel@tonic-gate yflag++; 2757c478bd9Sstevel@tonic-gate nflag = 0; 2767c478bd9Sstevel@tonic-gate break; 2777c478bd9Sstevel@tonic-gate 278355d6bb5Sswilcox default: 2797c478bd9Sstevel@tonic-gate usage(); 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate argc -= optind; 283355d6bb5Sswilcox argv += optind; 284355d6bb5Sswilcox 285355d6bb5Sswilcox if (argc == 0) 286355d6bb5Sswilcox usage(); 287355d6bb5Sswilcox 288355d6bb5Sswilcox rflag++; /* check raw devices where we can */ 289355d6bb5Sswilcox if (signal(SIGINT, SIG_IGN) != SIG_IGN) 2907c478bd9Sstevel@tonic-gate (void) signal(SIGINT, catch); 2917c478bd9Sstevel@tonic-gate if (preen) 2927c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, catchquit); 2937c478bd9Sstevel@tonic-gate 294355d6bb5Sswilcox /* 295355d6bb5Sswilcox * Push up our allowed memory limit so we can cope 296355d6bb5Sswilcox * with huge file systems. 297355d6bb5Sswilcox */ 298355d6bb5Sswilcox if (getrlimit(RLIMIT_DATA, &rlimit) == 0) { 299355d6bb5Sswilcox rlimit.rlim_cur = rlimit.rlim_max; 300355d6bb5Sswilcox (void) setrlimit(RLIMIT_DATA, &rlimit); 301355d6bb5Sswilcox } 302355d6bb5Sswilcox 303355d6bb5Sswilcox /* 304355d6bb5Sswilcox * There are a lot of places where we just exit if a problem is 305355d6bb5Sswilcox * found. This means that we won't necessarily check everything 306355d6bb5Sswilcox * we were asked to. It would be nice to do everything, and 307355d6bb5Sswilcox * then provide a summary when we're done. However, the 308355d6bb5Sswilcox * interface doesn't really allow us to do that in any useful 309355d6bb5Sswilcox * way. So, we'll just bail on the first unrecoverable 310355d6bb5Sswilcox * problem encountered. If we've been run by the generic 311355d6bb5Sswilcox * wrapper, we were only given one filesystem to check, so the 312355d6bb5Sswilcox * multi-fs case implies being run manually; that means the 313355d6bb5Sswilcox * user can rerun us on the remaining filesystems when it's 314355d6bb5Sswilcox * convenient for them. 315355d6bb5Sswilcox */ 316355d6bb5Sswilcox while (argc-- > 0) { 317355d6bb5Sswilcox if (wflag && !writable(*argv)) { 318355d6bb5Sswilcox (void) fprintf(stderr, "not writeable '%s'\n", *argv); 319355d6bb5Sswilcox argv++; 320355d6bb5Sswilcox if (exitstat == 0) 321355d6bb5Sswilcox exitstat = EXBADPARM; 322355d6bb5Sswilcox } else { 323355d6bb5Sswilcox checkfilesys(*argv++); 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate } 326355d6bb5Sswilcox if (interrupted) 327355d6bb5Sswilcox exitstat = EXSIGNAL; 328355d6bb5Sswilcox exit(exitstat); 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate 331355d6bb5Sswilcox /* 332355d6bb5Sswilcox * A relatively intelligent strtol(). Note that if str is NULL, we'll 333355d6bb5Sswilcox * exit, so ret does not actually need to be pre-initialized. Lint 334355d6bb5Sswilcox * doesn't believe this, and it's harmless enough to make lint happy here. 335355d6bb5Sswilcox */ 336355d6bb5Sswilcox static long 337355d6bb5Sswilcox argtol(int flag, char *req, char *str, int base) 338355d6bb5Sswilcox { 339355d6bb5Sswilcox char *cp = str; 340355d6bb5Sswilcox long ret = -1; 341355d6bb5Sswilcox 342355d6bb5Sswilcox errno = 0; 343355d6bb5Sswilcox if (str != NULL) 344355d6bb5Sswilcox ret = strtol(str, &cp, base); 345355d6bb5Sswilcox if (cp == str || *cp) { 346355d6bb5Sswilcox (void) fprintf(stderr, "-%c flag requires a %s\n", flag, req); 347355d6bb5Sswilcox exit(EXBADPARM); 348355d6bb5Sswilcox } 349355d6bb5Sswilcox if (errno != 0) { 350355d6bb5Sswilcox (void) fprintf(stderr, "-%c %s value out of range\n", 351355d6bb5Sswilcox flag, req); 352355d6bb5Sswilcox } 353355d6bb5Sswilcox 354355d6bb5Sswilcox return (ret); 355355d6bb5Sswilcox } 3567c478bd9Sstevel@tonic-gate 357355d6bb5Sswilcox /* 358355d6bb5Sswilcox * Check the specified file system. 359355d6bb5Sswilcox */ 360355d6bb5Sswilcox static void 361355d6bb5Sswilcox checkfilesys(char *filesys) 3627c478bd9Sstevel@tonic-gate { 3637c478bd9Sstevel@tonic-gate daddr32_t n_ffree, n_bfree; 3647c478bd9Sstevel@tonic-gate char *devstr; 365355d6bb5Sswilcox fsck_ino_t files; 366355d6bb5Sswilcox daddr32_t blks; 367355d6bb5Sswilcox fsck_ino_t inumber; 368355d6bb5Sswilcox int zlinks_printed; 369355d6bb5Sswilcox fsck_ino_t limbo_victim; 370355d6bb5Sswilcox double dbl_nffree, dbl_dsize; 371355d6bb5Sswilcox int quiet_dups; 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate mountfd = -1; 3747c478bd9Sstevel@tonic-gate hotroot = 0; 375355d6bb5Sswilcox mountedfs = M_NOMNT; 376355d6bb5Sswilcox reattached_dir = 0; 377355d6bb5Sswilcox broke_dir_link = 0; 378355d6bb5Sswilcox iscorrupt = 1; /* assume failure in setup() */ 3797c478bd9Sstevel@tonic-gate islog = 0; 3807c478bd9Sstevel@tonic-gate islogok = 0; 381355d6bb5Sswilcox overflowed_lf = 0; 3827c478bd9Sstevel@tonic-gate errorlocked = is_errorlocked(filesys); 383355d6bb5Sswilcox limbo_dirs = NULL; 3847c478bd9Sstevel@tonic-gate 385355d6bb5Sswilcox if ((devstr = setup(filesys)) == NULL) { 386355d6bb5Sswilcox if (!iscorrupt) { 3877c478bd9Sstevel@tonic-gate return; 388355d6bb5Sswilcox } 389355d6bb5Sswilcox 3907c478bd9Sstevel@tonic-gate if (preen) 3917c478bd9Sstevel@tonic-gate pfatal("CAN'T CHECK FILE SYSTEM."); 3927c478bd9Sstevel@tonic-gate if ((exitstat == 0) && (mflag)) 393355d6bb5Sswilcox exitstat = EXUMNTCHK; 3947c478bd9Sstevel@tonic-gate exit(exitstat); 395355d6bb5Sswilcox } else { 3967c478bd9Sstevel@tonic-gate devname = devstr; 397355d6bb5Sswilcox } 398355d6bb5Sswilcox 399355d6bb5Sswilcox if (mflag) { 400355d6bb5Sswilcox check_sanity(filesys); 401355d6bb5Sswilcox /* NOTREACHED */ 402355d6bb5Sswilcox } 403355d6bb5Sswilcox 4047c478bd9Sstevel@tonic-gate if (debug) 4057c478bd9Sstevel@tonic-gate printclean(); 406355d6bb5Sswilcox 407355d6bb5Sswilcox iscorrupt = 0; /* setup() succeeded, assume good filesystem */ 408355d6bb5Sswilcox 4097c478bd9Sstevel@tonic-gate /* 4107c478bd9Sstevel@tonic-gate * 1: scan inodes tallying blocks used 4117c478bd9Sstevel@tonic-gate */ 412355d6bb5Sswilcox if (!preen) { 413355d6bb5Sswilcox /* hotroot is reported as such in setup() if debug is on */ 414355d6bb5Sswilcox if (mountedfs != M_NOMNT) 415355d6bb5Sswilcox (void) printf("** Currently Mounted on %s\n", 416355d6bb5Sswilcox sblock.fs_fsmnt); 4177c478bd9Sstevel@tonic-gate else 418355d6bb5Sswilcox (void) printf("** Last Mounted on %s\n", 419355d6bb5Sswilcox sblock.fs_fsmnt); 420355d6bb5Sswilcox (void) printf("** Phase 1 - Check Blocks and Sizes\n"); 4217c478bd9Sstevel@tonic-gate } 4227c478bd9Sstevel@tonic-gate pass1(); 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate /* 4257c478bd9Sstevel@tonic-gate * 1b: locate first references to duplicates, if any 4267c478bd9Sstevel@tonic-gate */ 427355d6bb5Sswilcox if (have_dups()) { 4287c478bd9Sstevel@tonic-gate if (preen) 429355d6bb5Sswilcox pfatal("INTERNAL ERROR: dups with -o p"); 430355d6bb5Sswilcox (void) printf("** Phase 1b - Rescan For More DUPS\n"); 4317c478bd9Sstevel@tonic-gate pass1b(); 4327c478bd9Sstevel@tonic-gate } 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate /* 435b9a41fd3Sswilcox * 2: traverse directories from root to mark all connected directories 436355d6bb5Sswilcox */ 437355d6bb5Sswilcox if (!preen) 438355d6bb5Sswilcox (void) printf("** Phase 2 - Check Pathnames\n"); 439b9a41fd3Sswilcox pass2(); 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* 442355d6bb5Sswilcox * 3a: scan inodes looking for disconnected directories. 4437c478bd9Sstevel@tonic-gate */ 444355d6bb5Sswilcox if (!preen) 445355d6bb5Sswilcox (void) printf("** Phase 3a - Check Connectivity\n"); 446355d6bb5Sswilcox pass3a(); 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate /* 4497c478bd9Sstevel@tonic-gate * 3b: check acls 4507c478bd9Sstevel@tonic-gate */ 451355d6bb5Sswilcox if (!preen) 452355d6bb5Sswilcox (void) printf("** Phase 3b - Verify Shadows/ACLs\n"); 4537c478bd9Sstevel@tonic-gate pass3b(); 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate /* 4567c478bd9Sstevel@tonic-gate * 4: scan inodes looking for disconnected files; check reference counts 4577c478bd9Sstevel@tonic-gate */ 458355d6bb5Sswilcox if (!preen) 459355d6bb5Sswilcox (void) printf("** Phase 4 - Check Reference Counts\n"); 4607c478bd9Sstevel@tonic-gate pass4(); 4617c478bd9Sstevel@tonic-gate 4627c478bd9Sstevel@tonic-gate /* 4637c478bd9Sstevel@tonic-gate * 5: check and repair resource counts in cylinder groups 4647c478bd9Sstevel@tonic-gate */ 465355d6bb5Sswilcox if (!preen) 466355d6bb5Sswilcox (void) printf("** Phase 5 - Check Cylinder Groups\n"); 467355d6bb5Sswilcox recount: 4687c478bd9Sstevel@tonic-gate pass5(); 4697c478bd9Sstevel@tonic-gate 470355d6bb5Sswilcox if (overflowed_lf) { 471355d6bb5Sswilcox iscorrupt = 1; 472355d6bb5Sswilcox } 473355d6bb5Sswilcox 474504a199eSswilcox if (!nflag && mountedfs == M_RW) { 4755b4dc236Smishra (void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n"); 4765b4dc236Smishra rerun = 1; 477355d6bb5Sswilcox } 478355d6bb5Sswilcox 479355d6bb5Sswilcox if (have_dups()) { 480355d6bb5Sswilcox quiet_dups = (reply("LIST REMAINING DUPS") == 0); 481355d6bb5Sswilcox if (report_dups(quiet_dups) > 0) 482355d6bb5Sswilcox iscorrupt = 1; 483355d6bb5Sswilcox 484355d6bb5Sswilcox (void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO " 485355d6bb5Sswilcox "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n"); 486355d6bb5Sswilcox } 487355d6bb5Sswilcox 488355d6bb5Sswilcox if (limbo_dirs != NULL) { 489355d6bb5Sswilcox /* 490355d6bb5Sswilcox * Don't force iscorrupt, as this is sufficiently 491355d6bb5Sswilcox * harmless that the filesystem can be mounted and 492355d6bb5Sswilcox * used. We just leak some inodes and/or blocks. 493355d6bb5Sswilcox */ 494355d6bb5Sswilcox pwarn("Orphan directories not cleared or reconnected:\n"); 495355d6bb5Sswilcox 496355d6bb5Sswilcox twalk(limbo_dirs, report_limbo); 497355d6bb5Sswilcox 498355d6bb5Sswilcox while (limbo_dirs != NULL) { 499355d6bb5Sswilcox limbo_victim = *(fsck_ino_t *)limbo_dirs; 500355d6bb5Sswilcox if (limbo_victim != NULL) { 501355d6bb5Sswilcox (void) tdelete((void *)limbo_victim, 502355d6bb5Sswilcox &limbo_dirs, 503355d6bb5Sswilcox ino_t_cmp); 504355d6bb5Sswilcox } 505355d6bb5Sswilcox } 506355d6bb5Sswilcox 507355d6bb5Sswilcox rerun = 1; 508355d6bb5Sswilcox } 509355d6bb5Sswilcox 510355d6bb5Sswilcox if (iscorrupt) { 5115b4dc236Smishra if (mountedfs == M_RW) 5125b4dc236Smishra (void) printf("FS IS MOUNTED R/W AND" 5135b4dc236Smishra " FSCK DID ITS BEST TO FIX" 5145b4dc236Smishra " INCONSISTENCIES.\n"); 5155b4dc236Smishra else 5165b4dc236Smishra (void) printf("FILESYSTEM MAY STILL BE" 5175b4dc236Smishra " INCONSISTENT.\n"); 518355d6bb5Sswilcox rerun = 1; 519355d6bb5Sswilcox } 520355d6bb5Sswilcox 521355d6bb5Sswilcox /* 522355d6bb5Sswilcox * iscorrupt must be stable at this point. 523355d6bb5Sswilcox * updateclean() returns true when it had to discard the log. 524355d6bb5Sswilcox * This can only happen once, since sblock.fs_logbno gets 525355d6bb5Sswilcox * cleared as part of that operation. 526355d6bb5Sswilcox */ 527355d6bb5Sswilcox if (updateclean()) { 528355d6bb5Sswilcox if (!preen) 529355d6bb5Sswilcox (void) printf( 530355d6bb5Sswilcox "Log was discarded, updating cyl groups\n"); 531355d6bb5Sswilcox goto recount; 532355d6bb5Sswilcox } 533355d6bb5Sswilcox 5347c478bd9Sstevel@tonic-gate if (debug) 5357c478bd9Sstevel@tonic-gate printclean(); 5367c478bd9Sstevel@tonic-gate 537355d6bb5Sswilcox ckfini(); 538355d6bb5Sswilcox 5397c478bd9Sstevel@tonic-gate /* 5407c478bd9Sstevel@tonic-gate * print out summary statistics 5417c478bd9Sstevel@tonic-gate */ 5427c478bd9Sstevel@tonic-gate n_ffree = sblock.fs_cstotal.cs_nffree; 5437c478bd9Sstevel@tonic-gate n_bfree = sblock.fs_cstotal.cs_nbfree; 544355d6bb5Sswilcox files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files; 545355d6bb5Sswilcox blks = n_blks + 546355d6bb5Sswilcox sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); 547355d6bb5Sswilcox blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); 548355d6bb5Sswilcox blks += howmany(sblock.fs_cssize, sblock.fs_fsize); 549355d6bb5Sswilcox blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks; 550355d6bb5Sswilcox if (debug && (files > 0 || blks > 0)) { 551355d6bb5Sswilcox countdirs = sblock.fs_cstotal.cs_ndir - countdirs; 552355d6bb5Sswilcox pwarn("Reclaimed: %d directories, %d files, %lld fragments\n", 553355d6bb5Sswilcox countdirs, files - countdirs, 554355d6bb5Sswilcox (longlong_t)blks); 555355d6bb5Sswilcox } 556355d6bb5Sswilcox 557355d6bb5Sswilcox dbl_nffree = (double)n_ffree; 558355d6bb5Sswilcox dbl_dsize = (double)sblock.fs_dsize; 559355d6bb5Sswilcox 560355d6bb5Sswilcox if (!verbose) { 561355d6bb5Sswilcox /* 562355d6bb5Sswilcox * Done as one big string to try for a single write, 563355d6bb5Sswilcox * so the output doesn't get interleaved with other 564355d6bb5Sswilcox * preening fscks. 565355d6bb5Sswilcox */ 566355d6bb5Sswilcox pwarn("%ld files, %lld used, %lld free " 567355d6bb5Sswilcox "(%lld frags, %lld blocks, %.1f%% fragmentation)\n", 568355d6bb5Sswilcox (long)n_files, (longlong_t)n_blks, 569355d6bb5Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree, 570355d6bb5Sswilcox (longlong_t)n_ffree, (longlong_t)n_bfree, 571355d6bb5Sswilcox (dbl_nffree * 100.0) / dbl_dsize); 572355d6bb5Sswilcox } else { 573355d6bb5Sswilcox pwarn("\nFilesystem summary:\n"); 574355d6bb5Sswilcox pwarn("Inodes in use: %ld\n", (long)n_files); 575355d6bb5Sswilcox pwarn("Blocks in use: %lld\n", (longlong_t)n_blks); 576355d6bb5Sswilcox pwarn("Total free fragments: %lld\n", 577355d6bb5Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree); 578355d6bb5Sswilcox pwarn("Free fragments not in blocks: %lld\n", 579355d6bb5Sswilcox (longlong_t)n_ffree); 580355d6bb5Sswilcox pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree); 581355d6bb5Sswilcox pwarn("Fragment/block fragmentation: %.1f%%\n", 582355d6bb5Sswilcox (dbl_nffree * 100.0) / dbl_dsize); 583355d6bb5Sswilcox pwarn(""); 584355d6bb5Sswilcox 585355d6bb5Sswilcox if (files < 0) 586355d6bb5Sswilcox pwarn("%d inodes missing\n", -files); 587355d6bb5Sswilcox if (blks < 0) 588355d6bb5Sswilcox pwarn("%lld blocks missing\n", -(longlong_t)blks); 589355d6bb5Sswilcox 590355d6bb5Sswilcox zlinks_printed = 0; 591355d6bb5Sswilcox for (inumber = UFSROOTINO; inumber < maxino; inumber++) { 592355d6bb5Sswilcox if (S_IS_ZLINK(statemap[inumber])) { 593355d6bb5Sswilcox if (zlinks_printed == 0) { 594355d6bb5Sswilcox pwarn("The following zero " 595355d6bb5Sswilcox "link count inodes remain:"); 596355d6bb5Sswilcox } 597355d6bb5Sswilcox if (zlinks_printed) { 598355d6bb5Sswilcox if ((zlinks_printed % 9) == 0) 599355d6bb5Sswilcox (void) puts(",\n"); 600355d6bb5Sswilcox else 601355d6bb5Sswilcox (void) puts(", "); 602355d6bb5Sswilcox } 603355d6bb5Sswilcox (void) printf("%u", inumber); 604355d6bb5Sswilcox zlinks_printed++; 605355d6bb5Sswilcox } 6067c478bd9Sstevel@tonic-gate } 607355d6bb5Sswilcox if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0)) 608355d6bb5Sswilcox (void) putchar('\n'); 6097c478bd9Sstevel@tonic-gate } 610355d6bb5Sswilcox 611355d6bb5Sswilcox /* 612355d6bb5Sswilcox * Clean up after ourselves, so we can do the next filesystem. 613355d6bb5Sswilcox */ 614355d6bb5Sswilcox free_dup_state(); 6157c478bd9Sstevel@tonic-gate inocleanup(); 6167c478bd9Sstevel@tonic-gate free(blockmap); 6177c478bd9Sstevel@tonic-gate free(statemap); 618355d6bb5Sswilcox free((void *)lncntp); 6197c478bd9Sstevel@tonic-gate lncntp = NULL; 620355d6bb5Sswilcox blockmap = NULL; 621355d6bb5Sswilcox statemap = NULL; 622355d6bb5Sswilcox if (iscorrupt && exitstat == 0) 623355d6bb5Sswilcox exitstat = EXFNDERRS; 624355d6bb5Sswilcox if (fsmodified) 625355d6bb5Sswilcox (void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 626355d6bb5Sswilcox if (overflowed_lf) 627355d6bb5Sswilcox (void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n", 628355d6bb5Sswilcox lfname); 629355d6bb5Sswilcox if (reattached_dir) { 630355d6bb5Sswilcox (void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK " 631355d6bb5Sswilcox "COUNTS MAY NOT BE CORRECT.\n"); 632355d6bb5Sswilcox rerun = 1; 633355d6bb5Sswilcox } 634355d6bb5Sswilcox if (broke_dir_link) { 635355d6bb5Sswilcox (void) printf( 636355d6bb5Sswilcox "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n"); 637355d6bb5Sswilcox rerun = 1; 638355d6bb5Sswilcox } 6397c478bd9Sstevel@tonic-gate if (iscorrupt) 640355d6bb5Sswilcox (void) printf("***** FILE SYSTEM IS BAD *****\n"); 6417c478bd9Sstevel@tonic-gate 6425b4dc236Smishra if (rerun) { 6435b4dc236Smishra if (mountedfs == M_RW) 6445b4dc236Smishra (void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED" 6455b4dc236Smishra " FILE SYSTEM *****\n"); 6465b4dc236Smishra else 6475b4dc236Smishra (void) printf("\n***** PLEASE RERUN FSCK *****\n"); 6485b4dc236Smishra } 6497c478bd9Sstevel@tonic-gate 650355d6bb5Sswilcox if ((exitstat == 0) && 651355d6bb5Sswilcox (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) { 652355d6bb5Sswilcox exitstat = EXROOTOKAY; 653355d6bb5Sswilcox } 6547c478bd9Sstevel@tonic-gate 655355d6bb5Sswilcox if ((exitstat == 0) && rerun) 656355d6bb5Sswilcox exitstat = EXFNDERRS; 657355d6bb5Sswilcox 658355d6bb5Sswilcox if (mountedfs != M_NOMNT) { 659355d6bb5Sswilcox if (!fsmodified) 660355d6bb5Sswilcox return; 661355d6bb5Sswilcox /* 662355d6bb5Sswilcox * _FIOFFS is much more effective than a simple sync(). 663355d6bb5Sswilcox * Note that the original fswritefd was discarded in 664355d6bb5Sswilcox * ckfini(). 665355d6bb5Sswilcox */ 666355d6bb5Sswilcox fswritefd = open(devstr, O_RDWR, 0); 667355d6bb5Sswilcox if (fswritefd != -1) { 668355d6bb5Sswilcox (void) ioctl(fswritefd, _FIOFFS, NULL); 669355d6bb5Sswilcox (void) close(fswritefd); 6707c478bd9Sstevel@tonic-gate } 671355d6bb5Sswilcox 672355d6bb5Sswilcox if (!preen) 673355d6bb5Sswilcox (void) printf("\n***** REBOOT NOW *****\n"); 674355d6bb5Sswilcox 675355d6bb5Sswilcox exitstat = EXREBOOTNOW; 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate } 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate /* 680355d6bb5Sswilcox * fsck -m: does the filesystem pass cursory examination 681355d6bb5Sswilcox * 682355d6bb5Sswilcox * XXX This is very redundant with setup(). The right thing would be 683355d6bb5Sswilcox * for setup() to modify its behaviour when mflag is set (less 684355d6bb5Sswilcox * chatty, exit instead of return, etc). 6857c478bd9Sstevel@tonic-gate */ 6867c478bd9Sstevel@tonic-gate void 687355d6bb5Sswilcox check_sanity(char *filename) 6887c478bd9Sstevel@tonic-gate { 6897c478bd9Sstevel@tonic-gate struct stat64 stbd, stbr; 6907c478bd9Sstevel@tonic-gate char *devname; 691355d6bb5Sswilcox struct ustat usb; 6927c478bd9Sstevel@tonic-gate char vfsfilename[MAXPATHLEN]; 6937c478bd9Sstevel@tonic-gate struct vfstab vfsbuf; 6947c478bd9Sstevel@tonic-gate FILE *vfstab; 6957c478bd9Sstevel@tonic-gate struct statvfs vfs_stat; 696355d6bb5Sswilcox int found_magic[MAGIC_LIMIT]; 697355d6bb5Sswilcox int magic_cnt; 698355d6bb5Sswilcox int is_magic = 0; 699355d6bb5Sswilcox int is_block; 700355d6bb5Sswilcox 701355d6bb5Sswilcox (void) memset((void *)found_magic, 0, sizeof (found_magic)); 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate if (stat64(filename, &stbd) < 0) { 704355d6bb5Sswilcox (void) fprintf(stderr, 7057c478bd9Sstevel@tonic-gate "ufs fsck: sanity check failed : cannot stat %s\n", filename); 706355d6bb5Sswilcox exit(EXNOSTAT); 7077c478bd9Sstevel@tonic-gate } 7087c478bd9Sstevel@tonic-gate 709355d6bb5Sswilcox if ((stbd.st_mode & S_IFMT) == S_IFBLK) { 7107c478bd9Sstevel@tonic-gate is_block = 1; 711355d6bb5Sswilcox } else if ((stbd.st_mode & S_IFMT) == S_IFCHR) { 7127c478bd9Sstevel@tonic-gate is_block = 0; 713355d6bb5Sswilcox } else { 714355d6bb5Sswilcox /* 715355d6bb5Sswilcox * In !mflag mode, we allow checking the contents 716355d6bb5Sswilcox * of a file. Since this is intended primarily for 717355d6bb5Sswilcox * speeding up boot-time checks and allowing for a 718355d6bb5Sswilcox * file complicates the ok-input tests, we'll disallow 719355d6bb5Sswilcox * that option. 720355d6bb5Sswilcox */ 721355d6bb5Sswilcox (void) fprintf(stderr, 722*ce37393aSowenr "ufs fsck: sanity check failed: " 723*ce37393aSowenr "%s not block or character device\n", filename); 724355d6bb5Sswilcox exit(EXNOSTAT); 7257c478bd9Sstevel@tonic-gate } 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gate /* 7287c478bd9Sstevel@tonic-gate * Determine if this is the root file system via vfstab. Give up 729355d6bb5Sswilcox * silently on failures. The whole point of this is to be tolerant 730355d6bb5Sswilcox * of the magic file systems being already mounted. 7317c478bd9Sstevel@tonic-gate */ 7327c478bd9Sstevel@tonic-gate if ((vfstab = fopen(VFSTAB, "r")) != 0) { 733355d6bb5Sswilcox for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) { 734355d6bb5Sswilcox if (magic_cnt == MAGIC_NONE) 735355d6bb5Sswilcox continue; 736355d6bb5Sswilcox if (getvfsfile(vfstab, &vfsbuf, 737355d6bb5Sswilcox magic_fs[magic_cnt]) == 0) { 738355d6bb5Sswilcox if (is_block) 739355d6bb5Sswilcox devname = vfsbuf.vfs_special; 740355d6bb5Sswilcox else 741355d6bb5Sswilcox devname = vfsbuf.vfs_fsckdev; 742355d6bb5Sswilcox if (stat64(devname, &stbr) == 0) { 743355d6bb5Sswilcox if (stbr.st_rdev == stbd.st_rdev) { 744355d6bb5Sswilcox found_magic[magic_cnt] = 1; 745355d6bb5Sswilcox is_magic = magic_cnt; 746355d6bb5Sswilcox break; 747355d6bb5Sswilcox } 748355d6bb5Sswilcox } 749355d6bb5Sswilcox } 7507c478bd9Sstevel@tonic-gate } 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate 7537c478bd9Sstevel@tonic-gate /* 754355d6bb5Sswilcox * Only works if filename is a block device or if 755355d6bb5Sswilcox * character and block device has the same dev_t value. 756355d6bb5Sswilcox * This is currently true, but nothing really forces it. 7577c478bd9Sstevel@tonic-gate */ 758355d6bb5Sswilcox if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) { 759355d6bb5Sswilcox (void) fprintf(stderr, 760355d6bb5Sswilcox "ufs fsck: sanity check: %s already mounted\n", filename); 761355d6bb5Sswilcox exit(EXMOUNTED); 7627c478bd9Sstevel@tonic-gate } 7637c478bd9Sstevel@tonic-gate 764355d6bb5Sswilcox if (is_magic) { 765355d6bb5Sswilcox (void) strcpy(vfsfilename, magic_fs[is_magic]); 7667c478bd9Sstevel@tonic-gate if (statvfs(vfsfilename, &vfs_stat) != 0) { 767355d6bb5Sswilcox (void) fprintf(stderr, "ufs fsck: Cannot stat %s\n", 768355d6bb5Sswilcox vfsfilename); 769355d6bb5Sswilcox exit(EXNOSTAT); 7707c478bd9Sstevel@tonic-gate } 7717c478bd9Sstevel@tonic-gate 7727c478bd9Sstevel@tonic-gate if (!(vfs_stat.f_flag & ST_RDONLY)) { 7737c478bd9Sstevel@tonic-gate /* 7747c478bd9Sstevel@tonic-gate * The file system is mounted read/write 775355d6bb5Sswilcox * We need to exit saying this. If it's only 7767c478bd9Sstevel@tonic-gate * mounted readonly, we can continue. 7777c478bd9Sstevel@tonic-gate */ 7787c478bd9Sstevel@tonic-gate 779355d6bb5Sswilcox (void) fprintf(stderr, 780*ce37393aSowenr "ufs fsck: sanity check:" 781*ce37393aSowenr "%s already mounted read/write\n", filename); 782355d6bb5Sswilcox exit(EXMOUNTED); 7837c478bd9Sstevel@tonic-gate } 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate 7867c478bd9Sstevel@tonic-gate /* 787355d6bb5Sswilcox * We know that at boot, the ufs root file system is mounted 788355d6bb5Sswilcox * read-only first. After fsck runs, it is remounted as 789355d6bb5Sswilcox * read-write. Therefore, we do not need to check for different 790355d6bb5Sswilcox * values for fs_state between the root file system and the 791355d6bb5Sswilcox * rest of the file systems. 7927c478bd9Sstevel@tonic-gate */ 7937c478bd9Sstevel@tonic-gate if (islog && !islogok) { 794355d6bb5Sswilcox (void) fprintf(stderr, 795355d6bb5Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename); 796355d6bb5Sswilcox exit(EXUMNTCHK); 7977c478bd9Sstevel@tonic-gate } 7987c478bd9Sstevel@tonic-gate if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) && 799*ce37393aSowenr (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE || 800*ce37393aSowenr (sblock.fs_clean == FSLOG && islog))) { 801355d6bb5Sswilcox (void) fprintf(stderr, 802355d6bb5Sswilcox "ufs fsck: sanity check: %s okay\n", filename); 8037c478bd9Sstevel@tonic-gate } else { 804355d6bb5Sswilcox (void) fprintf(stderr, 805355d6bb5Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename); 806355d6bb5Sswilcox exit(EXUMNTCHK); 8077c478bd9Sstevel@tonic-gate } 808355d6bb5Sswilcox exit(EXOKAY); 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate 811355d6bb5Sswilcox caddr_t 812355d6bb5Sswilcox hasvfsopt(struct vfstab *vfs, char *opt) 8137c478bd9Sstevel@tonic-gate { 814355d6bb5Sswilcox struct mnttab mtab; 8157c478bd9Sstevel@tonic-gate 816355d6bb5Sswilcox if (vfs->vfs_mntopts == NULL) 817355d6bb5Sswilcox return (NULL); 818355d6bb5Sswilcox mtab.mnt_mntopts = vfs->vfs_mntopts; 819355d6bb5Sswilcox return (hasmntopt(&mtab, opt)); 8207c478bd9Sstevel@tonic-gate } 8217c478bd9Sstevel@tonic-gate 822355d6bb5Sswilcox void 823355d6bb5Sswilcox usage(void) 8247c478bd9Sstevel@tonic-gate { 825355d6bb5Sswilcox (void) fprintf(stderr, 826355d6bb5Sswilcox "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] " 827355d6bb5Sswilcox "[-o p,b=#,w,f] [special ....]\n"); 8287c478bd9Sstevel@tonic-gate 829355d6bb5Sswilcox exit(EXBADPARM); 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate 832355d6bb5Sswilcox /*ARGSUSED*/ 833355d6bb5Sswilcox static void 834355d6bb5Sswilcox report_limbo(const void *node, VISIT order, int level) 8357c478bd9Sstevel@tonic-gate { 836355d6bb5Sswilcox fsck_ino_t ino = *(fsck_ino_t *)node; 8377c478bd9Sstevel@tonic-gate 838355d6bb5Sswilcox if ((order == postorder) || (order == leaf)) { 839355d6bb5Sswilcox (void) printf(" Inode %d\n", ino); 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate } 842