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