xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/main.c (revision 8509e9ca)
17c478bd9Sstevel@tonic-gate /*
2262234c2SMilan Cermak  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
47c478bd9Sstevel@tonic-gate  */
67c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7*8509e9caSToomas Soome /*	  All Rights Reserved	*/
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.
277c478bd9Sstevel@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  */
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"
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);
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 */
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 };
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 };
178d1a180b0Smaheshvs int
main(int argc,char * argv[])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;
188355d6bb5Sswilcox 	while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) {
1897c478bd9Sstevel@tonic-gate 		switch (c) {
191355d6bb5Sswilcox 		case QUICK_CHECK:
1927c478bd9Sstevel@tonic-gate 			mflag++;
1937c478bd9Sstevel@tonic-gate 			break;
195355d6bb5Sswilcox 		case ALL_no:
196355d6bb5Sswilcox 		case ALL_NO:
1977c478bd9Sstevel@tonic-gate 			nflag++;
1987c478bd9Sstevel@tonic-gate 			yflag = 0;
1997c478bd9Sstevel@tonic-gate 			break;
201355d6bb5Sswilcox 		case VERBOSE:
202355d6bb5Sswilcox 			verbose++;
203355d6bb5Sswilcox 			break;
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)) {
2227c478bd9Sstevel@tonic-gate 				case PREEN:
2237c478bd9Sstevel@tonic-gate 					preen++;
2247c478bd9Sstevel@tonic-gate 					break;
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;
2347c478bd9Sstevel@tonic-gate 				case DEBUG:
2357c478bd9Sstevel@tonic-gate 					debug++;
236355d6bb5Sswilcox 					verbose++;
2377c478bd9Sstevel@tonic-gate 					break;
2397c478bd9Sstevel@tonic-gate 				case ONLY_WRITES:
2407c478bd9Sstevel@tonic-gate 					/* check only writable filesystems */
2417c478bd9Sstevel@tonic-gate 					wflag++;
2427c478bd9Sstevel@tonic-gate 					break;
2447c478bd9Sstevel@tonic-gate 				case FORCE:
2457c478bd9Sstevel@tonic-gate 					fflag++;
2467c478bd9Sstevel@tonic-gate 					break;
2487c478bd9Sstevel@tonic-gate 				default:
2497c478bd9Sstevel@tonic-gate 					usage();
2507c478bd9Sstevel@tonic-gate 				}
2517c478bd9Sstevel@tonic-gate 			}
2527c478bd9Sstevel@tonic-gate 			break;
254355d6bb5Sswilcox 		case ECHO_CMD:
2557c478bd9Sstevel@tonic-gate 			{
2567c478bd9Sstevel@tonic-gate 				int	opt_count;
2577c478bd9Sstevel@tonic-gate 				char	*opt_text;
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;
270355d6bb5Sswilcox 		case ALL_yes:
271355d6bb5Sswilcox 		case ALL_YES:
2727c478bd9Sstevel@tonic-gate 			yflag++;
2737c478bd9Sstevel@tonic-gate 			nflag = 0;
2747c478bd9Sstevel@tonic-gate 			break;
276355d6bb5Sswilcox 		default:
2777c478bd9Sstevel@tonic-gate 			usage();
2787c478bd9Sstevel@tonic-gate 		}
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 	argc -= optind;
281355d6bb5Sswilcox 	argv += optind;
283355d6bb5Sswilcox 	if (argc == 0)
284355d6bb5Sswilcox 		usage();
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);
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 	}
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 }
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;
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 	}
352355d6bb5Sswilcox 	return (ret);
353355d6bb5Sswilcox }
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;
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;
383355d6bb5Sswilcox 	if ((devstr = setup(filesys)) == NULL) {
384355d6bb5Sswilcox 		if (!iscorrupt) {
3857c478bd9Sstevel@tonic-gate 			return;
386355d6bb5Sswilcox 		}
3887c478bd9Sstevel@tonic-gate 		if (preen)
3897c478bd9Sstevel@tonic-gate 			pfatal("CAN'T CHECK FILE SYSTEM.");
390262234c2SMilan Cermak 		if (exitstat == 0)
391262234c2SMilan Cermak 			exitstat = mflag ? EXUMNTCHK : EXERRFATAL;
3927c478bd9Sstevel@tonic-gate 		exit(exitstat);
393355d6bb5Sswilcox 	} else {
3947c478bd9Sstevel@tonic-gate 		devname = devstr;
395355d6bb5Sswilcox 	}
397355d6bb5Sswilcox 	if (mflag) {
398355d6bb5Sswilcox 		check_sanity(filesys);
399355d6bb5Sswilcox 		/* NOTREACHED */
400355d6bb5Sswilcox 	}
4027c478bd9Sstevel@tonic-gate 	if (debug)
4037c478bd9Sstevel@tonic-gate 		printclean();
405355d6bb5Sswilcox 	iscorrupt = 0;		/* setup() succeeded, assume good filesystem */
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();
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 	}
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();
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();
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();
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();
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();
468355d6bb5Sswilcox 	if (overflowed_lf) {
469355d6bb5Sswilcox 		iscorrupt = 1;
470355d6bb5Sswilcox 	}
472504a199eSswilcox 	if (!nflag && mountedfs == M_RW) {
4735b4dc236Smishra 		(void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
4745b4dc236Smishra 		rerun = 1;
475355d6bb5Sswilcox 	}
477355d6bb5Sswilcox 	if (have_dups()) {
478355d6bb5Sswilcox 		quiet_dups = (reply("LIST REMAINING DUPS") == 0);
479355d6bb5Sswilcox 		if (report_dups(quiet_dups) > 0)
480355d6bb5Sswilcox 			iscorrupt = 1;
482355d6bb5Sswilcox 		(void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
483355d6bb5Sswilcox 		    "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
484355d6bb5Sswilcox 	}
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");
494355d6bb5Sswilcox 		twalk(limbo_dirs, report_limbo);
496355d6bb5Sswilcox 		while (limbo_dirs != NULL) {
497355d6bb5Sswilcox 			limbo_victim = *(fsck_ino_t *)limbo_dirs;
498*8509e9caSToomas Soome 			if (limbo_victim != 0) {
499355d6bb5Sswilcox 				(void) tdelete((void *)limbo_victim,
500355d6bb5Sswilcox 				    &limbo_dirs,
501355d6bb5Sswilcox 				    ino_t_cmp);
502355d6bb5Sswilcox 			}
503355d6bb5Sswilcox 		}
505355d6bb5Sswilcox 		rerun = 1;
506355d6bb5Sswilcox 	}
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 	}