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