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