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#if 0
33#ifndef lint
34static const char sccsid[] = "@(#)setup.c	8.10 (Berkeley) 5/9/95";
35#endif /* not lint */
36#endif
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39
40#include <sys/param.h>
41#include <sys/disk.h>
42#include <sys/stat.h>
43#define FSTYPENAMES
44#include <sys/disklabel.h>
45#include <sys/file.h>
46#include <sys/sysctl.h>
47
48#include <ufs/ufs/dinode.h>
49#include <ufs/ffs/fs.h>
50
51#include <ctype.h>
52#include <err.h>
53#include <errno.h>
54#include <limits.h>
55#include <stdint.h>
56#include <string.h>
57#include <libufs.h>
58
59#include "fsck.h"
60
61struct inoinfo **inphead, **inpsort;
62
63struct uufsd disk;
64struct bufarea asblk;
65#define altsblock (*asblk.b_un.b_fs)
66#define POWEROF2(num)	(((num) & ((num) - 1)) == 0)
67
68static int calcsb(char *dev, int devfd, struct fs *fs);
69static void saverecovery(int readfd, int writefd);
70static int chkrecovery(int devfd);
71
72/*
73 * Read in a superblock finding an alternate if necessary.
74 * Return 1 if successful, 0 if unsuccessful, -1 if file system
75 * is already clean (ckclean and preen mode only).
76 */
77int
78setup(char *dev)
79{
80	long cg, asked, i, j;
81	long bmapsize;
82	struct stat statb;
83	struct fs proto;
84	size_t size;
85
86	havesb = 0;
87	fswritefd = -1;
88	cursnapshot = 0;
89	if (stat(dev, &statb) < 0) {
90		printf("Can't stat %s: %s\n", dev, strerror(errno));
91		if (bkgrdflag) {
92			unlink(snapname);
93			bkgrdflag = 0;
94		}
95		return (0);
96	}
97	if ((statb.st_mode & S_IFMT) != S_IFCHR &&
98	    (statb.st_mode & S_IFMT) != S_IFBLK) {
99		if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) {
100			unlink(snapname);
101			printf("background fsck lacks a snapshot\n");
102			exit(EEXIT);
103		}
104		if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) {
105			cursnapshot = statb.st_ino;
106		} else {
107			if (cvtlevel == 0 ||
108			    (statb.st_flags & SF_SNAPSHOT) == 0) {
109				if (preen && bkgrdflag) {
110					unlink(snapname);
111					bkgrdflag = 0;
112				}
113				pfatal("%s is not a disk device", dev);
114				if (reply("CONTINUE") == 0) {
115					if (bkgrdflag) {
116						unlink(snapname);
117						bkgrdflag = 0;
118					}
119					return (0);
120				}
121			} else {
122				if (bkgrdflag) {
123					unlink(snapname);
124					bkgrdflag = 0;
125				}
126				pfatal("cannot convert a snapshot");
127				exit(EEXIT);
128			}
129		}
130	}
131	if ((fsreadfd = open(dev, O_RDONLY)) < 0 ||
132	    ufs_disk_fillout(&disk, dev) < 0) {
133		if (bkgrdflag) {
134			unlink(snapname);
135			bkgrdflag = 0;
136		}
137		printf("Can't open %s: %s\n", dev, strerror(errno));
138		return (0);
139	}
140	if (bkgrdflag) {
141		unlink(snapname);
142		size = MIBSIZE;
143		if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0||
144		    sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0||
145		    sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 ||
146		    sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0||
147		    sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 ||
148		    sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) {
149			pfatal("kernel lacks background fsck support\n");
150			exit(EEXIT);
151		}
152		/*
153		 * When kernel is lack of runtime bgfsck superblock summary
154		 * adjustment functionality, it does not mean we can not
155		 * continue, as old kernels will recompute the summary at
156		 * mount time.  However, it will be an unexpected softupdates
157		 * inconsistency if it turns out that the summary is still
158		 * incorrect.  Set a flag so subsequent operation can know
159		 * this.
160		 */
161		bkgrdsumadj = 1;
162		if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 ||
163		    sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 ||
164		    sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 ||
165		    sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 ||
166		    sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) {
167			bkgrdsumadj = 0;
168			pwarn("kernel lacks runtime superblock summary adjustment support");
169		}
170		cmd.version = FFS_CMD_VERSION;
171		cmd.handle = fsreadfd;
172		fswritefd = -1;
173	}
174	if (preen == 0)
175		printf("** %s", dev);
176	if (bkgrdflag == 0 &&
177	    (nflag || ufs_disk_write(&disk) < 0 ||
178	     (fswritefd = dup(disk.d_fd)) < 0)) {
179		fswritefd = -1;
180		if (preen)
181			pfatal("NO WRITE ACCESS");
182		printf(" (NO WRITE)");
183	}
184	if (preen == 0)
185		printf("\n");
186	/*
187	 * Read in the superblock, looking for alternates if necessary
188	 */
189	if (readsb(1) == 0) {
190		skipclean = 0;
191		if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
192			return(0);
193		if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
194			return (0);
195		for (cg = 0; cg < proto.fs_ncg; cg++) {
196			bflag = fsbtodb(&proto, cgsblock(&proto, cg));
197			if (readsb(0) != 0)
198				break;
199		}
200		if (cg >= proto.fs_ncg) {
201			printf("%s %s\n%s %s\n%s %s\n",
202				"SEARCH FOR ALTERNATE SUPER-BLOCK",
203				"FAILED. YOU MUST USE THE",
204				"-b OPTION TO FSCK TO SPECIFY THE",
205				"LOCATION OF AN ALTERNATE",
206				"SUPER-BLOCK TO SUPPLY NEEDED",
207				"INFORMATION; SEE fsck_ffs(8).");
208			bflag = 0;
209			return(0);
210		}
211		pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag);
212		bflag = 0;
213	}
214	if (skipclean && ckclean && sblock.fs_clean) {
215		pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n");
216		return (-1);
217	}
218	maxfsblock = sblock.fs_size;
219	maxino = sblock.fs_ncg * sblock.fs_ipg;
220	/*
221	 * Check and potentially fix certain fields in the super block.
222	 */
223	if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
224		pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
225		if (reply("SET TO DEFAULT") == 1) {
226			sblock.fs_optim = FS_OPTTIME;
227			sbdirty();
228		}
229	}
230	if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
231		pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
232			sblock.fs_minfree);
233		if (reply("SET TO DEFAULT") == 1) {
234			sblock.fs_minfree = 10;
235			sbdirty();
236		}
237	}
238	if (sblock.fs_magic == FS_UFS1_MAGIC &&
239	    sblock.fs_old_inodefmt < FS_44INODEFMT) {
240		pwarn("Format of file system is too old.\n");
241		pwarn("Must update to modern format using a version of fsck\n");
242		pfatal("from before 2002 with the command ``fsck -c 2''\n");
243		exit(EEXIT);
244	}
245	if (asblk.b_dirty && !bflag) {
246		memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
247		flush(fswritefd, &asblk);
248	}
249	if (preen == 0 && yflag == 0 && sblock.fs_magic == FS_UFS2_MAGIC &&
250	    fswritefd != -1 && chkrecovery(fsreadfd) == 0 &&
251	    reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0)
252		saverecovery(fsreadfd, fswritefd);
253	/*
254	 * read in the summary info.
255	 */
256	asked = 0;
257	sblock.fs_csp = Calloc(1, sblock.fs_cssize);
258	if (sblock.fs_csp == NULL) {
259		printf("cannot alloc %u bytes for cg summary info\n",
260		    (unsigned)sblock.fs_cssize);
261		goto badsb;
262	}
263	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
264		size = MIN(sblock.fs_cssize - i, sblock.fs_bsize);
265		readcnt[sblk.b_type]++;
266		if (blread(fsreadfd, (char *)sblock.fs_csp + i,
267		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
268		    size) != 0 && !asked) {
269			pfatal("BAD SUMMARY INFORMATION");
270			if (reply("CONTINUE") == 0) {
271				ckfini(0);
272				exit(EEXIT);
273			}
274			asked++;
275		}
276	}
277	/*
278	 * allocate and initialize the necessary maps
279	 */
280	bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short));
281	blockmap = Calloc((unsigned)bmapsize, sizeof (char));
282	if (blockmap == NULL) {
283		printf("cannot alloc %u bytes for blockmap\n",
284		    (unsigned)bmapsize);
285		goto badsb;
286	}
287	inostathead = Calloc(sblock.fs_ncg, sizeof(struct inostatlist));
288	if (inostathead == NULL) {
289		printf("cannot alloc %u bytes for inostathead\n",
290		    (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg)));
291		goto badsb;
292	}
293	numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128);
294	dirhash = numdirs;
295	inplast = 0;
296	listmax = numdirs + 10;
297	inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *));
298	inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *));
299	if (inpsort == NULL || inphead == NULL) {
300		printf("cannot alloc %ju bytes for inphead\n",
301		    (uintmax_t)numdirs * sizeof(struct inoinfo *));
302		goto badsb;
303	}
304	bufinit();
305	if (sblock.fs_flags & FS_DOSOFTDEP)
306		usedsoftdep = 1;
307	else
308		usedsoftdep = 0;
309	return (1);
310
311badsb:
312	ckfini(0);
313	return (0);
314}
315
316/*
317 * Read in the super block and its summary info.
318 */
319int
320readsb(int listerr)
321{
322	off_t super;
323	int bad, ret;
324	struct fs *fs;
325
326	super = bflag ? bflag * dev_bsize : -1;
327	readcnt[sblk.b_type]++;
328	if ((ret = sbget(fsreadfd, &fs, super)) != 0) {
329		switch (ret) {
330		case EINVAL:
331			fprintf(stderr, "The previous newfs operation "
332			    "on this volume did not complete.\nYou must "
333			    "complete newfs before using this volume.\n");
334			exit(11);
335		case ENOENT:
336			if (bflag)
337				fprintf(stderr, "%jd is not a file system "
338				    "superblock\n", super / dev_bsize);
339			else
340				fprintf(stderr, "Cannot find file system "
341				    "superblock\n");
342			return (0);
343		case EIO:
344		default:
345			fprintf(stderr, "I/O error reading %jd\n",
346			    super / dev_bsize);
347			return (0);
348		}
349	}
350	memcpy(&sblock, fs, fs->fs_sbsize);
351	free(fs);
352	/*
353	 * Compute block size that the file system is based on,
354	 * according to fsbtodb, and adjust superblock block number
355	 * so we can tell if this is an alternate later.
356	 */
357	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
358	sblk.b_bno = sblock.fs_sblockactualloc / dev_bsize;
359	sblk.b_size = SBLOCKSIZE;
360	/*
361	 * Compare all fields that should not differ in alternate super block.
362	 * When an alternate super-block is specified this check is skipped.
363	 */
364	if (bflag)
365		goto out;
366	getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
367	if (asblk.b_errs)
368		return (0);
369	bad = 0;
370#define CHK(x, y)				\
371	if (altsblock.x != sblock.x) {		\
372		bad++;				\
373		if (listerr && debug)		\
374			printf("SUPER BLOCK VS ALTERNATE MISMATCH %s: " y " vs " y "\n", \
375			    #x, (intmax_t)sblock.x, (intmax_t)altsblock.x); \
376	}
377	CHK(fs_sblkno, "%jd");
378	CHK(fs_cblkno, "%jd");
379	CHK(fs_iblkno, "%jd");
380	CHK(fs_dblkno, "%jd");
381	CHK(fs_ncg, "%jd");
382	CHK(fs_bsize, "%jd");
383	CHK(fs_fsize, "%jd");
384	CHK(fs_frag, "%jd");
385	CHK(fs_bmask, "%#jx");
386	CHK(fs_fmask, "%#jx");
387	CHK(fs_bshift, "%jd");
388	CHK(fs_fshift, "%jd");
389	CHK(fs_fragshift, "%jd");
390	CHK(fs_fsbtodb, "%jd");
391	CHK(fs_sbsize, "%jd");
392	CHK(fs_nindir, "%jd");
393	CHK(fs_inopb, "%jd");
394	CHK(fs_cssize, "%jd");
395	CHK(fs_ipg, "%jd");
396	CHK(fs_fpg, "%jd");
397	CHK(fs_magic, "%#jx");
398#undef CHK
399	if (bad) {
400		if (listerr == 0)
401			return (0);
402		if (preen)
403			printf("%s: ", cdevname);
404		printf(
405		    "VALUES IN SUPER BLOCK LSB=%jd DISAGREE WITH THOSE IN\n"
406		    "LAST ALTERNATE LSB=%jd\n",
407		    sblk.b_bno, asblk.b_bno);
408		if (reply("IGNORE ALTERNATE SUPER BLOCK") == 0)
409			return (0);
410	}
411out:
412	/*
413	 * If not yet done, update UFS1 superblock with new wider fields.
414	 */
415	if (sblock.fs_magic == FS_UFS1_MAGIC &&
416	    sblock.fs_maxbsize != sblock.fs_bsize) {
417		sblock.fs_maxbsize = sblock.fs_bsize;
418		sblock.fs_time = sblock.fs_old_time;
419		sblock.fs_size = sblock.fs_old_size;
420		sblock.fs_dsize = sblock.fs_old_dsize;
421		sblock.fs_csaddr = sblock.fs_old_csaddr;
422		sblock.fs_cstotal.cs_ndir = sblock.fs_old_cstotal.cs_ndir;
423		sblock.fs_cstotal.cs_nbfree = sblock.fs_old_cstotal.cs_nbfree;
424		sblock.fs_cstotal.cs_nifree = sblock.fs_old_cstotal.cs_nifree;
425		sblock.fs_cstotal.cs_nffree = sblock.fs_old_cstotal.cs_nffree;
426	}
427	havesb = 1;
428	return (1);
429}
430
431void
432sblock_init(void)
433{
434
435	fswritefd = -1;
436	fsmodified = 0;
437	lfdir = 0;
438	initbarea(&sblk, BT_SUPERBLK);
439	initbarea(&asblk, BT_SUPERBLK);
440	sblk.b_un.b_buf = Malloc(SBLOCKSIZE);
441	asblk.b_un.b_buf = Malloc(SBLOCKSIZE);
442	if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
443		errx(EEXIT, "cannot allocate space for superblock");
444	dev_bsize = secsize = DEV_BSIZE;
445}
446
447/*
448 * Calculate a prototype superblock based on information in the boot area.
449 * When done the cgsblock macro can be calculated and the fs_ncg field
450 * can be used. Do NOT attempt to use other macros without verifying that
451 * their needed information is available!
452 */
453static int
454calcsb(char *dev, int devfd, struct fs *fs)
455{
456	struct fsrecovery *fsr;
457	char *fsrbuf;
458	u_int secsize;
459
460	/*
461	 * We need fragments-per-group and the partition-size.
462	 *
463	 * Newfs stores these details at the end of the boot block area
464	 * at the start of the filesystem partition. If they have been
465	 * overwritten by a boot block, we fail. But usually they are
466	 * there and we can use them.
467	 */
468	if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1)
469		return (0);
470	fsrbuf = Malloc(secsize);
471	if (fsrbuf == NULL)
472		errx(EEXIT, "calcsb: cannot allocate recovery buffer");
473	if (blread(devfd, fsrbuf,
474	    (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0)
475		return (0);
476	fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
477	if (fsr->fsr_magic != FS_UFS2_MAGIC)
478		return (0);
479	memset(fs, 0, sizeof(struct fs));
480	fs->fs_fpg = fsr->fsr_fpg;
481	fs->fs_fsbtodb = fsr->fsr_fsbtodb;
482	fs->fs_sblkno = fsr->fsr_sblkno;
483	fs->fs_magic = fsr->fsr_magic;
484	fs->fs_ncg = fsr->fsr_ncg;
485	free(fsrbuf);
486	return (1);
487}
488
489/*
490 * Check to see if recovery information exists.
491 * Return 1 if it exists or cannot be created.
492 * Return 0 if it does not exist and can be created.
493 */
494static int
495chkrecovery(int devfd)
496{
497	struct fsrecovery *fsr;
498	char *fsrbuf;
499	u_int secsize;
500
501	/*
502	 * Could not determine if backup material exists, so do not
503	 * offer to create it.
504	 */
505	if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 ||
506	    (fsrbuf = Malloc(secsize)) == NULL ||
507	    blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
508	      secsize) != 0)
509		return (1);
510	/*
511	 * Recovery material has already been created, so do not
512	 * need to create it again.
513	 */
514	fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
515	if (fsr->fsr_magic == FS_UFS2_MAGIC) {
516		free(fsrbuf);
517		return (1);
518	}
519	/*
520	 * Recovery material has not been created and can be if desired.
521	 */
522	free(fsrbuf);
523	return (0);
524}
525
526/*
527 * Read the last sector of the boot block, replace the last
528 * 20 bytes with the recovery information, then write it back.
529 * The recovery information only works for UFS2 filesystems.
530 */
531static void
532saverecovery(int readfd, int writefd)
533{
534	struct fsrecovery *fsr;
535	char *fsrbuf;
536	u_int secsize;
537
538	if (sblock.fs_magic != FS_UFS2_MAGIC ||
539	    ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 ||
540	    (fsrbuf = Malloc(secsize)) == NULL ||
541	    blread(readfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize,
542	      secsize) != 0) {
543		printf("RECOVERY DATA COULD NOT BE CREATED\n");
544		return;
545	}
546	fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr];
547	fsr->fsr_magic = sblock.fs_magic;
548	fsr->fsr_fpg = sblock.fs_fpg;
549	fsr->fsr_fsbtodb = sblock.fs_fsbtodb;
550	fsr->fsr_sblkno = sblock.fs_sblkno;
551	fsr->fsr_ncg = sblock.fs_ncg;
552	blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - secsize) / secsize, secsize);
553	free(fsrbuf);
554}
555