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