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);
1417c478bd9Sstevel@tonic-gate
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 */
1507c478bd9Sstevel@tonic-gate
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
main(int argc,char * argv[])179355d6bb5Sswilcox main(int argc, char *argv[])
1807c478bd9Sstevel@tonic-gate {
1817c478bd9Sstevel@tonic-gate int c;
182355d6bb5Sswilcox int wflag = 0;
1837c478bd9Sstevel@tonic-gate char *suboptions, *value;
184355d6bb5Sswilcox struct rlimit rlimit;
185355d6bb5Sswilcox extern int optind;
186355d6bb5Sswilcox extern char *optarg;
1877c478bd9Sstevel@tonic-gate
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;
2617c478bd9Sstevel@tonic-gate 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 */
3147c478bd9Sstevel@tonic-gate while (argc-- > 0) {
3157c478bd9Sstevel@tonic-gate if (wflag && !writable(*argv)) {
316355d6bb5Sswilcox (void) fprintf(stderr, "not writeable '%s'\n", *argv);
3177c478bd9Sstevel@tonic-gate argv++;
318355d6bb5Sswilcox if (exitstat == 0)
319355d6bb5Sswilcox exitstat = EXBADPARM;
320355d6bb5Sswilcox } else {
3217c478bd9Sstevel@tonic-gate checkfilesys(*argv++);
3227c478bd9Sstevel@tonic-gate }
323355d6bb5Sswilcox }
324355d6bb5Sswilcox if (interrupted)
325355d6bb5Sswilcox exitstat = EXSIGNAL;
3267c478bd9Sstevel@tonic-gate exit(exitstat);
3277c478bd9Sstevel@tonic-gate }
328355d6bb5Sswilcox
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
argtol(int flag,char * req,char * str,int base)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);
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate
352355d6bb5Sswilcox return (ret);
353355d6bb5Sswilcox }
3547c478bd9Sstevel@tonic-gate
355355d6bb5Sswilcox /*
356355d6bb5Sswilcox * Check the specified file system.
357355d6bb5Sswilcox */
358355d6bb5Sswilcox static void
checkfilesys(char * filesys)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);
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate
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);
5707c478bd9Sstevel@tonic-gate } 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("");
5827c478bd9Sstevel@tonic-gate
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 }
604355d6bb5Sswilcox }
605355d6bb5Sswilcox if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0))
606355d6bb5Sswilcox (void) putchar('\n');
607355d6bb5Sswilcox }
6087c478bd9Sstevel@tonic-gate
6097c478bd9Sstevel@tonic-gate /*
610355d6bb5Sswilcox * Clean up after ourselves, so we can do the next filesystem.
6117c478bd9Sstevel@tonic-gate */
612355d6bb5Sswilcox free_dup_state();
613355d6bb5Sswilcox inocleanup();
614355d6bb5Sswilcox free(blockmap);
615355d6bb5Sswilcox free(statemap);
616355d6bb5Sswilcox free((void *)lncntp);
617355d6bb5Sswilcox 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 }
637355d6bb5Sswilcox 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
645355d6bb5Sswilcox (void) printf("\n***** PLEASE RERUN FSCK *****\n");
6465b4dc236Smishra }
647355d6bb5Sswilcox
648355d6bb5Sswilcox if ((exitstat == 0) &&
649355d6bb5Sswilcox (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) {
650355d6bb5Sswilcox exitstat = EXROOTOKAY;
651355d6bb5Sswilcox }
652355d6bb5Sswilcox
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);
668355d6bb5Sswilcox }
669355d6bb5Sswilcox
670355d6bb5Sswilcox if (!preen)
671355d6bb5Sswilcox (void) printf("\n***** REBOOT NOW *****\n");
672355d6bb5Sswilcox
673355d6bb5Sswilcox exitstat = EXREBOOTNOW;
674355d6bb5Sswilcox }
675355d6bb5Sswilcox }
676355d6bb5Sswilcox
677355d6bb5Sswilcox /*
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).
683355d6bb5Sswilcox */
6847c478bd9Sstevel@tonic-gate void
check_sanity(char * filename)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) {
7277c478bd9Sstevel@tonic-gate if (is_block)
7287c478bd9Sstevel@tonic-gate devname = vfsbuf.vfs_special;
7297c478bd9Sstevel@tonic-gate else
7307c478bd9Sstevel@tonic-gate 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;
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate }
738355d6bb5Sswilcox }
739355d6bb5Sswilcox }
740355d6bb5Sswilcox }
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",
7577c478bd9Sstevel@tonic-gate 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,
7697c478bd9Sstevel@tonic-gate "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) &&
7887c478bd9Sstevel@tonic-gate (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
7897c478bd9Sstevel@tonic-gate (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
hasvfsopt(struct vfstab * vfs,char * opt)801355d6bb5Sswilcox hasvfsopt(struct vfstab *vfs, char *opt)
8027c478bd9Sstevel@tonic-gate {
803355d6bb5Sswilcox struct mnttab mtab;
8047c478bd9Sstevel@tonic-gate
8057c478bd9Sstevel@tonic-gate if (vfs->vfs_mntopts == NULL)
8067c478bd9Sstevel@tonic-gate return (NULL);
807355d6bb5Sswilcox mtab.mnt_mntopts = vfs->vfs_mntopts;
808355d6bb5Sswilcox return (hasmntopt(&mtab, opt));
8097c478bd9Sstevel@tonic-gate }
8107c478bd9Sstevel@tonic-gate
8117c478bd9Sstevel@tonic-gate void
usage(void)812355d6bb5Sswilcox usage(void)
8137c478bd9Sstevel@tonic-gate {
8147c478bd9Sstevel@tonic-gate (void) fprintf(stderr,
815355d6bb5Sswilcox "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
816355d6bb5Sswilcox "[-o p,b=#,w,f] [special ....]\n");
817355d6bb5Sswilcox
818355d6bb5Sswilcox exit(EXBADPARM);
819355d6bb5Sswilcox }
820355d6bb5Sswilcox
821355d6bb5Sswilcox /*ARGSUSED*/
822355d6bb5Sswilcox static void
report_limbo(const void * node,VISIT order,int level)823355d6bb5Sswilcox report_limbo(const void *node, VISIT order, int level)
824355d6bb5Sswilcox {
825355d6bb5Sswilcox fsck_ino_t ino = *(fsck_ino_t *)node;
826355d6bb5Sswilcox
827355d6bb5Sswilcox if ((order == postorder) || (order == leaf)) {
828355d6bb5Sswilcox (void) printf(" Inode %d\n", ino);
829355d6bb5Sswilcox }
8307c478bd9Sstevel@tonic-gate }
831