17c478bd9Sstevel@tonic-gate /* 2*262234c2SMilan Cermak * Copyright 2009 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 29355d6bb5Sswilcox /* 30355d6bb5Sswilcox * In-core structures: 31355d6bb5Sswilcox * blockmap[] 32355d6bb5Sswilcox * A bitmap of block usage very similar to what's on disk, but 33355d6bb5Sswilcox * for the entire filesystem rather than just a cylinder group. 34355d6bb5Sswilcox * Zero indicates free, one indicates allocated. Note that this 35355d6bb5Sswilcox * is opposite the interpretation of a cylinder group's free block 36355d6bb5Sswilcox * bitmap. 37355d6bb5Sswilcox * 38355d6bb5Sswilcox * statemap[] 39355d6bb5Sswilcox * Tracks what is known about each inode in the filesystem. 40355d6bb5Sswilcox * The fundamental state value is one of USTATE, FSTATE, DSTATE, 41355d6bb5Sswilcox * or SSTATE (unallocated, file, directory, shadow/acl). 42355d6bb5Sswilcox * 43355d6bb5Sswilcox * There are optional modifying attributes as well: INZLINK, 44355d6bb5Sswilcox * INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix 45355d6bb5Sswilcox * stands for inode. INZLINK declares that no links (di_nlink == 46355d6bb5Sswilcox * 0) to the inode have been found. It is used instead of 47355d6bb5Sswilcox * examining di_nlink because we've always got the statemap[] in 48355d6bb5Sswilcox * memory, and on average the odds are against having any given 49355d6bb5Sswilcox * inode in the cache. INFOUND flags that an inode was 50355d6bb5Sswilcox * encountered during the descent of the filesystem. In other 51355d6bb5Sswilcox * words, it's reachable, either by name or by being an acl or 52355d6bb5Sswilcox * attribute. INCLEAR declares an intent to call clri() on an 53ce37393aSowenr * inode. The INCLEAR and INZLINK attributes are treated in a 54ce37393aSowenr * mutually exclusive manner with INCLEAR taking higher precedence 55ce37393aSowenr * as the intent is to clear the inode. 56355d6bb5Sswilcox * 57355d6bb5Sswilcox * INORPHAN indicates that the inode has already been seen once 58355d6bb5Sswilcox * in pass3 and determined to be an orphan, so any additional 59355d6bb5Sswilcox * encounters don't need to waste cycles redetermining that status. 60355d6bb5Sswilcox * It also means we don't ask the user about doing something to the 61355d6bb5Sswilcox * inode N times. 62355d6bb5Sswilcox * 63355d6bb5Sswilcox * INDELAYD marks inodes that pass1 determined needed to be truncated. 64355d6bb5Sswilcox * They can't be truncated during that pass, because it depends on 65355d6bb5Sswilcox * having a stable world for building the block and inode tables from. 66355d6bb5Sswilcox * 67355d6bb5Sswilcox * The IN flags rarely used directly, but instead are 68355d6bb5Sswilcox * pre-combined through the {D,F,S}ZLINK, DFOUND, and 69355d6bb5Sswilcox * {D,F,S}CLEAR convenience macros. This mainly matters when 70355d6bb5Sswilcox * trying to use grep on the source. 71355d6bb5Sswilcox * 72355d6bb5Sswilcox * Three state-test macros are provided: S_IS_DUNFOUND(), 73355d6bb5Sswilcox * S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an 74355d6bb5Sswilcox * inode's state indicates that it is either a simple directory 75355d6bb5Sswilcox * (DSTATE without the INFOUND or INCLEAR modifiers) or a 76355d6bb5Sswilcox * directory with the INZLINK modifier set. By definition, if a 77355d6bb5Sswilcox * directory has zero links, then it can't be found. As for 78355d6bb5Sswilcox * S_IS_DVALID(), it decides if a directory inode is alive. 79355d6bb5Sswilcox * Effectively, this translates to whether or not it's been 80355d6bb5Sswilcox * flagged for clearing. If not, then it's valid for current 81355d6bb5Sswilcox * purposes. This is true even if INZLINK is set, as we may find 82355d6bb5Sswilcox * a reference to it later. Finally, S_IS_ZLINK() just picks out 83355d6bb5Sswilcox * the INZLINK flag from the state. 84355d6bb5Sswilcox * 85355d6bb5Sswilcox * The S_*() macros all work on a state value. To simplify a 86355d6bb5Sswilcox * bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode 87355d6bb5Sswilcox * number argument. The inode is looked up in the statemap[] and 88355d6bb5Sswilcox * the result handed off to the corresponding S_*() macro. This 89355d6bb5Sswilcox * is partly a holdover from working with different data 90355d6bb5Sswilcox * structures (with the same net intent) in the BSD fsck. 91355d6bb5Sswilcox * 92355d6bb5Sswilcox * lncntp 93355d6bb5Sswilcox * Each entry is initialized to the di_link from the on-disk 94355d6bb5Sswilcox * inode. Each time we find one of those links, we decrement it. 95355d6bb5Sswilcox * Once all the traversing is done, we should have a zero. If we 96355d6bb5Sswilcox * have a positive value, then some reference disappeared 97355d6bb5Sswilcox * (probably from a directory that got nuked); deal with it by 98355d6bb5Sswilcox * fixing the count. If we have a negative value, then we found 99355d6bb5Sswilcox * an extra reference. This is a can't-happen, except in the 100355d6bb5Sswilcox * special case of when we reconnect a directory to its parent or 101355d6bb5Sswilcox * to lost+found. An exact match between lncntp[] and the on-disk 102355d6bb5Sswilcox * inode means it's completely unreferenced. 103355d6bb5Sswilcox * 104355d6bb5Sswilcox * aclphead 105355d6bb5Sswilcox * This is a hash table of the acl inodes in the filesystem. 106355d6bb5Sswilcox * 107355d6bb5Sswilcox * aclpsort 108355d6bb5Sswilcox * The same acls as in aclphead, but as a simple linear array. 109355d6bb5Sswilcox * It is used to hold the acl pointers for sorting and scanning 110355d6bb5Sswilcox * in pass3b. 111355d6bb5Sswilcox */ 112355d6bb5Sswilcox 1137c478bd9Sstevel@tonic-gate #include <stdio.h> 114355d6bb5Sswilcox #include <stdlib.h> 115355d6bb5Sswilcox #include <unistd.h> 1167c478bd9Sstevel@tonic-gate #include <sys/types.h> 117355d6bb5Sswilcox #include <sys/param.h> 1187c478bd9Sstevel@tonic-gate #include <sys/int_types.h> 1197c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 1207c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 1217c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 1227c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 1237c478bd9Sstevel@tonic-gate #include <sys/stat.h> 124355d6bb5Sswilcox #include <fcntl.h> 1257c478bd9Sstevel@tonic-gate #include <sys/wait.h> 1267c478bd9Sstevel@tonic-gate #include <sys/mnttab.h> 127355d6bb5Sswilcox #include <signal.h> 1287c478bd9Sstevel@tonic-gate #include <string.h> 1297c478bd9Sstevel@tonic-gate #include <sys/vfstab.h> 1307c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 131355d6bb5Sswilcox #include <sys/filio.h> 132355d6bb5Sswilcox #include <ustat.h> 1337c478bd9Sstevel@tonic-gate #include <errno.h> 134355d6bb5Sswilcox #include "fsck.h" 1357c478bd9Sstevel@tonic-gate 136355d6bb5Sswilcox static void usage(void); 137355d6bb5Sswilcox static long argtol(int, char *, char *, int); 138355d6bb5Sswilcox static void checkfilesys(char *); 139355d6bb5Sswilcox static void check_sanity(char *); 140355d6bb5Sswilcox static void report_limbo(const void *, VISIT, int); 141355d6bb5Sswilcox 142355d6bb5Sswilcox #define QUICK_CHECK 'm' /* are things ok according to superblock? */ 143355d6bb5Sswilcox #define ALL_no 'n' /* auto-answer interactive questions `no' */ 144355d6bb5Sswilcox #define ALL_NO 'N' /* auto-answer interactive questions `no' */ 145355d6bb5Sswilcox #define UFS_OPTS 'o' /* ufs-specific options, see subopts[] */ 146355d6bb5Sswilcox #define ECHO_CMD 'V' /* echo the command line */ 147355d6bb5Sswilcox #define ALL_yes 'y' /* auto-answer interactive questions `yes' */ 148355d6bb5Sswilcox #define ALL_YES 'Y' /* auto-answer interactive questions `yes' */ 149355d6bb5Sswilcox #define VERBOSE 'v' /* be chatty */ 150355d6bb5Sswilcox 151355d6bb5Sswilcox static char *subopts[] = { 152355d6bb5Sswilcox #define PREEN 0 /* non-interactive mode (parent is parallel) */ 1537c478bd9Sstevel@tonic-gate "p", 154355d6bb5Sswilcox #define BLOCK 1 /* alternate superblock */ 1557c478bd9Sstevel@tonic-gate "b", 156355d6bb5Sswilcox #define DEBUG 2 /* yammer */ 1577c478bd9Sstevel@tonic-gate "d", 158355d6bb5Sswilcox #define ONLY_WRITES 3 /* check all writable filesystems */ 1597c478bd9Sstevel@tonic-gate "w", 160355d6bb5Sswilcox #define FORCE 4 /* force checking, even if clean */ 1617c478bd9Sstevel@tonic-gate "f", 1627c478bd9Sstevel@tonic-gate NULL 1637c478bd9Sstevel@tonic-gate }; 1647c478bd9Sstevel@tonic-gate 165355d6bb5Sswilcox /* 166355d6bb5Sswilcox * Filesystems that are `magical' - if they exist in vfstab, 167355d6bb5Sswilcox * then they have to be mounted for the system to have gotten 168355d6bb5Sswilcox * far enough to be able to run fsck. Thus, don't get all 169355d6bb5Sswilcox * bent out of shape if we're asked to check it and it is mounted. 170355d6bb5Sswilcox */ 171355d6bb5Sswilcox char *magic_fs[] = { 172355d6bb5Sswilcox "", /* MAGIC_NONE, for normal filesystems */ 173355d6bb5Sswilcox "/", /* MAGIC_ROOT */ 174355d6bb5Sswilcox "/usr", /* MAGIC_USR */ 175355d6bb5Sswilcox NULL /* MAGIC_LIMIT */ 176355d6bb5Sswilcox }; 177355d6bb5Sswilcox 178d1a180b0Smaheshvs int 179355d6bb5Sswilcox main(int argc, char *argv[]) 1807c478bd9Sstevel@tonic-gate { 181355d6bb5Sswilcox int c; 182355d6bb5Sswilcox int wflag = 0; 183355d6bb5Sswilcox char *suboptions, *value; 184355d6bb5Sswilcox struct rlimit rlimit; 185355d6bb5Sswilcox extern int optind; 186355d6bb5Sswilcox extern char *optarg; 187355d6bb5Sswilcox 188355d6bb5Sswilcox while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) { 1897c478bd9Sstevel@tonic-gate switch (c) { 1907c478bd9Sstevel@tonic-gate 191355d6bb5Sswilcox case QUICK_CHECK: 1927c478bd9Sstevel@tonic-gate mflag++; 1937c478bd9Sstevel@tonic-gate break; 1947c478bd9Sstevel@tonic-gate 195355d6bb5Sswilcox case ALL_no: 196355d6bb5Sswilcox case ALL_NO: 1977c478bd9Sstevel@tonic-gate nflag++; 1987c478bd9Sstevel@tonic-gate yflag = 0; 1997c478bd9Sstevel@tonic-gate break; 2007c478bd9Sstevel@tonic-gate 201355d6bb5Sswilcox case VERBOSE: 202355d6bb5Sswilcox verbose++; 203355d6bb5Sswilcox break; 204355d6bb5Sswilcox 205355d6bb5Sswilcox case UFS_OPTS: 2067c478bd9Sstevel@tonic-gate /* 2077c478bd9Sstevel@tonic-gate * ufs specific options. 2087c478bd9Sstevel@tonic-gate */ 209355d6bb5Sswilcox if (optarg == NULL) { 210355d6bb5Sswilcox usage(); 211355d6bb5Sswilcox /* 212355d6bb5Sswilcox * lint does not believe this, nor does it 213355d6bb5Sswilcox * believe #pragma does_not_return(usage) 214355d6bb5Sswilcox */ 215355d6bb5Sswilcox /* NOTREACHED */ 216355d6bb5Sswilcox } 2177c478bd9Sstevel@tonic-gate suboptions = optarg; 2187c478bd9Sstevel@tonic-gate while (*suboptions != '\0') { 219355d6bb5Sswilcox switch (getsubopt(&suboptions, subopts, 220355d6bb5Sswilcox &value)) { 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate case PREEN: 2237c478bd9Sstevel@tonic-gate preen++; 2247c478bd9Sstevel@tonic-gate break; 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate case BLOCK: 227355d6bb5Sswilcox bflag = argtol(BLOCK, "block", 228355d6bb5Sswilcox value, 10); 229355d6bb5Sswilcox (void) printf("Alternate super block " 230355d6bb5Sswilcox "location: %ld.\n", 231355d6bb5Sswilcox (long)bflag); 2327c478bd9Sstevel@tonic-gate break; 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate case DEBUG: 2357c478bd9Sstevel@tonic-gate debug++; 236355d6bb5Sswilcox verbose++; 2377c478bd9Sstevel@tonic-gate break; 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate case ONLY_WRITES: 2407c478bd9Sstevel@tonic-gate /* check only writable filesystems */ 2417c478bd9Sstevel@tonic-gate wflag++; 2427c478bd9Sstevel@tonic-gate break; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate case FORCE: 2457c478bd9Sstevel@tonic-gate fflag++; 2467c478bd9Sstevel@tonic-gate break; 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate default: 2497c478bd9Sstevel@tonic-gate usage(); 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate break; 2537c478bd9Sstevel@tonic-gate 254355d6bb5Sswilcox case ECHO_CMD: 2557c478bd9Sstevel@tonic-gate { 2567c478bd9Sstevel@tonic-gate int opt_count; 2577c478bd9Sstevel@tonic-gate char *opt_text; 2587c478bd9Sstevel@tonic-gate 259355d6bb5Sswilcox (void) printf("fsck -F ufs "); 2607c478bd9Sstevel@tonic-gate for (opt_count = 1; opt_count < argc; 261ce37393aSowenr opt_count++) { 2627c478bd9Sstevel@tonic-gate opt_text = argv[opt_count]; 2637c478bd9Sstevel@tonic-gate if (opt_text) 264355d6bb5Sswilcox (void) printf("%s ", opt_text); 2657c478bd9Sstevel@tonic-gate } 266355d6bb5Sswilcox (void) printf("\n"); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate break; 2697c478bd9Sstevel@tonic-gate 270355d6bb5Sswilcox case ALL_yes: 271355d6bb5Sswilcox case ALL_YES: 2727c478bd9Sstevel@tonic-gate yflag++; 2737c478bd9Sstevel@tonic-gate nflag = 0; 2747c478bd9Sstevel@tonic-gate break; 2757c478bd9Sstevel@tonic-gate 276355d6bb5Sswilcox default: 2777c478bd9Sstevel@tonic-gate usage(); 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate argc -= optind; 281355d6bb5Sswilcox argv += optind; 282355d6bb5Sswilcox 283355d6bb5Sswilcox if (argc == 0) 284355d6bb5Sswilcox usage(); 285355d6bb5Sswilcox 286355d6bb5Sswilcox rflag++; /* check raw devices where we can */ 287355d6bb5Sswilcox if (signal(SIGINT, SIG_IGN) != SIG_IGN) 2887c478bd9Sstevel@tonic-gate (void) signal(SIGINT, catch); 2897c478bd9Sstevel@tonic-gate if (preen) 2907c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, catchquit); 2917c478bd9Sstevel@tonic-gate 292355d6bb5Sswilcox /* 293355d6bb5Sswilcox * Push up our allowed memory limit so we can cope 294355d6bb5Sswilcox * with huge file systems. 295355d6bb5Sswilcox */ 296355d6bb5Sswilcox if (getrlimit(RLIMIT_DATA, &rlimit) == 0) { 297355d6bb5Sswilcox rlimit.rlim_cur = rlimit.rlim_max; 298355d6bb5Sswilcox (void) setrlimit(RLIMIT_DATA, &rlimit); 299355d6bb5Sswilcox } 300355d6bb5Sswilcox 301355d6bb5Sswilcox /* 302355d6bb5Sswilcox * There are a lot of places where we just exit if a problem is 303355d6bb5Sswilcox * found. This means that we won't necessarily check everything 304355d6bb5Sswilcox * we were asked to. It would be nice to do everything, and 305355d6bb5Sswilcox * then provide a summary when we're done. However, the 306355d6bb5Sswilcox * interface doesn't really allow us to do that in any useful 307355d6bb5Sswilcox * way. So, we'll just bail on the first unrecoverable 308355d6bb5Sswilcox * problem encountered. If we've been run by the generic 309355d6bb5Sswilcox * wrapper, we were only given one filesystem to check, so the 310355d6bb5Sswilcox * multi-fs case implies being run manually; that means the 311355d6bb5Sswilcox * user can rerun us on the remaining filesystems when it's 312355d6bb5Sswilcox * convenient for them. 313355d6bb5Sswilcox */ 314355d6bb5Sswilcox while (argc-- > 0) { 315355d6bb5Sswilcox if (wflag && !writable(*argv)) { 316355d6bb5Sswilcox (void) fprintf(stderr, "not writeable '%s'\n", *argv); 317355d6bb5Sswilcox argv++; 318355d6bb5Sswilcox if (exitstat == 0) 319355d6bb5Sswilcox exitstat = EXBADPARM; 320355d6bb5Sswilcox } else { 321355d6bb5Sswilcox checkfilesys(*argv++); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate } 324355d6bb5Sswilcox if (interrupted) 325355d6bb5Sswilcox exitstat = EXSIGNAL; 326355d6bb5Sswilcox exit(exitstat); 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate 329355d6bb5Sswilcox /* 330355d6bb5Sswilcox * A relatively intelligent strtol(). Note that if str is NULL, we'll 331355d6bb5Sswilcox * exit, so ret does not actually need to be pre-initialized. Lint 332355d6bb5Sswilcox * doesn't believe this, and it's harmless enough to make lint happy here. 333355d6bb5Sswilcox */ 334355d6bb5Sswilcox static long 335355d6bb5Sswilcox argtol(int flag, char *req, char *str, int base) 336355d6bb5Sswilcox { 337355d6bb5Sswilcox char *cp = str; 338355d6bb5Sswilcox long ret = -1; 339355d6bb5Sswilcox 340355d6bb5Sswilcox errno = 0; 341355d6bb5Sswilcox if (str != NULL) 342355d6bb5Sswilcox ret = strtol(str, &cp, base); 343355d6bb5Sswilcox if (cp == str || *cp) { 344355d6bb5Sswilcox (void) fprintf(stderr, "-%c flag requires a %s\n", flag, req); 345355d6bb5Sswilcox exit(EXBADPARM); 346355d6bb5Sswilcox } 347355d6bb5Sswilcox if (errno != 0) { 348355d6bb5Sswilcox (void) fprintf(stderr, "-%c %s value out of range\n", 349355d6bb5Sswilcox flag, req); 350355d6bb5Sswilcox } 351355d6bb5Sswilcox 352355d6bb5Sswilcox return (ret); 353355d6bb5Sswilcox } 3547c478bd9Sstevel@tonic-gate 355355d6bb5Sswilcox /* 356355d6bb5Sswilcox * Check the specified file system. 357355d6bb5Sswilcox */ 358355d6bb5Sswilcox static void 359355d6bb5Sswilcox checkfilesys(char *filesys) 3607c478bd9Sstevel@tonic-gate { 3617c478bd9Sstevel@tonic-gate daddr32_t n_ffree, n_bfree; 3627c478bd9Sstevel@tonic-gate char *devstr; 363355d6bb5Sswilcox fsck_ino_t files; 364355d6bb5Sswilcox daddr32_t blks; 365355d6bb5Sswilcox fsck_ino_t inumber; 366355d6bb5Sswilcox int zlinks_printed; 367355d6bb5Sswilcox fsck_ino_t limbo_victim; 368355d6bb5Sswilcox double dbl_nffree, dbl_dsize; 369355d6bb5Sswilcox int quiet_dups; 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate mountfd = -1; 3727c478bd9Sstevel@tonic-gate hotroot = 0; 373355d6bb5Sswilcox mountedfs = M_NOMNT; 374355d6bb5Sswilcox reattached_dir = 0; 375355d6bb5Sswilcox broke_dir_link = 0; 376355d6bb5Sswilcox iscorrupt = 1; /* assume failure in setup() */ 3777c478bd9Sstevel@tonic-gate islog = 0; 3787c478bd9Sstevel@tonic-gate islogok = 0; 379355d6bb5Sswilcox overflowed_lf = 0; 3807c478bd9Sstevel@tonic-gate errorlocked = is_errorlocked(filesys); 381355d6bb5Sswilcox limbo_dirs = NULL; 3827c478bd9Sstevel@tonic-gate 383355d6bb5Sswilcox if ((devstr = setup(filesys)) == NULL) { 384355d6bb5Sswilcox if (!iscorrupt) { 3857c478bd9Sstevel@tonic-gate return; 386355d6bb5Sswilcox } 387355d6bb5Sswilcox 3887c478bd9Sstevel@tonic-gate if (preen) 3897c478bd9Sstevel@tonic-gate pfatal("CAN'T CHECK FILE SYSTEM."); 390*262234c2SMilan Cermak if (exitstat == 0) 391*262234c2SMilan Cermak exitstat = mflag ? EXUMNTCHK : EXERRFATAL; 3927c478bd9Sstevel@tonic-gate exit(exitstat); 393355d6bb5Sswilcox } else { 3947c478bd9Sstevel@tonic-gate devname = devstr; 395355d6bb5Sswilcox } 396355d6bb5Sswilcox 397355d6bb5Sswilcox if (mflag) { 398355d6bb5Sswilcox check_sanity(filesys); 399355d6bb5Sswilcox /* NOTREACHED */ 400355d6bb5Sswilcox } 401355d6bb5Sswilcox 4027c478bd9Sstevel@tonic-gate if (debug) 4037c478bd9Sstevel@tonic-gate printclean(); 404355d6bb5Sswilcox 405355d6bb5Sswilcox iscorrupt = 0; /* setup() succeeded, assume good filesystem */ 406355d6bb5Sswilcox 4077c478bd9Sstevel@tonic-gate /* 4087c478bd9Sstevel@tonic-gate * 1: scan inodes tallying blocks used 4097c478bd9Sstevel@tonic-gate */ 410355d6bb5Sswilcox if (!preen) { 411355d6bb5Sswilcox /* hotroot is reported as such in setup() if debug is on */ 412355d6bb5Sswilcox if (mountedfs != M_NOMNT) 413355d6bb5Sswilcox (void) printf("** Currently Mounted on %s\n", 414355d6bb5Sswilcox sblock.fs_fsmnt); 4157c478bd9Sstevel@tonic-gate else 416355d6bb5Sswilcox (void) printf("** Last Mounted on %s\n", 417355d6bb5Sswilcox sblock.fs_fsmnt); 418355d6bb5Sswilcox (void) printf("** Phase 1 - Check Blocks and Sizes\n"); 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate pass1(); 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* 4237c478bd9Sstevel@tonic-gate * 1b: locate first references to duplicates, if any 4247c478bd9Sstevel@tonic-gate */ 425355d6bb5Sswilcox if (have_dups()) { 4267c478bd9Sstevel@tonic-gate if (preen) 427355d6bb5Sswilcox pfatal("INTERNAL ERROR: dups with -o p"); 428355d6bb5Sswilcox (void) printf("** Phase 1b - Rescan For More DUPS\n"); 4297c478bd9Sstevel@tonic-gate pass1b(); 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate /* 433b9a41fd3Sswilcox * 2: traverse directories from root to mark all connected directories 434355d6bb5Sswilcox */ 435355d6bb5Sswilcox if (!preen) 436355d6bb5Sswilcox (void) printf("** Phase 2 - Check Pathnames\n"); 437b9a41fd3Sswilcox pass2(); 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate /* 440355d6bb5Sswilcox * 3a: scan inodes looking for disconnected directories. 4417c478bd9Sstevel@tonic-gate */ 442355d6bb5Sswilcox if (!preen) 443355d6bb5Sswilcox (void) printf("** Phase 3a - Check Connectivity\n"); 444355d6bb5Sswilcox pass3a(); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate /* 4477c478bd9Sstevel@tonic-gate * 3b: check acls 4487c478bd9Sstevel@tonic-gate */ 449355d6bb5Sswilcox if (!preen) 450355d6bb5Sswilcox (void) printf("** Phase 3b - Verify Shadows/ACLs\n"); 4517c478bd9Sstevel@tonic-gate pass3b(); 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate /* 4547c478bd9Sstevel@tonic-gate * 4: scan inodes looking for disconnected files; check reference counts 4557c478bd9Sstevel@tonic-gate */ 456355d6bb5Sswilcox if (!preen) 457355d6bb5Sswilcox (void) printf("** Phase 4 - Check Reference Counts\n"); 4587c478bd9Sstevel@tonic-gate pass4(); 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* 4617c478bd9Sstevel@tonic-gate * 5: check and repair resource counts in cylinder groups 4627c478bd9Sstevel@tonic-gate */ 463355d6bb5Sswilcox if (!preen) 464355d6bb5Sswilcox (void) printf("** Phase 5 - Check Cylinder Groups\n"); 465355d6bb5Sswilcox recount: 4667c478bd9Sstevel@tonic-gate pass5(); 4677c478bd9Sstevel@tonic-gate 468355d6bb5Sswilcox if (overflowed_lf) { 469355d6bb5Sswilcox iscorrupt = 1; 470355d6bb5Sswilcox } 471355d6bb5Sswilcox 472504a199eSswilcox if (!nflag && mountedfs == M_RW) { 4735b4dc236Smishra (void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n"); 4745b4dc236Smishra rerun = 1; 475355d6bb5Sswilcox } 476355d6bb5Sswilcox 477355d6bb5Sswilcox if (have_dups()) { 478355d6bb5Sswilcox quiet_dups = (reply("LIST REMAINING DUPS") == 0); 479355d6bb5Sswilcox if (report_dups(quiet_dups) > 0) 480355d6bb5Sswilcox iscorrupt = 1; 481355d6bb5Sswilcox 482355d6bb5Sswilcox (void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO " 483355d6bb5Sswilcox "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n"); 484355d6bb5Sswilcox } 485355d6bb5Sswilcox 486355d6bb5Sswilcox if (limbo_dirs != NULL) { 487355d6bb5Sswilcox /* 488355d6bb5Sswilcox * Don't force iscorrupt, as this is sufficiently 489355d6bb5Sswilcox * harmless that the filesystem can be mounted and 490355d6bb5Sswilcox * used. We just leak some inodes and/or blocks. 491355d6bb5Sswilcox */ 492355d6bb5Sswilcox pwarn("Orphan directories not cleared or reconnected:\n"); 493355d6bb5Sswilcox 494355d6bb5Sswilcox twalk(limbo_dirs, report_limbo); 495355d6bb5Sswilcox 496355d6bb5Sswilcox while (limbo_dirs != NULL) { 497355d6bb5Sswilcox limbo_victim = *(fsck_ino_t *)limbo_dirs; 498355d6bb5Sswilcox if (limbo_victim != NULL) { 499355d6bb5Sswilcox (void) tdelete((void *)limbo_victim, 500355d6bb5Sswilcox &limbo_dirs, 501355d6bb5Sswilcox ino_t_cmp); 502355d6bb5Sswilcox } 503355d6bb5Sswilcox } 504355d6bb5Sswilcox 505355d6bb5Sswilcox rerun = 1; 506355d6bb5Sswilcox } 507355d6bb5Sswilcox 508355d6bb5Sswilcox if (iscorrupt) { 5095b4dc236Smishra if (mountedfs == M_RW) 5105b4dc236Smishra (void) printf("FS IS MOUNTED R/W AND" 5115b4dc236Smishra " FSCK DID ITS BEST TO FIX" 5125b4dc236Smishra " INCONSISTENCIES.\n"); 5135b4dc236Smishra else 5145b4dc236Smishra (void) printf("FILESYSTEM MAY STILL BE" 5155b4dc236Smishra " INCONSISTENT.\n"); 516355d6bb5Sswilcox rerun = 1; 517355d6bb5Sswilcox } 518355d6bb5Sswilcox 519355d6bb5Sswilcox /* 520355d6bb5Sswilcox * iscorrupt must be stable at this point. 521355d6bb5Sswilcox * updateclean() returns true when it had to discard the log. 522355d6bb5Sswilcox * This can only happen once, since sblock.fs_logbno gets 523355d6bb5Sswilcox * cleared as part of that operation. 524355d6bb5Sswilcox */ 525355d6bb5Sswilcox if (updateclean()) { 526355d6bb5Sswilcox if (!preen) 527355d6bb5Sswilcox (void) printf( 528355d6bb5Sswilcox "Log was discarded, updating cyl groups\n"); 529355d6bb5Sswilcox goto recount; 530355d6bb5Sswilcox } 531355d6bb5Sswilcox 5327c478bd9Sstevel@tonic-gate if (debug) 5337c478bd9Sstevel@tonic-gate printclean(); 5347c478bd9Sstevel@tonic-gate 535355d6bb5Sswilcox ckfini(); 536355d6bb5Sswilcox 5377c478bd9Sstevel@tonic-gate /* 5387c478bd9Sstevel@tonic-gate * print out summary statistics 5397c478bd9Sstevel@tonic-gate */ 5407c478bd9Sstevel@tonic-gate n_ffree = sblock.fs_cstotal.cs_nffree; 5417c478bd9Sstevel@tonic-gate n_bfree = sblock.fs_cstotal.cs_nbfree; 542355d6bb5Sswilcox files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files; 543355d6bb5Sswilcox blks = n_blks + 544355d6bb5Sswilcox sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0)); 545355d6bb5Sswilcox blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0); 546355d6bb5Sswilcox blks += howmany(sblock.fs_cssize, sblock.fs_fsize); 547355d6bb5Sswilcox blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks; 548355d6bb5Sswilcox if (debug && (files > 0 || blks > 0)) { 549355d6bb5Sswilcox countdirs = sblock.fs_cstotal.cs_ndir - countdirs; 550355d6bb5Sswilcox pwarn("Reclaimed: %d directories, %d files, %lld fragments\n", 551355d6bb5Sswilcox countdirs, files - countdirs, 552355d6bb5Sswilcox (longlong_t)blks); 553355d6bb5Sswilcox } 554355d6bb5Sswilcox 555355d6bb5Sswilcox dbl_nffree = (double)n_ffree; 556355d6bb5Sswilcox dbl_dsize = (double)sblock.fs_dsize; 557355d6bb5Sswilcox 558355d6bb5Sswilcox if (!verbose) { 559355d6bb5Sswilcox /* 560355d6bb5Sswilcox * Done as one big string to try for a single write, 561355d6bb5Sswilcox * so the output doesn't get interleaved with other 562355d6bb5Sswilcox * preening fscks. 563355d6bb5Sswilcox */ 564355d6bb5Sswilcox pwarn("%ld files, %lld used, %lld free " 565355d6bb5Sswilcox "(%lld frags, %lld blocks, %.1f%% fragmentation)\n", 566355d6bb5Sswilcox (long)n_files, (longlong_t)n_blks, 567355d6bb5Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree, 568355d6bb5Sswilcox (longlong_t)n_ffree, (longlong_t)n_bfree, 569355d6bb5Sswilcox (dbl_nffree * 100.0) / dbl_dsize); 570355d6bb5Sswilcox } else { 571355d6bb5Sswilcox pwarn("\nFilesystem summary:\n"); 572355d6bb5Sswilcox pwarn("Inodes in use: %ld\n", (long)n_files); 573355d6bb5Sswilcox pwarn("Blocks in use: %lld\n", (longlong_t)n_blks); 574355d6bb5Sswilcox pwarn("Total free fragments: %lld\n", 575355d6bb5Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree); 576355d6bb5Sswilcox pwarn("Free fragments not in blocks: %lld\n", 577355d6bb5Sswilcox (longlong_t)n_ffree); 578355d6bb5Sswilcox pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree); 579355d6bb5Sswilcox pwarn("Fragment/block fragmentation: %.1f%%\n", 580355d6bb5Sswilcox (dbl_nffree * 100.0) / dbl_dsize); 581355d6bb5Sswilcox pwarn(""); 582355d6bb5Sswilcox 583355d6bb5Sswilcox if (files < 0) 584355d6bb5Sswilcox pwarn("%d inodes missing\n", -files); 585355d6bb5Sswilcox if (blks < 0) 586355d6bb5Sswilcox pwarn("%lld blocks missing\n", -(longlong_t)blks); 587355d6bb5Sswilcox 588355d6bb5Sswilcox zlinks_printed = 0; 589355d6bb5Sswilcox for (inumber = UFSROOTINO; inumber < maxino; inumber++) { 590355d6bb5Sswilcox if (S_IS_ZLINK(statemap[inumber])) { 591355d6bb5Sswilcox if (zlinks_printed == 0) { 592355d6bb5Sswilcox pwarn("The following zero " 593355d6bb5Sswilcox "link count inodes remain:"); 594355d6bb5Sswilcox } 595355d6bb5Sswilcox if (zlinks_printed) { 596355d6bb5Sswilcox if ((zlinks_printed % 9) == 0) 597355d6bb5Sswilcox (void) puts(",\n"); 598355d6bb5Sswilcox else 599355d6bb5Sswilcox (void) puts(", "); 600355d6bb5Sswilcox } 601355d6bb5Sswilcox (void) printf("%u", inumber); 602355d6bb5Sswilcox zlinks_printed++; 603355d6bb5Sswilcox } 6047c478bd9Sstevel@tonic-gate } 605355d6bb5Sswilcox if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0)) 606355d6bb5Sswilcox (void) putchar('\n'); 6077c478bd9Sstevel@tonic-gate } 608355d6bb5Sswilcox 609355d6bb5Sswilcox /* 610355d6bb5Sswilcox * Clean up after ourselves, so we can do the next filesystem. 611355d6bb5Sswilcox */ 612355d6bb5Sswilcox free_dup_state(); 6137c478bd9Sstevel@tonic-gate inocleanup(); 6147c478bd9Sstevel@tonic-gate free(blockmap); 6157c478bd9Sstevel@tonic-gate free(statemap); 616355d6bb5Sswilcox free((void *)lncntp); 6177c478bd9Sstevel@tonic-gate lncntp = NULL; 618355d6bb5Sswilcox blockmap = NULL; 619355d6bb5Sswilcox statemap = NULL; 620355d6bb5Sswilcox if (iscorrupt && exitstat == 0) 621355d6bb5Sswilcox exitstat = EXFNDERRS; 622355d6bb5Sswilcox if (fsmodified) 623355d6bb5Sswilcox (void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n"); 624355d6bb5Sswilcox if (overflowed_lf) 625355d6bb5Sswilcox (void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n", 626355d6bb5Sswilcox lfname); 627355d6bb5Sswilcox if (reattached_dir) { 628355d6bb5Sswilcox (void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK " 629355d6bb5Sswilcox "COUNTS MAY NOT BE CORRECT.\n"); 630355d6bb5Sswilcox rerun = 1; 631355d6bb5Sswilcox } 632355d6bb5Sswilcox if (broke_dir_link) { 633355d6bb5Sswilcox (void) printf( 634355d6bb5Sswilcox "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n"); 635355d6bb5Sswilcox rerun = 1; 636355d6bb5Sswilcox } 6377c478bd9Sstevel@tonic-gate if (iscorrupt) 638355d6bb5Sswilcox (void) printf("***** FILE SYSTEM IS BAD *****\n"); 6397c478bd9Sstevel@tonic-gate 6405b4dc236Smishra if (rerun) { 6415b4dc236Smishra if (mountedfs == M_RW) 6425b4dc236Smishra (void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED" 6435b4dc236Smishra " FILE SYSTEM *****\n"); 6445b4dc236Smishra else 6455b4dc236Smishra (void) printf("\n***** PLEASE RERUN FSCK *****\n"); 6465b4dc236Smishra } 6477c478bd9Sstevel@tonic-gate 648355d6bb5Sswilcox if ((exitstat == 0) && 649355d6bb5Sswilcox (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) { 650355d6bb5Sswilcox exitstat = EXROOTOKAY; 651355d6bb5Sswilcox } 6527c478bd9Sstevel@tonic-gate 653355d6bb5Sswilcox if ((exitstat == 0) && rerun) 654355d6bb5Sswilcox exitstat = EXFNDERRS; 655355d6bb5Sswilcox 656355d6bb5Sswilcox if (mountedfs != M_NOMNT) { 657355d6bb5Sswilcox if (!fsmodified) 658355d6bb5Sswilcox return; 659355d6bb5Sswilcox /* 660355d6bb5Sswilcox * _FIOFFS is much more effective than a simple sync(). 661355d6bb5Sswilcox * Note that the original fswritefd was discarded in 662355d6bb5Sswilcox * ckfini(). 663355d6bb5Sswilcox */ 664355d6bb5Sswilcox fswritefd = open(devstr, O_RDWR, 0); 665355d6bb5Sswilcox if (fswritefd != -1) { 666355d6bb5Sswilcox (void) ioctl(fswritefd, _FIOFFS, NULL); 667355d6bb5Sswilcox (void) close(fswritefd); 6687c478bd9Sstevel@tonic-gate } 669355d6bb5Sswilcox 670355d6bb5Sswilcox if (!preen) 671355d6bb5Sswilcox (void) printf("\n***** REBOOT NOW *****\n"); 672355d6bb5Sswilcox 673355d6bb5Sswilcox exitstat = EXREBOOTNOW; 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate } 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate /* 678355d6bb5Sswilcox * fsck -m: does the filesystem pass cursory examination 679355d6bb5Sswilcox * 680355d6bb5Sswilcox * XXX This is very redundant with setup(). The right thing would be 681355d6bb5Sswilcox * for setup() to modify its behaviour when mflag is set (less 682355d6bb5Sswilcox * chatty, exit instead of return, etc). 6837c478bd9Sstevel@tonic-gate */ 6847c478bd9Sstevel@tonic-gate void 685355d6bb5Sswilcox check_sanity(char *filename) 6867c478bd9Sstevel@tonic-gate { 6877c478bd9Sstevel@tonic-gate struct stat64 stbd, stbr; 6887c478bd9Sstevel@tonic-gate char *devname; 689355d6bb5Sswilcox struct ustat usb; 6907c478bd9Sstevel@tonic-gate char vfsfilename[MAXPATHLEN]; 6917c478bd9Sstevel@tonic-gate struct vfstab vfsbuf; 6927c478bd9Sstevel@tonic-gate FILE *vfstab; 6937c478bd9Sstevel@tonic-gate struct statvfs vfs_stat; 694355d6bb5Sswilcox int found_magic[MAGIC_LIMIT]; 695355d6bb5Sswilcox int magic_cnt; 696355d6bb5Sswilcox int is_magic = 0; 69793239addSjohnlev int is_block = 0; 69893239addSjohnlev int is_file = 0; 699355d6bb5Sswilcox 700355d6bb5Sswilcox (void) memset((void *)found_magic, 0, sizeof (found_magic)); 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate if (stat64(filename, &stbd) < 0) { 703355d6bb5Sswilcox (void) fprintf(stderr, 7047c478bd9Sstevel@tonic-gate "ufs fsck: sanity check failed : cannot stat %s\n", filename); 705355d6bb5Sswilcox exit(EXNOSTAT); 7067c478bd9Sstevel@tonic-gate } 7077c478bd9Sstevel@tonic-gate 70893239addSjohnlev if (S_ISBLK(stbd.st_mode)) { 7097c478bd9Sstevel@tonic-gate is_block = 1; 71093239addSjohnlev } else if (S_ISCHR(stbd.st_mode)) { 7117c478bd9Sstevel@tonic-gate is_block = 0; 71293239addSjohnlev } else if (S_ISREG(stbd.st_mode)) { 71393239addSjohnlev is_file = 1; 7147c478bd9Sstevel@tonic-gate } 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate /* 7177c478bd9Sstevel@tonic-gate * Determine if this is the root file system via vfstab. Give up 718355d6bb5Sswilcox * silently on failures. The whole point of this is to be tolerant 719355d6bb5Sswilcox * of the magic file systems being already mounted. 7207c478bd9Sstevel@tonic-gate */ 72193239addSjohnlev if (!is_file && (vfstab = fopen(VFSTAB, "r")) != NULL) { 722355d6bb5Sswilcox for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) { 723355d6bb5Sswilcox if (magic_cnt == MAGIC_NONE) 724355d6bb5Sswilcox continue; 725355d6bb5Sswilcox if (getvfsfile(vfstab, &vfsbuf, 726355d6bb5Sswilcox magic_fs[magic_cnt]) == 0) { 727355d6bb5Sswilcox if (is_block) 728355d6bb5Sswilcox devname = vfsbuf.vfs_special; 729355d6bb5Sswilcox else 730355d6bb5Sswilcox devname = vfsbuf.vfs_fsckdev; 731355d6bb5Sswilcox if (stat64(devname, &stbr) == 0) { 732355d6bb5Sswilcox if (stbr.st_rdev == stbd.st_rdev) { 733355d6bb5Sswilcox found_magic[magic_cnt] = 1; 734355d6bb5Sswilcox is_magic = magic_cnt; 735355d6bb5Sswilcox break; 736355d6bb5Sswilcox } 737355d6bb5Sswilcox } 738355d6bb5Sswilcox } 7397c478bd9Sstevel@tonic-gate } 7407c478bd9Sstevel@tonic-gate } 7417c478bd9Sstevel@tonic-gate 7427c478bd9Sstevel@tonic-gate /* 743355d6bb5Sswilcox * Only works if filename is a block device or if 744355d6bb5Sswilcox * character and block device has the same dev_t value. 745355d6bb5Sswilcox * This is currently true, but nothing really forces it. 7467c478bd9Sstevel@tonic-gate */ 747355d6bb5Sswilcox if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) { 748355d6bb5Sswilcox (void) fprintf(stderr, 749355d6bb5Sswilcox "ufs fsck: sanity check: %s already mounted\n", filename); 750355d6bb5Sswilcox exit(EXMOUNTED); 7517c478bd9Sstevel@tonic-gate } 7527c478bd9Sstevel@tonic-gate 753355d6bb5Sswilcox if (is_magic) { 754355d6bb5Sswilcox (void) strcpy(vfsfilename, magic_fs[is_magic]); 7557c478bd9Sstevel@tonic-gate if (statvfs(vfsfilename, &vfs_stat) != 0) { 756355d6bb5Sswilcox (void) fprintf(stderr, "ufs fsck: Cannot stat %s\n", 757355d6bb5Sswilcox vfsfilename); 758355d6bb5Sswilcox exit(EXNOSTAT); 7597c478bd9Sstevel@tonic-gate } 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gate if (!(vfs_stat.f_flag & ST_RDONLY)) { 7627c478bd9Sstevel@tonic-gate /* 7637c478bd9Sstevel@tonic-gate * The file system is mounted read/write 764355d6bb5Sswilcox * We need to exit saying this. If it's only 7657c478bd9Sstevel@tonic-gate * mounted readonly, we can continue. 7667c478bd9Sstevel@tonic-gate */ 7677c478bd9Sstevel@tonic-gate 768355d6bb5Sswilcox (void) fprintf(stderr, 769ce37393aSowenr "ufs fsck: sanity check:" 770ce37393aSowenr "%s already mounted read/write\n", filename); 771355d6bb5Sswilcox exit(EXMOUNTED); 7727c478bd9Sstevel@tonic-gate } 7737c478bd9Sstevel@tonic-gate } 7747c478bd9Sstevel@tonic-gate 7757c478bd9Sstevel@tonic-gate /* 776355d6bb5Sswilcox * We know that at boot, the ufs root file system is mounted 777355d6bb5Sswilcox * read-only first. After fsck runs, it is remounted as 778355d6bb5Sswilcox * read-write. Therefore, we do not need to check for different 779355d6bb5Sswilcox * values for fs_state between the root file system and the 780355d6bb5Sswilcox * rest of the file systems. 7817c478bd9Sstevel@tonic-gate */ 7827c478bd9Sstevel@tonic-gate if (islog && !islogok) { 783355d6bb5Sswilcox (void) fprintf(stderr, 784355d6bb5Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename); 785355d6bb5Sswilcox exit(EXUMNTCHK); 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) && 788ce37393aSowenr (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE || 789ce37393aSowenr (sblock.fs_clean == FSLOG && islog))) { 790355d6bb5Sswilcox (void) fprintf(stderr, 791355d6bb5Sswilcox "ufs fsck: sanity check: %s okay\n", filename); 7927c478bd9Sstevel@tonic-gate } else { 793355d6bb5Sswilcox (void) fprintf(stderr, 794355d6bb5Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename); 795355d6bb5Sswilcox exit(EXUMNTCHK); 7967c478bd9Sstevel@tonic-gate } 797355d6bb5Sswilcox exit(EXOKAY); 7987c478bd9Sstevel@tonic-gate } 7997c478bd9Sstevel@tonic-gate 800355d6bb5Sswilcox caddr_t 801355d6bb5Sswilcox hasvfsopt(struct vfstab *vfs, char *opt) 8027c478bd9Sstevel@tonic-gate { 803355d6bb5Sswilcox struct mnttab mtab; 8047c478bd9Sstevel@tonic-gate 805355d6bb5Sswilcox if (vfs->vfs_mntopts == NULL) 806355d6bb5Sswilcox return (NULL); 807355d6bb5Sswilcox mtab.mnt_mntopts = vfs->vfs_mntopts; 808355d6bb5Sswilcox return (hasmntopt(&mtab, opt)); 8097c478bd9Sstevel@tonic-gate } 8107c478bd9Sstevel@tonic-gate 811355d6bb5Sswilcox void 812355d6bb5Sswilcox usage(void) 8137c478bd9Sstevel@tonic-gate { 814355d6bb5Sswilcox (void) fprintf(stderr, 815355d6bb5Sswilcox "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] " 816355d6bb5Sswilcox "[-o p,b=#,w,f] [special ....]\n"); 8177c478bd9Sstevel@tonic-gate 818355d6bb5Sswilcox exit(EXBADPARM); 8197c478bd9Sstevel@tonic-gate } 8207c478bd9Sstevel@tonic-gate 821355d6bb5Sswilcox /*ARGSUSED*/ 822355d6bb5Sswilcox static void 823355d6bb5Sswilcox report_limbo(const void *node, VISIT order, int level) 8247c478bd9Sstevel@tonic-gate { 825355d6bb5Sswilcox fsck_ino_t ino = *(fsck_ino_t *)node; 8267c478bd9Sstevel@tonic-gate 827355d6bb5Sswilcox if ((order == postorder) || (order == leaf)) { 828355d6bb5Sswilcox (void) printf(" Inode %d\n", ino); 8297c478bd9Sstevel@tonic-gate } 8307c478bd9Sstevel@tonic-gate } 831