xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/setup.c (revision 032624d56c174c5c55126582b32e314a6af15522)
1 /*
2  * Copyright 2005 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  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #define	DKTYPENAMES
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <malloc.h>
35 #include <limits.h>
36 #include <wait.h>
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/sysmacros.h>
40 #include <sys/mntent.h>
41 #include <sys/dkio.h>
42 #include <sys/filio.h>
43 #include <sys/isa_defs.h>	/* for ENDIAN defines */
44 #include <sys/int_const.h>
45 #include <sys/fs/ufs_fs.h>
46 #include <sys/vnode.h>
47 #include <sys/fs/ufs_fs.h>
48 #include <sys/fs/ufs_inode.h>
49 #include <sys/fs/ufs_log.h>
50 #include <sys/stat.h>
51 #include <sys/fcntl.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <sys/vfstab.h>
56 #include "roll_log.h"
57 #include "fsck.h"
58 
59 /*
60  * The size of a cylinder group is calculated by CGSIZE. The maximum size
61  * is limited by the fact that cylinder groups are at most one block.
62  * Its size is derived from the size of the maps maintained in the
63  * cylinder group and the (struct cg) size.
64  */
65 #define	CGSIZE(fs) \
66 	/* base cg */	  (sizeof (struct cg) + \
67 	/* blktot size */ (fs)->fs_cpg * sizeof (int32_t) + \
68 	/* blks size */	  (fs)->fs_cpg * (fs)->fs_nrpos * sizeof (short) + \
69 	/* inode map */	  howmany((fs)->fs_ipg, NBBY) + \
70 	/* block map */	  howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY))
71 
72 #define	altsblock (*asblk.b_un.b_fs)
73 #define	POWEROF2(num)	(((num) & ((num) - 1)) == 0)
74 
75 /*
76  * Methods of determining where alternate superblocks should
77  * be.  MAX_SB_STYLES must be the last one, and the others need
78  * to be positive.
79  */
80 typedef enum {
81 	MKFS_STYLE = 1, NEWFS_STYLE, MAX_SB_STYLES
82 } calcsb_t;
83 
84 static caddr_t calcsb_names[] = {
85 	"<UNKNOWN>", "MKFS", "NEWFS", "<OUT OF RANGE>"
86 };
87 
88 struct shadowclientinfo *shadowclientinfo = NULL;
89 struct shadowclientinfo *attrclientinfo = NULL;
90 int maxshadowclients = 1024;	/* allocation size, not limit  */
91 
92 static void badsb(int, caddr_t);
93 static int calcsb(calcsb_t, caddr_t, int, struct fs *);
94 static int checksb(int);
95 static void flush_fs(void);
96 static void sblock_init(void);
97 static void uncreate_maps(void);
98 
99 static int
100 read_super_block(int listerr)
101 {
102 	int fd;
103 	caddr_t err;
104 
105 	if (mount_point != NULL) {
106 		fd = open(mount_point, O_RDONLY);
107 		if (fd == -1) {
108 			errexit("fsck: open mount point error: %s",
109 			    strerror(errno));
110 			/* NOTREACHED */
111 		}
112 		/* get the latest super block */
113 		if (ioctl(fd, _FIOGETSUPERBLOCK, &sblock)) {
114 			errexit("fsck: ioctl _FIOGETSUPERBLOCK error: %s",
115 			    strerror(errno));
116 			/* NOTREACHED */
117 		}
118 		(void) close(fd);
119 	} else {
120 		(void) fsck_bread(fsreadfd, (caddr_t)&sblock,
121 			bflag != 0 ? (diskaddr_t)bflag : (diskaddr_t)SBLOCK,
122 			SBSIZE);
123 	}
124 
125 	/*
126 	 * Don't let trash from the disk trip us up later
127 	 * in ungetsummaryinfo().
128 	 */
129 	sblock.fs_u.fs_csp = NULL;
130 
131 	/*
132 	 * Rudimentary consistency checks.  Can't really call
133 	 * checksb() here, because there may be outstanding
134 	 * deltas that still need to be applied.
135 	 */
136 	if ((sblock.fs_magic != FS_MAGIC) &&
137 	    (sblock.fs_magic != MTB_UFS_MAGIC)) {
138 		err = "MAGIC NUMBER WRONG";
139 		goto fail;
140 	}
141 	if (sblock.fs_magic == MTB_UFS_MAGIC &&
142 		(sblock.fs_version > MTB_UFS_VERSION_1 ||
143 		sblock.fs_version < MTB_UFS_VERSION_MIN)) {
144 		err = "UNRECOGNIZED VERSION";
145 		goto fail;
146 	}
147 	if (sblock.fs_ncg < 1) {
148 		err = "NCG OUT OF RANGE";
149 		goto fail;
150 	}
151 	if (sblock.fs_cpg < 1) {
152 		err = "CPG OUT OF RANGE";
153 		goto fail;
154 	}
155 	if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
156 		(sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) {
157 		err = "NCYL IS INCONSISTENT WITH NCG*CPG";
158 		goto fail;
159 	}
160 	if (sblock.fs_sbsize < 0 || sblock.fs_sbsize > SBSIZE) {
161 		err = "SIZE OUT OF RANGE";
162 		goto fail;
163 	}
164 
165 	return (1);
166 
167 fail:
168 	badsb(listerr, err);
169 	return (0);
170 }
171 
172 static void
173 flush_fs()
174 {
175 	int fd;
176 
177 	if (mount_point != NULL) {
178 		fd = open(mount_point, O_RDONLY);
179 		if (fd == -1) {
180 			errexit("fsck: open mount point error: %s",
181 			    strerror(errno));
182 			/* NOTREACHED */
183 		}
184 		if (ioctl(fd, _FIOFFS, NULL)) { /* flush file system */
185 			errexit("fsck: ioctl _FIOFFS error: %s",
186 			    strerror(errno));
187 			/* NOTREACHED */
188 		}
189 		(void) close(fd);
190 	}
191 }
192 
193 /*
194  * Roll the embedded log, if any, and set up the global variables
195  * islog and islogok.
196  */
197 static int
198 logsetup(caddr_t devstr)
199 {
200 	void		*buf;
201 	extent_block_t	*ebp;
202 	ml_unit_t	*ul;
203 	ml_odunit_t	*ud;
204 	void		*ud_buf;
205 	int		badlog;
206 
207 	islog = islogok = 0;
208 	if (bflag != 0)
209 		return (1); /* can't roll log while alternate sb specified */
210 
211 	/*
212 	 * Roll the log, if any.  A bad sb implies we'll be using
213 	 * an alternate sb as far as logging goes, so just fail back
214 	 * to the caller if we can't read the default sb.  Suppress
215 	 * complaints, because the caller will be reading the same
216 	 * superblock again and running full verification on it, so
217 	 * whatever is bad will be reported then.
218 	 */
219 	sblock.fs_logbno = 0;
220 	badlog = 0;
221 	if (!read_super_block(0))
222 		return (1);
223 
224 	/*
225 	 * Roll the log in 3 cases:
226 	 * 1. If it's unmounted (mount_point == NULL) and it's not marked
227 	 *    as fully rolled (sblock.fs_rolled != FS_ALL_ROLLED)
228 	 * 2. If it's mounted and anything other than a sanity
229 	 *    check fsck (mflag) is being done, as we have the current
230 	 *    super block. Note, only a sanity check is done for
231 	 *    root/usr at boot. If a roll were done then the expensive
232 	 *    ufs_flush() gets called, leading to a slower boot.
233 	 * 3. If anything other then a sanity check (mflag) is being done
234 	 *    to a mounted filesystem while it is in read-only state
235 	 *    (e.g. root during early boot stages) we have to detect this
236 	 *    and have to roll the log as well. NB. the read-only mount
237 	 *    will flip fs_clean from FSLOG to FSSTABLE and marks the
238 	 *    log as FS_NEED_ROLL.
239 	 */
240 	if (sblock.fs_logbno &&
241 	    (((mount_point == NULL) && (sblock.fs_rolled != FS_ALL_ROLLED)) ||
242 	    ((mount_point != NULL) && !mflag))) {
243 		int roll_log_err = 0;
244 
245 		if (sblock.fs_ronly && (sblock.fs_clean == FSSTABLE) &&
246 			(sblock.fs_state + sblock.fs_time == FSOKAY)) {
247 			/*
248 			 * roll the log without a mount
249 			 */
250 			flush_fs();
251 		}
252 		if (sblock.fs_clean == FSLOG &&
253 			(sblock.fs_state + sblock.fs_time == FSOKAY)) {
254 			if (rl_roll_log(devstr) != RL_SUCCESS)
255 				roll_log_err = 1;
256 		}
257 		if (roll_log_err) {
258 			(void) printf("Can't roll the log for %s.\n", devstr);
259 			/*
260 			 * There are two cases where we want to set
261 			 * an error code and return:
262 			 *  - We're preening
263 			 *  - We're not on a live root and the user
264 			 *    chose *not* to ignore the log
265 			 * Otherwise, we want to mark the log as bad
266 			 * and continue to check the filesystem.  This
267 			 * has the side effect of destroying the log.
268 			 */
269 			if (preen || (!hotroot &&
270 			    reply(
271 			"DISCARDING THE LOG MAY DISCARD PENDING TRANSACTIONS.\n"
272 					"DISCARD THE LOG AND CONTINUE") == 0)) {
273 				exitstat = EXERRFATAL;
274 				return (0);
275 			}
276 			++badlog;
277 		}
278 	}
279 
280 	/* Logging UFS may be enabled */
281 	if (sblock.fs_logbno) {
282 		++islog;
283 
284 		/* log is not okay; check the fs */
285 		if (FSOKAY != (sblock.fs_state + sblock.fs_time))
286 			return (1);
287 
288 		/*
289 		 * If logging or (stable and mounted) then continue
290 		 */
291 		if (!((sblock.fs_clean == FSLOG) ||
292 		    (sblock.fs_clean == FSSTABLE) && (mount_point != NULL)))
293 			return (1);
294 
295 		/* get the log allocation block */
296 		buf = malloc(dev_bsize);
297 		if (buf == NULL) {
298 			return (1);
299 		}
300 		ud_buf = malloc(dev_bsize);
301 		if (ud_buf == NULL) {
302 			free(buf);
303 			return (1);
304 		}
305 		(void) fsck_bread(fsreadfd, buf,
306 		    logbtodb(&sblock, sblock.fs_logbno),
307 		    dev_bsize);
308 		ebp = (extent_block_t *)buf;
309 
310 		/* log allocation block is not okay; check the fs */
311 		if (ebp->type != LUFS_EXTENTS) {
312 			free(buf);
313 			free(ud_buf);
314 			return (1);
315 		}
316 
317 		/* get the log state block(s) */
318 		if (fsck_bread(fsreadfd, ud_buf,
319 		    (logbtodb(&sblock, ebp->extents[0].pbno)),
320 		    dev_bsize)) {
321 			(void) fsck_bread(fsreadfd, ud_buf,
322 			    (logbtodb(&sblock, ebp->extents[0].pbno)) + 1,
323 			    dev_bsize);
324 		}
325 		ud = (ml_odunit_t *)ud_buf;
326 		ul = (ml_unit_t *)malloc(sizeof (*ul));
327 		if (ul == NULL) {
328 			free(buf);
329 			free(ud_buf);
330 			return (1);
331 		}
332 		ul->un_ondisk = *ud;
333 
334 		/* log state is okay; don't need to check the fs */
335 		if ((ul->un_chksum == ul->un_head_ident + ul->un_tail_ident) &&
336 		    (ul->un_version == LUFS_VERSION_LATEST) &&
337 		    (ul->un_badlog == 0) && (!badlog)) {
338 			++islogok;
339 		}
340 		free(ud_buf);
341 		free(buf);
342 		free(ul);
343 	}
344 
345 	return (1);
346 }
347 
348 /*
349  * - given a pathname, determine the pathname to actually check
350  * - if a directory
351  *   - if it is in mnttab, set devstr to the special (block) name
352  *   - if it is in vfstab, set devstr to the special (block) name
353  *   - if it has not been found, bail
354  * - a file is used as-is, clear rflag
355  * - a device is converted to block version (so can search mnttab)
356  */
357 static void
358 derive_devstr(const caddr_t dev, caddr_t devstr, size_t str_size)
359 {
360 	mode_t mode;
361 	struct stat statb;
362 
363 	if (stat(dev, &statb) < 0) {
364 		exitstat = EXNOSTAT;
365 		errexit("fsck: could not stat %s: %s", dev, strerror(errno));
366 	}
367 
368 	mode = statb.st_mode & S_IFMT;
369 	switch (mode) {
370 	case S_IFDIR:
371 		/*
372 		 * The check_*() routines update devstr with the name.
373 		 */
374 		devstr[0] = '\0';
375 		if (!(check_mnttab(dev, devstr, str_size) ||
376 		    check_vfstab(dev, devstr, str_size))) {
377 			exitstat = EXBADPARM;
378 			errexit(
379 		    "fsck: could not find mountpoint %s in mnttab nor vfstab",
380 			    dev);
381 		}
382 		break;
383 	case S_IFREG:
384 		rflag = 0;
385 		break;
386 	case S_IFCHR:
387 	case S_IFBLK:
388 		(void) strlcpy(devstr, unrawname(dev), str_size);
389 		break;
390 	default:
391 		exitstat = EXBADPARM;
392 		errexit("fsck: %s must be a mountpoint, device, or file", dev);
393 		/* NOTREACHED */
394 	}
395 }
396 
397 /*
398  * Reports the index of the magic filesystem that mntp names.
399  * If it does not correspond any of them, returns zero (hence
400  * the backwards loop).
401  */
402 static int
403 which_corefs(const caddr_t mntp)
404 {
405 	int corefs;
406 
407 	for (corefs = MAGIC_LIMIT - 1; corefs > 0; corefs--)
408 		if (strcmp(mntp, magic_fs[corefs]) == 0)
409 			break;
410 
411 	return (corefs);
412 }
413 
414 /*
415  * - set mount_point to NULL
416  * - if name is mounted (search mnttab)
417  *   - if it is a device, clear rflag
418  *   - if mounted on /, /usr, or /var, set corefs
419  *   - if corefs and read-only, set hotroot and continue
420  *   - if errorlocked, continue
421  *   - if preening, bail
422  *   - ask user whether to continue, bail if not
423  * - if it is a device and not mounted and rflag, convert
424  *   name to raw version
425  */
426 static int
427 check_mount_state(caddr_t devstr, size_t str_size)
428 {
429 	int corefs = 0;
430 	int is_dev = 0;
431 	struct stat statb;
432 
433 	if (stat(devstr, &statb) < 0) {
434 		exitstat = EXNOSTAT;
435 		errexit("fsck: could not stat %s: %s", devstr, strerror(errno));
436 	}
437 	if (S_ISCHR(statb.st_mode) || S_ISBLK(statb.st_mode))
438 		is_dev = 1;
439 
440 	/*
441 	 * mounted() will update mount_point when returning true.
442 	 */
443 	mount_point = NULL;
444 	if ((mountedfs = mounted(devstr, devstr, str_size)) != M_NOMNT) {
445 		if (is_dev)
446 			rflag = 0;
447 		corefs = which_corefs(mount_point);
448 		if (corefs && (mountedfs == M_RO)) {
449 			hotroot++;
450 		} else if (errorlocked) {
451 			goto carry_on;
452 		} else if (preen) {
453 			exitstat = EXMOUNTED;
454 			pfatal("%s IS CURRENTLY MOUNTED%s.",
455 			    devstr, mountedfs == M_RW ? " READ/WRITE" : "");
456 		} else {
457 			pwarn("%s IS CURRENTLY MOUNTED READ/%s.",
458 			    devstr, mountedfs == M_RW ? "WRITE" : "ONLY");
459 			if (reply("CONTINUE") == 0) {
460 				exitstat = EXMOUNTED;
461 				errexit("Program terminated");
462 			}
463 		}
464 	} else if (is_dev && rflag) {
465 		(void) strlcpy(devstr, rawname(devstr), str_size);
466 	}
467 
468 carry_on:
469 	return (corefs);
470 }
471 
472 static int
473 open_and_intro(caddr_t devstr, int corefs)
474 {
475 	int retval = 0;
476 
477 	if ((fsreadfd = open64(devstr, O_RDONLY)) < 0) {
478 		(void) printf("Can't open %s: %s\n", devstr, strerror(errno));
479 		exitstat = EXNOSTAT;
480 		retval = -1;
481 		goto finish;
482 	}
483 	if (!preen || debug != 0)
484 		(void) printf("** %s", devstr);
485 
486 	if (errorlocked) {
487 		if (debug && elock_combuf != NULL)
488 			(void) printf(" error-lock comment: \"%s\" ",
489 			    elock_combuf);
490 		fflag = 1;
491 	}
492 	pid = getpid();
493 	if (nflag || roflag || (fswritefd = open64(devstr, O_WRONLY)) < 0) {
494 		fswritefd = -1;
495 		if (preen && !debug)
496 			pfatal("(NO WRITE ACCESS)\n");
497 		(void) printf(" (NO WRITE)");
498 	}
499 	if (!preen)
500 		(void) printf("\n");
501 	else if (debug)
502 		(void) printf(" pid %d\n", pid);
503 	if (debug && (hotroot || (mountedfs != M_NOMNT))) {
504 		(void) printf("** %s", devstr);
505 		if (hotroot)
506 			(void) printf(" is %s fs", magic_fs[corefs]);
507 		if (mountedfs != M_NOMNT)
508 			(void) printf(" and is mounted read-%s",
509 			    (mountedfs == M_RO) ? "only" : "write");
510 		if (errorlocked)
511 			(void) printf(" and is error-locked");
512 
513 		(void) printf(".\n");
514 	}
515 
516 finish:
517 	return (retval);
518 }
519 
520 static int
521 find_superblock(caddr_t devstr)
522 {
523 	int cg = 0;
524 	int retval = 0;
525 	int first;
526 	int found;
527 	calcsb_t style;
528 	struct fs proto;
529 
530 	/*
531 	 * Check the superblock, looking for alternates if necessary.
532 	 * In more-recent times, some UFS instances get created with
533 	 * only the first ten and last ten superblock backups.  Since
534 	 * if we can't get the necessary information from any of those,
535 	 * the odds are also against us for the ones in between, we'll
536 	 * just look at those twenty to save time.
537 	 */
538 	if (!read_super_block(1) || !checksb(1)) {
539 		if (bflag || preen) {
540 			retval = -1;
541 			goto finish;
542 		}
543 		for (style = MKFS_STYLE; style < MAX_SB_STYLES; style++) {
544 			if (reply("LOOK FOR ALTERNATE SUPERBLOCKS WITH %s",
545 			    calcsb_names[style]) == 0)
546 				continue;
547 			first = 1;
548 			found = 0;
549 			if (!calcsb(style, devstr, fsreadfd, &proto)) {
550 				cg = proto.fs_ncg;
551 				continue;
552 			}
553 			if (debug) {
554 				(void) printf(
555 			    "debug: calcsb(%s) gave fpg %d, cgoffset %d, ",
556 				    calcsb_names[style],
557 				    proto.fs_fpg, proto.fs_cgoffset);
558 				(void) printf("cgmask 0x%x, sblk %d, ncg %d\n",
559 				    proto.fs_cgmask, proto.fs_sblkno,
560 				    proto.fs_ncg);
561 			}
562 			for (cg = 0; cg < proto.fs_ncg; cg++) {
563 				bflag = fsbtodb(&proto, cgsblock(&proto, cg));
564 				if (debug)
565 					(void) printf(
566 					    "debug: trying block %lld\n",
567 					    (longlong_t)bflag);
568 				if (read_super_block(0) && checksb(0)) {
569 					(void) printf(
570 				    "FOUND ALTERNATE SUPERBLOCK %d WITH %s\n",
571 					    bflag, calcsb_names[style]);
572 					if (reply(
573 					    "USE ALTERNATE SUPERBLOCK") == 1) {
574 						found = 1;
575 						break;
576 					}
577 				}
578 				if (first && (cg >= 9)) {
579 					first = 0;
580 					if (proto.fs_ncg <= 9)
581 						cg = proto.fs_ncg;
582 					else if (proto.fs_ncg <= 19)
583 						cg = 9;
584 					else
585 						cg = proto.fs_ncg - 10;
586 				}
587 			}
588 
589 			if (found)
590 				break;
591 		}
592 
593 		/*
594 		 * Didn't find one?  Try to fake it.
595 		 */
596 		if (style >= MAX_SB_STYLES) {
597 			pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED.\n");
598 			for (style = MKFS_STYLE; style < MAX_SB_STYLES;
599 			    style++) {
600 				if (reply("USE GENERIC SUPERBLOCK FROM %s",
601 				    calcsb_names[style]) == 1 &&
602 				    calcsb(style, devstr, fsreadfd, &sblock)) {
603 					break;
604 			}
605 			/*
606 			 * We got something from mkfs/newfs, so use it.
607 			 */
608 			if (style < MAX_SB_STYLES)
609 				proto.fs_ncg = sblock.fs_ncg;
610 				bflag = 0;
611 			}
612 		}
613 
614 		/*
615 		 * Still no luck?  Tell the user they're on their own.
616 		 */
617 		if (style >= MAX_SB_STYLES) {
618 			pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED. "
619 			    "YOU MUST USE THE -o b OPTION\n"
620 			    "TO FSCK TO SPECIFY THE LOCATION OF A VALID "
621 			    "ALTERNATE SUPERBLOCK TO\n"
622 			    "SUPPLY NEEDED INFORMATION; SEE fsck(1M).\n");
623 			bflag = 0;
624 			retval = -1;
625 			goto finish;
626 		}
627 
628 		/*
629 		 * Need to make sure a human really wants us to use
630 		 * this.  -y mode could've gotten us this far, so
631 		 * we need to ask something that has to be answered
632 		 * in the negative.
633 		 *
634 		 * Note that we can't get here when preening.
635 		 */
636 		if (!found) {
637 			pwarn("CALCULATED GENERIC SUPERBLOCK WITH %s\n",
638 			    calcsb_names[style]);
639 		} else {
640 			pwarn("FOUND ALTERNATE SUPERBLOCK AT %d USING %s\n",
641 			    bflag, calcsb_names[style]);
642 		}
643 		pwarn("If filesystem was created with manually-specified ");
644 		pwarn("geometry, using\nauto-discovered superblock may ");
645 		pwarn("result in irrecoverable damage to\nfilesystem and ");
646 		pwarn("user data.\n");
647 		if (reply("CANCEL FILESYSTEM CHECK") == 1) {
648 			if (cg >= 0) {
649 				pwarn("Please verify that the indicated block "
650 				    "contains a proper\nsuperblock for the "
651 				    "filesystem (see fsdb(1M)).\n");
652 				if (yflag)
653 					pwarn("\nFSCK was running in YES "
654 					    "mode.  If you wish to run in "
655 					    "that mode using\nthe alternate "
656 					    "superblock, run "
657 					    "`fsck -y -o b=%d %s'.\n",
658 					    bflag, devstr);
659 			}
660 			retval = -1;
661 			goto finish;
662 		}
663 
664 		/*
665 		 * Pretend we found it as an alternate, so everything
666 		 * gets updated when we clean up at the end.
667 		 */
668 		if (!found) {
669 			havesb = 1;
670 			sblk.b_bno = fsbtodb(&sblock, cgsblock(&sblock, 0));
671 			bwrite(fswritefd, (caddr_t)&sblock, SBLOCK, SBSIZE);
672 			write_altsb(fswritefd);
673 		}
674 	}
675 
676 finish:
677 	return (retval);
678 }
679 
680 /*
681  * Check and potentially fix certain fields in the super block.
682  */
683 static void
684 fixup_superblock(void)
685 {
686 	/*
687 	 * Kernel looks for FS_OPTTIME, and assumes that if that's not
688 	 * what's there, it must be FS_OPTSPACE, so not fixing does not
689 	 * require setting iscorrupt.
690 	 */
691 	if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
692 		pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
693 		if (reply("SET TO DEFAULT") == 1) {
694 			sblock.fs_optim = FS_OPTTIME;
695 			sbdirty();
696 		}
697 	}
698 	if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
699 		pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
700 		    sblock.fs_minfree);
701 		if (reply("SET TO DEFAULT") == 1) {
702 			sblock.fs_minfree = 10;
703 			sbdirty();
704 		} else if (sblock.fs_minfree < 0) {
705 			/*
706 			 * Kernel uses minfree without verification,
707 			 * and a negative value would do bad things.
708 			 */
709 			iscorrupt = 1;
710 		}
711 	}
712 }
713 
714 static int
715 initial_error_state_adjust(void)
716 {
717 	int retval = 0;
718 
719 	/* do this right away to prevent any other fscks on this fs */
720 	switch (sblock.fs_clean) {
721 	case FSBAD:
722 		break;
723 	case FSFIX:
724 		if (preen)
725 			errexit("ERROR-LOCKED; MARKED \"FSFIX\"\n");
726 		if (reply("marked FSFIX, CONTINUE") == 0) {
727 			retval = -1;
728 			goto finish;
729 		}
730 		break;
731 	case FSCLEAN:
732 		if (preen)
733 			errexit("ERROR-LOCKED; MARKED \"FSCLEAN\"\n");
734 		if (reply("marked FSCLEAN, CONTINUE") == 0) {
735 			retval = -1;
736 			goto finish;
737 		}
738 		break;
739 	default:
740 		if (preen) {
741 			if (debug)
742 				pwarn("ERRORLOCKED; NOT MARKED \"FSBAD\"\n");
743 			else
744 				errexit("ERRORLOCKED; NOT MARKED \"FSBAD\"\n");
745 		} else {
746 			(void) printf("error-locked but not marked \"FSBAD\";");
747 			if (reply(" CONTINUE") == 0) {
748 				retval = -1;
749 				goto finish;
750 			}
751 		}
752 		break;
753 	}
754 
755 	if (!do_errorlock(LOCKFS_ELOCK)) {
756 		if (preen) {
757 			retval = -1;
758 			goto finish;
759 		}
760 		if (reply("error-lock reset failed; CONTINUE") == 0) {
761 			retval = -1;
762 			goto finish;
763 		}
764 	}
765 
766 	sblock.fs_state = FSOKAY - (long)sblock.fs_time;
767 	sblock.fs_clean = FSFIX;
768 	sbdirty();
769 	write_altsb(fswritefd);
770 
771 finish:
772 	return (retval);
773 }
774 
775 static void
776 getsummaryinfo(void)
777 {
778 	size_t size;
779 	int failed;
780 	int asked;
781 	int i, j;
782 	caddr_t sip;
783 
784 	/*
785 	 * read in the summary info.
786 	 */
787 	sblock.fs_u.fs_csp = calloc(1, sblock.fs_cssize);
788 	if (sblock.fs_u.fs_csp == NULL)
789 		errexit(
790 	    "cannot allocate %u bytes for cylinder group summary info\n",
791 		    (unsigned)sblock.fs_cssize);
792 	sip = (caddr_t)sblock.fs_u.fs_csp;
793 	asked = 0;
794 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
795 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
796 			sblock.fs_cssize - i : sblock.fs_bsize;
797 		failed = fsck_bread(fsreadfd, sip,
798 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
799 		    size);
800 		if (failed && !asked) {
801 			pfatal("BAD SUMMARY INFORMATION");
802 			if (reply("CONTINUE") == 0) {
803 				ckfini();
804 				exit(EXFNDERRS);
805 			}
806 			asked = 1;
807 		}
808 		sip += size;
809 	}
810 }
811 
812 /*
813  * Reverses the effects of getsummaryinfo().
814  */
815 static void
816 ungetsummaryinfo(void)
817 {
818 	if ((sblk.b_un.b_fs != NULL) &&
819 	    (sblk.b_un.b_fs->fs_u.fs_csp != NULL)) {
820 		free(sblk.b_un.b_fs->fs_u.fs_csp);
821 		sblk.b_un.b_fs->fs_u.fs_csp = NULL;
822 	}
823 }
824 
825 /*
826  * Allocate and initialize the global tables.
827  * It is the responsibility of the caller to clean up and allocations
828  * if an error is returned.
829  */
830 static int
831 create_and_init_maps(void)
832 {
833 	int64_t bmapsize;
834 	int retval = 0;
835 
836 	maxfsblock = sblock.fs_size;
837 	maxino = sblock.fs_ncg * sblock.fs_ipg;
838 
839 	bmapsize = roundup(howmany((uint64_t)maxfsblock, NBBY),
840 	    sizeof (short));
841 	blockmap = calloc((size_t)bmapsize, sizeof (char));
842 	if (blockmap == NULL) {
843 		(void) printf("cannot alloc %lld bytes for blockmap\n",
844 		    (longlong_t)bmapsize);
845 		retval = -1;
846 		goto finish;
847 	}
848 	statemap = calloc((size_t)(maxino + 1), sizeof (*statemap));
849 	if (statemap == NULL) {
850 		(void) printf("cannot alloc %lld bytes for statemap\n",
851 		    (longlong_t)(maxino + 1) * sizeof (*statemap));
852 		retval = -1;
853 		goto finish;
854 	}
855 	lncntp = (short *)calloc((size_t)(maxino + 1), sizeof (short));
856 	if (lncntp == NULL) {
857 		(void) printf("cannot alloc %lld bytes for lncntp\n",
858 		    (longlong_t)(maxino + 1) * sizeof (short));
859 		retval = -1;
860 		goto finish;
861 	}
862 
863 	/*
864 	 * If we had to fake up a superblock, it won't show that there
865 	 * are any directories at all.  This causes problems when we
866 	 * use numdirs to calculate hash keys, so use something at least
867 	 * vaguely plausible.
868 	 */
869 	numdirs = sblock.fs_cstotal.cs_ndir;
870 	if (numdirs == 0)
871 		numdirs = sblock.fs_ipg * sblock.fs_ncg / 2;
872 	listmax = numdirs + 10;
873 	inpsort = (struct inoinfo **)calloc((unsigned)listmax,
874 	    sizeof (struct inoinfo *));
875 	inphead = (struct inoinfo **)calloc((unsigned)numdirs,
876 	    sizeof (struct inoinfo *));
877 	if (inpsort == NULL || inphead == NULL) {
878 		(void) printf("cannot alloc %lld bytes for inphead\n",
879 		    (longlong_t)numdirs * sizeof (struct inoinfo *));
880 		retval = -1;
881 		goto finish;
882 	}
883 	if (debug) {
884 		if (listmax > USI_MAX)
885 			errexit("create_and_init_maps: listmax overflowed\n");
886 		if (numdirs > USI_MAX)
887 			errexit("create_and_init_maps: numdirs overflowed\n");
888 	}
889 
890 	numacls = numdirs;
891 	aclmax = numdirs + 10;
892 	aclpsort = (struct inoinfo **)calloc((unsigned)aclmax,
893 	    sizeof (struct inoinfo *));
894 	aclphead = (struct inoinfo **)calloc((unsigned)numacls,
895 	    sizeof (struct inoinfo *));
896 	if (aclpsort == NULL || aclphead == NULL) {
897 		(void) printf("cannot alloc %lld bytes for aclphead\n",
898 		    (longlong_t)numacls * sizeof (struct inoinfo *));
899 		retval = -1;
900 		goto finish;
901 	}
902 	if (debug) {
903 		if (aclmax > USI_MAX)
904 			errexit("create_and_init_maps: aclmax overflowed\n");
905 		if (numacls > USI_MAX)
906 			errexit("create_and_init_maps: numacls overflowed\n");
907 	}
908 	aclplast = 0L;
909 	inplast = 0L;
910 
911 finish:
912 	return (retval);
913 }
914 
915 caddr_t
916 setup(caddr_t dev)
917 {
918 	int corefs;
919 	static char devstr[MAXPATHLEN + 1];
920 
921 	havesb = 0;
922 	devname = devstr;
923 
924 	derive_devstr(dev, devstr, sizeof (devstr));
925 	errorlocked = is_errorlocked(devstr);
926 	corefs = check_mount_state(devstr, sizeof (devstr));
927 
928 	sblock_init();
929 
930 	if (open_and_intro(devstr, corefs) == -1)
931 		goto cleanup;
932 
933 	/*
934 	 * Check log state
935 	 */
936 	if (!logsetup(devstr))
937 		goto cleanup;
938 
939 	/*
940 	 * Flush fs if we're going to do anything other than a sanity check.
941 	 * Note, if logging then the fs was already flushed in logsetup().
942 	 */
943 	if (!islog && !mflag)
944 		flush_fs();
945 
946 	if (find_superblock(devstr) == -1)
947 		goto cleanup;
948 
949 	fixup_superblock();
950 
951 	if (errorlocked &&
952 	    (initial_error_state_adjust() == -1))
953 		goto cleanup;
954 
955 	/*
956 	 * asblk could be dirty because we found a mismatch between
957 	 * the primary superblock and one of its backups in checksb().
958 	 */
959 	if (asblk.b_dirty && !bflag) {
960 		(void) memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize);
961 		flush(fswritefd, &asblk);
962 	}
963 
964 	getsummaryinfo();
965 
966 	/*
967 	 * if not error-locked, using the standard superblock,
968 	 *   not bad log, not forced, preening, and is clean;
969 	 *   stop checking
970 	 */
971 	if (!errorlocked && (bflag == 0) &&
972 	    ((!islog || islogok) &&
973 	    (fflag == 0) && preen &&
974 	    (FSOKAY == (sblock.fs_state + sblock.fs_time)) &&
975 	    ((sblock.fs_clean == FSLOG && islog) ||
976 	    ((sblock.fs_clean == FSCLEAN) || (sblock.fs_clean == FSSTABLE))))) {
977 		iscorrupt = 0;
978 		printclean();
979 		goto cleanup;
980 	}
981 
982 	if (create_and_init_maps() == -1)
983 		goto nomaps;
984 
985 	bufinit();
986 	return (devstr);
987 
988 nomaps:
989 	ckfini();
990 	exitstat = EXERRFATAL;
991 	/* FALLTHROUGH */
992 
993 cleanup:
994 	unbufinit();
995 	uncreate_maps();
996 	ungetsummaryinfo();
997 
998 	/*
999 	 * Can't get rid of the superblock buffer, because our
1000 	 * caller references it to generate the summary statistics.
1001 	 */
1002 
1003 	return (NULL);
1004 }
1005 
1006 /*
1007  * Undoes the allocations in create_and_init_maps()
1008  */
1009 static void
1010 uncreate_maps(void)
1011 {
1012 	/*
1013 	 * No ordering dependency amongst these, so they are here in
1014 	 * the same order they were calculated.
1015 	 */
1016 	if (blockmap != NULL)
1017 		free(blockmap);
1018 	if (statemap != NULL)
1019 		free(statemap);
1020 	if (lncntp != NULL)
1021 		free(lncntp);
1022 	if (inpsort != NULL)
1023 		free(inpsort);
1024 	if (inphead != NULL)
1025 		free(inphead);
1026 	if (aclpsort != NULL)
1027 		free(aclpsort);
1028 	if (aclphead != NULL)
1029 		free(aclphead);
1030 }
1031 
1032 /*
1033  *  mkfs limits the size of the inode map to be no more than a third of
1034  *  the cylinder group space.  We'll use that value for sanity checking
1035  *  the superblock's inode per group value.
1036  */
1037 #define	MAXIpG	(roundup(sblock.fs_bsize * NBBY / 3, sblock.fs_inopb))
1038 
1039 /*
1040  * Check the super block and its summary info.
1041  */
1042 static int
1043 checksb(int listerr)
1044 {
1045 	caddr_t err;
1046 
1047 	/*
1048 	 * When the fs check is successfully completed, the alternate super
1049 	 * block at sblk.b_bno will be overwritten by ckfini() with the
1050 	 * repaired super block.
1051 	 */
1052 	sblk.b_bno = bflag ? bflag : (SBOFF / dev_bsize);
1053 	sblk.b_size = SBSIZE;
1054 
1055 	/*
1056 	 * Sanity-check some of the values we are going to use later
1057 	 * in allocation requests.
1058 	 */
1059 	if (sblock.fs_cstotal.cs_ndir < 1 ||
1060 	    sblock.fs_cstotal.cs_ndir > sblock.fs_ncg * sblock.fs_ipg) {
1061 		if (verbose)
1062 			(void) printf(
1063 	    "Found %d directories, should be between 1 and %d inclusive.\n",
1064 			    sblock.fs_cstotal.cs_ndir,
1065 			    sblock.fs_ncg * sblock.fs_ipg);
1066 		err = "NUMBER OF DIRECTORIES OUT OF RANGE";
1067 		goto failedsb;
1068 	}
1069 
1070 	if (sblock.fs_nrpos <= 0 || sblock.fs_postbloff < 0 ||
1071 	    sblock.fs_cpc < 0 ||
1072 	    (sblock.fs_postbloff +
1073 	    (sblock.fs_nrpos * sblock.fs_cpc * sizeof (short))) >
1074 	    sblock.fs_sbsize) {
1075 		err = "ROTATIONAL POSITION TABLE SIZE OUT OF RANGE";
1076 		goto failedsb;
1077 	}
1078 
1079 	if (sblock.fs_cssize !=
1080 	    fragroundup(&sblock, sblock.fs_ncg * sizeof (struct csum))) {
1081 		err = "SIZE OF CYLINDER GROUP SUMMARY AREA WRONG";
1082 		goto failedsb;
1083 	}
1084 
1085 	if (sblock.fs_inopb != (sblock.fs_bsize / sizeof (struct dinode))) {
1086 		err = "INOPB NONSENSICAL RELATIVE TO BSIZE";
1087 		goto failedsb;
1088 	}
1089 
1090 	if (sblock.fs_bsize > MAXBSIZE) {
1091 		err = "BLOCK SIZE LARGER THAN MAXIMUM SUPPORTED";
1092 		goto failedsb;
1093 	}
1094 
1095 	if (sblock.fs_bsize != (sblock.fs_frag * sblock.fs_fsize)) {
1096 		err = "FRAGS PER BLOCK OR FRAG SIZE WRONG";
1097 		goto failedsb;
1098 	}
1099 
1100 	if (sblock.fs_dsize >= sblock.fs_size) {
1101 		err = "NUMBER OF DATA BLOCKS OUT OF RANGE";
1102 		goto failedsb;
1103 	}
1104 
1105 #if 0
1106 	if (sblock.fs_size >
1107 	    (sblock.fs_nsect * sblock.fs_ntrak * sblock.fs_ncyl)) {
1108 		err = "FILESYSTEM SIZE LARGER THAN DEVICE";
1109 		goto failedsb;
1110 	}
1111 #endif
1112 
1113 	/*
1114 	 *  Check that the number of inodes per group isn't less than or
1115 	 *  equal to zero.  Also makes sure it isn't more than the
1116 	 *  maximum number mkfs enforces.
1117 	 */
1118 	if (sblock.fs_ipg <= 0 || sblock.fs_ipg > MAXIpG) {
1119 		err = "INODES PER GROUP OUT OF RANGE";
1120 		goto failedsb;
1121 	}
1122 
1123 	if (sblock.fs_cgsize > sblock.fs_bsize) {
1124 		err = "CG HEADER LARGER THAN ONE BLOCK";
1125 		goto failedsb;
1126 	}
1127 
1128 	/*
1129 	 * Set all possible fields that could differ, then do check
1130 	 * of whole super block against an alternate super block.
1131 	 * When an alternate super-block is specified this check is skipped.
1132 	 */
1133 	(void) getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1),
1134 	    (size_t)sblock.fs_sbsize);
1135 	if (asblk.b_errs != 0) {
1136 		brelse(&asblk);
1137 		return (0);
1138 	}
1139 	if (bflag != 0) {
1140 		/*
1141 		 * Invalidate clean flag and state information.
1142 		 * Note that we couldn't return until after the
1143 		 * above getblk(), because we're going to want to
1144 		 * update asblk when everything's done.
1145 		 */
1146 		sblock.fs_clean = FSACTIVE;
1147 		sblock.fs_state = (long)sblock.fs_time;
1148 		sblock.fs_reclaim = 0;
1149 		sbdirty();
1150 		havesb = 1;
1151 		return (1);
1152 	}
1153 	altsblock.fs_link = sblock.fs_link;
1154 	altsblock.fs_rolled = sblock.fs_rolled;
1155 	altsblock.fs_time = sblock.fs_time;
1156 	altsblock.fs_state = sblock.fs_state;
1157 	altsblock.fs_cstotal = sblock.fs_cstotal;
1158 	altsblock.fs_cgrotor = sblock.fs_cgrotor;
1159 	altsblock.fs_fmod = sblock.fs_fmod;
1160 	altsblock.fs_clean = sblock.fs_clean;
1161 	altsblock.fs_ronly = sblock.fs_ronly;
1162 	altsblock.fs_flags = sblock.fs_flags;
1163 	altsblock.fs_maxcontig = sblock.fs_maxcontig;
1164 	altsblock.fs_minfree = sblock.fs_minfree;
1165 	altsblock.fs_optim = sblock.fs_optim;
1166 	altsblock.fs_rotdelay = sblock.fs_rotdelay;
1167 	altsblock.fs_maxbpg = sblock.fs_maxbpg;
1168 	altsblock.fs_logbno = sblock.fs_logbno;
1169 	altsblock.fs_reclaim = sblock.fs_reclaim;
1170 	altsblock.fs_si = sblock.fs_si;
1171 	(void) memmove((void *)altsblock.fs_fsmnt, (void *)sblock.fs_fsmnt,
1172 	    sizeof (sblock.fs_fsmnt));
1173 	/*
1174 	 * The following should not have to be copied.
1175 	 */
1176 	(void) memmove((void *)altsblock.fs_u.fs_csp_pad,
1177 	    (void *)sblock.fs_u.fs_csp_pad, sizeof (sblock.fs_u.fs_csp_pad));
1178 	altsblock.fs_fsbtodb = sblock.fs_fsbtodb;
1179 	altsblock.fs_npsect = sblock.fs_npsect;
1180 	altsblock.fs_nrpos = sblock.fs_nrpos;
1181 	if (memcmp((void *)&sblock, (void *)&altsblock,
1182 	    (size_t)sblock.fs_sbsize) != 0) {
1183 		err = "BAD VALUES IN SUPER BLOCK";
1184 		goto failedsb;
1185 	}
1186 	havesb = 1;
1187 	return (1);
1188 
1189 failedsb:
1190 	badsb(listerr, err);
1191 	return (0);
1192 }
1193 
1194 static void
1195 badsb(int listerr, caddr_t s)
1196 {
1197 	if (!listerr)
1198 		return;
1199 	if (preen)
1200 		(void) printf("%s: ", devname);
1201 	(void) printf("BAD SUPERBLOCK AT BLOCK %d: %s\n",
1202 	    bflag != 0 ? bflag : SBLOCK, s);
1203 	if (preen) {
1204 		pwarn(
1205 	    "USE AN ALTERNATE SUPERBLOCK TO SUPPLY NEEDED INFORMATION;\n");
1206 		pwarn("e.g. fsck [-F ufs] -o b=# [special ...] \n");
1207 		exitstat = EXERRFATAL;
1208 		pfatal(
1209 	    "where # is the alternate super block. SEE fsck_ufs(1M). \n");
1210 	}
1211 	/* we're expected to return if not preening */
1212 }
1213 
1214 /*
1215  * Write out the super block into each of the alternate super blocks.
1216  */
1217 void
1218 write_altsb(int fd)
1219 {
1220 	int cylno;
1221 
1222 	for (cylno = 0; cylno < sblock.fs_ncg; cylno++)
1223 		bwrite(fd, (caddr_t)&sblock, fsbtodb(&sblock,
1224 		    cgsblock(&sblock, cylno)), sblock.fs_sbsize);
1225 }
1226 
1227 static void
1228 sblock_init(void)
1229 {
1230 	fsmodified = 0;
1231 	if (errorlocked)
1232 		isdirty = 1;
1233 	lfdir = 0;
1234 	initbarea(&sblk);
1235 	initbarea(&asblk);
1236 
1237 	/*
1238 	 * May have buffer left over from previous filesystem check.
1239 	 */
1240 	if (sblk.b_un.b_buf == NULL)
1241 		sblk.b_un.b_buf = calloc(1, SBSIZE);
1242 	if (asblk.b_un.b_buf == NULL)
1243 		asblk.b_un.b_buf = calloc(1, SBSIZE);
1244 	if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
1245 		errexit("cannot allocate space for superblock\n");
1246 	/*
1247 	 * Could get the actual sector size from the device here,
1248 	 * but considering how much would need to change in the rest
1249 	 * of the system before it'd be a problem for us, it's not
1250 	 * worth worrying about right now.
1251 	 */
1252 	dev_bsize = secsize = DEV_BSIZE;
1253 }
1254 
1255 /*
1256  * Calculate a prototype superblock based on information in the disk label.
1257  * When done the cgsblock macro can be calculated and the fs_ncg field
1258  * can be used. Do NOT attempt to use other macros without verifying that
1259  * their needed information is available!
1260  *
1261  * In BSD, the disk label includes all sorts of useful information,
1262  * like cpg.  Solaris doesn't have that, and deriving it (as well as
1263  * some other parameters) is difficult.  Rather than duplicate the
1264  * code, just ask mkfs what it would've come up with by default.
1265  * Ideally, we'd just link in the code, but given the source base
1266  * involved, it's more practical to just get a binary dump.
1267  *
1268  * The one minor drawback to the above approach is that newfs and mkfs
1269  * will produce vastly different layouts for the same partition if
1270  * they're allowed to default everything.  So, if the superblock that
1271  * mkfs gives us doesn't work for guessing where the alternates are,
1272  * we need to try newfs.
1273  */
1274 static int
1275 calcsb(calcsb_t style, caddr_t dev, int devfd, struct fs *fs)
1276 {
1277 #define	FROM_CHILD	0
1278 #define	TO_FSCK		1
1279 #define	CMD_IDX		0
1280 #define	DEV_IDX		3
1281 #define	SIZE_IDX	4
1282 
1283 	int child_pipe[2];
1284 	caddr_t mkfsline[] = {
1285 		"",		/* CMD_IDX */
1286 		"-o",
1287 		"calcbinsb,N",
1288 		NULL,		/* DEV_IDX */
1289 		NULL,		/* SIZE_IDX */
1290 		NULL
1291 	};
1292 	caddr_t newfsline[] = {
1293 		"",		/* CMD_IDX */
1294 		"-B",
1295 		"-N",
1296 		NULL,		/* DEV_IDX */
1297 		NULL
1298 	};
1299 	int pending, transferred;
1300 	caddr_t *cmdline;
1301 	caddr_t target;
1302 	caddr_t sizestr = NULL;
1303 	caddr_t path_old, path_new, mkfs_dir, mkfs_path, newfs_path;
1304 	caddr_t slash;
1305 	diskaddr_t size;
1306 	int devnull;
1307 
1308 	switch (style) {
1309 	case MKFS_STYLE:
1310 		if (debug)
1311 			(void) printf("calcsb() going with style MKFS\n");
1312 		cmdline = mkfsline;
1313 		break;
1314 	case NEWFS_STYLE:
1315 		if (debug)
1316 			(void) printf("calcsb() going with style NEWFS\n");
1317 		cmdline = newfsline;
1318 		break;
1319 	default:
1320 		if (debug)
1321 			(void) printf("calcsb() doesn't undestand style %d\n",
1322 			    style);
1323 		return (0);
1324 	}
1325 
1326 	cmdline[DEV_IDX] = dev;
1327 
1328 	/*
1329 	 * Normally, only use the stock versions of the utilities.
1330 	 * However, if we're debugging, the odds are that we're
1331 	 * using experimental versions of them as well, so allow
1332 	 * some flexibility.
1333 	 */
1334 	mkfs_path = getenv("MKFS_PATH");
1335 	if (!debug || (mkfs_path == NULL))
1336 		mkfs_path = MKFS_PATH;
1337 
1338 	newfs_path = getenv("NEWFS_PATH");
1339 	if (!debug || (newfs_path == NULL))
1340 		newfs_path = NEWFS_PATH;
1341 
1342 	if (style == MKFS_STYLE) {
1343 		cmdline[CMD_IDX] = mkfs_path;
1344 
1345 		size = getdisksize(dev, devfd);
1346 		if (size == 0)
1347 			return (0);
1348 
1349 		(void) fsck_asprintf(&sizestr, "%lld", (longlong_t)size);
1350 		cmdline[SIZE_IDX] = sizestr;
1351 	} else if (style == NEWFS_STYLE) {
1352 		/*
1353 		 * Make sure that newfs will find the right version of mkfs.
1354 		 */
1355 		cmdline[CMD_IDX] = newfs_path;
1356 		path_old = getenv("PATH");
1357 		/* mkfs_path is always initialized, despite lint's concerns */
1358 		mkfs_dir = strdup(mkfs_path);
1359 		if (mkfs_dir == NULL)
1360 			return (0);
1361 		/*
1362 		 * If no location data for mkfs, don't need to do
1363 		 * anything about PATH.
1364 		 */
1365 		slash = strrchr(mkfs_dir, '/');
1366 		if (slash != NULL) {
1367 			/*
1368 			 * Just want the dir, so discard the executable name.
1369 			 */
1370 			*slash = '\0';
1371 
1372 			/*
1373 			 * newfs uses system() to find mkfs, so make sure
1374 			 * that the one we want to use is first on the
1375 			 * list.  Don't free path_new upon success, as it
1376 			 * has become part of the environment.
1377 			 */
1378 			(void) fsck_asprintf(&path_new, "PATH=%s:%s",
1379 			    mkfs_dir, path_old);
1380 			if (putenv(path_new) != 0) {
1381 				free(mkfs_dir);
1382 				free(path_new);
1383 				return (0);
1384 			}
1385 		}
1386 		free(mkfs_dir);
1387 	} else {
1388 		/*
1389 		 * Bad search style, quietly return failure.
1390 		 */
1391 		if (debug) {
1392 			(void) printf("calcsb: got bad style number %d\n",
1393 			    (int)style);
1394 		}
1395 		return (0);
1396 	}
1397 
1398 	if (pipe(child_pipe) < 0) {
1399 		pfatal("calcsb: could not create pipe: %s\n", strerror(errno));
1400 		if (sizestr != NULL)
1401 			free(sizestr);
1402 		return (0);
1403 	}
1404 
1405 	switch (fork()) {
1406 	case -1:
1407 		pfatal("calcsb: fork failed: %s\n", strerror(errno));
1408 		if (sizestr != NULL)
1409 			free(sizestr);
1410 		return (0);
1411 	case 0:
1412 		if (dup2(child_pipe[TO_FSCK], fileno(stdout)) < 0) {
1413 			(void) printf(
1414 			    "calcsb: could not rename file descriptor: %s\n",
1415 			    strerror(errno));
1416 			exit(EXBADPARM);
1417 		}
1418 		devnull = open("/dev/null", O_WRONLY);
1419 		if (devnull == -1) {
1420 			(void) printf("calcsb: could not open /dev/null: %s\n",
1421 			    strerror(errno));
1422 			exit(EXBADPARM);
1423 		}
1424 		if (dup2(devnull, fileno(stderr)) < 0) {
1425 			(void) printf(
1426 			    "calcsb: could not rename file descriptor: %s\n",
1427 			    strerror(errno));
1428 			exit(EXBADPARM);
1429 		}
1430 		(void) close(child_pipe[FROM_CHILD]);
1431 		(void) execv(cmdline[CMD_IDX], cmdline);
1432 		(void) printf("calcsb: could not exec %s: %s\n",
1433 		    cmdline[CMD_IDX], strerror(errno));
1434 		exit(EXBADPARM);
1435 		/* NOTREACHED */
1436 	default:
1437 		break;
1438 	}
1439 
1440 	(void) close(child_pipe[TO_FSCK]);
1441 	if (sizestr != NULL)
1442 		free(sizestr);
1443 
1444 	pending = sizeof (struct fs);
1445 	target = (caddr_t)fs;
1446 	do {
1447 		transferred = read(child_pipe[FROM_CHILD], target, pending);
1448 		pending -= transferred;
1449 		target += transferred;
1450 	} while ((pending > 0) && (transferred > 0));
1451 
1452 	if (pending > 0) {
1453 		if (transferred < 0)
1454 			pfatal(
1455 		    "calcsb: binary read of superblock from %s failed: %s\n",
1456 			    (style == MKFS_STYLE) ? "mkfs" : "newfs",
1457 			    (transferred < 0) ? strerror(errno) : "");
1458 		else
1459 			pfatal(
1460 		    "calcsb: short read of superblock from %s\n",
1461 			    (style == MKFS_STYLE) ? "mkfs" : "newfs");
1462 		return (0);
1463 	}
1464 
1465 	(void) close(child_pipe[FROM_CHILD]);
1466 	(void) wait(NULL);
1467 
1468 	if ((fs->fs_magic != FS_MAGIC) &&
1469 	    (fs->fs_magic != MTB_UFS_MAGIC))
1470 		return (0);
1471 
1472 	return (1);
1473 }
1474