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