xref: /illumos-gate/usr/src/cmd/fs.d/ufs/fsck/setup.c (revision 504a199e)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
7 /*	  All Rights Reserved  	*/
8 
9 /*
10  * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms are permitted
14  * provided that: (1) source distributions retain this entire copyright
15  * notice and comment, and (2) distributions including binaries display
16  * the following acknowledgement:  ``This product includes software
17  * developed by the University of California, Berkeley and its contributors''
18  * in the documentation or other materials provided with the distribution
19  * and in all advertising materials mentioning features or use of this
20  * software. Neither the name of the University nor the names of its
21  * contributors may be used to endorse or promote products derived
22  * from this software without specific prior written permission.
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #define	DKTYPENAMES
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <malloc.h>
35 #include <limits.h>
36 #include <wait.h>
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/sysmacros.h>
40 #include <sys/mntent.h>
41 #include <sys/dkio.h>
42 #include <sys/filio.h>
43 #include <sys/isa_defs.h>	/* for ENDIAN defines */
44 #include <sys/int_const.h>
45 #include <sys/fs/ufs_fs.h>
46 #include <sys/vnode.h>
47 #include <sys/fs/ufs_fs.h>
48 #include <sys/fs/ufs_inode.h>
49 #include <sys/fs/ufs_log.h>
50 #include <sys/stat.h>
51 #include <sys/fcntl.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <sys/vfstab.h>
56 #include "roll_log.h"
57 #include "fsck.h"
58 
59 /*
60  * The size of a cylinder group is calculated by CGSIZE. The maximum size
61  * is limited by the fact that cylinder groups are at most one block.
62  * Its size is derived from the size of the maps maintained in the
63  * cylinder group and the (struct cg) size.
64  */
65 #define	CGSIZE(fs) \
66 	/* base cg */	  (sizeof (struct cg) + \
67 	/* blktot size */ (fs)->fs_cpg * sizeof (int32_t) + \
68 	/* blks size */	  (fs)->fs_cpg * (fs)->fs_nrpos * sizeof (short) + \
69 	/* inode map */	  howmany((fs)->fs_ipg, NBBY) + \
70 	/* block map */	  howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY))
71 
72 #define	altsblock (*asblk.b_un.b_fs)
73 #define	POWEROF2(num)	(((num) & ((num) - 1)) == 0)
74 
75 /*
76  * Methods of determining where alternate superblocks should
77  * be.  MAX_SB_STYLES must be the last one, and the others need
78  * to be positive.
79  */
80 typedef enum {
81 	MKFS_STYLE = 1, NEWFS_STYLE, MAX_SB_STYLES
82 } calcsb_t;
83 
84 static caddr_t calcsb_names[] = {
85 	"<UNKNOWN>", "MKFS", "NEWFS", "<OUT OF RANGE>"
86 };
87 
88 struct shadowclientinfo *shadowclientinfo = NULL;
89 struct shadowclientinfo *attrclientinfo = NULL;
90 int maxshadowclients = 1024;	/* allocation size, not limit  */
91 
92 static void badsb(int, caddr_t);
93 static int calcsb(calcsb_t, caddr_t, int, struct fs *);
94 static int checksb(int);
95 static void flush_fs(void);
96 static void sblock_init(void);
97 static void uncreate_maps(void);
98 
99 static int
100 read_super_block(int listerr)
101 {
102 	int fd;
103 	caddr_t err;
104 
105 	if (mount_point != NULL) {
106 		fd = open(mount_point, O_RDONLY);
107 		if (fd == -1) {
108 			errexit("fsck: open mount point error: %s",
109 			    strerror(errno));
110 			/* NOTREACHED */
111 		}
112 		/* get the latest super block */
113 		if (ioctl(fd, _FIOGETSUPERBLOCK, &sblock)) {
114 			errexit("fsck: ioctl _FIOGETSUPERBLOCK error: %s",
115 			    strerror(errno));
116 			/* NOTREACHED */
117 		}
118 		(void) close(fd);
119 	} else {
120 		(void) fsck_bread(fsreadfd, (caddr_t)&sblock,
121 			bflag != 0 ? (diskaddr_t)bflag : (diskaddr_t)SBLOCK,
122 			SBSIZE);
123 	}
124 
125 	/*
126 	 * Don't let trash from the disk trip us up later
127 	 * in ungetsummaryinfo().
128 	 */
129 	sblock.fs_u.fs_csp = NULL;
130 
131 	/*
132 	 * Rudimentary consistency checks.  Can't really call
133 	 * checksb() here, because there may be outstanding
134 	 * deltas that still need to be applied.
135 	 */
136 	if ((sblock.fs_magic != FS_MAGIC) &&
137 	    (sblock.fs_magic != MTB_UFS_MAGIC)) {
138 		err = "MAGIC NUMBER WRONG";
139 		goto fail;
140 	}
141 	if (sblock.fs_magic == FS_MAGIC &&
142 		(sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
143 		sblock.fs_version != UFS_VERSION_MIN)) {
144 		err = "UNRECOGNIZED VERSION";
145 		goto fail;
146 	}
147 	if (sblock.fs_magic == MTB_UFS_MAGIC &&
148 		(sblock.fs_version > MTB_UFS_VERSION_1 ||
149 		sblock.fs_version < MTB_UFS_VERSION_MIN)) {
150 		err = "UNRECOGNIZED VERSION";
151 		goto fail;
152 	}
153 	if (sblock.fs_ncg < 1) {
154 		err = "NCG OUT OF RANGE";
155 		goto fail;
156 	}
157 	if (sblock.fs_cpg < 1) {
158 		err = "CPG OUT OF RANGE";
159 		goto fail;
160 	}
161 	if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl ||
162 		(sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) {
163 		err = "NCYL IS INCONSISTENT WITH NCG*CPG";
164 		goto fail;
165 	}
166 	if (sblock.fs_sbsize < 0 || sblock.fs_sbsize > SBSIZE) {
167 		err = "SIZE OUT OF RANGE";
168 		goto fail;
169 	}
170 
171 	return (1);
172 
173 fail:
174 	badsb(listerr, err);
175 	return (0);
176 }
177 
178 static void
179 flush_fs()
180 {
181 	int fd;
182 
183 	if (mount_point != NULL) {
184 		fd = open(mount_point, O_RDONLY);
185 		if (fd == -1) {
186 			errexit("fsck: open mount point error: %s",
187 			    strerror(errno));
188 			/* NOTREACHED */
189 		}
190 		if (ioctl(fd, _FIOFFS, NULL)) { /* flush file system */
191 			errexit("fsck: ioctl _FIOFFS error: %s",
192 			    strerror(errno));
193 			/* NOTREACHED */
194 		}
195 		(void) close(fd);
196 	}
197 }
198 
199 /*
200  * Roll the embedded log, if any, and set up the global variables
201  * islog and islogok.
202  */
203 static int
204 logsetup(caddr_t devstr)
205 {
206 	void		*buf;
207 	extent_block_t	*ebp;
208 	ml_unit_t	*ul;
209 	ml_odunit_t	*ud;
210 	void		*ud_buf;
211 	int		badlog;
212 
213 	islog = islogok = 0;
214 	if (bflag != 0)
215 		return (1); /* can't roll log while alternate sb specified */
216 
217 	/*
218 	 * Roll the log, if any.  A bad sb implies we'll be using
219 	 * an alternate sb as far as logging goes, so just fail back
220 	 * to the caller if we can't read the default sb.  Suppress
221 	 * complaints, because the caller will be reading the same
222 	 * superblock again and running full verification on it, so
223 	 * whatever is bad will be reported then.
224 	 */
225 	sblock.fs_logbno = 0;
226 	badlog = 0;
227 	if (!read_super_block(0))
228 		return (1);
229 
230 	/*
231 	 * Roll the log in 3 cases:
232 	 * 1. If it's unmounted (mount_point == NULL) and it's not marked
233 	 *    as fully rolled (sblock.fs_rolled != FS_ALL_ROLLED)
234 	 * 2. If it's mounted and anything other than a sanity
235 	 *    check fsck (mflag) is being done, as we have the current
236 	 *    super block. Note, only a sanity check is done for
237 	 *    root/usr at boot. If a roll were done then the expensive
238 	 *    ufs_flush() gets called, leading to a slower boot.
239 	 * 3. If anything other then a sanity check (mflag) is being done
240 	 *    to a mounted filesystem while it is in read-only state
241 	 *    (e.g. root during early boot stages) we have to detect this
242 	 *    and have to roll the log as well. NB. the read-only mount
243 	 *    will flip fs_clean from FSLOG to FSSTABLE and marks the
244 	 *    log as FS_NEED_ROLL.
245 	 */
246 	if (sblock.fs_logbno &&
247 	    (((mount_point == NULL) && (sblock.fs_rolled != FS_ALL_ROLLED)) ||
248 	    ((mount_point != NULL) && !mflag))) {
249 		int roll_log_err = 0;
250 
251 		if (sblock.fs_ronly && (sblock.fs_clean == FSSTABLE) &&
252 			(sblock.fs_state + sblock.fs_time == FSOKAY)) {
253 			/*
254 			 * roll the log without a mount
255 			 */
256 			flush_fs();
257 		}
258 		if (sblock.fs_clean == FSLOG &&
259 			(sblock.fs_state + sblock.fs_time == FSOKAY)) {
260 			if (rl_roll_log(devstr) != RL_SUCCESS)
261 				roll_log_err = 1;
262 		}
263 		if (roll_log_err) {
264 			(void) printf("Can't roll the log for %s.\n", devstr);
265 			/*
266 			 * There are two cases where we want to set
267 			 * an error code and return:
268 			 *  - We're preening
269 			 *  - We're not on a live root and the user
270 			 *    chose *not* to ignore the log
271 			 * Otherwise, we want to mark the log as bad
272 			 * and continue to check the filesystem.  This
273 			 * has the side effect of destroying the log.
274 			 */
275 			if (preen || (!hotroot &&
276 			    reply(
277 			"DISCARDING THE LOG MAY DISCARD PENDING TRANSACTIONS.\n"
278 					"DISCARD THE LOG AND CONTINUE") == 0)) {
279 				exitstat = EXERRFATAL;
280 				return (0);
281 			}
282 			++badlog;
283 		}
284 	}
285 
286 	/* Logging UFS may be enabled */
287 	if (sblock.fs_logbno) {
288 		++islog;
289 
290 		/* log is not okay; check the fs */
291 		if (FSOKAY != (sblock.fs_state + sblock.fs_time))
292 			return (1);
293 
294 		/*
295 		 * If logging or (stable and mounted) then continue
296 		 */
297 		if (!((sblock.fs_clean == FSLOG) ||
298 		    (sblock.fs_clean == FSSTABLE) && (mount_point != NULL)))
299 			return (1);
300 
301 		/* get the log allocation block */
302 		buf = malloc(dev_bsize);
303 		if (buf == NULL) {
304 			return (1);
305 		}
306 		ud_buf = malloc(dev_bsize);
307 		if (ud_buf == NULL) {
308 			free(buf);
309 			return (1);
310 		}
311 		(void) fsck_bread(fsreadfd, buf,
312 		    logbtodb(&sblock, sblock.fs_logbno),
313 		    dev_bsize);
314 		ebp = (extent_block_t *)buf;
315 
316 		/* log allocation block is not okay; check the fs */
317 		if (ebp->type != LUFS_EXTENTS) {
318 			free(buf);
319 			free(ud_buf);
320 			return (1);
321 		}
322 
323 		/* get the log state block(s) */
324 		if (fsck_bread(fsreadfd, ud_buf,
325 		    (logbtodb(&sblock, ebp->extents[0].pbno)),
326 		    dev_bsize)) {
327 			(void) fsck_bread(fsreadfd, ud_buf,
328 			    (logbtodb(&sblock, ebp->extents[0].pbno)) + 1,
329 			    dev_bsize);
330 		}
331 		ud = (ml_odunit_t *)ud_buf;
332 		ul = (ml_unit_t *)malloc(sizeof (*ul));
333 		if (ul == NULL) {
334 			free(buf);
335 			free(ud_buf);
336 			return (1);
337 		}
338 		ul->un_ondisk = *ud;
339 
340 		/* log state is okay; don't need to check the fs */
341 		if ((ul->un_chksum == ul->un_head_ident + ul->un_tail_ident) &&
342 		    (ul->un_version == LUFS_VERSION_LATEST) &&
343 		    (ul->un_badlog == 0) && (!badlog)) {
344 			++islogok;
345 		}
346 		free(ud_buf);
347 		free(buf);
348 		free(ul);
349 	}
350 
351 	return (1);
352 }
353 
354 /*
355  * - given a pathname, determine the pathname to actually check
356  * - if a directory
357  *   - if it is in mnttab, set devstr to the special (block) name
358  *   - if it is in vfstab, set devstr to the special (block) name
359  *   - if it has not been found, bail
360  * - a file is used as-is, clear rflag
361  * - a device is converted to block version (so can search mnttab)
362  */
363 static void
364 derive_devstr(const caddr_t dev, caddr_t devstr, size_t str_size)
365 {
366 	mode_t mode;
367 	struct stat statb;
368 
369 	if (stat(dev, &statb) < 0) {
370 		exitstat = EXNOSTAT;
371 		errexit("fsck: could not stat %s: %s", dev, strerror(errno));
372 	}
373 
374 	mode = statb.st_mode & S_IFMT;
375 	switch (mode) {
376 	case S_IFDIR:
377 		/*
378 		 * The check_*() routines update devstr with the name.
379 		 */
380 		devstr[0] = '\0';
381 		if (!(check_mnttab(dev, devstr, str_size) ||
382 		    check_vfstab(dev, devstr, str_size))) {
383 			exitstat = EXBADPARM;
384 			errexit(
385 		    "fsck: could not find mountpoint %s in mnttab nor vfstab",
386 			    dev);
387 		}
388 		break;
389 	case S_IFREG:
390 		rflag = 0;
391 		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 			if (!nflag) {
464 				pwarn("%s IS CURRENTLY MOUNTED READ/%s.",
465 				    devstr, mountedfs == M_RW ? "WRITE" :
466 				    "ONLY");
467 				if (reply("CONTINUE") == 0) {
468 					exitstat = EXMOUNTED;
469 					errexit("Program terminated");
470 				}
471 			} else {
472 				pwarn("%s IS CURRENTLY MOUNTED READ/%s.\n",
473 				    devstr, mountedfs == M_RW ? "WRITE" :
474 				    "ONLY");
475 			}
476 		}
477 	} else if (is_dev && rflag) {
478 		(void) strlcpy(devstr, rawname(devstr), str_size);
479 	}
480 
481 carry_on:
482 	return (corefs);
483 }
484 
485 static int
486 open_and_intro(caddr_t devstr, int corefs)
487 {
488 	int retval = 0;
489 
490 	if ((fsreadfd = open64(devstr, O_RDONLY)) < 0) {
491 		(void) printf("Can't open %s: %s\n", devstr, strerror(errno));
492 		exitstat = EXNOSTAT;
493 		retval = -1;
494 		goto finish;
495 	}
496 	if (!preen || debug != 0)
497 		(void) printf("** %s", devstr);
498 
499 	if (errorlocked) {
500 		if (debug && elock_combuf != NULL)
501 			(void) printf(" error-lock comment: \"%s\" ",
502 			    elock_combuf);
503 		fflag = 1;
504 	}
505 	pid = getpid();
506 	if (nflag || roflag || (fswritefd = open64(devstr, O_WRONLY)) < 0) {
507 		fswritefd = -1;
508 		if (preen && !debug)
509 			pfatal("(NO WRITE ACCESS)\n");
510 		(void) printf(" (NO WRITE)");
511 	}
512 	if (!preen)
513 		(void) printf("\n");
514 	else if (debug)
515 		(void) printf(" pid %d\n", pid);
516 	if (debug && (hotroot || (mountedfs != M_NOMNT))) {
517 		(void) printf("** %s", devstr);
518 		if (hotroot)
519 			(void) printf(" is %s fs", magic_fs[corefs]);
520 		if (mountedfs != M_NOMNT)
521 			(void) printf(" and is mounted read-%s",
522 			    (mountedfs == M_RO) ? "only" : "write");
523 		if (errorlocked)
524 			(void) printf(" and is error-locked");
525 
526 		(void) printf(".\n");
527 	}
528 
529 finish:
530 	return (retval);
531 }
532 
533 static int
534 find_superblock(caddr_t devstr)
535 {
536 	int cg = 0;
537 	int retval = 0;
538 	int first;
539 	int found;
540 	calcsb_t style;
541 	struct fs proto;
542 
543 	/*
544 	 * Check the superblock, looking for alternates if necessary.
545 	 * In more-recent times, some UFS instances get created with
546 	 * only the first ten and last ten superblock backups.  Since
547 	 * if we can't get the necessary information from any of those,
548 	 * the odds are also against us for the ones in between, we'll
549 	 * just look at those twenty to save time.
550 	 */
551 	if (!read_super_block(1) || !checksb(1)) {
552 		if (bflag || preen) {
553 			retval = -1;
554 			goto finish;
555 		}
556 		for (style = MKFS_STYLE; style < MAX_SB_STYLES; style++) {
557 			if (reply("LOOK FOR ALTERNATE SUPERBLOCKS WITH %s",
558 			    calcsb_names[style]) == 0)
559 				continue;
560 			first = 1;
561 			found = 0;
562 			if (!calcsb(style, devstr, fsreadfd, &proto)) {
563 				cg = proto.fs_ncg;
564 				continue;
565 			}
566 			if (debug) {
567 				(void) printf(
568 			    "debug: calcsb(%s) gave fpg %d, cgoffset %d, ",
569 				    calcsb_names[style],
570 				    proto.fs_fpg, proto.fs_cgoffset);
571 				(void) printf("cgmask 0x%x, sblk %d, ncg %d\n",
572 				    proto.fs_cgmask, proto.fs_sblkno,
573 				    proto.fs_ncg);
574 			}
575 			for (cg = 0; cg < proto.fs_ncg; cg++) {
576 				bflag = fsbtodb(&proto, cgsblock(&proto, cg));
577 				if (debug)
578 					(void) printf(
579 					    "debug: trying block %lld\n",
580 					    (longlong_t)bflag);
581 				if (read_super_block(0) && checksb(0)) {
582 					(void) printf(
583 				    "FOUND ALTERNATE SUPERBLOCK %d WITH %s\n",
584 					    bflag, calcsb_names[style]);
585 					if (reply(
586 					    "USE ALTERNATE SUPERBLOCK") == 1) {
587 						found = 1;
588 						break;
589 					}
590 				}
591 				if (first && (cg >= 9)) {
592 					first = 0;
593 					if (proto.fs_ncg <= 9)
594 						cg = proto.fs_ncg;
595 					else if (proto.fs_ncg <= 19)
596 						cg = 9;
597 					else
598 						cg = proto.fs_ncg - 10;
599 				}
600 			}
601 
602 			if (found)
603 				break;
604 		}
605 
606 		/*
607 		 * Didn't find one?  Try to fake it.
608 		 */
609 		if (style >= MAX_SB_STYLES) {
610 			pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED.\n");
611 			for (style = MKFS_STYLE; style < MAX_SB_STYLES;
612 			    style++) {
613 				if (reply("USE GENERIC SUPERBLOCK FROM %s",
614 				    calcsb_names[style]) == 1 &&
615 				    calcsb(style, devstr, fsreadfd, &sblock)) {
616 					break;
617 			}
618 			/*
619 			 * We got something from mkfs/newfs, so use it.
620 			 */
621 			if (style < MAX_SB_STYLES)
622 				proto.fs_ncg = sblock.fs_ncg;
623 				bflag = 0;
624 			}
625 		}
626 
627 		/*
628 		 * Still no luck?  Tell the user they're on their own.
629 		 */
630 		if (style >= MAX_SB_STYLES) {
631 			pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED. "
632 			    "YOU MUST USE THE -o b OPTION\n"
633 			    "TO FSCK TO SPECIFY THE LOCATION OF A VALID "
634 			    "ALTERNATE SUPERBLOCK TO\n"
635 			    "SUPPLY NEEDED INFORMATION; SEE fsck(1M).\n");
636 			bflag = 0;
637 			retval = -1;
638 			goto finish;
639 		}
640 
641 		/*
642 		 * Need to make sure a human really wants us to use
643 		 * this.  -y mode could've gotten us this far, so
644 		 * we need to ask something that has to be answered
645 		 * in the negative.
646 		 *
647 		 * Note that we can't get here when preening.
648 		 */
649 		if (!found) {
650 			pwarn("CALCULATED GENERIC SUPERBLOCK WITH %s\n",
651 			    calcsb_names[style]);
652 		} else {
653 			pwarn("FOUND ALTERNATE SUPERBLOCK AT %d USING %s\n",
654 			    bflag, calcsb_names[style]);
655 		}
656 		pwarn("If filesystem was created with manually-specified ");
657 		pwarn("geometry, using\nauto-discovered superblock may ");
658 		pwarn("result in irrecoverable damage to\nfilesystem and ");
659 		pwarn("user data.\n");
660 		if (reply("CANCEL FILESYSTEM CHECK") == 1) {
661 			if (cg >= 0) {
662 				pwarn("Please verify that the indicated block "
663 				    "contains a proper\nsuperblock for the "
664 				    "filesystem (see fsdb(1M)).\n");
665 				if (yflag)
666 					pwarn("\nFSCK was running in YES "
667 					    "mode.  If you wish to run in "
668 					    "that mode using\nthe alternate "
669 					    "superblock, run "
670 					    "`fsck -y -o b=%d %s'.\n",
671 					    bflag, devstr);
672 			}
673 			retval = -1;
674 			goto finish;
675 		}
676 
677 		/*
678 		 * Pretend we found it as an alternate, so everything
679 		 * gets updated when we clean up at the end.
680 		 */
681 		if (!found) {
682 			havesb = 1;
683 			sblk.b_bno = fsbtodb(&sblock, cgsblock(&sblock, 0));
684 			bwrite(fswritefd, (caddr_t)&sblock, SBLOCK, SBSIZE);
685 			write_altsb(fswritefd);
686 		}
687 	}
688 
689 finish:
690 	return (retval);
691 }
692 
693 /*
694  * Check and potentially fix certain fields in the super block.
695  */
696 static void
697 fixup_superblock(void)
698 {
699 	/*
700 	 * Kernel looks for FS_OPTTIME, and assumes that if that's not
701 	 * what's there, it must be FS_OPTSPACE, so not fixing does not
702 	 * require setting iscorrupt.
703 	 */
704 	if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) {
705 		pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK");
706 		if (reply("SET TO DEFAULT") == 1) {
707 			sblock.fs_optim = FS_OPTTIME;
708 			sbdirty();
709 		}
710 	}
711 	if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) {
712 		pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK",
713 		    sblock.fs_minfree);
714 		if (reply("SET TO DEFAULT") == 1) {
715 			sblock.fs_minfree = 10;
716 			sbdirty();
717 		} else if (sblock.fs_minfree < 0) {
718 			/*
719 			 * Kernel uses minfree without verification,
720 			 * and a negative value would do bad things.
721 			 */
722 			iscorrupt = 1;
723 		}
724 	}
725 }
726 
727 static int
728 initial_error_state_adjust(void)
729 {
730 	int retval = 0;
731 
732 	/* do this right away to prevent any other fscks on this fs */
733 	switch (sblock.fs_clean) {
734 	case FSBAD:
735 		break;
736 	case FSFIX:
737 		if (preen)
738 			errexit("ERROR-LOCKED; MARKED \"FSFIX\"\n");
739 		if (reply("marked FSFIX, CONTINUE") == 0) {
740 			retval = -1;
741 			goto finish;
742 		}
743 		break;
744 	case FSCLEAN:
745 		if (preen)
746 			errexit("ERROR-LOCKED; MARKED \"FSCLEAN\"\n");
747 		if (reply("marked FSCLEAN, CONTINUE") == 0) {
748 			retval = -1;
749 			goto finish;
750 		}
751 		break;
752 	default:
753 		if (preen) {
754 			if (debug)
755 				pwarn("ERRORLOCKED; NOT MARKED \"FSBAD\"\n");
756 			else
757 				errexit("ERRORLOCKED; NOT MARKED \"FSBAD\"\n");
758 		} else {
759 			(void) printf("error-locked but not marked \"FSBAD\";");
760 			if (reply(" CONTINUE") == 0) {
761 				retval = -1;
762 				goto finish;
763 			}
764 		}
765 		break;
766 	}
767 
768 	if (!do_errorlock(LOCKFS_ELOCK)) {
769 		if (preen) {
770 			retval = -1;
771 			goto finish;
772 		}
773 		if (reply("error-lock reset failed; CONTINUE") == 0) {
774 			retval = -1;
775 			goto finish;
776 		}
777 	}
778 
779 	sblock.fs_state = FSOKAY - (long)sblock.fs_time;
780 	sblock.fs_clean = FSFIX;
781 	sbdirty();
782 	write_altsb(fswritefd);
783 
784 finish:
785 	return (retval);
786 }
787 
788 static void
789 getsummaryinfo(void)
790 {
791 	size_t size;
792 	int failed;
793 	int asked;
794 	int i, j;
795 	caddr_t sip;
796 
797 	/*
798 	 * read in the summary info.
799 	 */
800 	sblock.fs_u.fs_csp = calloc(1, sblock.fs_cssize);
801 	if (sblock.fs_u.fs_csp == NULL)
802 		errexit(
803 	    "cannot allocate %u bytes for cylinder group summary info\n",
804 		    (unsigned)sblock.fs_cssize);
805 	sip = (caddr_t)sblock.fs_u.fs_csp;
806 	asked = 0;
807 	for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
808 		size = sblock.fs_cssize - i < sblock.fs_bsize ?
809 			sblock.fs_cssize - i : sblock.fs_bsize;
810 		failed = fsck_bread(fsreadfd, sip,
811 		    fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
812 		    size);
813 		if (failed && !asked) {
814 			pfatal("BAD SUMMARY INFORMATION");
815 			if (reply("CONTINUE") == 0) {
816 				ckfini();
817 				exit(EXFNDERRS);
818 			}
819 			asked = 1;
820 		}
821 		sip += size;
822 	}
823 }
824 
825 /*
826  * Reverses the effects of getsummaryinfo().
827  */
828 static void
829 ungetsummaryinfo(void)
830 {
831 	if ((sblk.b_un.b_fs != NULL) &&
832 	    (sblk.b_un.b_fs->fs_u.fs_csp != NULL)) {
833 		free(sblk.b_un.b_fs->fs_u.fs_csp);
834 		sblk.b_un.b_fs->fs_u.fs_csp = NULL;
835 	}
836 }
837 
838 /*
839  * Allocate and initialize the global tables.
840  * It is the responsibility of the caller to clean up and allocations
841  * if an error is returned.
842  */
843 static int
844 create_and_init_maps(void)
845 {
846 	int64_t bmapsize;
847 	int retval = 0;
848 
849 	maxfsblock = sblock.fs_size;
850 	maxino = sblock.fs_ncg * sblock.fs_ipg;
851 
852 	bmapsize = roundup(howmany((uint64_t)maxfsblock, NBBY),
853 	    sizeof (short));
854 	blockmap = calloc((size_t)bmapsize, sizeof (char));
855 	if (blockmap == NULL) {
856 		(void) printf("cannot alloc %lld bytes for blockmap\n",
857 		    (longlong_t)bmapsize);
858 		retval = -1;
859 		goto finish;
860 	}
861 	statemap = calloc((size_t)(maxino + 1), sizeof (*statemap));
862 	if (statemap == NULL) {
863 		(void) printf("cannot alloc %lld bytes for statemap\n",
864 		    (longlong_t)(maxino + 1) * sizeof (*statemap));
865 		retval = -1;
866 		goto finish;
867 	}
868 	lncntp = (short *)calloc((size_t)(maxino + 1), sizeof (short));
869 	if (lncntp == NULL) {
870 		(void) printf("cannot alloc %lld bytes for lncntp\n",
871 		    (longlong_t)(maxino + 1) * sizeof (short));
872 		retval = -1;
873 		goto finish;
874 	}
875 
876 	/*
877 	 * If we had to fake up a superblock, it won't show that there
878 	 * are any directories at all.  This causes problems when we
879 	 * use numdirs to calculate hash keys, so use something at least
880 	 * vaguely plausible.
881 	 */
882 	numdirs = sblock.fs_cstotal.cs_ndir;
883 	if (numdirs == 0)
884 		numdirs = sblock.fs_ipg * sblock.fs_ncg / 2;
885 	listmax = numdirs + 10;
886 	inpsort = (struct inoinfo **)calloc((unsigned)listmax,
887 	    sizeof (struct inoinfo *));
888 	inphead = (struct inoinfo **)calloc((unsigned)numdirs,
889 	    sizeof (struct inoinfo *));
890 	if (inpsort == NULL || inphead == NULL) {
891 		(void) printf("cannot alloc %lld bytes for inphead\n",
892 		    (longlong_t)numdirs * sizeof (struct inoinfo *));
893 		retval = -1;
894 		goto finish;
895 	}
896 	if (debug) {
897 		if (listmax > ULONG_MAX)
898 			errexit("create_and_init_maps: listmax overflowed\n");
899 		if (numdirs > ULONG_MAX)
900 			errexit("create_and_init_maps: numdirs overflowed\n");
901 	}
902 
903 	numacls = numdirs;
904 	aclmax = numdirs + 10;
905 	aclpsort = (struct inoinfo **)calloc((unsigned)aclmax,
906 	    sizeof (struct inoinfo *));
907 	aclphead = (struct inoinfo **)calloc((unsigned)numacls,
908 	    sizeof (struct inoinfo *));
909 	if (aclpsort == NULL || aclphead == NULL) {
910 		(void) printf("cannot alloc %lld bytes for aclphead\n",
911 		    (longlong_t)numacls * sizeof (struct inoinfo *));
912 		retval = -1;
913 		goto finish;
914 	}
915 	if (debug) {
916 		if (aclmax > ULONG_MAX)
917 			errexit("create_and_init_maps: aclmax overflowed\n");
918 		if (numacls > ULONG_MAX)
919 			errexit("create_and_init_maps: numacls overflowed\n");
920 	}
921 	aclplast = 0L;
922 	inplast = 0L;
923 
924 finish:
925 	return (retval);
926 }
927 
928 caddr_t
929 setup(caddr_t dev)
930 {
931 	int corefs;
932 	static char devstr[MAXPATHLEN + 1];
933 
934 	havesb = 0;
935 	devname = devstr;
936 
937 	derive_devstr(dev, devstr, sizeof (devstr));
938 	errorlocked = is_errorlocked(devstr);
939 	corefs = check_mount_state(devstr, sizeof (devstr));
940 
941 	sblock_init();
942 
943 	if (open_and_intro(devstr, corefs) == -1)
944 		goto cleanup;
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