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
read_super_block(int listerr)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
flush_fs()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
logsetup(caddr_t devstr)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
derive_devstr(const caddr_t dev,caddr_t devstr,size_t str_size)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
which_corefs(const caddr_t mntp)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
check_mount_state(caddr_t devstr,size_t str_size)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
open_and_intro(caddr_t devstr,int corefs)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
find_superblock(caddr_t devstr)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(8).\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(8)).\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
fixup_superblock(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
initial_error_state_adjust(void)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
getsummaryinfo(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
ungetsummaryinfo(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
create_and_init_maps(void)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
setup(caddr_t dev)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
uncreate_maps(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
checksb(int listerr)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
badsb(int listerr,caddr_t s)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(8). \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
write_altsb(int fd)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
sblock_init(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
calcsb(calcsb_t style,caddr_t dev,int devfd,struct fs * fs)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