11558Srgrimes/*
21558Srgrimes * Copyright (c) 1980, 1986, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
30114589Sobrien#if 0
311558Srgrimes#ifndef lint
327585Sbdestatic const char copyright[] =
331558Srgrimes"@(#) Copyright (c) 1980, 1986, 1993\n\
341558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351558Srgrimes#endif /* not lint */
361558Srgrimes
371558Srgrimes#ifndef lint
3841477Sjulianstatic char sccsid[] = "@(#)main.c	8.6 (Berkeley) 5/14/95";
39114589Sobrien#endif /* not lint */
4041477Sjulian#endif
4193103Smarkm#include <sys/cdefs.h>
4293103Smarkm__FBSDID("$FreeBSD: stable/10/sbin/fsck_ffs/main.c 324675 2017-10-16 21:55:31Z mckusick $");
4393103Smarkm
441558Srgrimes#include <sys/param.h>
4575557Smckusick#include <sys/file.h>
461558Srgrimes#include <sys/mount.h>
4740918Smjacob#include <sys/resource.h>
48217769Smckusick#include <sys/stat.h>
4986514Siedowse#include <sys/sysctl.h>
50172236Srodrigc#include <sys/uio.h>
5198542Smckusick#include <sys/disklabel.h>
5223675Speter
531558Srgrimes#include <ufs/ufs/dinode.h>
541558Srgrimes#include <ufs/ffs/fs.h>
5523675Speter
5623675Speter#include <err.h>
5755725Speter#include <errno.h>
581558Srgrimes#include <fstab.h>
59120901Smckusick#include <grp.h>
60307536Smckusick#include <inttypes.h>
61172236Srodrigc#include <mntopts.h>
6255725Speter#include <paths.h>
63101037Smux#include <stdint.h>
6486514Siedowse#include <string.h>
65217769Smckusick#include <time.h>
6623675Speter
671558Srgrimes#include "fsck.h"
681558Srgrimes
69260178Sscottlint	restarts;
70260178Sscottl
7192839Simpstatic void usage(void) __dead2;
72307536Smckusickstatic intmax_t argtoimax(int flag, const char *req, const char *str, int base);
7392839Simpstatic int checkfilesys(char *filesys);
74171800Spjdstatic int chkdoreload(struct statfs *mntp);
7592839Simpstatic struct statfs *getmntpt(const char *);
7623675Speter
777585Sbdeint
7892839Simpmain(int argc, char *argv[])
791558Srgrimes{
801558Srgrimes	int ch;
8141474Sjulian	struct rlimit rlimit;
82126345Sscottl	struct itimerval itimerval;
8366861Sadrian	int ret = 0;
841558Srgrimes
851558Srgrimes	sync();
8666861Sadrian	skipclean = 1;
87188110Smckusick	inoopt = 0;
88260178Sscottl	while ((ch = getopt(argc, argv, "b:Bc:CdEfFm:npRrSyZ")) != -1) {
891558Srgrimes		switch (ch) {
901558Srgrimes		case 'b':
9166861Sadrian			skipclean = 0;
92307536Smckusick			bflag = argtoimax('b', "number", optarg, 10);
93307536Smckusick			printf("Alternate super block location: %jd\n", bflag);
941558Srgrimes			break;
951558Srgrimes
9674556Smckusick		case 'B':
9774556Smckusick			bkgrdflag = 1;
9874556Smckusick			break;
9974556Smckusick
1001558Srgrimes		case 'c':
10166861Sadrian			skipclean = 0;
102307536Smckusick			cvtlevel = argtoimax('c', "conversion level", optarg,
103307536Smckusick			    10);
10498542Smckusick			if (cvtlevel < 3)
10598542Smckusick				errx(EEXIT, "cannot do level %d conversion",
10698542Smckusick				    cvtlevel);
1071558Srgrimes			break;
1088871Srgrimes
1091558Srgrimes		case 'd':
1101558Srgrimes			debug++;
1111558Srgrimes			break;
1121558Srgrimes
113221233Sdes		case 'E':
114221233Sdes			Eflag++;
115221233Sdes			break;
116221233Sdes
1172153Sdg		case 'f':
11866861Sadrian			skipclean = 0;
1192153Sdg			break;
1202153Sdg
12175927Smckusick		case 'F':
12275927Smckusick			bkgrdcheck = 1;
12375927Smckusick			break;
12475927Smckusick
1251558Srgrimes		case 'm':
126307536Smckusick			lfmode = argtoimax('m', "mode", optarg, 8);
1271558Srgrimes			if (lfmode &~ 07777)
12823675Speter				errx(EEXIT, "bad mode to -m: %o", lfmode);
1291558Srgrimes			printf("** lost+found creation mode %o\n", lfmode);
1301558Srgrimes			break;
1311558Srgrimes
1321558Srgrimes		case 'n':
1331558Srgrimes			nflag++;
1341558Srgrimes			yflag = 0;
1351558Srgrimes			break;
1361558Srgrimes
13766861Sadrian		case 'p':
13866861Sadrian			preen++;
139187931Sobrien			/*FALLTHROUGH*/
140187931Sobrien
141187931Sobrien		case 'C':
142187931Sobrien			ckclean++;
14366861Sadrian			break;
14466861Sadrian
145260178Sscottl		case 'R':
146260178Sscottl			wantrestart = 1;
147260178Sscottl			break;
148188110Smckusick		case 'r':
149188110Smckusick			inoopt++;
150188110Smckusick			break;
151188110Smckusick
152253822Sscottl		case 'S':
153253822Sscottl			surrender = 1;
154253822Sscottl			break;
155253822Sscottl
1561558Srgrimes		case 'y':
1571558Srgrimes			yflag++;
1581558Srgrimes			nflag = 0;
1591558Srgrimes			break;
1601558Srgrimes
161250056Sdes		case 'Z':
162250056Sdes			Zflag++;
163250056Sdes			break;
164250056Sdes
1651558Srgrimes		default:
16666861Sadrian			usage();
1671558Srgrimes		}
1681558Srgrimes	}
1691558Srgrimes	argc -= optind;
1701558Srgrimes	argv += optind;
17166861Sadrian
17266861Sadrian	if (!argc)
17366861Sadrian		usage();
17466861Sadrian
1751558Srgrimes	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1761558Srgrimes		(void)signal(SIGINT, catch);
177187931Sobrien	if (ckclean)
1781558Srgrimes		(void)signal(SIGQUIT, catchquit);
17970050Siedowse	signal(SIGINFO, infohandler);
180126345Sscottl	if (bkgrdflag) {
181126345Sscottl		signal(SIGALRM, alarmhandler);
182126345Sscottl		itimerval.it_interval.tv_sec = 5;
183126345Sscottl		itimerval.it_interval.tv_usec = 0;
184126345Sscottl		itimerval.it_value.tv_sec = 5;
185126345Sscottl		itimerval.it_value.tv_usec = 0;
186126345Sscottl		setitimer(ITIMER_REAL, &itimerval, NULL);
187126345Sscottl	}
18841474Sjulian	/*
18941474Sjulian	 * Push up our allowed memory limit so we can cope
190102231Strhodes	 * with huge file systems.
19141474Sjulian	 */
19241474Sjulian	if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
19341474Sjulian		rlimit.rlim_cur = rlimit.rlim_max;
19441474Sjulian		(void)setrlimit(RLIMIT_DATA, &rlimit);
19541474Sjulian	}
196260178Sscottl	while (argc > 0) {
197260178Sscottl		if (checkfilesys(*argv) == ERESTART)
198260178Sscottl			continue;
199260178Sscottl		argc--;
200260178Sscottl		argv++;
201260178Sscottl	}
20241474Sjulian
2031558Srgrimes	if (returntosingle)
20466861Sadrian		ret = 2;
2051558Srgrimes	exit(ret);
2061558Srgrimes}
2071558Srgrimes
208307536Smckusickstatic intmax_t
209307536Smckusickargtoimax(int flag, const char *req, const char *str, int base)
2101558Srgrimes{
2111558Srgrimes	char *cp;
212307536Smckusick	intmax_t ret;
2131558Srgrimes
214307536Smckusick	ret = strtoimax(str, &cp, base);
2151558Srgrimes	if (cp == str || *cp)
21623675Speter		errx(EEXIT, "-%c flag requires a %s", flag, req);
2171558Srgrimes	return (ret);
2181558Srgrimes}
2191558Srgrimes
2201558Srgrimes/*
221102231Strhodes * Check the specified file system.
2221558Srgrimes */
2231558Srgrimes/* ARGSUSED */
22423675Speterstatic int
22592839Simpcheckfilesys(char *filesys)
2261558Srgrimes{
22798542Smckusick	ufs2_daddr_t n_ffree, n_bfree;
2281558Srgrimes	struct dups *dp;
22974556Smckusick	struct statfs *mntp;
230120901Smckusick	struct stat snapdir;
231120901Smckusick	struct group *grp;
232172236Srodrigc	struct iovec *iov;
233172236Srodrigc	char errmsg[255];
234324675Smckusick	int ofsmodified;
235172236Srodrigc	int iovlen;
236171800Spjd	int cylno;
237241035Smdf	intmax_t blks, files;
238101037Smux	size_t size;
2391558Srgrimes
240172236Srodrigc	iov = NULL;
241172236Srodrigc	iovlen = 0;
242172236Srodrigc	errmsg[0] = '\0';
243260178Sscottl	fsutilinit();
244260178Sscottl	fsckinit();
245172236Srodrigc
2461558Srgrimes	cdevname = filesys;
247187931Sobrien	if (debug && ckclean)
2481558Srgrimes		pwarn("starting\n");
24975927Smckusick	/*
25075927Smckusick	 * Make best effort to get the disk name. Check first to see
251102231Strhodes	 * if it is listed among the mounted file systems. Failing that
25275927Smckusick	 * check to see if it is listed in /etc/fstab.
25375927Smckusick	 */
25475927Smckusick	mntp = getmntpt(filesys);
25575927Smckusick	if (mntp != NULL)
25675927Smckusick		filesys = mntp->f_mntfromname;
25775927Smckusick	else
25875927Smckusick		filesys = blockcheck(filesys);
25975927Smckusick	/*
26075927Smckusick	 * If -F flag specified, check to see whether a background check
26175927Smckusick	 * is possible and needed. If possible and needed, exit with
26275927Smckusick	 * status zero. Otherwise exit with status non-zero. A non-zero
26375927Smckusick	 * exit status will cause a foreground check to be run.
26475927Smckusick	 */
26575557Smckusick	sblock_init();
26675927Smckusick	if (bkgrdcheck) {
26775927Smckusick		if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
26875927Smckusick			exit(3);	/* Cannot read superblock */
26975927Smckusick		close(fsreadfd);
270207141Sjeff		/* Earlier background failed or journaled */
271207141Sjeff		if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ))
272207141Sjeff			exit(4);
27375927Smckusick		if ((sblock.fs_flags & FS_DOSOFTDEP) == 0)
27475927Smckusick			exit(5);	/* Not running soft updates */
27575927Smckusick		size = MIBSIZE;
27675927Smckusick		if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0)
27775927Smckusick			exit(6);	/* Lacks kernel support */
27875927Smckusick		if ((mntp == NULL && sblock.fs_clean == 1) ||
27975927Smckusick		    (mntp != NULL && (sblock.fs_flags & FS_UNCLEAN) == 0))
28075927Smckusick			exit(7);	/* Filesystem clean, report it now */
28175927Smckusick		exit(0);
28275927Smckusick	}
283187931Sobrien	if (ckclean && skipclean) {
284163845Spjd		/*
285163845Spjd		 * If file system is gjournaled, check it here.
286163845Spjd		 */
287163845Spjd		if ((fsreadfd = open(filesys, O_RDONLY)) < 0 || readsb(0) == 0)
288163845Spjd			exit(3);	/* Cannot read superblock */
289163845Spjd		close(fsreadfd);
290163845Spjd		if ((sblock.fs_flags & FS_GJOURNAL) != 0) {
291163845Spjd			//printf("GJournaled file system detected on %s.\n",
292163845Spjd			//    filesys);
293163845Spjd			if (sblock.fs_clean == 1) {
294163845Spjd				pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
295163845Spjd				exit(0);
296163845Spjd			}
297163845Spjd			if ((sblock.fs_flags & (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
298163845Spjd				gjournal_check(filesys);
299171800Spjd				if (chkdoreload(mntp) == 0)
300171800Spjd					exit(0);
301171800Spjd				exit(4);
302163845Spjd			} else {
303240405Sobrien				pfatal(
304240405Sobrien			    "UNEXPECTED INCONSISTENCY, CANNOT RUN FAST FSCK\n");
305163845Spjd			}
306163845Spjd		}
307163845Spjd	}
30874556Smckusick	/*
30974556Smckusick	 * If we are to do a background check:
310102231Strhodes	 *	Get the mount point information of the file system
31174556Smckusick	 *	create snapshot file
31274556Smckusick	 *	return created snapshot file
31374556Smckusick	 *	if not found, clear bkgrdflag and proceed with normal fsck
31474556Smckusick	 */
31574556Smckusick	if (bkgrdflag) {
31674556Smckusick		if (mntp == NULL) {
31774556Smckusick			bkgrdflag = 0;
31875557Smckusick			pfatal("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
31974556Smckusick		} else if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
32074556Smckusick			bkgrdflag = 0;
321240405Sobrien			pfatal(
322240405Sobrien			  "NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n");
32374556Smckusick		} else if ((mntp->f_flags & MNT_RDONLY) != 0) {
32474556Smckusick			bkgrdflag = 0;
32575557Smckusick			pfatal("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
32675557Smckusick		} else if ((fsreadfd = open(filesys, O_RDONLY)) >= 0) {
32775557Smckusick			if (readsb(0) != 0) {
328207141Sjeff				if (sblock.fs_flags & (FS_NEEDSFSCK | FS_SUJ)) {
32975557Smckusick					bkgrdflag = 0;
330240405Sobrien					pfatal(
331240405Sobrien			"UNEXPECTED INCONSISTENCY, CANNOT RUN IN BACKGROUND\n");
33275557Smckusick				}
33375557Smckusick				if ((sblock.fs_flags & FS_UNCLEAN) == 0 &&
334187931Sobrien				    skipclean && ckclean) {
33575557Smckusick					/*
336102231Strhodes					 * file system is clean;
33775557Smckusick					 * skip snapshot and report it clean
33875557Smckusick					 */
339240405Sobrien					pwarn(
340240405Sobrien					"FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
34175557Smckusick					goto clean;
34275557Smckusick				}
34375557Smckusick			}
34475557Smckusick			close(fsreadfd);
34575557Smckusick		}
34675557Smckusick		if (bkgrdflag) {
347120901Smckusick			snprintf(snapname, sizeof snapname, "%s/.snap",
34874556Smckusick			    mntp->f_mntonname);
349120901Smckusick			if (stat(snapname, &snapdir) < 0) {
350120901Smckusick				if (errno != ENOENT) {
351120901Smckusick					bkgrdflag = 0;
352240405Sobrien					pfatal(
353240405Sobrien	"CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
354240405Sobrien					    snapname, strerror(errno));
355120901Smckusick				} else if ((grp = getgrnam("operator")) == 0 ||
356120901Smckusick				    mkdir(snapname, 0770) < 0 ||
357120901Smckusick				    chown(snapname, -1, grp->gr_gid) < 0 ||
358120901Smckusick				    chmod(snapname, 0770) < 0) {
359120901Smckusick					bkgrdflag = 0;
360240405Sobrien					pfatal(
361240405Sobrien	"CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, CANNOT RUN IN BACKGROUND\n",
362240405Sobrien					    snapname, strerror(errno));
363120901Smckusick				}
364120901Smckusick			} else if (!S_ISDIR(snapdir.st_mode)) {
365120901Smckusick				bkgrdflag = 0;
366240405Sobrien				pfatal(
367240405Sobrien			"%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
368240405Sobrien				    snapname);
369120901Smckusick			}
370120901Smckusick		}
371120901Smckusick		if (bkgrdflag) {
372120901Smckusick			snprintf(snapname, sizeof snapname,
373120901Smckusick			    "%s/.snap/fsck_snapshot", mntp->f_mntonname);
374172236Srodrigc			build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
375172236Srodrigc			build_iovec(&iov, &iovlen, "from", snapname,
376172236Srodrigc			    (size_t)-1);
377172236Srodrigc			build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname,
378172236Srodrigc			    (size_t)-1);
379172236Srodrigc			build_iovec(&iov, &iovlen, "errmsg", errmsg,
380172236Srodrigc			    sizeof(errmsg));
381182027Srodrigc			build_iovec(&iov, &iovlen, "update", NULL, 0);
382182027Srodrigc			build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
383172236Srodrigc
384186471Sobrien			while (nmount(iov, iovlen, mntp->f_flags) < 0) {
38574556Smckusick				if (errno == EEXIST && unlink(snapname) == 0)
38674556Smckusick					continue;
38774556Smckusick				bkgrdflag = 0;
388172236Srodrigc				pfatal("CANNOT CREATE SNAPSHOT %s: %s %s\n",
389172236Srodrigc				    snapname, strerror(errno), errmsg);
39074556Smckusick				break;
39174556Smckusick			}
39274556Smckusick			if (bkgrdflag != 0)
39374556Smckusick				filesys = snapname;
39474556Smckusick		}
39574556Smckusick	}
39674556Smckusick
39723675Speter	switch (setup(filesys)) {
39823675Speter	case 0:
3991558Srgrimes		if (preen)
4001558Srgrimes			pfatal("CAN'T CHECK FILE SYSTEM.");
40141477Sjulian		return (0);
40223675Speter	case -1:
40375557Smckusick	clean:
40486514Siedowse		pwarn("clean, %ld free ", (long)(sblock.fs_cstotal.cs_nffree +
40586514Siedowse		    sblock.fs_frag * sblock.fs_cstotal.cs_nbfree));
406241035Smdf		printf("(%jd frags, %jd blocks, %.1f%% fragmentation)\n",
407241035Smdf		    (intmax_t)sblock.fs_cstotal.cs_nffree,
408241035Smdf		    (intmax_t)sblock.fs_cstotal.cs_nbfree,
40941477Sjulian		    sblock.fs_cstotal.cs_nffree * 100.0 / sblock.fs_dsize);
41031904Sbde		return (0);
4112153Sdg	}
412207141Sjeff	/*
413207141Sjeff	 * Determine if we can and should do journal recovery.
414207141Sjeff	 */
415209408Sdelphij	if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) {
416209408Sdelphij		if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK && skipclean) {
417210793Sbz			if (preen || reply("USE JOURNAL")) {
418209408Sdelphij				if (suj_check(filesys) == 0) {
419209408Sdelphij					printf("\n***** FILE SYSTEM MARKED CLEAN *****\n");
420209408Sdelphij					if (chkdoreload(mntp) == 0)
421209408Sdelphij						exit(0);
422209408Sdelphij					exit(4);
423209408Sdelphij				}
424207141Sjeff			}
425209408Sdelphij			printf("** Skipping journal, falling through to full fsck\n\n");
426207141Sjeff		}
427207141Sjeff		/*
428207141Sjeff		 * Write the superblock so we don't try to recover the
429324675Smckusick		 * journal on another pass. If this is the only change
430324675Smckusick		 * to the filesystem, we do not want it to be called
431324675Smckusick		 * out as modified.
432207141Sjeff		 */
433207141Sjeff		sblock.fs_mtime = time(NULL);
434207141Sjeff		sbdirty();
435324675Smckusick		ofsmodified = fsmodified;
436324675Smckusick		flush(fswritefd, &sblk);
437324675Smckusick		fsmodified = ofsmodified;
438207141Sjeff	}
439221110Sdes
44055275Speter	/*
44134266Sjulian	 * Cleared if any questions answered no. Used to decide if
44234266Sjulian	 * the superblock should be marked clean.
44334266Sjulian	 */
44434266Sjulian	resolved = 1;
44534266Sjulian	/*
4461558Srgrimes	 * 1: scan inodes tallying blocks used
4471558Srgrimes	 */
4481558Srgrimes	if (preen == 0) {
4491558Srgrimes		printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
45074556Smckusick		if (mntp != NULL && mntp->f_flags & MNT_ROOTFS)
451102231Strhodes			printf("** Root file system\n");
4521558Srgrimes		printf("** Phase 1 - Check Blocks and Sizes\n");
4531558Srgrimes	}
454247212Smckusick	clock_gettime(CLOCK_REALTIME_PRECISE, &startprog);
4551558Srgrimes	pass1();
456247212Smckusick	IOstats("Pass1");
4571558Srgrimes
4581558Srgrimes	/*
4591558Srgrimes	 * 1b: locate first references to duplicates, if any
4601558Srgrimes	 */
4611558Srgrimes	if (duplist) {
46234266Sjulian		if (preen || usedsoftdep)
463201708Smckusick			pfatal("INTERNAL ERROR: dups with %s%s%s",
464201708Smckusick			    preen ? "-p" : "",
465201708Smckusick			    (preen && usedsoftdep) ? " and " : "",
466201708Smckusick			    usedsoftdep ? "softupdates" : "");
4671558Srgrimes		printf("** Phase 1b - Rescan For More DUPS\n");
4681558Srgrimes		pass1b();
469247212Smckusick		IOstats("Pass1b");
4701558Srgrimes	}
4711558Srgrimes
4721558Srgrimes	/*
4731558Srgrimes	 * 2: traverse directories from root to mark all connected directories
4741558Srgrimes	 */
4751558Srgrimes	if (preen == 0)
4761558Srgrimes		printf("** Phase 2 - Check Pathnames\n");
4771558Srgrimes	pass2();
478247212Smckusick	IOstats("Pass2");
4791558Srgrimes
4801558Srgrimes	/*
4811558Srgrimes	 * 3: scan inodes looking for disconnected directories
4821558Srgrimes	 */
4831558Srgrimes	if (preen == 0)
4841558Srgrimes		printf("** Phase 3 - Check Connectivity\n");
4851558Srgrimes	pass3();
486247212Smckusick	IOstats("Pass3");
4871558Srgrimes
4881558Srgrimes	/*
4891558Srgrimes	 * 4: scan inodes looking for disconnected files; check reference counts
4901558Srgrimes	 */
4911558Srgrimes	if (preen == 0)
4921558Srgrimes		printf("** Phase 4 - Check Reference Counts\n");
4931558Srgrimes	pass4();
494247212Smckusick	IOstats("Pass4");
4951558Srgrimes
4961558Srgrimes	/*
4971558Srgrimes	 * 5: check and repair resource counts in cylinder groups
4981558Srgrimes	 */
4991558Srgrimes	if (preen == 0)
5001558Srgrimes		printf("** Phase 5 - Check Cyl groups\n");
5011558Srgrimes	pass5();
502247212Smckusick	IOstats("Pass5");
5031558Srgrimes
5041558Srgrimes	/*
5051558Srgrimes	 * print out summary statistics
5061558Srgrimes	 */
5071558Srgrimes	n_ffree = sblock.fs_cstotal.cs_nffree;
5081558Srgrimes	n_bfree = sblock.fs_cstotal.cs_nbfree;
50974556Smckusick	files = maxino - ROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
51074556Smckusick	blks = n_blks +
51174556Smckusick	    sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
51274556Smckusick	blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
51374556Smckusick	blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
51474556Smckusick	blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
51574556Smckusick	if (bkgrdflag && (files > 0 || blks > 0)) {
51674556Smckusick		countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
517241035Smdf		pwarn("Reclaimed: %ld directories, %jd files, %jd fragments\n",
518241035Smdf		    countdirs, files - countdirs, blks);
51974556Smckusick	}
520101037Smux	pwarn("%ld files, %jd used, %ju free ",
521101037Smux	    (long)n_files, (intmax_t)n_blks,
522101037Smux	    (uintmax_t)n_ffree + sblock.fs_frag * n_bfree);
523101037Smux	printf("(%ju frags, %ju blocks, %.1f%% fragmentation)\n",
524101037Smux	    (uintmax_t)n_ffree, (uintmax_t)n_bfree,
525101037Smux	    n_ffree * 100.0 / sblock.fs_dsize);
5261558Srgrimes	if (debug) {
52774556Smckusick		if (files < 0)
528241035Smdf			printf("%jd inodes missing\n", -files);
52974556Smckusick		if (blks < 0)
530241035Smdf			printf("%jd blocks missing\n", -blks);
5311558Srgrimes		if (duplist != NULL) {
5321558Srgrimes			printf("The following duplicate blocks remain:");
5331558Srgrimes			for (dp = duplist; dp; dp = dp->next)
534241035Smdf				printf(" %jd,", (intmax_t)dp->dup);
5351558Srgrimes			printf("\n");
5361558Srgrimes		}
5371558Srgrimes	}
5381558Srgrimes	duplist = (struct dups *)0;
5391558Srgrimes	muldup = (struct dups *)0;
5401558Srgrimes	inocleanup();
5412179Sdg	if (fsmodified) {
54241474Sjulian		sblock.fs_time = time(NULL);
5431558Srgrimes		sbdirty();
5441558Srgrimes	}
5451558Srgrimes	if (cvtlevel && sblk.b_dirty) {
5468871Srgrimes		/*
5471558Srgrimes		 * Write out the duplicate super blocks
5481558Srgrimes		 */
5491558Srgrimes		for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
550163845Spjd			blwrite(fswritefd, (char *)&sblock,
55198542Smckusick			    fsbtodb(&sblock, cgsblock(&sblock, cylno)),
55298542Smckusick			    SBLOCKSIZE);
5531558Srgrimes	}
55434266Sjulian	if (rerun)
55534266Sjulian		resolved = 0;
556247212Smckusick	finalIOstats();
55755275Speter
55855725Speter	/*
559102231Strhodes	 * Check to see if the file system is mounted read-write.
56055725Speter	 */
56174556Smckusick	if (bkgrdflag == 0 && mntp != NULL && (mntp->f_flags & MNT_RDONLY) == 0)
56255275Speter		resolved = 0;
56334266Sjulian	ckfini(resolved);
56441474Sjulian
56541474Sjulian	for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
56641474Sjulian		if (inostathead[cylno].il_stat != NULL)
56741474Sjulian			free((char *)inostathead[cylno].il_stat);
56841474Sjulian	free((char *)inostathead);
56941474Sjulian	inostathead = NULL;
57041474Sjulian	if (fsmodified && !preen)
5711558Srgrimes		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
572260178Sscottl	if (rerun) {
573260178Sscottl		if (wantrestart && (restarts++ < 10) &&
574260178Sscottl		    (preen || reply("RESTART")))
575260178Sscottl			return (ERESTART);
57618808Sguido		printf("\n***** PLEASE RERUN FSCK *****\n");
577260178Sscottl	}
578171800Spjd	if (chkdoreload(mntp) != 0) {
57941474Sjulian		if (!fsmodified)
58041474Sjulian			return (0);
5811558Srgrimes		if (!preen)
5821558Srgrimes			printf("\n***** REBOOT NOW *****\n");
5831558Srgrimes		sync();
5841558Srgrimes		return (4);
5851558Srgrimes	}
5861558Srgrimes	return (0);
5871558Srgrimes}
58855275Speter
589171800Spjdstatic int
590171800Spjdchkdoreload(struct statfs *mntp)
591171800Spjd{
592172236Srodrigc	struct iovec *iov;
593172236Srodrigc	int iovlen;
594172236Srodrigc	char errmsg[255];
595171800Spjd
596171800Spjd	if (mntp == NULL)
597171800Spjd		return (0);
598172236Srodrigc
599172236Srodrigc	iov = NULL;
600172236Srodrigc	iovlen = 0;
601172236Srodrigc	errmsg[0] = '\0';
602171800Spjd	/*
603171800Spjd	 * We modified a mounted file system.  Do a mount update on
604171800Spjd	 * it unless it is read-write, so we can continue using it
605171800Spjd	 * as safely as possible.
606171800Spjd	 */
607171800Spjd	if (mntp->f_flags & MNT_RDONLY) {
608172236Srodrigc		build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
609172236Srodrigc		build_iovec(&iov, &iovlen, "from", mntp->f_mntfromname,
610172236Srodrigc		    (size_t)-1);
611172236Srodrigc		build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname,
612172236Srodrigc		    (size_t)-1);
613172236Srodrigc		build_iovec(&iov, &iovlen, "errmsg", errmsg,
614172236Srodrigc		    sizeof(errmsg));
615176822Srodrigc		build_iovec(&iov, &iovlen, "update", NULL, 0);
616182027Srodrigc		build_iovec(&iov, &iovlen, "reload", NULL, 0);
617177905Srodrigc		/*
618177905Srodrigc		 * XX: We need the following line until we clean up
619177905Srodrigc		 * nmount parsing of root mounts and NFS root mounts.
620221110Sdes		 */
621176822Srodrigc		build_iovec(&iov, &iovlen, "ro", NULL, 0);
622186471Sobrien		if (nmount(iov, iovlen, mntp->f_flags) == 0) {
623171800Spjd			return (0);
624171800Spjd		}
625172236Srodrigc		pwarn("mount reload of '%s' failed: %s %s\n\n",
626172236Srodrigc		    mntp->f_mntonname, strerror(errno), errmsg);
627171800Spjd		return (1);
628171800Spjd	}
629171800Spjd	return (0);
630171800Spjd}
631171800Spjd
63255275Speter/*
63375927Smckusick * Get the mount point information for name.
63455275Speter */
63555275Speterstatic struct statfs *
63692839Simpgetmntpt(const char *name)
63755275Speter{
63855725Speter	struct stat devstat, mntdevstat;
63955725Speter	char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
640100935Sphk	char *ddevname;
64175927Smckusick	struct statfs *mntbuf, *statfsp;
64275927Smckusick	int i, mntsize, isdev;
64355275Speter
64475927Smckusick	if (stat(name, &devstat) != 0)
64555275Speter		return (NULL);
64675927Smckusick	if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))
64775927Smckusick		isdev = 1;
64875927Smckusick	else
64975927Smckusick		isdev = 0;
65055275Speter	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
65155275Speter	for (i = 0; i < mntsize; i++) {
65275927Smckusick		statfsp = &mntbuf[i];
653100935Sphk		ddevname = statfsp->f_mntfromname;
654100935Sphk		if (*ddevname != '/') {
655301782Sngie			if (strlen(_PATH_DEV) + strlen(ddevname) + 1 >
656301782Sngie			    sizeof(statfsp->f_mntfromname))
657301782Sngie				continue;
65855725Speter			strcpy(device, _PATH_DEV);
659100935Sphk			strcat(device, ddevname);
66075927Smckusick			strcpy(statfsp->f_mntfromname, device);
66155725Speter		}
66275927Smckusick		if (isdev == 0) {
66375927Smckusick			if (strcmp(name, statfsp->f_mntonname))
66475927Smckusick				continue;
66575927Smckusick			return (statfsp);
66675927Smckusick		}
667100935Sphk		if (stat(ddevname, &mntdevstat) == 0 &&
66855725Speter		    mntdevstat.st_rdev == devstat.st_rdev)
66975927Smckusick			return (statfsp);
67055275Speter	}
67175927Smckusick	statfsp = NULL;
67275927Smckusick	return (statfsp);
67355275Speter}
67466861Sadrian
67566861Sadrianstatic void
67692839Simpusage(void)
67766861Sadrian{
678221110Sdes	(void) fprintf(stderr,
679240405Sobrien"usage: %s [-BEFfnpry] [-b block] [-c level] [-m mode] filesystem ...\n",
680221110Sdes	    getprogname());
681221110Sdes	exit(1);
68266861Sadrian}
683260178Sscottl
684260178Sscottlvoid
685260178Sscottlinfohandler(int sig __unused)
686260178Sscottl{
687260178Sscottl	got_siginfo = 1;
688260178Sscottl}
689260178Sscottl
690260178Sscottlvoid
691260178Sscottlalarmhandler(int sig __unused)
692260178Sscottl{
693260178Sscottl	got_sigalarm = 1;
694260178Sscottl}
695