1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1980, 1986, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#define _WANT_P_OSREL
33#include <sys/param.h>
34#include <sys/file.h>
35#include <sys/mount.h>
36#include <sys/resource.h>
37#include <sys/stat.h>
38#include <sys/sysctl.h>
39#include <sys/uio.h>
40#include <sys/disklabel.h>
41
42#include <ufs/ufs/dinode.h>
43#include <ufs/ffs/fs.h>
44
45#include <err.h>
46#include <errno.h>
47#include <fstab.h>
48#include <grp.h>
49#include <inttypes.h>
50#include <mntopts.h>
51#include <paths.h>
52#include <stdint.h>
53#include <string.h>
54#include <time.h>
55
56#include "fsck.h"
57
58static int  restarts;
59static char snapname[BUFSIZ];	/* when doing snapshots, the name of the file */
60
61static void usage(void) __dead2;
62static intmax_t argtoimax(int flag, const char *req, const char *str, int base);
63static int checkfilesys(char *filesys);
64static int setup_bkgrdchk(struct statfs *mntp, int sbrdfailed, char **filesys);
65
66int
67main(int argc, char *argv[])
68{
69	int ch;
70	struct rlimit rlimit;
71	struct itimerval itimerval;
72	int fsret;
73	int ret = 0;
74
75	sync();
76	skipclean = 1;
77	inoopt = 0;
78	while ((ch = getopt(argc, argv, "b:Bc:CdEfFm:npRrSyZz")) != -1) {
79		switch (ch) {
80		case 'b':
81			skipclean = 0;
82			bflag = argtoimax('b', "number", optarg, 10);
83			printf("Alternate super block location: %jd\n", bflag);
84			break;
85
86		case 'B':
87			bkgrdflag = 1;
88			break;
89
90		case 'c':
91			skipclean = 0;
92			cvtlevel = argtoimax('c', "conversion level", optarg,
93			    10);
94			if (cvtlevel < 3)
95				errx(EEXIT, "cannot do level %d conversion",
96				    cvtlevel);
97			break;
98
99		case 'd':
100			debug++;
101			break;
102
103		case 'E':
104			Eflag++;
105			break;
106
107		case 'f':
108			skipclean = 0;
109			break;
110
111		case 'F':
112			bkgrdcheck = 1;
113			break;
114
115		case 'm':
116			lfmode = argtoimax('m', "mode", optarg, 8);
117			if (lfmode &~ 07777)
118				errx(EEXIT, "bad mode to -m: %o", lfmode);
119			printf("** lost+found creation mode %o\n", lfmode);
120			break;
121
122		case 'n':
123			nflag++;
124			yflag = 0;
125			break;
126
127		case 'p':
128			preen++;
129			/*FALLTHROUGH*/
130
131		case 'C':
132			ckclean++;
133			break;
134
135		case 'R':
136			wantrestart = 1;
137			break;
138		case 'r':
139			inoopt++;
140			break;
141
142		case 'S':
143			surrender = 1;
144			break;
145
146		case 'y':
147			yflag++;
148			nflag = 0;
149			break;
150
151		case 'Z':
152			Zflag++;
153			break;
154
155		case 'z':
156			zflag++;
157			break;
158
159		default:
160			usage();
161		}
162	}
163	argc -= optind;
164	argv += optind;
165
166	if (!argc)
167		usage();
168
169	if (bkgrdflag && cvtlevel > 0) {
170		pfatal("CANNOT CONVERT A SNAPSHOT\n");
171		exit(EEXIT);
172	}
173
174	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
175		(void)signal(SIGINT, catch);
176	if (ckclean)
177		(void)signal(SIGQUIT, catchquit);
178	signal(SIGINFO, infohandler);
179	if (bkgrdflag) {
180		signal(SIGALRM, alarmhandler);
181		itimerval.it_interval.tv_sec = 5;
182		itimerval.it_interval.tv_usec = 0;
183		itimerval.it_value.tv_sec = 5;
184		itimerval.it_value.tv_usec = 0;
185		setitimer(ITIMER_REAL, &itimerval, NULL);
186	}
187	/*
188	 * Push up our allowed memory limit so we can cope
189	 * with huge file systems.
190	 */
191	if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
192		rlimit.rlim_cur = rlimit.rlim_max;
193		(void)setrlimit(RLIMIT_DATA, &rlimit);
194	}
195	while (argc > 0) {
196		if ((fsret = checkfilesys(*argv)) == ERESTART)
197			continue;
198		ret |= fsret;
199		argc--;
200		argv++;
201	}
202
203	if (returntosingle)
204		ret = 2;
205	exit(ret);
206}
207
208static intmax_t
209argtoimax(int flag, const char *req, const char *str, int base)
210{
211	char *cp;
212	intmax_t ret;
213
214	ret = strtoimax(str, &cp, base);
215	if (cp == str || *cp)
216		errx(EEXIT, "-%c flag requires a %s", flag, req);
217	return (ret);
218}
219
220/*
221 * Check the specified file system.
222 */
223/* ARGSUSED */
224static int
225checkfilesys(char *filesys)
226{
227	ufs2_daddr_t n_ffree, n_bfree;
228	struct dups *dp;
229	struct statfs *mntp;
230	intmax_t blks, files;
231	size_t size;
232	int sbreadfailed, ofsmodified;
233
234	fsutilinit();
235	fsckinit();
236
237	cdevname = filesys;
238	if (debug && ckclean)
239		pwarn("starting\n");
240	/*
241	 * Make best effort to get the disk name. Check first to see
242	 * if it is listed among the mounted file systems. Failing that
243	 * check to see if it is listed in /etc/fstab.
244	 */
245	mntp = getmntpoint(filesys);
246	if (mntp != NULL)
247		filesys = mntp->f_mntfromname;
248	else
249		filesys = blockcheck(filesys);
250	/*
251	 * If -F flag specified, check to see whether a background check
252	 * is possible and needed. If possible and needed, exit with
253	 * status zero. Otherwise exit with status non-zero. A non-zero
254	 * exit status will cause a foreground check to be run.
255	 */
256	sblock_init();
257	sbreadfailed = 0;
258	if (openfilesys(filesys) == 0 || readsb() == 0)
259		sbreadfailed = 1;
260	if (bkgrdcheck) {
261		if (sbreadfailed)
262			exit(3);	/* Cannot read superblock */
263		if ((sblock.fs_flags & FS_NEEDSFSCK) == FS_NEEDSFSCK)
264			exit(4);	/* Earlier background failed */
265		if ((sblock.fs_flags & FS_SUJ) == FS_SUJ) {
266			maxino = sblock.fs_ncg * sblock.fs_ipg;
267			maxfsblock = sblock.fs_size;
268			bufinit();
269			preen = 1;
270			if (suj_check(filesys) == 0)
271				exit(4); /* Journal good, run it now */
272		}
273		if ((sblock.fs_flags & FS_DOSOFTDEP) == 0)
274			exit(5);	/* Not running soft updates */
275		size = MIBSIZE;
276		if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0)
277			exit(6);	/* Lacks kernel support */
278		if ((mntp == NULL && sblock.fs_clean == 1) ||
279		    (mntp != NULL && (sblock.fs_flags & FS_UNCLEAN) == 0))
280			exit(7);	/* Filesystem clean, report it now */
281		exit(0);
282	}
283	if (ckclean && skipclean) {
284		/*
285		 * If file system is gjournaled, check it here.
286		 */
287		if (sbreadfailed)
288			exit(3);	/* Cannot read superblock */
289		if (bkgrdflag == 0 &&
290		    (nflag || (fswritefd = open(filesys, O_WRONLY)) < 0)) {
291			fswritefd = -1;
292			if (preen)
293				pfatal("NO WRITE ACCESS");
294			printf(" (NO WRITE)");
295		}
296		if ((sblock.fs_flags & FS_GJOURNAL) != 0) {
297			if (sblock.fs_clean == 1) {
298				pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
299				exit(0);
300			}
301			if ((sblock.fs_flags &
302			    (FS_UNCLEAN | FS_NEEDSFSCK)) == 0) {
303				bufinit();
304				gjournal_check(filesys);
305				if (chkdoreload(mntp, pwarn) == 0)
306					exit(0);
307				exit(4);
308			} else {
309				pfatal("FULL FSCK NEEDED, CANNOT RUN FAST "
310				    "FSCK\n");
311			}
312		}
313		close(fswritefd);
314		fswritefd = -1;
315	}
316	if (bkgrdflag) {
317		switch (setup_bkgrdchk(mntp, sbreadfailed, &filesys)) {
318		case -1: /* filesystem clean */
319			goto clean;
320		case 0: /* cannot do background, give up */
321			exit(EEXIT);
322		case 1: /* doing background check, preen rules apply */
323			preen = 1;
324			break;
325		}
326	}
327
328	switch (setup(filesys)) {
329	case 0:
330		if (preen)
331			pfatal("CAN'T CHECK FILE SYSTEM.");
332		return (EEXIT);
333	case -1:
334	clean:
335		pwarn("clean, %ld free ", (long)(sblock.fs_cstotal.cs_nffree +
336		    sblock.fs_frag * sblock.fs_cstotal.cs_nbfree));
337		printf("(%jd frags, %jd blocks, %.1f%% fragmentation)\n",
338		    (intmax_t)sblock.fs_cstotal.cs_nffree,
339		    (intmax_t)sblock.fs_cstotal.cs_nbfree,
340		    sblock.fs_cstotal.cs_nffree * 100.0 / sblock.fs_dsize);
341		return (0);
342	}
343	/*
344	 * Determine if we can and should do journal recovery.
345	 */
346	if (bkgrdflag == 0 && (sblock.fs_flags & FS_SUJ) == FS_SUJ) {
347		if ((sblock.fs_flags & FS_NEEDSFSCK) != FS_NEEDSFSCK &&
348		    skipclean) {
349			sujrecovery = 1;
350			if (suj_check(filesys) == 0) {
351				pwarn("\n**** FILE SYSTEM MARKED CLEAN ****\n");
352				if (chkdoreload(mntp, pwarn) == 0)
353					exit(0);
354				exit(4);
355			}
356			sujrecovery = 0;
357			pwarn("Skipping journal, "
358			    "falling through to full fsck\n");
359		}
360		if (fswritefd != -1) {
361			/*
362			 * Write the superblock so we don't try to recover the
363			 * journal on another pass. If this is the only change
364			 * to the filesystem, we do not want it to be called
365			 * out as modified.
366			 */
367			sblock.fs_mtime = time(NULL);
368			sbdirty();
369			ofsmodified = fsmodified;
370			flush(fswritefd, &sblk);
371			fsmodified = ofsmodified;
372		}
373	}
374	/*
375	 * If the filesystem was run on an old kernel that did not
376	 * support check hashes, clear the check-hash flags so that
377	 * we do not try to verify them.
378	 */
379	if ((sblock.fs_flags & FS_METACKHASH) == 0)
380		sblock.fs_metackhash = 0;
381	/*
382	 * If we are running on a kernel that can provide check hashes
383	 * that are not yet enabled for the filesystem and we are
384	 * running manually without the -y flag, offer to add any
385	 * supported check hashes that are not already enabled.
386	 */
387	ckhashadd = 0;
388	if (preen == 0 && yflag == 0 && sblock.fs_magic != FS_UFS1_MAGIC &&
389	    fswritefd != -1 && getosreldate() >= P_OSREL_CK_CYLGRP) {
390		if ((sblock.fs_metackhash & CK_CYLGRP) == 0 &&
391		    reply("ADD CYLINDER GROUP CHECK-HASH PROTECTION") != 0) {
392			ckhashadd |= CK_CYLGRP;
393			sblock.fs_metackhash |= CK_CYLGRP;
394		}
395		if ((sblock.fs_metackhash & CK_SUPERBLOCK) == 0 &&
396		    getosreldate() >= P_OSREL_CK_SUPERBLOCK &&
397		    reply("ADD SUPERBLOCK CHECK-HASH PROTECTION") != 0) {
398			ckhashadd |= CK_SUPERBLOCK;
399			sblock.fs_metackhash |= CK_SUPERBLOCK;
400		}
401		if ((sblock.fs_metackhash & CK_INODE) == 0 &&
402		    getosreldate() >= P_OSREL_CK_INODE &&
403		    reply("ADD INODE CHECK-HASH PROTECTION") != 0) {
404			ckhashadd |= CK_INODE;
405			sblock.fs_metackhash |= CK_INODE;
406		}
407#ifdef notyet
408		if ((sblock.fs_metackhash & CK_INDIR) == 0 &&
409		    getosreldate() >= P_OSREL_CK_INDIR &&
410		    reply("ADD INDIRECT BLOCK CHECK-HASH PROTECTION") != 0) {
411			ckhashadd |= CK_INDIR;
412			sblock.fs_metackhash |= CK_INDIR;
413		}
414		if ((sblock.fs_metackhash & CK_DIR) == 0 &&
415		    getosreldate() >= P_OSREL_CK_DIR &&
416		    reply("ADD DIRECTORY CHECK-HASH PROTECTION") != 0) {
417			ckhashadd |= CK_DIR;
418			sblock.fs_metackhash |= CK_DIR;
419		}
420#endif /* notyet */
421		if (ckhashadd != 0) {
422			sblock.fs_flags |= FS_METACKHASH;
423			sbdirty();
424		}
425	}
426	/*
427	 * Cleared if any questions answered no. Used to decide if
428	 * the superblock should be marked clean.
429	 */
430	resolved = 1;
431	/*
432	 * 1: scan inodes tallying blocks used
433	 */
434	if (preen == 0 || debug) {
435		printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
436		if (mntp != NULL && mntp->f_flags & MNT_ROOTFS)
437			printf("** Root file system\n");
438		printf("** Phase 1 - Check Blocks and Sizes\n");
439	}
440	clock_gettime(CLOCK_REALTIME_PRECISE, &startprog);
441	pass1();
442	IOstats("Pass1");
443
444	/*
445	 * 1b: locate first references to duplicates, if any
446	 */
447	if (duplist) {
448		if (preen || usedsoftdep)
449			pfatal("INTERNAL ERROR: DUPS WITH %s%s%s",
450			    preen ? "-p" : "",
451			    (preen && usedsoftdep) ? " AND " : "",
452			    usedsoftdep ? "SOFTUPDATES" : "");
453		if (preen == 0 || debug)
454			printf("** Phase 1b - Rescan For More DUPS\n");
455		pass1b();
456		IOstats("Pass1b");
457	}
458
459	/*
460	 * 2: traverse directories from root to mark all connected directories
461	 */
462	if (preen == 0 || debug)
463		printf("** Phase 2 - Check Pathnames\n");
464	pass2();
465	IOstats("Pass2");
466
467	/*
468	 * 3: scan inodes looking for disconnected directories
469	 */
470	if (preen == 0 || debug)
471		printf("** Phase 3 - Check Connectivity\n");
472	pass3();
473	IOstats("Pass3");
474
475	/*
476	 * 4: scan inodes looking for disconnected files; check reference counts
477	 */
478	if (preen == 0 || debug)
479		printf("** Phase 4 - Check Reference Counts\n");
480	pass4();
481	IOstats("Pass4");
482
483	/*
484	 * 5: check and repair resource counts in cylinder groups
485	 */
486	if (preen == 0 || debug)
487		printf("** Phase 5 - Check Cyl groups\n");
488	snapflush(std_checkblkavail);
489	if (cgheader_corrupt) {
490		printf("PHASE 5 SKIPPED DUE TO CORRUPT CYLINDER GROUP "
491		    "HEADER(S)\n\n");
492	} else {
493		pass5();
494		IOstats("Pass5");
495	}
496
497	/*
498	 * print out summary statistics
499	 */
500	n_ffree = sblock.fs_cstotal.cs_nffree;
501	n_bfree = sblock.fs_cstotal.cs_nbfree;
502	files = maxino - UFS_ROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
503	blks = n_blks +
504	    sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
505	blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
506	blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
507	blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
508	if (bkgrdflag && (files > 0 || blks > 0)) {
509		countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
510		pwarn("Reclaimed: %ld directories, %jd files, %jd fragments\n",
511		    countdirs, files - countdirs, blks);
512	}
513	pwarn("%ld files, %jd used, %ju free ",
514	    (long)n_files, (intmax_t)n_blks,
515	    (uintmax_t)n_ffree + sblock.fs_frag * n_bfree);
516	printf("(%ju frags, %ju blocks, %.1f%% fragmentation)\n",
517	    (uintmax_t)n_ffree, (uintmax_t)n_bfree,
518	    n_ffree * 100.0 / sblock.fs_dsize);
519	if (debug) {
520		if (files < 0)
521			printf("%jd inodes missing\n", -files);
522		if (blks < 0)
523			printf("%jd blocks missing\n", -blks);
524		if (duplist != NULL) {
525			printf("The following duplicate blocks remain:");
526			for (dp = duplist; dp; dp = dp->next)
527				printf(" %jd,", (intmax_t)dp->dup);
528			printf("\n");
529		}
530	}
531	duplist = (struct dups *)0;
532	muldup = (struct dups *)0;
533	inocleanup();
534	if (fsmodified) {
535		sblock.fs_time = time(NULL);
536		sbdirty();
537	}
538	if (cvtlevel && (sblk.b_flags & B_DIRTY) != 0) {
539		/*
540		 * Write out the duplicate super blocks
541		 */
542		if (sbput(fswritefd, &sblock, sblock.fs_ncg) == 0)
543			fsmodified = 1;
544	}
545	if (rerun)
546		resolved = 0;
547
548	/*
549	 * Check to see if the file system is mounted read-write.
550	 */
551	if (bkgrdflag == 0 && mntp != NULL && (mntp->f_flags & MNT_RDONLY) == 0)
552		resolved = 0;
553	ckfini(resolved);
554
555	if (fsmodified && !preen)
556		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
557	if (rerun) {
558		if (wantrestart && (restarts++ < 10) &&
559		    (preen || reply("RESTART")))
560			return (ERESTART);
561		printf("\n***** PLEASE RERUN FSCK *****\n");
562	}
563	if (chkdoreload(mntp, pwarn) != 0) {
564		if (!fsmodified)
565			return (0);
566		if (!preen)
567			printf("\n***** REBOOT NOW *****\n");
568		sync();
569		return (4);
570	}
571	return (rerun ? ERERUN : 0);
572}
573
574/*
575 * If we are to do a background check:
576 *	Get the mount point information of the file system
577 *	If already clean, return -1
578 *	Check that kernel supports background fsck
579 *	Find or create the snapshot directory
580 *	Create the snapshot file
581 *	Open snapshot
582 *	If anything fails print reason and return 0 which exits
583 */
584static int
585setup_bkgrdchk(struct statfs *mntp, int sbreadfailed, char **filesys)
586{
587	struct stat snapdir;
588	struct group *grp;
589	struct iovec *iov;
590	char errmsg[255];
591	int iovlen;
592	size_t size;
593
594	/* Get the mount point information of the file system */
595	if (mntp == NULL) {
596		pwarn("NOT MOUNTED, CANNOT RUN IN BACKGROUND\n");
597		return (0);
598	}
599	if ((mntp->f_flags & MNT_RDONLY) != 0) {
600		pwarn("MOUNTED READ-ONLY, CANNOT RUN IN BACKGROUND\n");
601		return (0);
602	}
603	if ((mntp->f_flags & MNT_SOFTDEP) == 0) {
604		pwarn("NOT USING SOFT UPDATES, CANNOT RUN IN BACKGROUND\n");
605		return (0);
606	}
607	if (sbreadfailed) {
608		pwarn("SUPERBLOCK READ FAILED, CANNOT RUN IN BACKGROUND\n");
609		return (0);
610	}
611	if ((sblock.fs_flags & FS_NEEDSFSCK) != 0) {
612		pwarn("FULL FSCK NEEDED, CANNOT RUN IN BACKGROUND\n");
613		return (0);
614	}
615	if (skipclean && ckclean &&
616	   (sblock.fs_flags & (FS_UNCLEAN|FS_NEEDSFSCK)) == 0) {
617		/*
618		 * file system is clean;
619		 * skip snapshot and report it clean
620		 */
621		pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
622		return (-1);
623	}
624	/* Check that kernel supports background fsck */
625	size = MIBSIZE;
626	if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
627	    sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
628	    sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
629	    sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
630	    sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
631	    sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
632		pwarn("KERNEL LACKS BACKGROUND FSCK SUPPORT\n");
633		return (0);
634	}
635	/*
636	 * When kernel lacks runtime bgfsck superblock summary
637	 * adjustment functionality, it does not mean we can not
638	 * continue, as old kernels will recompute the summary at
639	 * mount time. However, it will be an unexpected softupdates
640	 * inconsistency if it turns out that the summary is still
641	 * incorrect. Set a flag so subsequent operation can know this.
642	 */
643	bkgrdsumadj = 1;
644	if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
645	   sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
646	   sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
647	   sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
648	   sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters,
649	   &size) < 0) {
650		bkgrdsumadj = 0;
651		pwarn("KERNEL LACKS RUNTIME SUPERBLOCK SUMMARY ADJUSTMENT "
652		    "SUPPORT\n");
653	}
654	/* Find or create the snapshot directory */
655	snprintf(snapname, sizeof snapname, "%s/.snap", mntp->f_mntonname);
656	if (stat(snapname, &snapdir) < 0) {
657		if (errno != ENOENT) {
658			pwarn("CANNOT FIND SNAPSHOT DIRECTORY %s: %s, CANNOT "
659			    "RUN IN BACKGROUND\n", snapname, strerror(errno));
660			return (0);
661		}
662		if ((grp = getgrnam("operator")) == NULL ||
663			   mkdir(snapname, 0770) < 0 ||
664			   chown(snapname, -1, grp->gr_gid) < 0 ||
665			   chmod(snapname, 0770) < 0) {
666			pwarn("CANNOT CREATE SNAPSHOT DIRECTORY %s: %s, "
667			    "CANNOT RUN IN BACKGROUND\n", snapname,
668			    strerror(errno));
669			return (0);
670		}
671	} else if (!S_ISDIR(snapdir.st_mode)) {
672		pwarn("%s IS NOT A DIRECTORY, CANNOT RUN IN BACKGROUND\n",
673		    snapname);
674		return (0);
675	}
676	/* Create the snapshot file */
677	iov = NULL;
678	iovlen = 0;
679	errmsg[0] = '\0';
680	snprintf(snapname, sizeof snapname, "%s/.snap/fsck_snapshot",
681	    mntp->f_mntonname);
682	build_iovec(&iov, &iovlen, "fstype", "ffs", 4);
683	build_iovec(&iov, &iovlen, "from", snapname, (size_t)-1);
684	build_iovec(&iov, &iovlen, "fspath", mntp->f_mntonname, (size_t)-1);
685	build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
686	build_iovec(&iov, &iovlen, "update", NULL, 0);
687	build_iovec(&iov, &iovlen, "snapshot", NULL, 0);
688	/* Create snapshot, removing old snapshot if it exists */
689	while (nmount(iov, iovlen, mntp->f_flags) < 0) {
690		if (errno == EEXIST && unlink(snapname) == 0)
691			continue;
692		pwarn("CANNOT CREATE SNAPSHOT %s: %s %s\n", snapname,
693		    strerror(errno), errmsg);
694		return (0);
695	}
696	/* Open snapshot */
697	if (openfilesys(snapname) == 0) {
698		unlink(snapname);
699		pwarn("CANNOT OPEN SNAPSHOT %s: %s, CANNOT RUN IN "
700		    "BACKGROUND\n", snapname, strerror(errno));
701		return (0);
702	}
703	/* Immediately unlink snapshot so that it will be deleted when closed */
704	unlink(snapname);
705	free(sblock.fs_csp);
706	free(sblock.fs_si);
707	if (readsb() == 0) {
708		pwarn("CANNOT READ SNAPSHOT SUPERBLOCK\n");
709		return (0);
710	}
711	*filesys = snapname;
712	cmd.version = FFS_CMD_VERSION;
713	cmd.handle = fsreadfd;
714	return (1);
715}
716
717static void
718usage(void)
719{
720	(void) fprintf(stderr,
721"usage: %s [-BCdEFfnpRrSyZ] [-b block] [-c level] [-m mode] filesystem ...\n",
722	    getprogname());
723	exit(1);
724}
725
726void
727infohandler(int sig __unused)
728{
729	got_siginfo = 1;
730}
731
732void
733alarmhandler(int sig __unused)
734{
735	got_sigalarm = 1;
736}
737