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