xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/main.c (revision fcdb3229a31dd4ff700c69238814e326aad49098)
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 
136 static void usage(void) __NORETURN;
137 static long argtol(int, char *, char *, int);
138 static void checkfilesys(char *);
139 static void check_sanity(char *);
140 static 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 
151 static 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  */
171 char *magic_fs[] = {
172 	"",			/* MAGIC_NONE, for normal filesystems */
173 	"/",			/* MAGIC_ROOT */
174 	"/usr",			/* MAGIC_USR */
175 	NULL			/* MAGIC_LIMIT */
176 };
177 
178 daddr32_t bflag;
179 daddr32_t n_blks;
180 daddr32_t maxfsblock;
181 int debug;
182 int errorlocked;
183 int exitstat;
184 int fflag;
185 int fsmodified;
186 int fswritefd;
187 int iscorrupt;
188 int islog;
189 int islogok;
190 int interrupted;
191 int mflag;
192 int mountfd;
193 int overflowed_lf;
194 int rflag;
195 int reattached_dir;
196 int broke_dir_link;
197 int verbose;
198 char hotroot;
199 char mountedfs;
200 char nflag;
201 char preen;
202 char rerun;
203 char *blockmap;
204 char *devname;
205 char yflag;
206 short *lncntp;
207 ushort_t *statemap;
208 fsck_ino_t maxino;
209 fsck_ino_t countdirs;
210 fsck_ino_t n_files;
211 void *limbo_dirs;
212 
213 int
main(int argc,char * argv[])214 main(int argc, char *argv[])
215 {
216 	int c;
217 	int wflag = 0;
218 	char *suboptions, *value;
219 	struct rlimit rlimit;
220 	extern int optind;
221 	extern char *optarg;
222 
223 	while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) {
224 		switch (c) {
225 
226 		case QUICK_CHECK:
227 			mflag++;
228 			break;
229 
230 		case ALL_no:
231 		case ALL_NO:
232 			nflag++;
233 			yflag = 0;
234 			break;
235 
236 		case VERBOSE:
237 			verbose++;
238 			break;
239 
240 		case UFS_OPTS:
241 			/*
242 			 * ufs specific options.
243 			 */
244 			if (optarg == NULL) {
245 				usage();
246 			}
247 			suboptions = optarg;
248 			while (*suboptions != '\0') {
249 				switch (getsubopt(&suboptions, subopts,
250 				    &value)) {
251 
252 				case PREEN:
253 					preen++;
254 					break;
255 
256 				case BLOCK:
257 					bflag = argtol(BLOCK, "block",
258 					    value, 10);
259 					(void) printf("Alternate super block "
260 					    "location: %ld.\n",
261 					    (long)bflag);
262 					break;
263 
264 				case DEBUG:
265 					debug++;
266 					verbose++;
267 					break;
268 
269 				case ONLY_WRITES:
270 					/* check only writable filesystems */
271 					wflag++;
272 					break;
273 
274 				case FORCE:
275 					fflag++;
276 					break;
277 
278 				default:
279 					usage();
280 				}
281 			}
282 			break;
283 
284 		case ECHO_CMD:
285 			{
286 				int	opt_count;
287 				char	*opt_text;
288 
289 				(void) printf("fsck -F ufs ");
290 				for (opt_count = 1; opt_count < argc;
291 				    opt_count++) {
292 					opt_text = argv[opt_count];
293 					if (opt_text)
294 						(void) printf("%s ", opt_text);
295 				}
296 				(void) printf("\n");
297 			}
298 			break;
299 
300 		case ALL_yes:
301 		case ALL_YES:
302 			yflag++;
303 			nflag = 0;
304 			break;
305 
306 		default:
307 			usage();
308 		}
309 	}
310 	argc -= optind;
311 	argv += optind;
312 
313 	if (argc == 0)
314 		usage();
315 
316 	rflag++; /* check raw devices where we can */
317 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
318 		(void) signal(SIGINT, catch);
319 	if (preen)
320 		(void) signal(SIGQUIT, catchquit);
321 
322 	/*
323 	 * Push up our allowed memory limit so we can cope
324 	 * with huge file systems.
325 	 */
326 	if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
327 		rlimit.rlim_cur = rlimit.rlim_max;
328 		(void) setrlimit(RLIMIT_DATA, &rlimit);
329 	}
330 
331 	/*
332 	 * There are a lot of places where we just exit if a problem is
333 	 * found.  This means that we won't necessarily check everything
334 	 * we were asked to.  It would be nice to do everything, and
335 	 * then provide a summary when we're done.  However, the
336 	 * interface doesn't really allow us to do that in any useful
337 	 * way.  So, we'll just bail on the first unrecoverable
338 	 * problem encountered.  If we've been run by the generic
339 	 * wrapper, we were only given one filesystem to check, so the
340 	 * multi-fs case implies being run manually; that means the
341 	 * user can rerun us on the remaining filesystems when it's
342 	 * convenient for them.
343 	 */
344 	while (argc-- > 0) {
345 		if (wflag && !writable(*argv)) {
346 			(void) fprintf(stderr, "not writeable '%s'\n", *argv);
347 			argv++;
348 			if (exitstat == 0)
349 				exitstat = EXBADPARM;
350 		} else {
351 			checkfilesys(*argv++);
352 		}
353 	}
354 	if (interrupted)
355 		exitstat = EXSIGNAL;
356 	exit(exitstat);
357 }
358 
359 /*
360  * A relatively intelligent strtol().  Note that if str is NULL, we'll
361  * exit, so ret does not actually need to be pre-initialized.  Lint
362  * doesn't believe this, and it's harmless enough to make lint happy here.
363  */
364 static long
argtol(int flag,char * req,char * str,int base)365 argtol(int flag, char *req, char *str, int base)
366 {
367 	char *cp = str;
368 	long ret = -1;
369 
370 	errno = 0;
371 	if (str != NULL)
372 		ret = strtol(str, &cp, base);
373 	if (cp == str || *cp) {
374 		(void) fprintf(stderr, "-%c flag requires a %s\n", flag, req);
375 		exit(EXBADPARM);
376 	}
377 	if (errno != 0) {
378 		(void) fprintf(stderr, "-%c %s value out of range\n",
379 		    flag, req);
380 	}
381 
382 	return (ret);
383 }
384 
385 /*
386  * Check the specified file system.
387  */
388 static void
checkfilesys(char * filesys)389 checkfilesys(char *filesys)
390 {
391 	daddr32_t n_ffree, n_bfree;
392 	char *devstr;
393 	fsck_ino_t files;
394 	daddr32_t blks;
395 	fsck_ino_t inumber;
396 	int zlinks_printed;
397 	fsck_ino_t limbo_victim;
398 	double dbl_nffree, dbl_dsize;
399 	int quiet_dups;
400 
401 	mountfd = -1;
402 	hotroot = 0;
403 	mountedfs = M_NOMNT;
404 	reattached_dir = 0;
405 	broke_dir_link = 0;
406 	iscorrupt = 1;		/* assume failure in setup() */
407 	islog = 0;
408 	islogok = 0;
409 	overflowed_lf = 0;
410 	errorlocked = is_errorlocked(filesys);
411 	limbo_dirs = NULL;
412 
413 	if ((devstr = setup(filesys)) == NULL) {
414 		if (!iscorrupt) {
415 			return;
416 		}
417 
418 		if (preen)
419 			pfatal("CAN'T CHECK FILE SYSTEM.");
420 		if (exitstat == 0)
421 			exitstat = mflag ? EXUMNTCHK : EXERRFATAL;
422 		exit(exitstat);
423 	} else {
424 		devname = devstr;
425 	}
426 
427 	if (mflag) {
428 		check_sanity(filesys);
429 		/* NOTREACHED */
430 	}
431 
432 	if (debug)
433 		printclean();
434 
435 	iscorrupt = 0;		/* setup() succeeded, assume good filesystem */
436 
437 	/*
438 	 * 1: scan inodes tallying blocks used
439 	 */
440 	if (!preen) {
441 		/* hotroot is reported as such in setup() if debug is on */
442 		if (mountedfs != M_NOMNT)
443 			(void) printf("** Currently Mounted on %s\n",
444 			    sblock.fs_fsmnt);
445 		else
446 			(void) printf("** Last Mounted on %s\n",
447 			    sblock.fs_fsmnt);
448 		(void) printf("** Phase 1 - Check Blocks and Sizes\n");
449 	}
450 	pass1();
451 
452 	/*
453 	 * 1b: locate first references to duplicates, if any
454 	 */
455 	if (have_dups()) {
456 		if (preen)
457 			pfatal("INTERNAL ERROR: dups with -o p");
458 		(void) printf("** Phase 1b - Rescan For More DUPS\n");
459 		pass1b();
460 	}
461 
462 	/*
463 	 * 2: traverse directories from root to mark all connected directories
464 	 */
465 	if (!preen)
466 		(void) printf("** Phase 2 - Check Pathnames\n");
467 	pass2();
468 
469 	/*
470 	 * 3a: scan inodes looking for disconnected directories.
471 	 */
472 	if (!preen)
473 		(void) printf("** Phase 3a - Check Connectivity\n");
474 	pass3a();
475 
476 	/*
477 	 * 3b: check acls
478 	 */
479 	if (!preen)
480 		(void) printf("** Phase 3b - Verify Shadows/ACLs\n");
481 	pass3b();
482 
483 	/*
484 	 * 4: scan inodes looking for disconnected files; check reference counts
485 	 */
486 	if (!preen)
487 		(void) printf("** Phase 4 - Check Reference Counts\n");
488 	pass4();
489 
490 	/*
491 	 * 5: check and repair resource counts in cylinder groups
492 	 */
493 	if (!preen)
494 		(void) printf("** Phase 5 - Check Cylinder Groups\n");
495 recount:
496 	pass5();
497 
498 	if (overflowed_lf) {
499 		iscorrupt = 1;
500 	}
501 
502 	if (!nflag && mountedfs == M_RW) {
503 		(void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
504 		rerun = 1;
505 	}
506 
507 	if (have_dups()) {
508 		quiet_dups = (reply("LIST REMAINING DUPS") == 0);
509 		if (report_dups(quiet_dups) > 0)
510 			iscorrupt = 1;
511 
512 		(void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
513 		    "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
514 	}
515 
516 	if (limbo_dirs != NULL) {
517 		/*
518 		 * Don't force iscorrupt, as this is sufficiently
519 		 * harmless that the filesystem can be mounted and
520 		 * used.  We just leak some inodes and/or blocks.
521 		 */
522 		pwarn("Orphan directories not cleared or reconnected:\n");
523 
524 		twalk(limbo_dirs, report_limbo);
525 
526 		while (limbo_dirs != NULL) {
527 			limbo_victim = *(fsck_ino_t *)limbo_dirs;
528 			if (limbo_victim != 0) {
529 				(void) tdelete((void *)limbo_victim,
530 				    &limbo_dirs,
531 				    ino_t_cmp);
532 			}
533 		}
534 
535 		rerun = 1;
536 	}
537 
538 	if (iscorrupt) {
539 		if (mountedfs == M_RW)
540 			(void) printf("FS IS MOUNTED R/W AND"
541 			    " FSCK DID ITS BEST TO FIX"
542 			    " INCONSISTENCIES.\n");
543 		else
544 			(void) printf("FILESYSTEM MAY STILL BE"
545 			    " INCONSISTENT.\n");
546 		rerun = 1;
547 	}
548 
549 	/*
550 	 * iscorrupt must be stable at this point.
551 	 * updateclean() returns true when it had to discard the log.
552 	 * This can only happen once, since sblock.fs_logbno gets
553 	 * cleared as part of that operation.
554 	 */
555 	if (updateclean()) {
556 		if (!preen)
557 			(void) printf(
558 			    "Log was discarded, updating cyl groups\n");
559 		goto recount;
560 	}
561 
562 	if (debug)
563 		printclean();
564 
565 	ckfini();
566 
567 	/*
568 	 * print out summary statistics
569 	 */
570 	n_ffree = sblock.fs_cstotal.cs_nffree;
571 	n_bfree = sblock.fs_cstotal.cs_nbfree;
572 	files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
573 	blks = n_blks +
574 	    sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
575 	blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
576 	blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
577 	blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
578 	if (debug && (files > 0 || blks > 0)) {
579 		countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
580 		pwarn("Reclaimed: %d directories, %d files, %lld fragments\n",
581 		    countdirs, files - countdirs,
582 		    (longlong_t)blks);
583 	}
584 
585 	dbl_nffree = (double)n_ffree;
586 	dbl_dsize = (double)sblock.fs_dsize;
587 
588 	if (!verbose) {
589 		/*
590 		 * Done as one big string to try for a single write,
591 		 * so the output doesn't get interleaved with other
592 		 * preening fscks.
593 		 */
594 		pwarn("%ld files, %lld used, %lld free "
595 		    "(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
596 		    (long)n_files, (longlong_t)n_blks,
597 		    (longlong_t)n_ffree + sblock.fs_frag * n_bfree,
598 		    (longlong_t)n_ffree, (longlong_t)n_bfree,
599 		    (dbl_nffree * 100.0) / dbl_dsize);
600 	} else {
601 		pwarn("\nFilesystem summary:\n");
602 		pwarn("Inodes in use: %ld\n", (long)n_files);
603 		pwarn("Blocks in use: %lld\n", (longlong_t)n_blks);
604 		pwarn("Total free fragments: %lld\n",
605 		    (longlong_t)n_ffree + sblock.fs_frag * n_bfree);
606 		pwarn("Free fragments not in blocks: %lld\n",
607 		    (longlong_t)n_ffree);
608 		pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree);
609 		pwarn("Fragment/block fragmentation: %.1f%%\n",
610 		    (dbl_nffree * 100.0) / dbl_dsize);
611 		pwarn("");
612 
613 		if (files < 0)
614 			pwarn("%d inodes missing\n", -files);
615 		if (blks < 0)
616 			pwarn("%lld blocks missing\n", -(longlong_t)blks);
617 
618 		zlinks_printed = 0;
619 		for (inumber = UFSROOTINO; inumber < maxino; inumber++) {
620 			if (S_IS_ZLINK(statemap[inumber])) {
621 				if (zlinks_printed == 0) {
622 					pwarn("The following zero "
623 					    "link count inodes remain:");
624 				}
625 				if (zlinks_printed) {
626 					if ((zlinks_printed % 9) == 0)
627 						(void) puts(",\n");
628 					else
629 						(void) puts(", ");
630 				}
631 				(void) printf("%u", inumber);
632 				zlinks_printed++;
633 			}
634 		}
635 		if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0))
636 			(void) putchar('\n');
637 	}
638 
639 	/*
640 	 * Clean up after ourselves, so we can do the next filesystem.
641 	 */
642 	free_dup_state();
643 	inocleanup();
644 	free(blockmap);
645 	free(statemap);
646 	free((void *)lncntp);
647 	lncntp = NULL;
648 	blockmap = NULL;
649 	statemap = NULL;
650 	if (iscorrupt && exitstat == 0)
651 		exitstat = EXFNDERRS;
652 	if (fsmodified)
653 		(void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
654 	if (overflowed_lf)
655 		(void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n",
656 		    lfname);
657 	if (reattached_dir) {
658 		(void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK "
659 		    "COUNTS MAY NOT BE CORRECT.\n");
660 		rerun = 1;
661 	}
662 	if (broke_dir_link) {
663 		(void) printf(
664 		    "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n");
665 		rerun = 1;
666 	}
667 	if (iscorrupt)
668 		(void) printf("***** FILE SYSTEM IS BAD *****\n");
669 
670 	if (rerun) {
671 		if (mountedfs == M_RW)
672 			(void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED"
673 			    " FILE SYSTEM *****\n");
674 		else
675 			(void) printf("\n***** PLEASE RERUN FSCK *****\n");
676 	}
677 
678 	if ((exitstat == 0) &&
679 	    (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) {
680 		exitstat = EXROOTOKAY;
681 	}
682 
683 	if ((exitstat == 0) && rerun)
684 		exitstat = EXFNDERRS;
685 
686 	if (mountedfs != M_NOMNT) {
687 		if (!fsmodified)
688 			return;
689 		/*
690 		 * _FIOFFS is much more effective than a simple sync().
691 		 * Note that the original fswritefd was discarded in
692 		 * ckfini().
693 		 */
694 		fswritefd = open(devstr, O_RDWR, 0);
695 		if (fswritefd != -1) {
696 			(void) ioctl(fswritefd, _FIOFFS, NULL);
697 			(void) close(fswritefd);
698 		}
699 
700 		if (!preen)
701 			(void) printf("\n***** REBOOT NOW *****\n");
702 
703 		exitstat = EXREBOOTNOW;
704 	}
705 }
706 
707 /*
708  * fsck -m: does the filesystem pass cursory examination
709  *
710  * XXX This is very redundant with setup().  The right thing would be
711  *     for setup() to modify its behaviour when mflag is set (less
712  *     chatty, exit instead of return, etc).
713  */
714 void
check_sanity(char * filename)715 check_sanity(char *filename)
716 {
717 	struct stat64 stbd, stbr;
718 	char *devname;
719 	struct ustat usb;
720 	char vfsfilename[MAXPATHLEN];
721 	struct vfstab vfsbuf;
722 	FILE *vfstab;
723 	struct statvfs vfs_stat;
724 	int found_magic[MAGIC_LIMIT];
725 	int magic_cnt;
726 	int is_magic = 0;
727 	int is_block = 0;
728 	int is_file = 0;
729 
730 	(void) memset((void *)found_magic, 0, sizeof (found_magic));
731 
732 	if (stat64(filename, &stbd) < 0) {
733 		(void) fprintf(stderr,
734 		"ufs fsck: sanity check failed : cannot stat %s\n", filename);
735 		exit(EXNOSTAT);
736 	}
737 
738 	if (S_ISBLK(stbd.st_mode)) {
739 		is_block = 1;
740 	} else if (S_ISCHR(stbd.st_mode)) {
741 		is_block = 0;
742 	} else if (S_ISREG(stbd.st_mode)) {
743 		is_file = 1;
744 	}
745 
746 	/*
747 	 * Determine if this is the root file system via vfstab. Give up
748 	 * silently on failures. The whole point of this is to be tolerant
749 	 * of the magic file systems being already mounted.
750 	 */
751 	if (!is_file && (vfstab = fopen(VFSTAB, "r")) != NULL) {
752 		for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) {
753 			if (magic_cnt == MAGIC_NONE)
754 				continue;
755 			if (getvfsfile(vfstab, &vfsbuf,
756 			    magic_fs[magic_cnt]) == 0) {
757 				if (is_block)
758 					devname = vfsbuf.vfs_special;
759 				else
760 					devname = vfsbuf.vfs_fsckdev;
761 				if (stat64(devname, &stbr) == 0) {
762 					if (stbr.st_rdev == stbd.st_rdev) {
763 						found_magic[magic_cnt] = 1;
764 						is_magic = magic_cnt;
765 						break;
766 					}
767 				}
768 			}
769 		}
770 	}
771 
772 	/*
773 	 * Only works if filename is a block device or if
774 	 * character and block device has the same dev_t value.
775 	 * This is currently true, but nothing really forces it.
776 	 */
777 	if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) {
778 		(void) fprintf(stderr,
779 		    "ufs fsck: sanity check: %s already mounted\n", filename);
780 		exit(EXMOUNTED);
781 	}
782 
783 	if (is_magic) {
784 		(void) strcpy(vfsfilename, magic_fs[is_magic]);
785 		if (statvfs(vfsfilename, &vfs_stat) != 0) {
786 			(void) fprintf(stderr, "ufs fsck: Cannot stat %s\n",
787 			    vfsfilename);
788 			exit(EXNOSTAT);
789 		}
790 
791 		if (!(vfs_stat.f_flag & ST_RDONLY)) {
792 			/*
793 			 * The file system is mounted read/write
794 			 * We need to exit saying this. If it's only
795 			 * mounted readonly, we can continue.
796 			 */
797 
798 			(void) fprintf(stderr,
799 			    "ufs fsck: sanity check:"
800 			    "%s already mounted read/write\n", filename);
801 			exit(EXMOUNTED);
802 		}
803 	}
804 
805 	/*
806 	 * We know that at boot, the ufs root file system is mounted
807 	 * read-only first.  After fsck runs, it is remounted as
808 	 * read-write.  Therefore, we do not need to check for different
809 	 * values for fs_state between the root file system and the
810 	 * rest of the file systems.
811 	 */
812 	if (islog && !islogok) {
813 		(void) fprintf(stderr,
814 		    "ufs fsck: sanity check: %s needs checking\n", filename);
815 		exit(EXUMNTCHK);
816 	}
817 	if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) &&
818 	    (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
819 	    (sblock.fs_clean == FSLOG && islog))) {
820 		(void) fprintf(stderr,
821 		    "ufs fsck: sanity check: %s okay\n", filename);
822 	} else {
823 		(void) fprintf(stderr,
824 		    "ufs fsck: sanity check: %s needs checking\n", filename);
825 		exit(EXUMNTCHK);
826 	}
827 	exit(EXOKAY);
828 }
829 
830 caddr_t
hasvfsopt(struct vfstab * vfs,char * opt)831 hasvfsopt(struct vfstab *vfs, char *opt)
832 {
833 	struct mnttab mtab;
834 
835 	if (vfs->vfs_mntopts == NULL)
836 		return (NULL);
837 	mtab.mnt_mntopts = vfs->vfs_mntopts;
838 	return (hasmntopt(&mtab, opt));
839 }
840 
841 static void __NORETURN
usage(void)842 usage(void)
843 {
844 	(void) fprintf(stderr,
845 	    "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
846 	    "[-o p,b=#,w,f] [special ....]\n");
847 
848 	exit(EXBADPARM);
849 }
850 
851 /*ARGSUSED*/
852 static void
report_limbo(const void * node,VISIT order,int level)853 report_limbo(const void *node, VISIT order, int level)
854 {
855 	fsck_ino_t ino = *(fsck_ino_t *)node;
856 
857 	if ((order == postorder) || (order == leaf)) {
858 		(void) printf("    Inode %d\n", ino);
859 	}
860 }
861