xref: /openbsd-src/sbin/fsck_ffs/setup.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: setup.c,v 1.40 2008/06/10 23:10:29 otto Exp $	*/
2 /*	$NetBSD: setup.c,v 1.27 1996/09/27 22:45:19 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1986, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)setup.c	8.5 (Berkeley) 11/23/94";
36 #else
37 static const char rcsid[] = "$OpenBSD: setup.c,v 1.40 2008/06/10 23:10:29 otto Exp $";
38 #endif
39 #endif /* not lint */
40 
41 #define DKTYPENAMES
42 #include <sys/param.h>
43 #include <sys/time.h>
44 #include <ufs/ufs/dinode.h>
45 #include <ufs/ffs/fs.h>
46 #include <sys/stat.h>
47 #include <sys/ioctl.h>
48 #include <sys/disklabel.h>
49 
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <ctype.h>
56 
57 #include "fsck.h"
58 #include "extern.h"
59 #include "fsutil.h"
60 
61 #define altsblock (*asblk.b_un.b_fs)
62 #define POWEROF2(num)	(((num) & ((num) - 1)) == 0)
63 
64 void badsb(int, char *);
65 int calcsb(char *, int, struct fs *);
66 static struct disklabel *getdisklabel(char *, int);
67 static int readsb(int);
68 static int cmpsb(struct fs *, struct fs *);
69 
70 long numdirs, listmax, inplast;
71 
72 /*
73  * Possible locations for the superblock.
74  */
75 static const int sbtry[] = SBLOCKSEARCH;
76 /* locations the 1st alternate sb can be at */
77 static const int altsbtry[] = { 32, 64, 128, 144, 160, 192, 256 };
78 
79 int
80 setup(char *dev)
81 {
82 	long cg, size, asked, i, j, bmapsize;
83 	struct disklabel *lp;
84 	off_t sizepb;
85 	struct stat statb;
86 	struct fs proto;
87 	int doskipclean;
88 	int32_t maxsymlinklen, nindir, inopb;
89 	u_int64_t maxfilesize;
90 
91 	havesb = 0;
92 	fswritefd = fsreadfd = -1;
93 	doskipclean = skipclean;
94 	if (stat(dev, &statb) < 0) {
95 		printf("Can't stat %s: %s\n", dev, strerror(errno));
96 		return (0);
97 	}
98 	if (!S_ISCHR(statb.st_mode)) {
99 		pfatal("%s is not a character device", dev);
100 		if (reply("CONTINUE") == 0)
101 			return (0);
102 	}
103 	if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
104 		printf("Can't open %s: %s\n", dev, strerror(errno));
105 		return (0);
106 	}
107 	if (preen == 0)
108 		printf("** %s", dev);
109 	if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
110 		fswritefd = -1;
111 		if (preen)
112 			pfatal("NO WRITE ACCESS");
113 		printf(" (NO WRITE)");
114 	}
115 	if (preen == 0)
116 		printf("\n");
117 	fsmodified = 0;
118 	lfdir = 0;
119 	initbarea(&sblk);
120 	initbarea(&asblk);
121 	sblk.b_un.b_buf = malloc(SBSIZE);
122 	asblk.b_un.b_buf = malloc(SBSIZE);
123 	if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
124 		errexit("cannot allocate space for superblock\n");
125 	if ((lp = getdisklabel(NULL, fsreadfd)) != NULL)
126 		dev_bsize = secsize = lp->d_secsize;
127 	else
128 		dev_bsize = secsize = DEV_BSIZE;
129 	/*
130 	 * Read in the superblock, looking for alternates if necessary
131 	 */
132 	if (readsb(1) == 0) {
133 		if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
134 			return(0);
135 		if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
136 			return (0);
137 		for (i = 0; i < sizeof(altsbtry) / sizeof(altsbtry[0]); i++) {
138 			bflag = altsbtry[i];
139 			/* proto partially setup by calcsb */
140 			if (readsb(0) != 0 &&
141 			    proto.fs_fsize == sblock.fs_fsize &&
142 			    proto.fs_bsize == sblock.fs_bsize)
143 				goto found;
144 		}
145 		for (cg = 0; cg < proto.fs_ncg; cg++) {
146 			bflag = fsbtodb(&proto, cgsblock(&proto, cg));
147 			if (readsb(0) != 0)
148 				break;
149 		}
150 		if (cg >= proto.fs_ncg) {
151 			printf("%s %s\n%s %s\n%s %s\n",
152 			    "SEARCH FOR ALTERNATE SUPER-BLOCK",
153 			    "FAILED. YOU MUST USE THE",
154 			    "-b OPTION TO FSCK_FFS TO SPECIFY THE",
155 			    "LOCATION OF AN ALTERNATE",
156 			    "SUPER-BLOCK TO SUPPLY NEEDED",
157 			    "INFORMATION; SEE fsck_ffs(8).");
158 			return(0);
159 		}
160 found:
161 		doskipclean = 0;
162 		pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
163 	}
164 	if (debug)
165 		printf("clean = %d\n", sblock.fs_clean);
166 	if (sblock.fs_clean & FS_ISCLEAN) {
167 		if (doskipclean) {
168 			pwarn("%sile system is clean; not checking\n",
169 			    preen ? "f" : "** F");
170 			return (-1);
171 		}
172 		if (!preen)
173 			pwarn("** File system is already clean\n");
174 	}
175 	maxfsblock = sblock.fs_size;
176 	maxino = sblock.fs_ncg * sblock.fs_ipg;
177 	sizepb = sblock.fs_bsize;
178 	maxfilesize = sblock.fs_bsize * NDADDR - 1;
179 	for (i = 0; i < NIADDR; i++) {
180 		sizepb *= NINDIR(&sblock);
181 		maxfilesize += sizepb;
182 	}
183 	/*
184 	 * Check and potentially fix certain fields in the super block.
185 	 */
186 	if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
187 		pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
188 		if (reply("SET TO DEFAULT") == 1) {
189 			sblock.fs_optim = FS_OPTTIME;
190 			sbdirty();
191 		}
192 	}
193 	if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
194 		pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
195 		    sblock.fs_minfree);
196 		if (reply("SET TO DEFAULT") == 1) {
197 			sblock.fs_minfree = 10;
198 			sbdirty();
199 		}
200 	}
201 	if (sblock.fs_npsect < sblock.fs_nsect ||
202 	    sblock.fs_npsect > sblock.fs_nsect*2) {
203 		pwarn("IMPOSSIBLE NPSECT=%d IN SUPERBLOCK",
204 		    sblock.fs_npsect);
205 		sblock.fs_npsect = sblock.fs_nsect;
206 		if (preen)
207 			printf(" (FIXED)\n");
208 		if (preen || reply("SET TO DEFAULT") == 1) {
209 			sbdirty();
210 			dirty(&asblk);
211 		}
212 	}
213 	if (sblock.fs_bmask != ~(sblock.fs_bsize - 1)) {
214 		pwarn("INCORRECT BMASK=%x IN SUPERBLOCK",
215 		    sblock.fs_bmask);
216 		sblock.fs_bmask = ~(sblock.fs_bsize - 1);
217 		if (preen)
218 			printf(" (FIXED)\n");
219 		if (preen || reply("FIX") == 1) {
220 			sbdirty();
221 			dirty(&asblk);
222 		}
223 	}
224 	if (sblock.fs_fmask != ~(sblock.fs_fsize - 1)) {
225 		pwarn("INCORRECT FMASK=%x IN SUPERBLOCK",
226 		    sblock.fs_fmask);
227 		sblock.fs_fmask = ~(sblock.fs_fsize - 1);
228 		if (preen)
229 			printf(" (FIXED)\n");
230 		if (preen || reply("FIX") == 1) {
231 			sbdirty();
232 			dirty(&asblk);
233 		}
234 	}
235 	if (1 << sblock.fs_bshift != sblock.fs_bsize) {
236 		pwarn("INCORRECT BSHIFT=%d IN SUPERBLOCK", sblock.fs_bshift);
237 		sblock.fs_bshift = ffs(sblock.fs_bsize) - 1;
238 		if (preen)
239 			printf(" (FIXED)\n");
240 		if (preen || reply("FIX") == 1) {
241 			sbdirty();
242 			dirty(&asblk);
243 		}
244 	}
245 	if (1 << sblock.fs_fshift != sblock.fs_fsize) {
246 		pwarn("INCORRECT FSHIFT=%d IN SUPERBLOCK", sblock.fs_fshift);
247 		sblock.fs_fshift = ffs(sblock.fs_fsize) - 1;
248 		if (preen)
249 			printf(" (FIXED)\n");
250 		if (preen || reply("FIX") == 1) {
251 			sbdirty();
252 			dirty(&asblk);
253 		}
254 	}
255 	if (sblock.fs_inodefmt >= FS_44INODEFMT) {
256 		if (sblock.fs_maxfilesize != maxfilesize) {
257 			pwarn("INCORRECT MAXFILESIZE=%llu IN SUPERBLOCK",
258 			    (unsigned long long)sblock.fs_maxfilesize);
259 			sblock.fs_maxfilesize = maxfilesize;
260 			if (preen)
261 				printf(" (FIXED)\n");
262 			if (preen || reply("FIX") == 1) {
263 				sbdirty();
264 				dirty(&asblk);
265 			}
266 		}
267 		maxsymlinklen = sblock.fs_magic == FS_UFS1_MAGIC ?
268 		    MAXSYMLINKLEN_UFS1 : MAXSYMLINKLEN_UFS2;
269 		if (sblock.fs_maxsymlinklen != maxsymlinklen) {
270 			pwarn("INCORRECT MAXSYMLINKLEN=%d IN SUPERBLOCK",
271 			    sblock.fs_maxsymlinklen);
272 			sblock.fs_maxsymlinklen = maxsymlinklen;
273 			if (preen)
274 				printf(" (FIXED)\n");
275 			if (preen || reply("FIX") == 1) {
276 				sbdirty();
277 				dirty(&asblk);
278 			}
279 		}
280 		if (sblock.fs_qbmask != ~sblock.fs_bmask) {
281 			pwarn("INCORRECT QBMASK=%lx IN SUPERBLOCK",
282 			    (unsigned long)sblock.fs_qbmask);
283 			sblock.fs_qbmask = ~sblock.fs_bmask;
284 			if (preen)
285 				printf(" (FIXED)\n");
286 			if (preen || reply("FIX") == 1) {
287 				sbdirty();
288 				dirty(&asblk);
289 			}
290 		}
291 		if (sblock.fs_qfmask != ~sblock.fs_fmask) {
292 			pwarn("INCORRECT QFMASK=%lx IN SUPERBLOCK",
293 			    (unsigned long)sblock.fs_qfmask);
294 			sblock.fs_qfmask = ~sblock.fs_fmask;
295 			if (preen)
296 				printf(" (FIXED)\n");
297 			if (preen || reply("FIX") == 1) {
298 				sbdirty();
299 				dirty(&asblk);
300 			}
301 		}
302 		newinofmt = 1;
303 	} else {
304 		sblock.fs_qbmask = ~sblock.fs_bmask;
305 		sblock.fs_qfmask = ~sblock.fs_fmask;
306 		newinofmt = 0;
307 	}
308 	/*
309 	 * Convert to new inode format.
310 	 */
311 	if (cvtlevel >= 2 && sblock.fs_inodefmt < FS_44INODEFMT) {
312 		if (preen)
313 			pwarn("CONVERTING TO NEW INODE FORMAT\n");
314 		else if (!reply("CONVERT TO NEW INODE FORMAT"))
315 			return(0);
316 		doinglevel2++;
317 		sblock.fs_inodefmt = FS_44INODEFMT;
318 		sblock.fs_maxfilesize = maxfilesize;
319 		sblock.fs_maxsymlinklen = MAXSYMLINKLEN_UFS1;
320 		sblock.fs_qbmask = ~sblock.fs_bmask;
321 		sblock.fs_qfmask = ~sblock.fs_fmask;
322 		sbdirty();
323 		dirty(&asblk);
324 	}
325 	/*
326 	 * Convert to new cylinder group format.
327 	 */
328 	if (cvtlevel >= 1 && sblock.fs_postblformat == FS_42POSTBLFMT) {
329 		if (preen)
330 			pwarn("CONVERTING TO NEW CYLINDER GROUP FORMAT\n");
331 		else if (!reply("CONVERT TO NEW CYLINDER GROUP FORMAT"))
332 			return(0);
333 		doinglevel1++;
334 		sblock.fs_postblformat = FS_DYNAMICPOSTBLFMT;
335 		sblock.fs_nrpos = 8;
336 		sblock.fs_postbloff =
337 		    (char *)(&sblock.fs_maxbsize) -
338 		    (char *)(&sblock.fs_firstfield);
339 		sblock.fs_rotbloff = &sblock.fs_space[0] -
340 		    (u_char *)(&sblock.fs_firstfield);
341 		sblock.fs_cgsize =
342 			fragroundup(&sblock, CGSIZE(&sblock));
343 		sbdirty();
344 		dirty(&asblk);
345 	}
346 	if (sblock.fs_cgsize != fragroundup(&sblock, CGSIZE(&sblock))) {
347 		pwarn("INCONSISTENT CGSIZE=%d\n", sblock.fs_cgsize);
348 		sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
349 		if (preen)
350 			printf(" (FIXED)\n");
351 		if (preen || reply("FIX") == 1) {
352 			sbdirty();
353 			dirty(&asblk);
354 		}
355 	}
356 	if (sblock.fs_magic == FS_UFS2_MAGIC)
357 		inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
358 	else
359 		inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
360 	if (INOPB(&sblock) != inopb) {
361 		pwarn("INCONSISTENT INOPB=%d\n", INOPB(&sblock));
362 		sblock.fs_inopb = inopb;
363 		if (preen)
364 			printf(" (FIXED)\n");
365 		if (preen || reply("FIX") == 1) {
366 			sbdirty();
367 			dirty(&asblk);
368 		}
369 	}
370 	if (sblock.fs_magic == FS_UFS2_MAGIC)
371 		nindir = sblock.fs_bsize / sizeof(int64_t);
372 	else
373 		nindir = sblock.fs_bsize / sizeof(int32_t);
374 	if (NINDIR(&sblock) != nindir) {
375 		pwarn("INCONSISTENT NINDIR=%d\n", NINDIR(&sblock));
376 		sblock.fs_nindir = nindir;
377 		if (preen)
378 			printf(" (FIXED)\n");
379 		if (preen || reply("FIX") == 1) {
380 			sbdirty();
381 			dirty(&asblk);
382 		}
383 	}
384 	if (asblk.b_dirty && !bflag) {
385 		memcpy(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
386 		flush(fswritefd, &asblk);
387 	}
388 	/*
389 	 * read in the summary info.
390 	 */
391 	asked = 0;
392 	sblock.fs_csp = calloc(1, sblock.fs_cssize);
393 	if (sblock.fs_csp == NULL) {
394 		printf("cannot alloc %u bytes for cylinder group summary area\n",
395 		    (unsigned)sblock.fs_cssize);
396 		goto badsblabel;
397 	}
398 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
399 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
400 		    sblock.fs_cssize - i : sblock.fs_bsize;
401 		if (bread(fsreadfd, (char *)sblock.fs_csp + i,
402 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
403 		    size) != 0 && !asked) {
404 			pfatal("BAD SUMMARY INFORMATION");
405 			if (reply("CONTINUE") == 0) {
406 				ckfini(0);
407 				errexit("%s", "");
408 			}
409 			asked++;
410 		}
411 	}
412 	/*
413 	 * allocate and initialize the necessary maps
414 	 */
415 	bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t));
416 	blockmap = calloc((unsigned)bmapsize, sizeof(char));
417 	if (blockmap == NULL) {
418 		printf("cannot alloc %u bytes for blockmap\n",
419 		    (unsigned)bmapsize);
420 		goto badsblabel;
421 	}
422 	stmap = calloc((unsigned)(maxino + 1), sizeof(char));
423 	if (stmap == NULL) {
424 		printf("cannot alloc %u bytes for stmap\n",
425 		    (unsigned)(maxino + 1));
426 		goto badsblabel;
427 	}
428 	lncntp = calloc((unsigned)(maxino + 1), sizeof(int16_t));
429 	if (lncntp == NULL) {
430 		printf("cannot alloc %zu bytes for lncntp\n",
431 		    (maxino + 1) * sizeof(int16_t));
432 		goto badsblabel;
433 	}
434 	cginosused = calloc((unsigned)sblock.fs_ncg, sizeof(long));
435 	if (cginosused == NULL) {
436 		printf("cannot alloc %u bytes for cginosused\n",
437 		    (unsigned)sblock.fs_ncg);
438 		goto badsblabel;
439 	}
440 	numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128);
441 	inplast = 0;
442 	listmax = numdirs + 10;
443 	inpsort = calloc((unsigned)listmax, sizeof(struct inoinfo *));
444 	if (inpsort == NULL) {
445 		printf("cannot alloc %zu bytes for inpsort\n",
446 		    (unsigned)listmax * sizeof(struct inoinfo *));
447 		goto badsblabel;
448 	}
449 	inphead = calloc((unsigned)numdirs, sizeof(struct inoinfo *));
450 	if (inphead == NULL) {
451 		printf("cannot alloc %zu bytes for inphead\n",
452 		    (unsigned)numdirs * sizeof(struct inoinfo *));
453 		goto badsblabel;
454 	}
455 	bufinit();
456 	if (sblock.fs_flags & FS_DOSOFTDEP)
457 		usedsoftdep = 1;
458 	else
459 		usedsoftdep = 0;
460 	return (1);
461 
462 badsblabel:
463 	ckfini(0);
464 	return (0);
465 }
466 
467 
468 /*
469  * Read in the super block and its summary info.
470  */
471 static int
472 readsb(int listerr)
473 {
474 	daddr64_t super = 0;
475 	int i;
476 
477 	if (bflag) {
478 		super = bflag;
479 
480 		if (bread(fsreadfd, (char *)&sblock, super, (long)SBSIZE) != 0)
481 			return (0);
482 
483 		if (sblock.fs_magic != FS_UFS1_MAGIC &&
484 		    sblock.fs_magic != FS_UFS2_MAGIC) {
485 			badsb(listerr, "MAGIC NUMBER WRONG");
486 			return (0);
487 		}
488 	} else {
489 		for (i = 0; sbtry[i] != -1; i++) {
490 			super = sbtry[i] / dev_bsize;
491 
492 			if (bread(fsreadfd, (char *)&sblock, super,
493 			    (long)SBSIZE) != 0)
494 				return (0);
495 
496 			if (sblock.fs_magic != FS_UFS1_MAGIC &&
497 			    sblock.fs_magic != FS_UFS2_MAGIC)
498 				continue; /* Not a superblock */
499 
500 			/*
501 			 * Do not look for an FFS1 file system at SBLOCK_UFS2.
502 			 * Doing so will find the wrong super-block for file
503 			 * systems with 64k block size.
504 			 */
505 			if (sblock.fs_magic == FS_UFS1_MAGIC &&
506 			    sbtry[i] == SBLOCK_UFS2)
507 				continue;
508 
509 			if (sblock.fs_magic == FS_UFS2_MAGIC &&
510 			    sblock.fs_sblockloc != sbtry[i])
511 				continue; /* Not a superblock */
512 
513 			break;
514 		}
515 
516 		if (sbtry[i] == -1) {
517 			badsb(listerr, "MAGIC NUMBER WRONG");
518 			return (0);
519 		}
520 	}
521 
522 	sblk.b_bno = super;
523 	sblk.b_size = SBSIZE;
524 
525 	/*
526 	 * run a few consistency checks of the super block
527 	 */
528 	if (sblock.fs_ncg < 1) {
529 		badsb(listerr, "NCG OUT OF RANGE");
530 		return (0);
531 	}
532 	if (sblock.fs_cpg < 1) {
533 		badsb(listerr, "CPG OUT OF RANGE");
534 		return (0);
535 	}
536 	if (sblock.fs_magic == FS_UFS1_MAGIC) {
537 		if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
538 		    (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) {
539 			badsb(listerr, "NCYL LESS THAN NCG*CPG");
540 			return (0);
541 		}
542 	}
543 	if (sblock.fs_sbsize > SBSIZE) {
544 		badsb(listerr, "SBSIZE PREPOSTEROUSLY LARGE");
545 		return (0);
546 	}
547 
548 	if (!POWEROF2(sblock.fs_bsize) || sblock.fs_bsize < MINBSIZE ||
549 	    sblock.fs_bsize > MAXBSIZE) {
550 		badsb(listerr, "ILLEGAL BLOCK SIZE IN SUPERBLOCK");
551 		return (0);
552 	}
553 
554 	if (!POWEROF2(sblock.fs_fsize) || sblock.fs_fsize > sblock.fs_bsize ||
555 	    sblock.fs_fsize < sblock.fs_bsize / MAXFRAG) {
556 		badsb(listerr, "ILLEGAL FRAGMENT SIZE IN SUPERBLOCK");
557 		return (0);
558 	}
559 
560 
561 	/*
562 	 * Compute block size that the filesystem is based on,
563 	 * according to fsbtodb, and adjust superblock block number
564 	 * so we can tell if this is an alternate later.
565 	 */
566 	super *= dev_bsize;
567 	dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
568 	sblk.b_bno = super / dev_bsize;
569 	if (bflag)
570 		goto out;
571 	getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize);
572 	if (asblk.b_errs)
573 		return (0);
574 	if (cmpsb(&sblock, &altsblock)) {
575 		if (debug) {
576 			long *nlp, *olp, *endlp;
577 
578 			printf("superblock mismatches\n");
579 			nlp = (long *)&altsblock;
580 			olp = (long *)&sblock;
581 			endlp = olp + (sblock.fs_sbsize / sizeof *olp);
582 			for ( ; olp < endlp; olp++, nlp++) {
583 				if (*olp == *nlp)
584 					continue;
585 				printf("offset %d, original %ld, alternate %ld\n",
586 				    (int)(olp - (long *)&sblock), *olp, *nlp);
587 			}
588 		}
589 		badsb(listerr,
590 		    "VALUES IN SUPER BLOCK DISAGREE WITH THOSE IN LAST ALTERNATE");
591 		return (0);
592 	}
593 out:
594 	if (sblock.fs_magic == FS_UFS1_MAGIC) {
595 		sblock.fs_time = sblock.fs_ffs1_time;
596 		sblock.fs_size = sblock.fs_ffs1_size;
597 		sblock.fs_dsize = sblock.fs_ffs1_dsize;
598 		sblock.fs_csaddr = sblock.fs_ffs1_csaddr;
599 		sblock.fs_cstotal.cs_ndir = sblock.fs_ffs1_cstotal.cs_ndir;
600 		sblock.fs_cstotal.cs_nbfree = sblock.fs_ffs1_cstotal.cs_nbfree;
601 		sblock.fs_cstotal.cs_nifree = sblock.fs_ffs1_cstotal.cs_nifree;
602 		sblock.fs_cstotal.cs_nffree = sblock.fs_ffs1_cstotal.cs_nffree;
603 	}
604 	havesb = 1;
605 	return (1);
606 }
607 
608 void
609 badsb(int listerr, char *s)
610 {
611 
612 	if (!listerr)
613 		return;
614 	if (preen)
615 		printf("%s: ", cdevname());
616 	pfatal("BAD SUPER BLOCK: %s\n", s);
617 }
618 
619 /*
620  * Calculate a prototype superblock based on information in the disk label.
621  * When done the cgsblock macro can be calculated and the fs_ncg field
622  * can be used. Do NOT attempt to use other macros without verifying that
623  * their needed information is available!
624  */
625 int
626 calcsb(char *dev, int devfd, struct fs *fs)
627 {
628 	struct disklabel *lp;
629 	struct partition *pp;
630 	char *cp;
631 	int i;
632 
633 	cp = strchr(dev, '\0') - 1;
634 	if ((cp == (char *)-1 || (*cp < 'a' || *cp >= 'a' + MAXPARTITIONS)) &&
635 	    !isdigit(*cp)) {
636 		pfatal("%s: CANNOT FIGURE OUT FILE SYSTEM PARTITION\n", dev);
637 		return (0);
638 	}
639 	lp = getdisklabel(dev, devfd);
640 	if (isdigit(*cp))
641 		pp = &lp->d_partitions[0];
642 	else
643 		pp = &lp->d_partitions[*cp - 'a'];
644 	if (pp->p_fstype != FS_BSDFFS) {
645 		pfatal("%s: NOT LABELED AS A BSD FILE SYSTEM (%s)\n",
646 		    dev, pp->p_fstype < FSMAXTYPES ?
647 		    fstypenames[pp->p_fstype] : "unknown");
648 		return (0);
649 	}
650 	memset(fs, 0, sizeof(struct fs));
651 	fs->fs_fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock);
652 	fs->fs_frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock);
653 	fs->fs_bsize = fs->fs_fsize * fs->fs_frag;
654 	fs->fs_cpg = pp->p_cpg;
655 	fs->fs_nspf = fs->fs_fsize / lp->d_secsize;
656 	/* unit for fs->fs_size is fragments, for pp->p_size it is sectors */
657 	fs->fs_size = pp->p_size / fs->fs_nspf;
658 	fs->fs_ntrak = lp->d_ntracks;
659 	fs->fs_nsect = lp->d_nsectors;
660 	fs->fs_spc = lp->d_secpercyl;
661 	/* we can't use lp->d_sbsize, it is the max sb size */
662 	fs->fs_sblkno = roundup(
663 		howmany(lp->d_bbsize + SBSIZE, fs->fs_fsize),
664 		fs->fs_frag);
665 again:
666 	fs->fs_cgmask = 0xffffffff;
667 	for (i = fs->fs_ntrak; i > 1; i >>= 1)
668 		fs->fs_cgmask <<= 1;
669 	if (!POWEROF2(fs->fs_ntrak))
670 		fs->fs_cgmask <<= 1;
671 	fs->fs_cgoffset = roundup(
672 		howmany(fs->fs_nsect, NSPF(fs)), fs->fs_frag);
673 	fs->fs_fpg = (fs->fs_cpg * fs->fs_spc) / NSPF(fs);
674 	fs->fs_ncg = howmany(pp->p_size / fs->fs_spc, fs->fs_cpg);
675 	for (fs->fs_fsbtodb = 0, i = NSPF(fs); i > 1; i >>= 1)
676 		fs->fs_fsbtodb++;
677 	/*
678 	 * Mimick what mkfs is doing to get an acceptable cgsize,
679 	 * not all fields used by CGSIZE() are filled in, but it's a best
680 	 * effort anyway.
681 	 */
682 	if (CGSIZE(fs) > fs->fs_bsize && fs->fs_ntrak > 1) {
683 		fs->fs_ntrak >>= 1;
684 		fs->fs_spc >>= 1;
685 		goto again;
686 	}
687 	dev_bsize = lp->d_secsize;
688 	return (1);
689 }
690 
691 static struct disklabel *
692 getdisklabel(char *s, int fd)
693 {
694 	static struct disklabel lab;
695 
696 	if (ioctl(fd, DIOCGDINFO, (char *)&lab) < 0) {
697 		if (s == NULL)
698 			return (NULL);
699 		pwarn("ioctl (GCINFO): %s\n", strerror(errno));
700 		errexit("%s: can't read disk label\n", s);
701 	}
702 	return (&lab);
703 }
704 
705 /*
706  * Compare two superblocks
707  */
708 static int
709 cmpsb(struct fs *sb, struct fs *asb)
710 {
711 	/*
712 	 * Only compare fields which should be the same, and ignore ones
713 	 * likely to change to ensure future compatibility.
714 	 */
715 	if (asb->fs_sblkno != sb->fs_sblkno ||
716 	    asb->fs_cblkno != sb->fs_cblkno ||
717 	    asb->fs_iblkno != sb->fs_iblkno ||
718 	    asb->fs_dblkno != sb->fs_dblkno ||
719 	    asb->fs_cgoffset != sb->fs_cgoffset ||
720 	    asb->fs_cgmask != sb->fs_cgmask ||
721 	    asb->fs_ncg != sb->fs_ncg ||
722 	    asb->fs_bsize != sb->fs_bsize ||
723 	    asb->fs_fsize != sb->fs_fsize ||
724 	    asb->fs_frag != sb->fs_frag ||
725 	    asb->fs_bmask != sb->fs_bmask ||
726 	    asb->fs_fmask != sb->fs_fmask ||
727 	    asb->fs_bshift != sb->fs_bshift ||
728 	    asb->fs_fshift != sb->fs_fshift ||
729 	    asb->fs_fragshift != sb->fs_fragshift ||
730 	    asb->fs_fsbtodb != sb->fs_fsbtodb ||
731 	    asb->fs_sbsize != sb->fs_sbsize ||
732 	    asb->fs_nindir != sb->fs_nindir ||
733 	    asb->fs_inopb != sb->fs_inopb ||
734 	    asb->fs_cssize != sb->fs_cssize ||
735 	    asb->fs_cpg != sb->fs_cpg ||
736 	    asb->fs_ipg != sb->fs_ipg ||
737 	    asb->fs_fpg != sb->fs_fpg ||
738 	    asb->fs_magic != sb->fs_magic)
739 		    return (1);
740 	/* they're the same */
741 	return (0);
742 }
743