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