1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7/*	  All Rights Reserved  	*/
8
9
10/*
11 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms are permitted
15 * provided that: (1) source distributions retain this entire copyright
16 * notice and comment, and (2) distributions including binaries display
17 * the following acknowledgement:  ``This product includes software
18 * developed by the University of California, Berkeley and its contributors''
19 * in the documentation or other materials provided with the distribution
20 * and in all advertising materials mentioning features or use of this
21 * software. Neither the name of the University nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED '`AS IS'' AND WITHOUT ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 */
28
29/*
30 * In-core structures:
31 * blockmap[]
32 *	A bitmap of block usage very similar to what's on disk, but
33 *	for the entire filesystem rather than just a cylinder group.
34 *	Zero indicates free, one indicates allocated.  Note that this
35 *	is opposite the interpretation of a cylinder group's free block
36 *	bitmap.
37 *
38 * statemap[]
39 *	Tracks what is known about each inode in the filesystem.
40 *	The fundamental state value is one of USTATE, FSTATE, DSTATE,
41 *	or SSTATE (unallocated, file, directory, shadow/acl).
42 *
43 *	There are optional modifying attributes as well: INZLINK,
44 *	INFOUND, INCLEAR, INORPHAN, and INDELAYD.  The IN prefix
45 *	stands for inode.  INZLINK declares that no links (di_nlink ==
46 *	0) to the inode have been found.  It is used instead of
47 *	examining di_nlink because we've always got the statemap[] in
48 *	memory, and on average the odds are against having any given
49 *	inode in the cache.  INFOUND flags that an inode was
50 *	encountered during the descent of the filesystem.  In other
51 *	words, it's reachable, either by name or by being an acl or
52 *	attribute.  INCLEAR declares an intent to call clri() on an
53 *	inode. The INCLEAR and INZLINK attributes are treated in a
54 *	mutually exclusive manner with INCLEAR taking higher precedence
55 *	as the intent is to clear the inode.
56 *
57 *	INORPHAN indicates that the inode has already been seen once
58 *	in pass3 and determined to be an orphan, so any additional
59 *	encounters don't need to waste cycles redetermining that status.
60 *	It also means we don't ask the user about doing something to the
61 *	inode N times.
62 *
63 *	INDELAYD marks inodes that pass1 determined needed to be truncated.
64 *	They can't be truncated during that pass, because it depends on
65 *	having a stable world for building the block and inode tables from.
66 *
67 *	The IN flags rarely used directly, but instead are
68 *	pre-combined through the {D,F,S}ZLINK, DFOUND, and
69 *	{D,F,S}CLEAR convenience macros.  This mainly matters when
70 *	trying to use grep on the source.
71 *
72 *	Three state-test macros are provided: S_IS_DUNFOUND(),
73 *	S_IS_DVALID(), and S_IS_ZLINK().  The first is true when an
74 *	inode's state indicates that it is either a simple directory
75 *	(DSTATE without the INFOUND or INCLEAR modifiers) or a
76 *	directory with the INZLINK modifier set.  By definition, if a
77 *	directory has zero links, then it can't be found.  As for
78 *	S_IS_DVALID(), it decides if a directory inode is alive.
79 *	Effectively, this translates to whether or not it's been
80 *	flagged for clearing.  If not, then it's valid for current
81 *	purposes.  This is true even if INZLINK is set, as we may find
82 *	a reference to it later.  Finally, S_IS_ZLINK() just picks out
83 *	the INZLINK flag from the state.
84 *
85 *	The S_*() macros all work on a state value.  To simplify a
86 *	bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode
87 *	number argument.  The inode is looked up in the statemap[] and
88 *	the result handed off to the corresponding S_*() macro.  This
89 *	is partly a holdover from working with different data
90 *	structures (with the same net intent) in the BSD fsck.
91 *
92 * lncntp
93 *	Each entry is initialized to the di_link from the on-disk
94 *	inode.  Each time we find one of those links, we decrement it.
95 *	Once all the traversing is done, we should have a zero.  If we
96 *	have a positive value, then some reference disappeared
97 *	(probably from a directory that got nuked); deal with it by
98 *	fixing the count.  If we have a negative value, then we found
99 *	an extra reference.  This is a can't-happen, except in the
100 *	special case of when we reconnect a directory to its parent or
101 *	to lost+found.  An exact match between lncntp[] and the on-disk
102 *      inode means it's completely unreferenced.
103 *
104 * aclphead
105 *	This is a hash table of the acl inodes in the filesystem.
106 *
107 * aclpsort
108 *	The same acls as in aclphead, but as a simple linear array.
109 *	It is used to hold the acl pointers for sorting and scanning
110 *	in pass3b.
111 */
112
113#include <stdio.h>
114#include <stdlib.h>
115#include <unistd.h>
116#include <sys/types.h>
117#include <sys/param.h>
118#include <sys/int_types.h>
119#include <sys/mntent.h>
120#include <sys/fs/ufs_fs.h>
121#include <sys/vnode.h>
122#include <sys/fs/ufs_inode.h>
123#include <sys/stat.h>
124#include <fcntl.h>
125#include <sys/wait.h>
126#include <sys/mnttab.h>
127#include <signal.h>
128#include <string.h>
129#include <sys/vfstab.h>
130#include <sys/statvfs.h>
131#include <sys/filio.h>
132#include <ustat.h>
133#include <errno.h>
134#include "fsck.h"
135
136static void usage(void);
137static long argtol(int, char *, char *, int);
138static void checkfilesys(char *);
139static void check_sanity(char *);
140static void report_limbo(const void *, VISIT, int);
141
142#define	QUICK_CHECK	'm'	/* are things ok according to superblock? */
143#define	ALL_no		'n'	/* auto-answer interactive questions `no' */
144#define	ALL_NO		'N'	/* auto-answer interactive questions `no' */
145#define	UFS_OPTS	'o'	/* ufs-specific options, see subopts[] */
146#define	ECHO_CMD	'V'	/* echo the command line */
147#define	ALL_yes		'y'	/* auto-answer interactive questions `yes' */
148#define	ALL_YES		'Y'	/* auto-answer interactive questions `yes' */
149#define	VERBOSE		'v'	/* be chatty */
150
151static char *subopts[] = {
152#define	PREEN		0	/* non-interactive mode (parent is parallel) */
153	"p",
154#define	BLOCK		1	/* alternate superblock */
155	"b",
156#define	DEBUG		2	/* yammer */
157	"d",
158#define	ONLY_WRITES	3	/* check all writable filesystems */
159	"w",
160#define	FORCE		4	/* force checking, even if clean */
161	"f",
162	NULL
163};
164
165/*
166 * Filesystems that are `magical' - if they exist in vfstab,
167 * then they have to be mounted for the system to have gotten
168 * far enough to be able to run fsck.  Thus, don't get all
169 * bent out of shape if we're asked to check it and it is mounted.
170 */
171char *magic_fs[] = {
172	"",			/* MAGIC_NONE, for normal filesystems */
173	"/",			/* MAGIC_ROOT */
174	"/usr",			/* MAGIC_USR */
175	NULL			/* MAGIC_LIMIT */
176};
177
178int
179main(int argc, char *argv[])
180{
181	int c;
182	int wflag = 0;
183	char *suboptions, *value;
184	struct rlimit rlimit;
185	extern int optind;
186	extern char *optarg;
187
188	while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) {
189		switch (c) {
190
191		case QUICK_CHECK:
192			mflag++;
193			break;
194
195		case ALL_no:
196		case ALL_NO:
197			nflag++;
198			yflag = 0;
199			break;
200
201		case VERBOSE:
202			verbose++;
203			break;
204
205		case UFS_OPTS:
206			/*
207			 * ufs specific options.
208			 */
209			if (optarg == NULL) {
210				usage();
211				/*
212				 * lint does not believe this, nor does it
213				 * believe #pragma does_not_return(usage)
214				 */
215				/* NOTREACHED */
216			}
217			suboptions = optarg;
218			while (*suboptions != '\0') {
219				switch (getsubopt(&suboptions, subopts,
220				    &value)) {
221
222				case PREEN:
223					preen++;
224					break;
225
226				case BLOCK:
227					bflag = argtol(BLOCK, "block",
228					    value, 10);
229					(void) printf("Alternate super block "
230					    "location: %ld.\n",
231					    (long)bflag);
232					break;
233
234				case DEBUG:
235					debug++;
236					verbose++;
237					break;
238
239				case ONLY_WRITES:
240					/* check only writable filesystems */
241					wflag++;
242					break;
243
244				case FORCE:
245					fflag++;
246					break;
247
248				default:
249					usage();
250				}
251			}
252			break;
253
254		case ECHO_CMD:
255			{
256				int	opt_count;
257				char	*opt_text;
258
259				(void) printf("fsck -F ufs ");
260				for (opt_count = 1; opt_count < argc;
261				    opt_count++) {
262					opt_text = argv[opt_count];
263					if (opt_text)
264						(void) printf("%s ", opt_text);
265				}
266				(void) printf("\n");
267			}
268			break;
269
270		case ALL_yes:
271		case ALL_YES:
272			yflag++;
273			nflag = 0;
274			break;
275
276		default:
277			usage();
278		}
279	}
280	argc -= optind;
281	argv += optind;
282
283	if (argc == 0)
284		usage();
285
286	rflag++; /* check raw devices where we can */
287	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
288		(void) signal(SIGINT, catch);
289	if (preen)
290		(void) signal(SIGQUIT, catchquit);
291
292	/*
293	 * Push up our allowed memory limit so we can cope
294	 * with huge file systems.
295	 */
296	if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
297		rlimit.rlim_cur = rlimit.rlim_max;
298		(void) setrlimit(RLIMIT_DATA, &rlimit);
299	}
300
301	/*
302	 * There are a lot of places where we just exit if a problem is
303	 * found.  This means that we won't necessarily check everything
304	 * we were asked to.  It would be nice to do everything, and
305	 * then provide a summary when we're done.  However, the
306	 * interface doesn't really allow us to do that in any useful
307	 * way.  So, we'll just bail on the first unrecoverable
308	 * problem encountered.  If we've been run by the generic
309	 * wrapper, we were only given one filesystem to check, so the
310	 * multi-fs case implies being run manually; that means the
311	 * user can rerun us on the remaining filesystems when it's
312	 * convenient for them.
313	 */
314	while (argc-- > 0) {
315		if (wflag && !writable(*argv)) {
316			(void) fprintf(stderr, "not writeable '%s'\n", *argv);
317			argv++;
318			if (exitstat == 0)
319				exitstat = EXBADPARM;
320		} else {
321			checkfilesys(*argv++);
322		}
323	}
324	if (interrupted)
325		exitstat = EXSIGNAL;
326	exit(exitstat);
327}
328
329/*
330 * A relatively intelligent strtol().  Note that if str is NULL, we'll
331 * exit, so ret does not actually need to be pre-initialized.  Lint
332 * doesn't believe this, and it's harmless enough to make lint happy here.
333 */
334static long
335argtol(int flag, char *req, char *str, int base)
336{
337	char *cp = str;
338	long ret = -1;
339
340	errno = 0;
341	if (str != NULL)
342		ret = strtol(str, &cp, base);
343	if (cp == str || *cp) {
344		(void) fprintf(stderr, "-%c flag requires a %s\n", flag, req);
345		exit(EXBADPARM);
346	}
347	if (errno != 0) {
348		(void) fprintf(stderr, "-%c %s value out of range\n",
349		    flag, req);
350	}
351
352	return (ret);
353}
354
355/*
356 * Check the specified file system.
357 */
358static void
359checkfilesys(char *filesys)
360{
361	daddr32_t n_ffree, n_bfree;
362	char *devstr;
363	fsck_ino_t files;
364	daddr32_t blks;
365	fsck_ino_t inumber;
366	int zlinks_printed;
367	fsck_ino_t limbo_victim;
368	double dbl_nffree, dbl_dsize;
369	int quiet_dups;
370
371	mountfd = -1;
372	hotroot = 0;
373	mountedfs = M_NOMNT;
374	reattached_dir = 0;
375	broke_dir_link = 0;
376	iscorrupt = 1;		/* assume failure in setup() */
377	islog = 0;
378	islogok = 0;
379	overflowed_lf = 0;
380	errorlocked = is_errorlocked(filesys);
381	limbo_dirs = NULL;
382
383	if ((devstr = setup(filesys)) == NULL) {
384		if (!iscorrupt) {
385			return;
386		}
387
388		if (preen)
389			pfatal("CAN'T CHECK FILE SYSTEM.");
390		if (exitstat == 0)
391			exitstat = mflag ? EXUMNTCHK : EXERRFATAL;
392		exit(exitstat);
393	} else {
394		devname = devstr;
395	}
396
397	if (mflag) {
398		check_sanity(filesys);
399		/* NOTREACHED */
400	}
401
402	if (debug)
403		printclean();
404
405	iscorrupt = 0;		/* setup() succeeded, assume good filesystem */
406
407	/*
408	 * 1: scan inodes tallying blocks used
409	 */
410	if (!preen) {
411		/* hotroot is reported as such in setup() if debug is on */
412		if (mountedfs != M_NOMNT)
413			(void) printf("** Currently Mounted on %s\n",
414			    sblock.fs_fsmnt);
415		else
416			(void) printf("** Last Mounted on %s\n",
417			    sblock.fs_fsmnt);
418		(void) printf("** Phase 1 - Check Blocks and Sizes\n");
419	}
420	pass1();
421
422	/*
423	 * 1b: locate first references to duplicates, if any
424	 */
425	if (have_dups()) {
426		if (preen)
427			pfatal("INTERNAL ERROR: dups with -o p");
428		(void) printf("** Phase 1b - Rescan For More DUPS\n");
429		pass1b();
430	}
431
432	/*
433	 * 2: traverse directories from root to mark all connected directories
434	 */
435	if (!preen)
436		(void) printf("** Phase 2 - Check Pathnames\n");
437	pass2();
438
439	/*
440	 * 3a: scan inodes looking for disconnected directories.
441	 */
442	if (!preen)
443		(void) printf("** Phase 3a - Check Connectivity\n");
444	pass3a();
445
446	/*
447	 * 3b: check acls
448	 */
449	if (!preen)
450		(void) printf("** Phase 3b - Verify Shadows/ACLs\n");
451	pass3b();
452
453	/*
454	 * 4: scan inodes looking for disconnected files; check reference counts
455	 */
456	if (!preen)
457		(void) printf("** Phase 4 - Check Reference Counts\n");
458	pass4();
459
460	/*
461	 * 5: check and repair resource counts in cylinder groups
462	 */
463	if (!preen)
464		(void) printf("** Phase 5 - Check Cylinder Groups\n");
465recount:
466	pass5();
467
468	if (overflowed_lf) {
469		iscorrupt = 1;
470	}
471
472	if (!nflag && mountedfs == M_RW) {
473		(void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
474		rerun = 1;
475	}
476
477	if (have_dups()) {
478		quiet_dups = (reply("LIST REMAINING DUPS") == 0);
479		if (report_dups(quiet_dups) > 0)
480			iscorrupt = 1;
481
482		(void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
483		    "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
484	}
485
486	if (limbo_dirs != NULL) {
487		/*
488		 * Don't force iscorrupt, as this is sufficiently
489		 * harmless that the filesystem can be mounted and
490		 * used.  We just leak some inodes and/or blocks.
491		 */
492		pwarn("Orphan directories not cleared or reconnected:\n");
493
494		twalk(limbo_dirs, report_limbo);
495
496		while (limbo_dirs != NULL) {
497			limbo_victim = *(fsck_ino_t *)limbo_dirs;
498			if (limbo_victim != NULL) {
499				(void) tdelete((void *)limbo_victim,
500				    &limbo_dirs,
501				    ino_t_cmp);
502			}
503		}
504
505		rerun = 1;
506	}
507
508	if (iscorrupt) {
509		if (mountedfs == M_RW)
510			(void) printf("FS IS MOUNTED R/W AND"
511			    " FSCK DID ITS BEST TO FIX"
512			    " INCONSISTENCIES.\n");
513		else
514			(void) printf("FILESYSTEM MAY STILL BE"
515			    " INCONSISTENT.\n");
516		rerun = 1;
517	}
518
519	/*
520	 * iscorrupt must be stable at this point.
521	 * updateclean() returns true when it had to discard the log.
522	 * This can only happen once, since sblock.fs_logbno gets
523	 * cleared as part of that operation.
524	 */
525	if (updateclean()) {
526		if (!preen)
527			(void) printf(
528			    "Log was discarded, updating cyl groups\n");
529		goto recount;
530	}
531
532	if (debug)
533		printclean();
534
535	ckfini();
536
537	/*
538	 * print out summary statistics
539	 */
540	n_ffree = sblock.fs_cstotal.cs_nffree;
541	n_bfree = sblock.fs_cstotal.cs_nbfree;
542	files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
543	blks = n_blks +
544	    sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
545	blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
546	blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
547	blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
548	if (debug && (files > 0 || blks > 0)) {
549		countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
550		pwarn("Reclaimed: %d directories, %d files, %lld fragments\n",
551		    countdirs, files - countdirs,
552		    (longlong_t)blks);
553	}
554
555	dbl_nffree = (double)n_ffree;
556	dbl_dsize = (double)sblock.fs_dsize;
557
558	if (!verbose) {
559		/*
560		 * Done as one big string to try for a single write,
561		 * so the output doesn't get interleaved with other
562		 * preening fscks.
563		 */
564		pwarn("%ld files, %lld used, %lld free "
565		    "(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
566		    (long)n_files, (longlong_t)n_blks,
567		    (longlong_t)n_ffree + sblock.fs_frag * n_bfree,
568		    (longlong_t)n_ffree, (longlong_t)n_bfree,
569		    (dbl_nffree * 100.0) / dbl_dsize);
570	} else {
571		pwarn("\nFilesystem summary:\n");
572		pwarn("Inodes in use: %ld\n", (long)n_files);
573		pwarn("Blocks in use: %lld\n", (longlong_t)n_blks);
574		pwarn("Total free fragments: %lld\n",
575		    (longlong_t)n_ffree + sblock.fs_frag * n_bfree);
576		pwarn("Free fragments not in blocks: %lld\n",
577		    (longlong_t)n_ffree);
578		pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree);
579		pwarn("Fragment/block fragmentation: %.1f%%\n",
580		    (dbl_nffree * 100.0) / dbl_dsize);
581		pwarn("");
582
583		if (files < 0)
584			pwarn("%d inodes missing\n", -files);
585		if (blks < 0)
586			pwarn("%lld blocks missing\n", -(longlong_t)blks);
587
588		zlinks_printed = 0;
589		for (inumber = UFSROOTINO; inumber < maxino; inumber++) {
590			if (S_IS_ZLINK(statemap[inumber])) {
591				if (zlinks_printed == 0) {
592					pwarn("The following zero "
593					    "link count inodes remain:");
594				}
595				if (zlinks_printed) {
596					if ((zlinks_printed % 9) == 0)
597						(void) puts(",\n");
598					else
599						(void) puts(", ");
600				}
601				(void) printf("%u", inumber);
602				zlinks_printed++;
603			}
604		}
605		if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0))
606			(void) putchar('\n');
607	}
608
609	/*
610	 * Clean up after ourselves, so we can do the next filesystem.
611	 */
612	free_dup_state();
613	inocleanup();
614	free(blockmap);
615	free(statemap);
616	free((void *)lncntp);
617	lncntp = NULL;
618	blockmap = NULL;
619	statemap = NULL;
620	if (iscorrupt && exitstat == 0)
621		exitstat = EXFNDERRS;
622	if (fsmodified)
623		(void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
624	if (overflowed_lf)
625		(void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n",
626		    lfname);
627	if (reattached_dir) {
628		(void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK "
629		    "COUNTS MAY NOT BE CORRECT.\n");
630		rerun = 1;
631	}
632	if (broke_dir_link) {
633		(void) printf(
634		    "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n");
635		rerun = 1;
636	}
637	if (iscorrupt)
638		(void) printf("***** FILE SYSTEM IS BAD *****\n");
639
640	if (rerun) {
641		if (mountedfs == M_RW)
642			(void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED"
643			    " FILE SYSTEM *****\n");
644		else
645			(void) printf("\n***** PLEASE RERUN FSCK *****\n");
646	}
647
648	if ((exitstat == 0) &&
649	    (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) {
650		exitstat = EXROOTOKAY;
651	}
652
653	if ((exitstat == 0) && rerun)
654		exitstat = EXFNDERRS;
655
656	if (mountedfs != M_NOMNT) {
657		if (!fsmodified)
658			return;
659		/*
660		 * _FIOFFS is much more effective than a simple sync().
661		 * Note that the original fswritefd was discarded in
662		 * ckfini().
663		 */
664		fswritefd = open(devstr, O_RDWR, 0);
665		if (fswritefd != -1) {
666			(void) ioctl(fswritefd, _FIOFFS, NULL);
667			(void) close(fswritefd);
668		}
669
670		if (!preen)
671			(void) printf("\n***** REBOOT NOW *****\n");
672
673		exitstat = EXREBOOTNOW;
674	}
675}
676
677/*
678 * fsck -m: does the filesystem pass cursory examination
679 *
680 * XXX This is very redundant with setup().  The right thing would be
681 *     for setup() to modify its behaviour when mflag is set (less
682 *     chatty, exit instead of return, etc).
683 */
684void
685check_sanity(char *filename)
686{
687	struct stat64 stbd, stbr;
688	char *devname;
689	struct ustat usb;
690	char vfsfilename[MAXPATHLEN];
691	struct vfstab vfsbuf;
692	FILE *vfstab;
693	struct statvfs vfs_stat;
694	int found_magic[MAGIC_LIMIT];
695	int magic_cnt;
696	int is_magic = 0;
697	int is_block = 0;
698	int is_file = 0;
699
700	(void) memset((void *)found_magic, 0, sizeof (found_magic));
701
702	if (stat64(filename, &stbd) < 0) {
703		(void) fprintf(stderr,
704		"ufs fsck: sanity check failed : cannot stat %s\n", filename);
705		exit(EXNOSTAT);
706	}
707
708	if (S_ISBLK(stbd.st_mode)) {
709		is_block = 1;
710	} else if (S_ISCHR(stbd.st_mode)) {
711		is_block = 0;
712	} else if (S_ISREG(stbd.st_mode)) {
713		is_file = 1;
714	}
715
716	/*
717	 * Determine if this is the root file system via vfstab. Give up
718	 * silently on failures. The whole point of this is to be tolerant
719	 * of the magic file systems being already mounted.
720	 */
721	if (!is_file && (vfstab = fopen(VFSTAB, "r")) != NULL) {
722		for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) {
723			if (magic_cnt == MAGIC_NONE)
724				continue;
725			if (getvfsfile(vfstab, &vfsbuf,
726			    magic_fs[magic_cnt]) == 0) {
727				if (is_block)
728					devname = vfsbuf.vfs_special;
729				else
730					devname = vfsbuf.vfs_fsckdev;
731				if (stat64(devname, &stbr) == 0) {
732					if (stbr.st_rdev == stbd.st_rdev) {
733						found_magic[magic_cnt] = 1;
734						is_magic = magic_cnt;
735						break;
736					}
737				}
738			}
739		}
740	}
741
742	/*
743	 * Only works if filename is a block device or if
744	 * character and block device has the same dev_t value.
745	 * This is currently true, but nothing really forces it.
746	 */
747	if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) {
748		(void) fprintf(stderr,
749		    "ufs fsck: sanity check: %s already mounted\n", filename);
750		exit(EXMOUNTED);
751	}
752
753	if (is_magic) {
754		(void) strcpy(vfsfilename, magic_fs[is_magic]);
755		if (statvfs(vfsfilename, &vfs_stat) != 0) {
756			(void) fprintf(stderr, "ufs fsck: Cannot stat %s\n",
757			    vfsfilename);
758			exit(EXNOSTAT);
759		}
760
761		if (!(vfs_stat.f_flag & ST_RDONLY)) {
762			/*
763			 * The file system is mounted read/write
764			 * We need to exit saying this. If it's only
765			 * mounted readonly, we can continue.
766			 */
767
768			(void) fprintf(stderr,
769			    "ufs fsck: sanity check:"
770			    "%s already mounted read/write\n", filename);
771			exit(EXMOUNTED);
772		}
773	}
774
775	/*
776	 * We know that at boot, the ufs root file system is mounted
777	 * read-only first.  After fsck runs, it is remounted as
778	 * read-write.  Therefore, we do not need to check for different
779	 * values for fs_state between the root file system and the
780	 * rest of the file systems.
781	 */
782	if (islog && !islogok) {
783		(void) fprintf(stderr,
784		    "ufs fsck: sanity check: %s needs checking\n", filename);
785		exit(EXUMNTCHK);
786	}
787	if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) &&
788	    (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
789	    (sblock.fs_clean == FSLOG && islog))) {
790		(void) fprintf(stderr,
791		    "ufs fsck: sanity check: %s okay\n", filename);
792	} else {
793		(void) fprintf(stderr,
794		    "ufs fsck: sanity check: %s needs checking\n", filename);
795		exit(EXUMNTCHK);
796	}
797	exit(EXOKAY);
798}
799
800caddr_t
801hasvfsopt(struct vfstab *vfs, char *opt)
802{
803	struct mnttab mtab;
804
805	if (vfs->vfs_mntopts == NULL)
806		return (NULL);
807	mtab.mnt_mntopts = vfs->vfs_mntopts;
808	return (hasmntopt(&mtab, opt));
809}
810
811void
812usage(void)
813{
814	(void) fprintf(stderr,
815	    "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
816	    "[-o p,b=#,w,f] [special ....]\n");
817
818	exit(EXBADPARM);
819}
820
821/*ARGSUSED*/
822static void
823report_limbo(const void *node, VISIT order, int level)
824{
825	fsck_ino_t ino = *(fsck_ino_t *)node;
826
827	if ((order == postorder) || (order == leaf)) {
828		(void) printf("    Inode %d\n", ino);
829	}
830}
831