17c478bd9Sstevel@tonic-gate /* 2*504a199eSswilcox * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 77c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 87c478bd9Sstevel@tonic-gate 97c478bd9Sstevel@tonic-gate /* 107c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 117c478bd9Sstevel@tonic-gate * All rights reserved. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 147c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright 157c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display 167c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software 177c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors'' 187c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution 197c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this 207c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its 217c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived 227c478bd9Sstevel@tonic-gate * from this software without specific prior written permission. 237c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 247c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 257c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 267c478bd9Sstevel@tonic-gate */ 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #define DKTYPENAMES 317c478bd9Sstevel@tonic-gate #include <stdio.h> 327c478bd9Sstevel@tonic-gate #include <stdlib.h> 337c478bd9Sstevel@tonic-gate #include <errno.h> 34355d6bb5Sswilcox #include <malloc.h> 35355d6bb5Sswilcox #include <limits.h> 36355d6bb5Sswilcox #include <wait.h> 377c478bd9Sstevel@tonic-gate #include <sys/param.h> 387c478bd9Sstevel@tonic-gate #include <sys/types.h> 397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 407c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 417c478bd9Sstevel@tonic-gate #include <sys/dkio.h> 427c478bd9Sstevel@tonic-gate #include <sys/filio.h> 437c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h> /* for ENDIAN defines */ 447c478bd9Sstevel@tonic-gate #include <sys/int_const.h> 457c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 467c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 47355d6bb5Sswilcox #include <sys/fs/ufs_fs.h> 487c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 497c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_log.h> 507c478bd9Sstevel@tonic-gate #include <sys/stat.h> 517c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 527c478bd9Sstevel@tonic-gate #include <string.h> 537c478bd9Sstevel@tonic-gate #include <unistd.h> 547c478bd9Sstevel@tonic-gate #include <fcntl.h> 557c478bd9Sstevel@tonic-gate #include <sys/vfstab.h> 567c478bd9Sstevel@tonic-gate #include "roll_log.h" 57355d6bb5Sswilcox #include "fsck.h" 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate /* 607c478bd9Sstevel@tonic-gate * The size of a cylinder group is calculated by CGSIZE. The maximum size 617c478bd9Sstevel@tonic-gate * is limited by the fact that cylinder groups are at most one block. 627c478bd9Sstevel@tonic-gate * Its size is derived from the size of the maps maintained in the 637c478bd9Sstevel@tonic-gate * cylinder group and the (struct cg) size. 647c478bd9Sstevel@tonic-gate */ 657c478bd9Sstevel@tonic-gate #define CGSIZE(fs) \ 667c478bd9Sstevel@tonic-gate /* base cg */ (sizeof (struct cg) + \ 67355d6bb5Sswilcox /* blktot size */ (fs)->fs_cpg * sizeof (int32_t) + \ 687c478bd9Sstevel@tonic-gate /* blks size */ (fs)->fs_cpg * (fs)->fs_nrpos * sizeof (short) + \ 697c478bd9Sstevel@tonic-gate /* inode map */ howmany((fs)->fs_ipg, NBBY) + \ 707c478bd9Sstevel@tonic-gate /* block map */ howmany((fs)->fs_cpg * (fs)->fs_spc / NSPF(fs), NBBY)) 717c478bd9Sstevel@tonic-gate 72355d6bb5Sswilcox #define altsblock (*asblk.b_un.b_fs) 73355d6bb5Sswilcox #define POWEROF2(num) (((num) & ((num) - 1)) == 0) 74355d6bb5Sswilcox 75355d6bb5Sswilcox /* 76355d6bb5Sswilcox * Methods of determining where alternate superblocks should 77355d6bb5Sswilcox * be. MAX_SB_STYLES must be the last one, and the others need 78355d6bb5Sswilcox * to be positive. 79355d6bb5Sswilcox */ 80355d6bb5Sswilcox typedef enum { 81355d6bb5Sswilcox MKFS_STYLE = 1, NEWFS_STYLE, MAX_SB_STYLES 82355d6bb5Sswilcox } calcsb_t; 83355d6bb5Sswilcox 84355d6bb5Sswilcox static caddr_t calcsb_names[] = { 85355d6bb5Sswilcox "<UNKNOWN>", "MKFS", "NEWFS", "<OUT OF RANGE>" 86355d6bb5Sswilcox }; 87355d6bb5Sswilcox 88355d6bb5Sswilcox struct shadowclientinfo *shadowclientinfo = NULL; 89355d6bb5Sswilcox struct shadowclientinfo *attrclientinfo = NULL; 90355d6bb5Sswilcox int maxshadowclients = 1024; /* allocation size, not limit */ 91355d6bb5Sswilcox 92355d6bb5Sswilcox static void badsb(int, caddr_t); 93355d6bb5Sswilcox static int calcsb(calcsb_t, caddr_t, int, struct fs *); 94355d6bb5Sswilcox static int checksb(int); 95355d6bb5Sswilcox static void flush_fs(void); 96355d6bb5Sswilcox static void sblock_init(void); 97355d6bb5Sswilcox static void uncreate_maps(void); 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate static int 100355d6bb5Sswilcox read_super_block(int listerr) 1017c478bd9Sstevel@tonic-gate { 1027c478bd9Sstevel@tonic-gate int fd; 103355d6bb5Sswilcox caddr_t err; 1047c478bd9Sstevel@tonic-gate 105355d6bb5Sswilcox if (mount_point != NULL) { 1067c478bd9Sstevel@tonic-gate fd = open(mount_point, O_RDONLY); 1077c478bd9Sstevel@tonic-gate if (fd == -1) { 108355d6bb5Sswilcox errexit("fsck: open mount point error: %s", 109355d6bb5Sswilcox strerror(errno)); 110355d6bb5Sswilcox /* NOTREACHED */ 1117c478bd9Sstevel@tonic-gate } 1127c478bd9Sstevel@tonic-gate /* get the latest super block */ 1137c478bd9Sstevel@tonic-gate if (ioctl(fd, _FIOGETSUPERBLOCK, &sblock)) { 114355d6bb5Sswilcox errexit("fsck: ioctl _FIOGETSUPERBLOCK error: %s", 115355d6bb5Sswilcox strerror(errno)); 116355d6bb5Sswilcox /* NOTREACHED */ 1177c478bd9Sstevel@tonic-gate } 118355d6bb5Sswilcox (void) close(fd); 1197c478bd9Sstevel@tonic-gate } else { 120355d6bb5Sswilcox (void) fsck_bread(fsreadfd, (caddr_t)&sblock, 1217c478bd9Sstevel@tonic-gate bflag != 0 ? (diskaddr_t)bflag : (diskaddr_t)SBLOCK, 122355d6bb5Sswilcox SBSIZE); 1237c478bd9Sstevel@tonic-gate } 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate /* 126355d6bb5Sswilcox * Don't let trash from the disk trip us up later 127355d6bb5Sswilcox * in ungetsummaryinfo(). 128355d6bb5Sswilcox */ 129355d6bb5Sswilcox sblock.fs_u.fs_csp = NULL; 130355d6bb5Sswilcox 131355d6bb5Sswilcox /* 132355d6bb5Sswilcox * Rudimentary consistency checks. Can't really call 133355d6bb5Sswilcox * checksb() here, because there may be outstanding 134355d6bb5Sswilcox * deltas that still need to be applied. 1357c478bd9Sstevel@tonic-gate */ 1367c478bd9Sstevel@tonic-gate if ((sblock.fs_magic != FS_MAGIC) && 137355d6bb5Sswilcox (sblock.fs_magic != MTB_UFS_MAGIC)) { 138355d6bb5Sswilcox err = "MAGIC NUMBER WRONG"; 139355d6bb5Sswilcox goto fail; 1407c478bd9Sstevel@tonic-gate } 1416451fdbcSvsakar if (sblock.fs_magic == FS_MAGIC && 1426451fdbcSvsakar (sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 && 1436451fdbcSvsakar sblock.fs_version != UFS_VERSION_MIN)) { 1446451fdbcSvsakar err = "UNRECOGNIZED VERSION"; 1456451fdbcSvsakar goto fail; 1466451fdbcSvsakar } 1477c478bd9Sstevel@tonic-gate if (sblock.fs_magic == MTB_UFS_MAGIC && 1487c478bd9Sstevel@tonic-gate (sblock.fs_version > MTB_UFS_VERSION_1 || 1497c478bd9Sstevel@tonic-gate sblock.fs_version < MTB_UFS_VERSION_MIN)) { 150355d6bb5Sswilcox err = "UNRECOGNIZED VERSION"; 151355d6bb5Sswilcox goto fail; 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate if (sblock.fs_ncg < 1) { 154355d6bb5Sswilcox err = "NCG OUT OF RANGE"; 155355d6bb5Sswilcox goto fail; 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate if (sblock.fs_cpg < 1) { 158355d6bb5Sswilcox err = "CPG OUT OF RANGE"; 159355d6bb5Sswilcox goto fail; 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate if (sblock.fs_ncg * sblock.fs_cpg < sblock.fs_ncyl || 1627c478bd9Sstevel@tonic-gate (sblock.fs_ncg - 1) * sblock.fs_cpg >= sblock.fs_ncyl) { 163355d6bb5Sswilcox err = "NCYL IS INCONSISTENT WITH NCG*CPG"; 164355d6bb5Sswilcox goto fail; 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate if (sblock.fs_sbsize < 0 || sblock.fs_sbsize > SBSIZE) { 167355d6bb5Sswilcox err = "SIZE OUT OF RANGE"; 168355d6bb5Sswilcox goto fail; 1697c478bd9Sstevel@tonic-gate } 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate return (1); 172355d6bb5Sswilcox 173355d6bb5Sswilcox fail: 174355d6bb5Sswilcox badsb(listerr, err); 175355d6bb5Sswilcox return (0); 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 178355d6bb5Sswilcox static void 1797c478bd9Sstevel@tonic-gate flush_fs() 1807c478bd9Sstevel@tonic-gate { 1817c478bd9Sstevel@tonic-gate int fd; 1827c478bd9Sstevel@tonic-gate 183355d6bb5Sswilcox if (mount_point != NULL) { 1847c478bd9Sstevel@tonic-gate fd = open(mount_point, O_RDONLY); 1857c478bd9Sstevel@tonic-gate if (fd == -1) { 186355d6bb5Sswilcox errexit("fsck: open mount point error: %s", 187355d6bb5Sswilcox strerror(errno)); 188355d6bb5Sswilcox /* NOTREACHED */ 1897c478bd9Sstevel@tonic-gate } 1907c478bd9Sstevel@tonic-gate if (ioctl(fd, _FIOFFS, NULL)) { /* flush file system */ 191355d6bb5Sswilcox errexit("fsck: ioctl _FIOFFS error: %s", 192355d6bb5Sswilcox strerror(errno)); 193355d6bb5Sswilcox /* NOTREACHED */ 1947c478bd9Sstevel@tonic-gate } 195355d6bb5Sswilcox (void) close(fd); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate } 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate /* 2007c478bd9Sstevel@tonic-gate * Roll the embedded log, if any, and set up the global variables 201355d6bb5Sswilcox * islog and islogok. 2027c478bd9Sstevel@tonic-gate */ 2037c478bd9Sstevel@tonic-gate static int 204355d6bb5Sswilcox logsetup(caddr_t devstr) 2057c478bd9Sstevel@tonic-gate { 2067c478bd9Sstevel@tonic-gate void *buf; 2077c478bd9Sstevel@tonic-gate extent_block_t *ebp; 2087c478bd9Sstevel@tonic-gate ml_unit_t *ul; 2097c478bd9Sstevel@tonic-gate ml_odunit_t *ud; 2107c478bd9Sstevel@tonic-gate void *ud_buf; 2117c478bd9Sstevel@tonic-gate int badlog; 2127c478bd9Sstevel@tonic-gate 213355d6bb5Sswilcox islog = islogok = 0; 214355d6bb5Sswilcox if (bflag != 0) 2157c478bd9Sstevel@tonic-gate return (1); /* can't roll log while alternate sb specified */ 2167c478bd9Sstevel@tonic-gate 217355d6bb5Sswilcox /* 218355d6bb5Sswilcox * Roll the log, if any. A bad sb implies we'll be using 219355d6bb5Sswilcox * an alternate sb as far as logging goes, so just fail back 220355d6bb5Sswilcox * to the caller if we can't read the default sb. Suppress 221355d6bb5Sswilcox * complaints, because the caller will be reading the same 222355d6bb5Sswilcox * superblock again and running full verification on it, so 223355d6bb5Sswilcox * whatever is bad will be reported then. 224355d6bb5Sswilcox */ 2257c478bd9Sstevel@tonic-gate sblock.fs_logbno = 0; 2267c478bd9Sstevel@tonic-gate badlog = 0; 227355d6bb5Sswilcox if (!read_super_block(0)) 228355d6bb5Sswilcox return (1); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate /* 2317c478bd9Sstevel@tonic-gate * Roll the log in 3 cases: 2327c478bd9Sstevel@tonic-gate * 1. If it's unmounted (mount_point == NULL) and it's not marked 2337c478bd9Sstevel@tonic-gate * as fully rolled (sblock.fs_rolled != FS_ALL_ROLLED) 2347c478bd9Sstevel@tonic-gate * 2. If it's mounted and anything other than a sanity 2357c478bd9Sstevel@tonic-gate * check fsck (mflag) is being done, as we have the current 2367c478bd9Sstevel@tonic-gate * super block. Note, only a sanity check is done for 2377c478bd9Sstevel@tonic-gate * root/usr at boot. If a roll were done then the expensive 2387c478bd9Sstevel@tonic-gate * ufs_flush() gets called, leading to a slower boot. 2397c478bd9Sstevel@tonic-gate * 3. If anything other then a sanity check (mflag) is being done 2407c478bd9Sstevel@tonic-gate * to a mounted filesystem while it is in read-only state 2417c478bd9Sstevel@tonic-gate * (e.g. root during early boot stages) we have to detect this 2427c478bd9Sstevel@tonic-gate * and have to roll the log as well. NB. the read-only mount 2437c478bd9Sstevel@tonic-gate * will flip fs_clean from FSLOG to FSSTABLE and marks the 2447c478bd9Sstevel@tonic-gate * log as FS_NEED_ROLL. 2457c478bd9Sstevel@tonic-gate */ 2467c478bd9Sstevel@tonic-gate if (sblock.fs_logbno && 2477c478bd9Sstevel@tonic-gate (((mount_point == NULL) && (sblock.fs_rolled != FS_ALL_ROLLED)) || 248355d6bb5Sswilcox ((mount_point != NULL) && !mflag))) { 2497c478bd9Sstevel@tonic-gate int roll_log_err = 0; 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate if (sblock.fs_ronly && (sblock.fs_clean == FSSTABLE) && 2527c478bd9Sstevel@tonic-gate (sblock.fs_state + sblock.fs_time == FSOKAY)) { 2537c478bd9Sstevel@tonic-gate /* 2547c478bd9Sstevel@tonic-gate * roll the log without a mount 2557c478bd9Sstevel@tonic-gate */ 2567c478bd9Sstevel@tonic-gate flush_fs(); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate if (sblock.fs_clean == FSLOG && 2597c478bd9Sstevel@tonic-gate (sblock.fs_state + sblock.fs_time == FSOKAY)) { 2607c478bd9Sstevel@tonic-gate if (rl_roll_log(devstr) != RL_SUCCESS) 2617c478bd9Sstevel@tonic-gate roll_log_err = 1; 2627c478bd9Sstevel@tonic-gate } 2637c478bd9Sstevel@tonic-gate if (roll_log_err) { 2647c478bd9Sstevel@tonic-gate (void) printf("Can't roll the log for %s.\n", devstr); 2657c478bd9Sstevel@tonic-gate /* 2667c478bd9Sstevel@tonic-gate * There are two cases where we want to set 2677c478bd9Sstevel@tonic-gate * an error code and return: 2687c478bd9Sstevel@tonic-gate * - We're preening 2697c478bd9Sstevel@tonic-gate * - We're not on a live root and the user 2707c478bd9Sstevel@tonic-gate * chose *not* to ignore the log 2717c478bd9Sstevel@tonic-gate * Otherwise, we want to mark the log as bad 2727c478bd9Sstevel@tonic-gate * and continue to check the filesystem. This 2737c478bd9Sstevel@tonic-gate * has the side effect of destroying the log. 2747c478bd9Sstevel@tonic-gate */ 2757c478bd9Sstevel@tonic-gate if (preen || (!hotroot && 2767c478bd9Sstevel@tonic-gate reply( 2777c478bd9Sstevel@tonic-gate "DISCARDING THE LOG MAY DISCARD PENDING TRANSACTIONS.\n" 2787c478bd9Sstevel@tonic-gate "DISCARD THE LOG AND CONTINUE") == 0)) { 279355d6bb5Sswilcox exitstat = EXERRFATAL; 2807c478bd9Sstevel@tonic-gate return (0); 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate ++badlog; 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate } 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate /* Logging UFS may be enabled */ 2877c478bd9Sstevel@tonic-gate if (sblock.fs_logbno) { 2887c478bd9Sstevel@tonic-gate ++islog; 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* log is not okay; check the fs */ 2917c478bd9Sstevel@tonic-gate if (FSOKAY != (sblock.fs_state + sblock.fs_time)) 2927c478bd9Sstevel@tonic-gate return (1); 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate /* 2957c478bd9Sstevel@tonic-gate * If logging or (stable and mounted) then continue 2967c478bd9Sstevel@tonic-gate */ 297355d6bb5Sswilcox if (!((sblock.fs_clean == FSLOG) || 298355d6bb5Sswilcox (sblock.fs_clean == FSSTABLE) && (mount_point != NULL))) 2997c478bd9Sstevel@tonic-gate return (1); 3007c478bd9Sstevel@tonic-gate 3017c478bd9Sstevel@tonic-gate /* get the log allocation block */ 302355d6bb5Sswilcox buf = malloc(dev_bsize); 303355d6bb5Sswilcox if (buf == NULL) { 3047c478bd9Sstevel@tonic-gate return (1); 3057c478bd9Sstevel@tonic-gate } 306355d6bb5Sswilcox ud_buf = malloc(dev_bsize); 307355d6bb5Sswilcox if (ud_buf == NULL) { 3087c478bd9Sstevel@tonic-gate free(buf); 3097c478bd9Sstevel@tonic-gate return (1); 3107c478bd9Sstevel@tonic-gate } 311355d6bb5Sswilcox (void) fsck_bread(fsreadfd, buf, 3127c478bd9Sstevel@tonic-gate logbtodb(&sblock, sblock.fs_logbno), 313355d6bb5Sswilcox dev_bsize); 3147c478bd9Sstevel@tonic-gate ebp = (extent_block_t *)buf; 3157c478bd9Sstevel@tonic-gate 3167c478bd9Sstevel@tonic-gate /* log allocation block is not okay; check the fs */ 3177c478bd9Sstevel@tonic-gate if (ebp->type != LUFS_EXTENTS) { 3187c478bd9Sstevel@tonic-gate free(buf); 3197c478bd9Sstevel@tonic-gate free(ud_buf); 3207c478bd9Sstevel@tonic-gate return (1); 3217c478bd9Sstevel@tonic-gate } 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate /* get the log state block(s) */ 324355d6bb5Sswilcox if (fsck_bread(fsreadfd, ud_buf, 3257c478bd9Sstevel@tonic-gate (logbtodb(&sblock, ebp->extents[0].pbno)), 326355d6bb5Sswilcox dev_bsize)) { 327355d6bb5Sswilcox (void) fsck_bread(fsreadfd, ud_buf, 328355d6bb5Sswilcox (logbtodb(&sblock, ebp->extents[0].pbno)) + 1, 329355d6bb5Sswilcox dev_bsize); 3307c478bd9Sstevel@tonic-gate } 3317c478bd9Sstevel@tonic-gate ud = (ml_odunit_t *)ud_buf; 3327c478bd9Sstevel@tonic-gate ul = (ml_unit_t *)malloc(sizeof (*ul)); 3337c478bd9Sstevel@tonic-gate if (ul == NULL) { 3347c478bd9Sstevel@tonic-gate free(buf); 3357c478bd9Sstevel@tonic-gate free(ud_buf); 3367c478bd9Sstevel@tonic-gate return (1); 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate ul->un_ondisk = *ud; 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate /* log state is okay; don't need to check the fs */ 3417c478bd9Sstevel@tonic-gate if ((ul->un_chksum == ul->un_head_ident + ul->un_tail_ident) && 3427c478bd9Sstevel@tonic-gate (ul->un_version == LUFS_VERSION_LATEST) && 343355d6bb5Sswilcox (ul->un_badlog == 0) && (!badlog)) { 3447c478bd9Sstevel@tonic-gate ++islogok; 345355d6bb5Sswilcox } 3467c478bd9Sstevel@tonic-gate free(ud_buf); 3477c478bd9Sstevel@tonic-gate free(buf); 3487c478bd9Sstevel@tonic-gate free(ul); 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate 3517c478bd9Sstevel@tonic-gate return (1); 3527c478bd9Sstevel@tonic-gate } 3537c478bd9Sstevel@tonic-gate 354355d6bb5Sswilcox /* 355355d6bb5Sswilcox * - given a pathname, determine the pathname to actually check 356355d6bb5Sswilcox * - if a directory 357355d6bb5Sswilcox * - if it is in mnttab, set devstr to the special (block) name 358355d6bb5Sswilcox * - if it is in vfstab, set devstr to the special (block) name 359355d6bb5Sswilcox * - if it has not been found, bail 360355d6bb5Sswilcox * - a file is used as-is, clear rflag 361355d6bb5Sswilcox * - a device is converted to block version (so can search mnttab) 362355d6bb5Sswilcox */ 363355d6bb5Sswilcox static void 364355d6bb5Sswilcox derive_devstr(const caddr_t dev, caddr_t devstr, size_t str_size) 3657c478bd9Sstevel@tonic-gate { 366355d6bb5Sswilcox mode_t mode; 367355d6bb5Sswilcox struct stat statb; 3687c478bd9Sstevel@tonic-gate 369355d6bb5Sswilcox if (stat(dev, &statb) < 0) { 370355d6bb5Sswilcox exitstat = EXNOSTAT; 371355d6bb5Sswilcox errexit("fsck: could not stat %s: %s", dev, strerror(errno)); 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate 374355d6bb5Sswilcox mode = statb.st_mode & S_IFMT; 375355d6bb5Sswilcox switch (mode) { 376355d6bb5Sswilcox case S_IFDIR: 3777c478bd9Sstevel@tonic-gate /* 378355d6bb5Sswilcox * The check_*() routines update devstr with the name. 3797c478bd9Sstevel@tonic-gate */ 380355d6bb5Sswilcox devstr[0] = '\0'; 381355d6bb5Sswilcox if (!(check_mnttab(dev, devstr, str_size) || 382355d6bb5Sswilcox check_vfstab(dev, devstr, str_size))) { 383355d6bb5Sswilcox exitstat = EXBADPARM; 384355d6bb5Sswilcox errexit( 385355d6bb5Sswilcox "fsck: could not find mountpoint %s in mnttab nor vfstab", 386355d6bb5Sswilcox dev); 3877c478bd9Sstevel@tonic-gate } 388355d6bb5Sswilcox break; 389355d6bb5Sswilcox case S_IFREG: 3907c478bd9Sstevel@tonic-gate rflag = 0; 391355d6bb5Sswilcox break; 392355d6bb5Sswilcox case S_IFCHR: 393355d6bb5Sswilcox case S_IFBLK: 394355d6bb5Sswilcox (void) strlcpy(devstr, unrawname(dev), str_size); 395355d6bb5Sswilcox break; 396355d6bb5Sswilcox default: 397355d6bb5Sswilcox exitstat = EXBADPARM; 398355d6bb5Sswilcox errexit("fsck: %s must be a mountpoint, device, or file", dev); 399355d6bb5Sswilcox /* NOTREACHED */ 4007c478bd9Sstevel@tonic-gate } 401355d6bb5Sswilcox } 4027c478bd9Sstevel@tonic-gate 403355d6bb5Sswilcox /* 404355d6bb5Sswilcox * Reports the index of the magic filesystem that mntp names. 405355d6bb5Sswilcox * If it does not correspond any of them, returns zero (hence 406355d6bb5Sswilcox * the backwards loop). 407355d6bb5Sswilcox */ 408355d6bb5Sswilcox static int 409355d6bb5Sswilcox which_corefs(const caddr_t mntp) 410355d6bb5Sswilcox { 411355d6bb5Sswilcox int corefs; 412355d6bb5Sswilcox 413355d6bb5Sswilcox for (corefs = MAGIC_LIMIT - 1; corefs > 0; corefs--) 414355d6bb5Sswilcox if (strcmp(mntp, magic_fs[corefs]) == 0) 415355d6bb5Sswilcox break; 416355d6bb5Sswilcox 417355d6bb5Sswilcox return (corefs); 418355d6bb5Sswilcox } 419355d6bb5Sswilcox 420355d6bb5Sswilcox /* 421355d6bb5Sswilcox * - set mount_point to NULL 422355d6bb5Sswilcox * - if name is mounted (search mnttab) 423355d6bb5Sswilcox * - if it is a device, clear rflag 424355d6bb5Sswilcox * - if mounted on /, /usr, or /var, set corefs 425355d6bb5Sswilcox * - if corefs and read-only, set hotroot and continue 426355d6bb5Sswilcox * - if errorlocked, continue 427355d6bb5Sswilcox * - if preening, bail 428355d6bb5Sswilcox * - ask user whether to continue, bail if not 429355d6bb5Sswilcox * - if it is a device and not mounted and rflag, convert 430355d6bb5Sswilcox * name to raw version 431355d6bb5Sswilcox */ 432355d6bb5Sswilcox static int 433355d6bb5Sswilcox check_mount_state(caddr_t devstr, size_t str_size) 434355d6bb5Sswilcox { 435355d6bb5Sswilcox int corefs = 0; 436355d6bb5Sswilcox int is_dev = 0; 437355d6bb5Sswilcox struct stat statb; 438355d6bb5Sswilcox 439355d6bb5Sswilcox if (stat(devstr, &statb) < 0) { 440355d6bb5Sswilcox exitstat = EXNOSTAT; 441355d6bb5Sswilcox errexit("fsck: could not stat %s: %s", devstr, strerror(errno)); 4427c478bd9Sstevel@tonic-gate } 443355d6bb5Sswilcox if (S_ISCHR(statb.st_mode) || S_ISBLK(statb.st_mode)) 444355d6bb5Sswilcox is_dev = 1; 445355d6bb5Sswilcox 446355d6bb5Sswilcox /* 447355d6bb5Sswilcox * mounted() will update mount_point when returning true. 448355d6bb5Sswilcox */ 449355d6bb5Sswilcox mount_point = NULL; 450355d6bb5Sswilcox if ((mountedfs = mounted(devstr, devstr, str_size)) != M_NOMNT) { 451355d6bb5Sswilcox if (is_dev) 452355d6bb5Sswilcox rflag = 0; 453355d6bb5Sswilcox corefs = which_corefs(mount_point); 454355d6bb5Sswilcox if (corefs && (mountedfs == M_RO)) { 455355d6bb5Sswilcox hotroot++; 456355d6bb5Sswilcox } else if (errorlocked) { 457355d6bb5Sswilcox goto carry_on; 458355d6bb5Sswilcox } else if (preen) { 459355d6bb5Sswilcox exitstat = EXMOUNTED; 460355d6bb5Sswilcox pfatal("%s IS CURRENTLY MOUNTED%s.", 461355d6bb5Sswilcox devstr, mountedfs == M_RW ? " READ/WRITE" : ""); 462355d6bb5Sswilcox } else { 463*504a199eSswilcox if (!nflag) { 464*504a199eSswilcox pwarn("%s IS CURRENTLY MOUNTED READ/%s.", 465*504a199eSswilcox devstr, mountedfs == M_RW ? "WRITE" : 466*504a199eSswilcox "ONLY"); 467*504a199eSswilcox if (reply("CONTINUE") == 0) { 468*504a199eSswilcox exitstat = EXMOUNTED; 469*504a199eSswilcox errexit("Program terminated"); 470*504a199eSswilcox } 471*504a199eSswilcox } else { 472*504a199eSswilcox pwarn("%s IS CURRENTLY MOUNTED READ/%s.\n", 473*504a199eSswilcox devstr, mountedfs == M_RW ? "WRITE" : 474*504a199eSswilcox "ONLY"); 475355d6bb5Sswilcox } 4767c478bd9Sstevel@tonic-gate } 477355d6bb5Sswilcox } else if (is_dev && rflag) { 478355d6bb5Sswilcox (void) strlcpy(devstr, rawname(devstr), str_size); 4797c478bd9Sstevel@tonic-gate } 480355d6bb5Sswilcox 481355d6bb5Sswilcox carry_on: 482355d6bb5Sswilcox return (corefs); 483355d6bb5Sswilcox } 484355d6bb5Sswilcox 485355d6bb5Sswilcox static int 486355d6bb5Sswilcox open_and_intro(caddr_t devstr, int corefs) 487355d6bb5Sswilcox { 488355d6bb5Sswilcox int retval = 0; 489355d6bb5Sswilcox 4907c478bd9Sstevel@tonic-gate if ((fsreadfd = open64(devstr, O_RDONLY)) < 0) { 491355d6bb5Sswilcox (void) printf("Can't open %s: %s\n", devstr, strerror(errno)); 492355d6bb5Sswilcox exitstat = EXNOSTAT; 493355d6bb5Sswilcox retval = -1; 494355d6bb5Sswilcox goto finish; 4957c478bd9Sstevel@tonic-gate } 496355d6bb5Sswilcox if (!preen || debug != 0) 497355d6bb5Sswilcox (void) printf("** %s", devstr); 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate if (errorlocked) { 500355d6bb5Sswilcox if (debug && elock_combuf != NULL) 501355d6bb5Sswilcox (void) printf(" error-lock comment: \"%s\" ", 502355d6bb5Sswilcox elock_combuf); 5037c478bd9Sstevel@tonic-gate fflag = 1; 5047c478bd9Sstevel@tonic-gate } 5057c478bd9Sstevel@tonic-gate pid = getpid(); 506355d6bb5Sswilcox if (nflag || roflag || (fswritefd = open64(devstr, O_WRONLY)) < 0) { 5077c478bd9Sstevel@tonic-gate fswritefd = -1; 5087c478bd9Sstevel@tonic-gate if (preen && !debug) 5097c478bd9Sstevel@tonic-gate pfatal("(NO WRITE ACCESS)\n"); 510355d6bb5Sswilcox (void) printf(" (NO WRITE)"); 5117c478bd9Sstevel@tonic-gate } 512355d6bb5Sswilcox if (!preen) 513355d6bb5Sswilcox (void) printf("\n"); 5147c478bd9Sstevel@tonic-gate else if (debug) 515355d6bb5Sswilcox (void) printf(" pid %d\n", pid); 516355d6bb5Sswilcox if (debug && (hotroot || (mountedfs != M_NOMNT))) { 517355d6bb5Sswilcox (void) printf("** %s", devstr); 5187c478bd9Sstevel@tonic-gate if (hotroot) 519355d6bb5Sswilcox (void) printf(" is %s fs", magic_fs[corefs]); 520355d6bb5Sswilcox if (mountedfs != M_NOMNT) 521355d6bb5Sswilcox (void) printf(" and is mounted read-%s", 522355d6bb5Sswilcox (mountedfs == M_RO) ? "only" : "write"); 5237c478bd9Sstevel@tonic-gate if (errorlocked) 524355d6bb5Sswilcox (void) printf(" and is error-locked"); 5257c478bd9Sstevel@tonic-gate 526355d6bb5Sswilcox (void) printf(".\n"); 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate 529355d6bb5Sswilcox finish: 530355d6bb5Sswilcox return (retval); 531355d6bb5Sswilcox } 5327c478bd9Sstevel@tonic-gate 533355d6bb5Sswilcox static int 534355d6bb5Sswilcox find_superblock(caddr_t devstr) 535355d6bb5Sswilcox { 536355d6bb5Sswilcox int cg = 0; 537355d6bb5Sswilcox int retval = 0; 538355d6bb5Sswilcox int first; 539355d6bb5Sswilcox int found; 540355d6bb5Sswilcox calcsb_t style; 541355d6bb5Sswilcox struct fs proto; 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate /* 544355d6bb5Sswilcox * Check the superblock, looking for alternates if necessary. 545355d6bb5Sswilcox * In more-recent times, some UFS instances get created with 546355d6bb5Sswilcox * only the first ten and last ten superblock backups. Since 547355d6bb5Sswilcox * if we can't get the necessary information from any of those, 548355d6bb5Sswilcox * the odds are also against us for the ones in between, we'll 549355d6bb5Sswilcox * just look at those twenty to save time. 5507c478bd9Sstevel@tonic-gate */ 551355d6bb5Sswilcox if (!read_super_block(1) || !checksb(1)) { 552355d6bb5Sswilcox if (bflag || preen) { 553355d6bb5Sswilcox retval = -1; 554355d6bb5Sswilcox goto finish; 555355d6bb5Sswilcox } 556355d6bb5Sswilcox for (style = MKFS_STYLE; style < MAX_SB_STYLES; style++) { 557355d6bb5Sswilcox if (reply("LOOK FOR ALTERNATE SUPERBLOCKS WITH %s", 558355d6bb5Sswilcox calcsb_names[style]) == 0) 559355d6bb5Sswilcox continue; 560355d6bb5Sswilcox first = 1; 561355d6bb5Sswilcox found = 0; 562355d6bb5Sswilcox if (!calcsb(style, devstr, fsreadfd, &proto)) { 563355d6bb5Sswilcox cg = proto.fs_ncg; 564355d6bb5Sswilcox continue; 565355d6bb5Sswilcox } 566355d6bb5Sswilcox if (debug) { 567355d6bb5Sswilcox (void) printf( 568355d6bb5Sswilcox "debug: calcsb(%s) gave fpg %d, cgoffset %d, ", 569355d6bb5Sswilcox calcsb_names[style], 570355d6bb5Sswilcox proto.fs_fpg, proto.fs_cgoffset); 571355d6bb5Sswilcox (void) printf("cgmask 0x%x, sblk %d, ncg %d\n", 572355d6bb5Sswilcox proto.fs_cgmask, proto.fs_sblkno, 573355d6bb5Sswilcox proto.fs_ncg); 574355d6bb5Sswilcox } 575355d6bb5Sswilcox for (cg = 0; cg < proto.fs_ncg; cg++) { 576355d6bb5Sswilcox bflag = fsbtodb(&proto, cgsblock(&proto, cg)); 577355d6bb5Sswilcox if (debug) 578355d6bb5Sswilcox (void) printf( 579355d6bb5Sswilcox "debug: trying block %lld\n", 580355d6bb5Sswilcox (longlong_t)bflag); 581355d6bb5Sswilcox if (read_super_block(0) && checksb(0)) { 582355d6bb5Sswilcox (void) printf( 583355d6bb5Sswilcox "FOUND ALTERNATE SUPERBLOCK %d WITH %s\n", 584355d6bb5Sswilcox bflag, calcsb_names[style]); 585355d6bb5Sswilcox if (reply( 586355d6bb5Sswilcox "USE ALTERNATE SUPERBLOCK") == 1) { 587355d6bb5Sswilcox found = 1; 588355d6bb5Sswilcox break; 589355d6bb5Sswilcox } 590355d6bb5Sswilcox } 591355d6bb5Sswilcox if (first && (cg >= 9)) { 592355d6bb5Sswilcox first = 0; 593355d6bb5Sswilcox if (proto.fs_ncg <= 9) 594355d6bb5Sswilcox cg = proto.fs_ncg; 595355d6bb5Sswilcox else if (proto.fs_ncg <= 19) 596355d6bb5Sswilcox cg = 9; 597355d6bb5Sswilcox else 598355d6bb5Sswilcox cg = proto.fs_ncg - 10; 599355d6bb5Sswilcox } 600355d6bb5Sswilcox } 601355d6bb5Sswilcox 602355d6bb5Sswilcox if (found) 603355d6bb5Sswilcox break; 604355d6bb5Sswilcox } 605355d6bb5Sswilcox 606355d6bb5Sswilcox /* 607355d6bb5Sswilcox * Didn't find one? Try to fake it. 608355d6bb5Sswilcox */ 609355d6bb5Sswilcox if (style >= MAX_SB_STYLES) { 610355d6bb5Sswilcox pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED.\n"); 611355d6bb5Sswilcox for (style = MKFS_STYLE; style < MAX_SB_STYLES; 612355d6bb5Sswilcox style++) { 613355d6bb5Sswilcox if (reply("USE GENERIC SUPERBLOCK FROM %s", 614355d6bb5Sswilcox calcsb_names[style]) == 1 && 615355d6bb5Sswilcox calcsb(style, devstr, fsreadfd, &sblock)) { 616355d6bb5Sswilcox break; 617355d6bb5Sswilcox } 618355d6bb5Sswilcox /* 619355d6bb5Sswilcox * We got something from mkfs/newfs, so use it. 620355d6bb5Sswilcox */ 621355d6bb5Sswilcox if (style < MAX_SB_STYLES) 622355d6bb5Sswilcox proto.fs_ncg = sblock.fs_ncg; 623355d6bb5Sswilcox bflag = 0; 624355d6bb5Sswilcox } 625355d6bb5Sswilcox } 626355d6bb5Sswilcox 627355d6bb5Sswilcox /* 628355d6bb5Sswilcox * Still no luck? Tell the user they're on their own. 629355d6bb5Sswilcox */ 630355d6bb5Sswilcox if (style >= MAX_SB_STYLES) { 631355d6bb5Sswilcox pwarn("SEARCH FOR ALTERNATE SUPERBLOCKS FAILED. " 632355d6bb5Sswilcox "YOU MUST USE THE -o b OPTION\n" 633355d6bb5Sswilcox "TO FSCK TO SPECIFY THE LOCATION OF A VALID " 634355d6bb5Sswilcox "ALTERNATE SUPERBLOCK TO\n" 635355d6bb5Sswilcox "SUPPLY NEEDED INFORMATION; SEE fsck(1M).\n"); 636355d6bb5Sswilcox bflag = 0; 637355d6bb5Sswilcox retval = -1; 638355d6bb5Sswilcox goto finish; 639355d6bb5Sswilcox } 640355d6bb5Sswilcox 641355d6bb5Sswilcox /* 642355d6bb5Sswilcox * Need to make sure a human really wants us to use 643355d6bb5Sswilcox * this. -y mode could've gotten us this far, so 644355d6bb5Sswilcox * we need to ask something that has to be answered 645355d6bb5Sswilcox * in the negative. 646355d6bb5Sswilcox * 647355d6bb5Sswilcox * Note that we can't get here when preening. 648355d6bb5Sswilcox */ 649355d6bb5Sswilcox if (!found) { 650355d6bb5Sswilcox pwarn("CALCULATED GENERIC SUPERBLOCK WITH %s\n", 651355d6bb5Sswilcox calcsb_names[style]); 652355d6bb5Sswilcox } else { 653355d6bb5Sswilcox pwarn("FOUND ALTERNATE SUPERBLOCK AT %d USING %s\n", 654355d6bb5Sswilcox bflag, calcsb_names[style]); 655355d6bb5Sswilcox } 656355d6bb5Sswilcox pwarn("If filesystem was created with manually-specified "); 657355d6bb5Sswilcox pwarn("geometry, using\nauto-discovered superblock may "); 658355d6bb5Sswilcox pwarn("result in irrecoverable damage to\nfilesystem and "); 659355d6bb5Sswilcox pwarn("user data.\n"); 660355d6bb5Sswilcox if (reply("CANCEL FILESYSTEM CHECK") == 1) { 661355d6bb5Sswilcox if (cg >= 0) { 662355d6bb5Sswilcox pwarn("Please verify that the indicated block " 663355d6bb5Sswilcox "contains a proper\nsuperblock for the " 664355d6bb5Sswilcox "filesystem (see fsdb(1M)).\n"); 665355d6bb5Sswilcox if (yflag) 666355d6bb5Sswilcox pwarn("\nFSCK was running in YES " 667355d6bb5Sswilcox "mode. If you wish to run in " 668355d6bb5Sswilcox "that mode using\nthe alternate " 669355d6bb5Sswilcox "superblock, run " 670355d6bb5Sswilcox "`fsck -y -o b=%d %s'.\n", 671355d6bb5Sswilcox bflag, devstr); 672355d6bb5Sswilcox } 673355d6bb5Sswilcox retval = -1; 674355d6bb5Sswilcox goto finish; 675355d6bb5Sswilcox } 676355d6bb5Sswilcox 677355d6bb5Sswilcox /* 678355d6bb5Sswilcox * Pretend we found it as an alternate, so everything 679355d6bb5Sswilcox * gets updated when we clean up at the end. 680355d6bb5Sswilcox */ 681355d6bb5Sswilcox if (!found) { 682355d6bb5Sswilcox havesb = 1; 683355d6bb5Sswilcox sblk.b_bno = fsbtodb(&sblock, cgsblock(&sblock, 0)); 684355d6bb5Sswilcox bwrite(fswritefd, (caddr_t)&sblock, SBLOCK, SBSIZE); 685355d6bb5Sswilcox write_altsb(fswritefd); 686355d6bb5Sswilcox } 687355d6bb5Sswilcox } 688355d6bb5Sswilcox 689355d6bb5Sswilcox finish: 690355d6bb5Sswilcox return (retval); 691355d6bb5Sswilcox } 692355d6bb5Sswilcox 693355d6bb5Sswilcox /* 694355d6bb5Sswilcox * Check and potentially fix certain fields in the super block. 695355d6bb5Sswilcox */ 696355d6bb5Sswilcox static void 697355d6bb5Sswilcox fixup_superblock(void) 698355d6bb5Sswilcox { 6997c478bd9Sstevel@tonic-gate /* 700355d6bb5Sswilcox * Kernel looks for FS_OPTTIME, and assumes that if that's not 701355d6bb5Sswilcox * what's there, it must be FS_OPTSPACE, so not fixing does not 702355d6bb5Sswilcox * require setting iscorrupt. 7037c478bd9Sstevel@tonic-gate */ 7047c478bd9Sstevel@tonic-gate if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { 7057c478bd9Sstevel@tonic-gate pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); 7067c478bd9Sstevel@tonic-gate if (reply("SET TO DEFAULT") == 1) { 7077c478bd9Sstevel@tonic-gate sblock.fs_optim = FS_OPTTIME; 7087c478bd9Sstevel@tonic-gate sbdirty(); 7097c478bd9Sstevel@tonic-gate } 7107c478bd9Sstevel@tonic-gate } 7117c478bd9Sstevel@tonic-gate if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { 7127c478bd9Sstevel@tonic-gate pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", 713355d6bb5Sswilcox sblock.fs_minfree); 7147c478bd9Sstevel@tonic-gate if (reply("SET TO DEFAULT") == 1) { 7157c478bd9Sstevel@tonic-gate sblock.fs_minfree = 10; 7167c478bd9Sstevel@tonic-gate sbdirty(); 717355d6bb5Sswilcox } else if (sblock.fs_minfree < 0) { 7187c478bd9Sstevel@tonic-gate /* 719355d6bb5Sswilcox * Kernel uses minfree without verification, 720355d6bb5Sswilcox * and a negative value would do bad things. 7217c478bd9Sstevel@tonic-gate */ 722355d6bb5Sswilcox iscorrupt = 1; 7237c478bd9Sstevel@tonic-gate } 7247c478bd9Sstevel@tonic-gate } 725355d6bb5Sswilcox } 726355d6bb5Sswilcox 727355d6bb5Sswilcox static int 728355d6bb5Sswilcox initial_error_state_adjust(void) 729355d6bb5Sswilcox { 730355d6bb5Sswilcox int retval = 0; 731355d6bb5Sswilcox 732355d6bb5Sswilcox /* do this right away to prevent any other fscks on this fs */ 733355d6bb5Sswilcox switch (sblock.fs_clean) { 734355d6bb5Sswilcox case FSBAD: 735355d6bb5Sswilcox break; 736355d6bb5Sswilcox case FSFIX: 737355d6bb5Sswilcox if (preen) 738355d6bb5Sswilcox errexit("ERROR-LOCKED; MARKED \"FSFIX\"\n"); 739355d6bb5Sswilcox if (reply("marked FSFIX, CONTINUE") == 0) { 740355d6bb5Sswilcox retval = -1; 741355d6bb5Sswilcox goto finish; 742355d6bb5Sswilcox } 743355d6bb5Sswilcox break; 744355d6bb5Sswilcox case FSCLEAN: 745355d6bb5Sswilcox if (preen) 746355d6bb5Sswilcox errexit("ERROR-LOCKED; MARKED \"FSCLEAN\"\n"); 747355d6bb5Sswilcox if (reply("marked FSCLEAN, CONTINUE") == 0) { 748355d6bb5Sswilcox retval = -1; 749355d6bb5Sswilcox goto finish; 750355d6bb5Sswilcox } 751355d6bb5Sswilcox break; 752355d6bb5Sswilcox default: 753355d6bb5Sswilcox if (preen) { 754355d6bb5Sswilcox if (debug) 7557c478bd9Sstevel@tonic-gate pwarn("ERRORLOCKED; NOT MARKED \"FSBAD\"\n"); 756355d6bb5Sswilcox else 7577c478bd9Sstevel@tonic-gate errexit("ERRORLOCKED; NOT MARKED \"FSBAD\"\n"); 758355d6bb5Sswilcox } else { 759355d6bb5Sswilcox (void) printf("error-locked but not marked \"FSBAD\";"); 760355d6bb5Sswilcox if (reply(" CONTINUE") == 0) { 761355d6bb5Sswilcox retval = -1; 762355d6bb5Sswilcox goto finish; 7637c478bd9Sstevel@tonic-gate } 7647c478bd9Sstevel@tonic-gate } 765355d6bb5Sswilcox break; 766355d6bb5Sswilcox } 7677c478bd9Sstevel@tonic-gate 768355d6bb5Sswilcox if (!do_errorlock(LOCKFS_ELOCK)) { 769355d6bb5Sswilcox if (preen) { 770355d6bb5Sswilcox retval = -1; 771355d6bb5Sswilcox goto finish; 772355d6bb5Sswilcox } 773355d6bb5Sswilcox if (reply("error-lock reset failed; CONTINUE") == 0) { 774355d6bb5Sswilcox retval = -1; 775355d6bb5Sswilcox goto finish; 7767c478bd9Sstevel@tonic-gate } 7777c478bd9Sstevel@tonic-gate } 778355d6bb5Sswilcox 779355d6bb5Sswilcox sblock.fs_state = FSOKAY - (long)sblock.fs_time; 780355d6bb5Sswilcox sblock.fs_clean = FSFIX; 781355d6bb5Sswilcox sbdirty(); 782355d6bb5Sswilcox write_altsb(fswritefd); 783355d6bb5Sswilcox 784355d6bb5Sswilcox finish: 785355d6bb5Sswilcox return (retval); 786355d6bb5Sswilcox } 787355d6bb5Sswilcox 788355d6bb5Sswilcox static void 789355d6bb5Sswilcox getsummaryinfo(void) 790355d6bb5Sswilcox { 791355d6bb5Sswilcox size_t size; 792355d6bb5Sswilcox int failed; 793355d6bb5Sswilcox int asked; 794355d6bb5Sswilcox int i, j; 795355d6bb5Sswilcox caddr_t sip; 796355d6bb5Sswilcox 7977c478bd9Sstevel@tonic-gate /* 7987c478bd9Sstevel@tonic-gate * read in the summary info. 7997c478bd9Sstevel@tonic-gate */ 800355d6bb5Sswilcox sblock.fs_u.fs_csp = calloc(1, sblock.fs_cssize); 801355d6bb5Sswilcox if (sblock.fs_u.fs_csp == NULL) 802355d6bb5Sswilcox errexit( 803355d6bb5Sswilcox "cannot allocate %u bytes for cylinder group summary info\n", 804355d6bb5Sswilcox (unsigned)sblock.fs_cssize); 805355d6bb5Sswilcox sip = (caddr_t)sblock.fs_u.fs_csp; 806355d6bb5Sswilcox asked = 0; 8077c478bd9Sstevel@tonic-gate for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 8087c478bd9Sstevel@tonic-gate size = sblock.fs_cssize - i < sblock.fs_bsize ? 809355d6bb5Sswilcox sblock.fs_cssize - i : sblock.fs_bsize; 810355d6bb5Sswilcox failed = fsck_bread(fsreadfd, sip, 8117c478bd9Sstevel@tonic-gate fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 812355d6bb5Sswilcox size); 813355d6bb5Sswilcox if (failed && !asked) { 814355d6bb5Sswilcox pfatal("BAD SUMMARY INFORMATION"); 815355d6bb5Sswilcox if (reply("CONTINUE") == 0) { 816355d6bb5Sswilcox ckfini(); 817355d6bb5Sswilcox exit(EXFNDERRS); 818355d6bb5Sswilcox } 819355d6bb5Sswilcox asked = 1; 820355d6bb5Sswilcox } 8217c478bd9Sstevel@tonic-gate sip += size; 8227c478bd9Sstevel@tonic-gate } 823355d6bb5Sswilcox } 824355d6bb5Sswilcox 825355d6bb5Sswilcox /* 826355d6bb5Sswilcox * Reverses the effects of getsummaryinfo(). 827355d6bb5Sswilcox */ 828355d6bb5Sswilcox static void 829355d6bb5Sswilcox ungetsummaryinfo(void) 830355d6bb5Sswilcox { 831355d6bb5Sswilcox if ((sblk.b_un.b_fs != NULL) && 832355d6bb5Sswilcox (sblk.b_un.b_fs->fs_u.fs_csp != NULL)) { 833355d6bb5Sswilcox free(sblk.b_un.b_fs->fs_u.fs_csp); 834355d6bb5Sswilcox sblk.b_un.b_fs->fs_u.fs_csp = NULL; 8357c478bd9Sstevel@tonic-gate } 836355d6bb5Sswilcox } 837355d6bb5Sswilcox 838355d6bb5Sswilcox /* 839355d6bb5Sswilcox * Allocate and initialize the global tables. 840355d6bb5Sswilcox * It is the responsibility of the caller to clean up and allocations 841355d6bb5Sswilcox * if an error is returned. 842355d6bb5Sswilcox */ 843355d6bb5Sswilcox static int 844355d6bb5Sswilcox create_and_init_maps(void) 845355d6bb5Sswilcox { 846355d6bb5Sswilcox int64_t bmapsize; 847355d6bb5Sswilcox int retval = 0; 848355d6bb5Sswilcox 849355d6bb5Sswilcox maxfsblock = sblock.fs_size; 850355d6bb5Sswilcox maxino = sblock.fs_ncg * sblock.fs_ipg; 851355d6bb5Sswilcox 8527c478bd9Sstevel@tonic-gate bmapsize = roundup(howmany((uint64_t)maxfsblock, NBBY), 8537c478bd9Sstevel@tonic-gate sizeof (short)); 8547c478bd9Sstevel@tonic-gate blockmap = calloc((size_t)bmapsize, sizeof (char)); 8557c478bd9Sstevel@tonic-gate if (blockmap == NULL) { 856355d6bb5Sswilcox (void) printf("cannot alloc %lld bytes for blockmap\n", 857355d6bb5Sswilcox (longlong_t)bmapsize); 858355d6bb5Sswilcox retval = -1; 859355d6bb5Sswilcox goto finish; 8607c478bd9Sstevel@tonic-gate } 861355d6bb5Sswilcox statemap = calloc((size_t)(maxino + 1), sizeof (*statemap)); 8627c478bd9Sstevel@tonic-gate if (statemap == NULL) { 863355d6bb5Sswilcox (void) printf("cannot alloc %lld bytes for statemap\n", 864355d6bb5Sswilcox (longlong_t)(maxino + 1) * sizeof (*statemap)); 865355d6bb5Sswilcox retval = -1; 866355d6bb5Sswilcox goto finish; 8677c478bd9Sstevel@tonic-gate } 868355d6bb5Sswilcox lncntp = (short *)calloc((size_t)(maxino + 1), sizeof (short)); 8697c478bd9Sstevel@tonic-gate if (lncntp == NULL) { 870355d6bb5Sswilcox (void) printf("cannot alloc %lld bytes for lncntp\n", 871355d6bb5Sswilcox (longlong_t)(maxino + 1) * sizeof (short)); 872355d6bb5Sswilcox retval = -1; 873355d6bb5Sswilcox goto finish; 8747c478bd9Sstevel@tonic-gate } 875355d6bb5Sswilcox 876355d6bb5Sswilcox /* 877355d6bb5Sswilcox * If we had to fake up a superblock, it won't show that there 878355d6bb5Sswilcox * are any directories at all. This causes problems when we 879355d6bb5Sswilcox * use numdirs to calculate hash keys, so use something at least 880355d6bb5Sswilcox * vaguely plausible. 881355d6bb5Sswilcox */ 8827c478bd9Sstevel@tonic-gate numdirs = sblock.fs_cstotal.cs_ndir; 883355d6bb5Sswilcox if (numdirs == 0) 884355d6bb5Sswilcox numdirs = sblock.fs_ipg * sblock.fs_ncg / 2; 8857c478bd9Sstevel@tonic-gate listmax = numdirs + 10; 8867c478bd9Sstevel@tonic-gate inpsort = (struct inoinfo **)calloc((unsigned)listmax, 8877c478bd9Sstevel@tonic-gate sizeof (struct inoinfo *)); 8887c478bd9Sstevel@tonic-gate inphead = (struct inoinfo **)calloc((unsigned)numdirs, 8897c478bd9Sstevel@tonic-gate sizeof (struct inoinfo *)); 8907c478bd9Sstevel@tonic-gate if (inpsort == NULL || inphead == NULL) { 891355d6bb5Sswilcox (void) printf("cannot alloc %lld bytes for inphead\n", 892355d6bb5Sswilcox (longlong_t)numdirs * sizeof (struct inoinfo *)); 893355d6bb5Sswilcox retval = -1; 894355d6bb5Sswilcox goto finish; 895355d6bb5Sswilcox } 896355d6bb5Sswilcox if (debug) { 897d1a180b0Smaheshvs if (listmax > ULONG_MAX) 898355d6bb5Sswilcox errexit("create_and_init_maps: listmax overflowed\n"); 899d1a180b0Smaheshvs if (numdirs > ULONG_MAX) 900355d6bb5Sswilcox errexit("create_and_init_maps: numdirs overflowed\n"); 9017c478bd9Sstevel@tonic-gate } 902355d6bb5Sswilcox 9037c478bd9Sstevel@tonic-gate numacls = numdirs; 9047c478bd9Sstevel@tonic-gate aclmax = numdirs + 10; 905355d6bb5Sswilcox aclpsort = (struct inoinfo **)calloc((unsigned)aclmax, 906355d6bb5Sswilcox sizeof (struct inoinfo *)); 907355d6bb5Sswilcox aclphead = (struct inoinfo **)calloc((unsigned)numacls, 908355d6bb5Sswilcox sizeof (struct inoinfo *)); 9097c478bd9Sstevel@tonic-gate if (aclpsort == NULL || aclphead == NULL) { 910355d6bb5Sswilcox (void) printf("cannot alloc %lld bytes for aclphead\n", 911355d6bb5Sswilcox (longlong_t)numacls * sizeof (struct inoinfo *)); 912355d6bb5Sswilcox retval = -1; 913355d6bb5Sswilcox goto finish; 914355d6bb5Sswilcox } 915355d6bb5Sswilcox if (debug) { 916d1a180b0Smaheshvs if (aclmax > ULONG_MAX) 917355d6bb5Sswilcox errexit("create_and_init_maps: aclmax overflowed\n"); 918d1a180b0Smaheshvs if (numacls > ULONG_MAX) 919355d6bb5Sswilcox errexit("create_and_init_maps: numacls overflowed\n"); 9207c478bd9Sstevel@tonic-gate } 9217c478bd9Sstevel@tonic-gate aclplast = 0L; 9227c478bd9Sstevel@tonic-gate inplast = 0L; 923355d6bb5Sswilcox 924355d6bb5Sswilcox finish: 925355d6bb5Sswilcox return (retval); 926355d6bb5Sswilcox } 927355d6bb5Sswilcox 928355d6bb5Sswilcox caddr_t 929355d6bb5Sswilcox setup(caddr_t dev) 930355d6bb5Sswilcox { 931355d6bb5Sswilcox int corefs; 932355d6bb5Sswilcox static char devstr[MAXPATHLEN + 1]; 933355d6bb5Sswilcox 934355d6bb5Sswilcox havesb = 0; 935355d6bb5Sswilcox devname = devstr; 936355d6bb5Sswilcox 937355d6bb5Sswilcox derive_devstr(dev, devstr, sizeof (devstr)); 938355d6bb5Sswilcox errorlocked = is_errorlocked(devstr); 939355d6bb5Sswilcox corefs = check_mount_state(devstr, sizeof (devstr)); 940355d6bb5Sswilcox 941355d6bb5Sswilcox sblock_init(); 942355d6bb5Sswilcox 943355d6bb5Sswilcox if (open_and_intro(devstr, corefs) == -1) 944355d6bb5Sswilcox goto cleanup; 945355d6bb5Sswilcox 946355d6bb5Sswilcox /* 947355d6bb5Sswilcox * Check log state 948355d6bb5Sswilcox */ 949355d6bb5Sswilcox if (!logsetup(devstr)) 950355d6bb5Sswilcox goto cleanup; 951355d6bb5Sswilcox 952355d6bb5Sswilcox /* 953355d6bb5Sswilcox * Flush fs if we're going to do anything other than a sanity check. 954355d6bb5Sswilcox * Note, if logging then the fs was already flushed in logsetup(). 955355d6bb5Sswilcox */ 956355d6bb5Sswilcox if (!islog && !mflag) 957355d6bb5Sswilcox flush_fs(); 958355d6bb5Sswilcox 959355d6bb5Sswilcox if (find_superblock(devstr) == -1) 960355d6bb5Sswilcox goto cleanup; 961355d6bb5Sswilcox 962355d6bb5Sswilcox fixup_superblock(); 963355d6bb5Sswilcox 964355d6bb5Sswilcox if (errorlocked && 965355d6bb5Sswilcox (initial_error_state_adjust() == -1)) 966355d6bb5Sswilcox goto cleanup; 967355d6bb5Sswilcox 968355d6bb5Sswilcox /* 969355d6bb5Sswilcox * asblk could be dirty because we found a mismatch between 970355d6bb5Sswilcox * the primary superblock and one of its backups in checksb(). 971355d6bb5Sswilcox */ 972355d6bb5Sswilcox if (asblk.b_dirty && !bflag) { 973355d6bb5Sswilcox (void) memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize); 974355d6bb5Sswilcox flush(fswritefd, &asblk); 975355d6bb5Sswilcox } 976355d6bb5Sswilcox 977355d6bb5Sswilcox getsummaryinfo(); 978355d6bb5Sswilcox 979355d6bb5Sswilcox /* 980355d6bb5Sswilcox * if not error-locked, using the standard superblock, 981355d6bb5Sswilcox * not bad log, not forced, preening, and is clean; 982355d6bb5Sswilcox * stop checking 983355d6bb5Sswilcox */ 984355d6bb5Sswilcox if (!errorlocked && (bflag == 0) && 985355d6bb5Sswilcox ((!islog || islogok) && 986355d6bb5Sswilcox (fflag == 0) && preen && 987355d6bb5Sswilcox (FSOKAY == (sblock.fs_state + sblock.fs_time)) && 988355d6bb5Sswilcox ((sblock.fs_clean == FSLOG && islog) || 989355d6bb5Sswilcox ((sblock.fs_clean == FSCLEAN) || (sblock.fs_clean == FSSTABLE))))) { 990355d6bb5Sswilcox iscorrupt = 0; 991355d6bb5Sswilcox printclean(); 992355d6bb5Sswilcox goto cleanup; 993355d6bb5Sswilcox } 994355d6bb5Sswilcox 995355d6bb5Sswilcox if (create_and_init_maps() == -1) 996355d6bb5Sswilcox goto nomaps; 997355d6bb5Sswilcox 9987c478bd9Sstevel@tonic-gate bufinit(); 9997c478bd9Sstevel@tonic-gate return (devstr); 10007c478bd9Sstevel@tonic-gate 1001355d6bb5Sswilcox nomaps: 10027c478bd9Sstevel@tonic-gate ckfini(); 1003355d6bb5Sswilcox exitstat = EXERRFATAL; 1004355d6bb5Sswilcox /* FALLTHROUGH */ 1005355d6bb5Sswilcox 1006355d6bb5Sswilcox cleanup: 1007355d6bb5Sswilcox unbufinit(); 1008355d6bb5Sswilcox uncreate_maps(); 1009355d6bb5Sswilcox ungetsummaryinfo(); 1010355d6bb5Sswilcox 1011355d6bb5Sswilcox /* 1012355d6bb5Sswilcox * Can't get rid of the superblock buffer, because our 1013355d6bb5Sswilcox * caller references it to generate the summary statistics. 1014355d6bb5Sswilcox */ 1015355d6bb5Sswilcox 1016355d6bb5Sswilcox return (NULL); 1017355d6bb5Sswilcox } 1018355d6bb5Sswilcox 1019355d6bb5Sswilcox /* 1020355d6bb5Sswilcox * Undoes the allocations in create_and_init_maps() 1021355d6bb5Sswilcox */ 1022355d6bb5Sswilcox static void 1023355d6bb5Sswilcox uncreate_maps(void) 1024355d6bb5Sswilcox { 1025355d6bb5Sswilcox /* 1026355d6bb5Sswilcox * No ordering dependency amongst these, so they are here in 1027355d6bb5Sswilcox * the same order they were calculated. 1028355d6bb5Sswilcox */ 1029355d6bb5Sswilcox if (blockmap != NULL) 1030355d6bb5Sswilcox free(blockmap); 1031355d6bb5Sswilcox if (statemap != NULL) 1032355d6bb5Sswilcox free(statemap); 1033355d6bb5Sswilcox if (lncntp != NULL) 1034355d6bb5Sswilcox free(lncntp); 1035355d6bb5Sswilcox if (inpsort != NULL) 1036355d6bb5Sswilcox free(inpsort); 1037355d6bb5Sswilcox if (inphead != NULL) 1038355d6bb5Sswilcox free(inphead); 1039355d6bb5Sswilcox if (aclpsort != NULL) 1040355d6bb5Sswilcox free(aclpsort); 1041355d6bb5Sswilcox if (aclphead != NULL) 1042355d6bb5Sswilcox free(aclphead); 10437c478bd9Sstevel@tonic-gate } 10447c478bd9Sstevel@tonic-gate 10457c478bd9Sstevel@tonic-gate /* 10467c478bd9Sstevel@tonic-gate * mkfs limits the size of the inode map to be no more than a third of 10477c478bd9Sstevel@tonic-gate * the cylinder group space. We'll use that value for sanity checking 10487c478bd9Sstevel@tonic-gate * the superblock's inode per group value. 10497c478bd9Sstevel@tonic-gate */ 10507c478bd9Sstevel@tonic-gate #define MAXIpG (roundup(sblock.fs_bsize * NBBY / 3, sblock.fs_inopb)) 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate /* 10537c478bd9Sstevel@tonic-gate * Check the super block and its summary info. 10547c478bd9Sstevel@tonic-gate */ 10557c478bd9Sstevel@tonic-gate static int 10567c478bd9Sstevel@tonic-gate checksb(int listerr) 10577c478bd9Sstevel@tonic-gate { 1058355d6bb5Sswilcox caddr_t err; 1059355d6bb5Sswilcox 10607c478bd9Sstevel@tonic-gate /* 10617c478bd9Sstevel@tonic-gate * When the fs check is successfully completed, the alternate super 10627c478bd9Sstevel@tonic-gate * block at sblk.b_bno will be overwritten by ckfini() with the 10637c478bd9Sstevel@tonic-gate * repaired super block. 10647c478bd9Sstevel@tonic-gate */ 1065355d6bb5Sswilcox sblk.b_bno = bflag ? bflag : (SBOFF / dev_bsize); 10667c478bd9Sstevel@tonic-gate sblk.b_size = SBSIZE; 10677c478bd9Sstevel@tonic-gate 10687c478bd9Sstevel@tonic-gate /* 1069355d6bb5Sswilcox * Sanity-check some of the values we are going to use later 1070355d6bb5Sswilcox * in allocation requests. 10717c478bd9Sstevel@tonic-gate */ 10727c478bd9Sstevel@tonic-gate if (sblock.fs_cstotal.cs_ndir < 1 || 10737c478bd9Sstevel@tonic-gate sblock.fs_cstotal.cs_ndir > sblock.fs_ncg * sblock.fs_ipg) { 1074355d6bb5Sswilcox if (verbose) 1075355d6bb5Sswilcox (void) printf( 1076355d6bb5Sswilcox "Found %d directories, should be between 1 and %d inclusive.\n", 1077355d6bb5Sswilcox sblock.fs_cstotal.cs_ndir, 1078355d6bb5Sswilcox sblock.fs_ncg * sblock.fs_ipg); 1079355d6bb5Sswilcox err = "NUMBER OF DIRECTORIES OUT OF RANGE"; 1080355d6bb5Sswilcox goto failedsb; 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate if (sblock.fs_nrpos <= 0 || sblock.fs_postbloff < 0 || 10847c478bd9Sstevel@tonic-gate sblock.fs_cpc < 0 || 10857c478bd9Sstevel@tonic-gate (sblock.fs_postbloff + 1086355d6bb5Sswilcox (sblock.fs_nrpos * sblock.fs_cpc * sizeof (short))) > 10877c478bd9Sstevel@tonic-gate sblock.fs_sbsize) { 1088355d6bb5Sswilcox err = "ROTATIONAL POSITION TABLE SIZE OUT OF RANGE"; 1089355d6bb5Sswilcox goto failedsb; 10907c478bd9Sstevel@tonic-gate } 10917c478bd9Sstevel@tonic-gate 10927c478bd9Sstevel@tonic-gate if (sblock.fs_cssize != 1093355d6bb5Sswilcox fragroundup(&sblock, sblock.fs_ncg * sizeof (struct csum))) { 1094355d6bb5Sswilcox err = "SIZE OF CYLINDER GROUP SUMMARY AREA WRONG"; 1095355d6bb5Sswilcox goto failedsb; 10967c478bd9Sstevel@tonic-gate } 10977c478bd9Sstevel@tonic-gate 10987c478bd9Sstevel@tonic-gate if (sblock.fs_inopb != (sblock.fs_bsize / sizeof (struct dinode))) { 1099355d6bb5Sswilcox err = "INOPB NONSENSICAL RELATIVE TO BSIZE"; 1100355d6bb5Sswilcox goto failedsb; 1101355d6bb5Sswilcox } 1102355d6bb5Sswilcox 1103355d6bb5Sswilcox if (sblock.fs_bsize > MAXBSIZE) { 1104355d6bb5Sswilcox err = "BLOCK SIZE LARGER THAN MAXIMUM SUPPORTED"; 1105355d6bb5Sswilcox goto failedsb; 11067c478bd9Sstevel@tonic-gate } 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate if (sblock.fs_bsize != (sblock.fs_frag * sblock.fs_fsize)) { 1109355d6bb5Sswilcox err = "FRAGS PER BLOCK OR FRAG SIZE WRONG"; 1110355d6bb5Sswilcox goto failedsb; 11117c478bd9Sstevel@tonic-gate } 11127c478bd9Sstevel@tonic-gate 11137c478bd9Sstevel@tonic-gate if (sblock.fs_dsize >= sblock.fs_size) { 1114355d6bb5Sswilcox err = "NUMBER OF DATA BLOCKS OUT OF RANGE"; 1115355d6bb5Sswilcox goto failedsb; 11167c478bd9Sstevel@tonic-gate } 11177c478bd9Sstevel@tonic-gate 1118355d6bb5Sswilcox #if 0 1119355d6bb5Sswilcox if (sblock.fs_size > 1120355d6bb5Sswilcox (sblock.fs_nsect * sblock.fs_ntrak * sblock.fs_ncyl)) { 1121355d6bb5Sswilcox err = "FILESYSTEM SIZE LARGER THAN DEVICE"; 1122355d6bb5Sswilcox goto failedsb; 1123355d6bb5Sswilcox } 1124355d6bb5Sswilcox #endif 1125355d6bb5Sswilcox 11267c478bd9Sstevel@tonic-gate /* 11277c478bd9Sstevel@tonic-gate * Check that the number of inodes per group isn't less than or 11287c478bd9Sstevel@tonic-gate * equal to zero. Also makes sure it isn't more than the 11297c478bd9Sstevel@tonic-gate * maximum number mkfs enforces. 11307c478bd9Sstevel@tonic-gate */ 11317c478bd9Sstevel@tonic-gate if (sblock.fs_ipg <= 0 || sblock.fs_ipg > MAXIpG) { 1132355d6bb5Sswilcox err = "INODES PER GROUP OUT OF RANGE"; 1133355d6bb5Sswilcox goto failedsb; 1134355d6bb5Sswilcox } 1135355d6bb5Sswilcox 1136355d6bb5Sswilcox if (sblock.fs_cgsize > sblock.fs_bsize) { 1137355d6bb5Sswilcox err = "CG HEADER LARGER THAN ONE BLOCK"; 1138355d6bb5Sswilcox goto failedsb; 11397c478bd9Sstevel@tonic-gate } 11407c478bd9Sstevel@tonic-gate 11417c478bd9Sstevel@tonic-gate /* 11427c478bd9Sstevel@tonic-gate * Set all possible fields that could differ, then do check 11437c478bd9Sstevel@tonic-gate * of whole super block against an alternate super block. 11447c478bd9Sstevel@tonic-gate * When an alternate super-block is specified this check is skipped. 11457c478bd9Sstevel@tonic-gate */ 1146355d6bb5Sswilcox (void) getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), 1147355d6bb5Sswilcox (size_t)sblock.fs_sbsize); 1148355d6bb5Sswilcox if (asblk.b_errs != 0) { 1149355d6bb5Sswilcox brelse(&asblk); 11507c478bd9Sstevel@tonic-gate return (0); 1151355d6bb5Sswilcox } 1152355d6bb5Sswilcox if (bflag != 0) { 11537c478bd9Sstevel@tonic-gate /* 1154355d6bb5Sswilcox * Invalidate clean flag and state information. 1155355d6bb5Sswilcox * Note that we couldn't return until after the 1156355d6bb5Sswilcox * above getblk(), because we're going to want to 1157355d6bb5Sswilcox * update asblk when everything's done. 11587c478bd9Sstevel@tonic-gate */ 11597c478bd9Sstevel@tonic-gate sblock.fs_clean = FSACTIVE; 11607c478bd9Sstevel@tonic-gate sblock.fs_state = (long)sblock.fs_time; 11617c478bd9Sstevel@tonic-gate sblock.fs_reclaim = 0; 11627c478bd9Sstevel@tonic-gate sbdirty(); 11637c478bd9Sstevel@tonic-gate havesb = 1; 11647c478bd9Sstevel@tonic-gate return (1); 11657c478bd9Sstevel@tonic-gate } 11667c478bd9Sstevel@tonic-gate altsblock.fs_link = sblock.fs_link; 11677c478bd9Sstevel@tonic-gate altsblock.fs_rolled = sblock.fs_rolled; 11687c478bd9Sstevel@tonic-gate altsblock.fs_time = sblock.fs_time; 11697c478bd9Sstevel@tonic-gate altsblock.fs_state = sblock.fs_state; 11707c478bd9Sstevel@tonic-gate altsblock.fs_cstotal = sblock.fs_cstotal; 11717c478bd9Sstevel@tonic-gate altsblock.fs_cgrotor = sblock.fs_cgrotor; 11727c478bd9Sstevel@tonic-gate altsblock.fs_fmod = sblock.fs_fmod; 11737c478bd9Sstevel@tonic-gate altsblock.fs_clean = sblock.fs_clean; 11747c478bd9Sstevel@tonic-gate altsblock.fs_ronly = sblock.fs_ronly; 11757c478bd9Sstevel@tonic-gate altsblock.fs_flags = sblock.fs_flags; 11767c478bd9Sstevel@tonic-gate altsblock.fs_maxcontig = sblock.fs_maxcontig; 11777c478bd9Sstevel@tonic-gate altsblock.fs_minfree = sblock.fs_minfree; 11787c478bd9Sstevel@tonic-gate altsblock.fs_optim = sblock.fs_optim; 11797c478bd9Sstevel@tonic-gate altsblock.fs_rotdelay = sblock.fs_rotdelay; 11807c478bd9Sstevel@tonic-gate altsblock.fs_maxbpg = sblock.fs_maxbpg; 11817c478bd9Sstevel@tonic-gate altsblock.fs_logbno = sblock.fs_logbno; 11827c478bd9Sstevel@tonic-gate altsblock.fs_reclaim = sblock.fs_reclaim; 11837c478bd9Sstevel@tonic-gate altsblock.fs_si = sblock.fs_si; 1184355d6bb5Sswilcox (void) memmove((void *)altsblock.fs_fsmnt, (void *)sblock.fs_fsmnt, 1185355d6bb5Sswilcox sizeof (sblock.fs_fsmnt)); 11867c478bd9Sstevel@tonic-gate /* 11877c478bd9Sstevel@tonic-gate * The following should not have to be copied. 11887c478bd9Sstevel@tonic-gate */ 1189355d6bb5Sswilcox (void) memmove((void *)altsblock.fs_u.fs_csp_pad, 1190355d6bb5Sswilcox (void *)sblock.fs_u.fs_csp_pad, sizeof (sblock.fs_u.fs_csp_pad)); 11917c478bd9Sstevel@tonic-gate altsblock.fs_fsbtodb = sblock.fs_fsbtodb; 11927c478bd9Sstevel@tonic-gate altsblock.fs_npsect = sblock.fs_npsect; 11937c478bd9Sstevel@tonic-gate altsblock.fs_nrpos = sblock.fs_nrpos; 1194355d6bb5Sswilcox if (memcmp((void *)&sblock, (void *)&altsblock, 1195355d6bb5Sswilcox (size_t)sblock.fs_sbsize) != 0) { 1196355d6bb5Sswilcox err = "BAD VALUES IN SUPER BLOCK"; 1197355d6bb5Sswilcox goto failedsb; 11987c478bd9Sstevel@tonic-gate } 11997c478bd9Sstevel@tonic-gate havesb = 1; 12007c478bd9Sstevel@tonic-gate return (1); 1201355d6bb5Sswilcox 1202355d6bb5Sswilcox failedsb: 1203355d6bb5Sswilcox badsb(listerr, err); 1204355d6bb5Sswilcox return (0); 12057c478bd9Sstevel@tonic-gate } 12067c478bd9Sstevel@tonic-gate 12077c478bd9Sstevel@tonic-gate static void 1208355d6bb5Sswilcox badsb(int listerr, caddr_t s) 12097c478bd9Sstevel@tonic-gate { 12107c478bd9Sstevel@tonic-gate if (!listerr) 12117c478bd9Sstevel@tonic-gate return; 12127c478bd9Sstevel@tonic-gate if (preen) 1213355d6bb5Sswilcox (void) printf("%s: ", devname); 1214355d6bb5Sswilcox (void) printf("BAD SUPERBLOCK AT BLOCK %d: %s\n", 1215355d6bb5Sswilcox bflag != 0 ? bflag : SBLOCK, s); 1216355d6bb5Sswilcox if (preen) { 1217355d6bb5Sswilcox pwarn( 1218355d6bb5Sswilcox "USE AN ALTERNATE SUPERBLOCK TO SUPPLY NEEDED INFORMATION;\n"); 1219355d6bb5Sswilcox pwarn("e.g. fsck [-F ufs] -o b=# [special ...] \n"); 1220355d6bb5Sswilcox exitstat = EXERRFATAL; 1221355d6bb5Sswilcox pfatal( 1222355d6bb5Sswilcox "where # is the alternate super block. SEE fsck_ufs(1M). \n"); 1223355d6bb5Sswilcox } 1224355d6bb5Sswilcox /* we're expected to return if not preening */ 12257c478bd9Sstevel@tonic-gate } 12267c478bd9Sstevel@tonic-gate 12277c478bd9Sstevel@tonic-gate /* 12287c478bd9Sstevel@tonic-gate * Write out the super block into each of the alternate super blocks. 12297c478bd9Sstevel@tonic-gate */ 12307c478bd9Sstevel@tonic-gate void 12317c478bd9Sstevel@tonic-gate write_altsb(int fd) 12327c478bd9Sstevel@tonic-gate { 12337c478bd9Sstevel@tonic-gate int cylno; 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate for (cylno = 0; cylno < sblock.fs_ncg; cylno++) 1236355d6bb5Sswilcox bwrite(fd, (caddr_t)&sblock, fsbtodb(&sblock, 1237355d6bb5Sswilcox cgsblock(&sblock, cylno)), sblock.fs_sbsize); 1238355d6bb5Sswilcox } 1239355d6bb5Sswilcox 1240355d6bb5Sswilcox static void 1241355d6bb5Sswilcox sblock_init(void) 1242355d6bb5Sswilcox { 1243355d6bb5Sswilcox fsmodified = 0; 1244355d6bb5Sswilcox if (errorlocked) 1245355d6bb5Sswilcox isdirty = 1; 1246355d6bb5Sswilcox lfdir = 0; 1247355d6bb5Sswilcox initbarea(&sblk); 1248355d6bb5Sswilcox initbarea(&asblk); 1249355d6bb5Sswilcox 1250355d6bb5Sswilcox /* 1251355d6bb5Sswilcox * May have buffer left over from previous filesystem check. 1252355d6bb5Sswilcox */ 1253355d6bb5Sswilcox if (sblk.b_un.b_buf == NULL) 1254355d6bb5Sswilcox sblk.b_un.b_buf = calloc(1, SBSIZE); 1255355d6bb5Sswilcox if (asblk.b_un.b_buf == NULL) 1256355d6bb5Sswilcox asblk.b_un.b_buf = calloc(1, SBSIZE); 1257355d6bb5Sswilcox if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) 1258355d6bb5Sswilcox errexit("cannot allocate space for superblock\n"); 1259355d6bb5Sswilcox /* 1260355d6bb5Sswilcox * Could get the actual sector size from the device here, 1261355d6bb5Sswilcox * but considering how much would need to change in the rest 1262355d6bb5Sswilcox * of the system before it'd be a problem for us, it's not 1263355d6bb5Sswilcox * worth worrying about right now. 1264355d6bb5Sswilcox */ 1265355d6bb5Sswilcox dev_bsize = secsize = DEV_BSIZE; 1266355d6bb5Sswilcox } 1267355d6bb5Sswilcox 1268355d6bb5Sswilcox /* 1269355d6bb5Sswilcox * Calculate a prototype superblock based on information in the disk label. 1270355d6bb5Sswilcox * When done the cgsblock macro can be calculated and the fs_ncg field 1271355d6bb5Sswilcox * can be used. Do NOT attempt to use other macros without verifying that 1272355d6bb5Sswilcox * their needed information is available! 1273355d6bb5Sswilcox * 1274355d6bb5Sswilcox * In BSD, the disk label includes all sorts of useful information, 1275355d6bb5Sswilcox * like cpg. Solaris doesn't have that, and deriving it (as well as 1276355d6bb5Sswilcox * some other parameters) is difficult. Rather than duplicate the 1277355d6bb5Sswilcox * code, just ask mkfs what it would've come up with by default. 1278355d6bb5Sswilcox * Ideally, we'd just link in the code, but given the source base 1279355d6bb5Sswilcox * involved, it's more practical to just get a binary dump. 1280355d6bb5Sswilcox * 1281355d6bb5Sswilcox * The one minor drawback to the above approach is that newfs and mkfs 1282355d6bb5Sswilcox * will produce vastly different layouts for the same partition if 1283355d6bb5Sswilcox * they're allowed to default everything. So, if the superblock that 1284355d6bb5Sswilcox * mkfs gives us doesn't work for guessing where the alternates are, 1285355d6bb5Sswilcox * we need to try newfs. 1286355d6bb5Sswilcox */ 1287355d6bb5Sswilcox static int 1288355d6bb5Sswilcox calcsb(calcsb_t style, caddr_t dev, int devfd, struct fs *fs) 1289355d6bb5Sswilcox { 1290355d6bb5Sswilcox #define FROM_CHILD 0 1291355d6bb5Sswilcox #define TO_FSCK 1 1292355d6bb5Sswilcox #define CMD_IDX 0 1293355d6bb5Sswilcox #define DEV_IDX 3 1294355d6bb5Sswilcox #define SIZE_IDX 4 1295355d6bb5Sswilcox 1296355d6bb5Sswilcox int child_pipe[2]; 1297355d6bb5Sswilcox caddr_t mkfsline[] = { 1298355d6bb5Sswilcox "", /* CMD_IDX */ 1299355d6bb5Sswilcox "-o", 1300355d6bb5Sswilcox "calcbinsb,N", 1301355d6bb5Sswilcox NULL, /* DEV_IDX */ 1302355d6bb5Sswilcox NULL, /* SIZE_IDX */ 1303355d6bb5Sswilcox NULL 1304355d6bb5Sswilcox }; 1305355d6bb5Sswilcox caddr_t newfsline[] = { 1306355d6bb5Sswilcox "", /* CMD_IDX */ 1307355d6bb5Sswilcox "-B", 1308355d6bb5Sswilcox "-N", 1309355d6bb5Sswilcox NULL, /* DEV_IDX */ 1310355d6bb5Sswilcox NULL 1311355d6bb5Sswilcox }; 1312355d6bb5Sswilcox int pending, transferred; 1313355d6bb5Sswilcox caddr_t *cmdline; 1314355d6bb5Sswilcox caddr_t target; 1315355d6bb5Sswilcox caddr_t sizestr = NULL; 1316355d6bb5Sswilcox caddr_t path_old, path_new, mkfs_dir, mkfs_path, newfs_path; 1317355d6bb5Sswilcox caddr_t slash; 1318355d6bb5Sswilcox diskaddr_t size; 1319355d6bb5Sswilcox int devnull; 1320355d6bb5Sswilcox 1321355d6bb5Sswilcox switch (style) { 1322355d6bb5Sswilcox case MKFS_STYLE: 1323355d6bb5Sswilcox if (debug) 1324355d6bb5Sswilcox (void) printf("calcsb() going with style MKFS\n"); 1325355d6bb5Sswilcox cmdline = mkfsline; 1326355d6bb5Sswilcox break; 1327355d6bb5Sswilcox case NEWFS_STYLE: 1328355d6bb5Sswilcox if (debug) 1329355d6bb5Sswilcox (void) printf("calcsb() going with style NEWFS\n"); 1330355d6bb5Sswilcox cmdline = newfsline; 1331355d6bb5Sswilcox break; 1332355d6bb5Sswilcox default: 1333355d6bb5Sswilcox if (debug) 1334355d6bb5Sswilcox (void) printf("calcsb() doesn't undestand style %d\n", 1335355d6bb5Sswilcox style); 1336355d6bb5Sswilcox return (0); 1337355d6bb5Sswilcox } 1338355d6bb5Sswilcox 1339355d6bb5Sswilcox cmdline[DEV_IDX] = dev; 1340355d6bb5Sswilcox 1341355d6bb5Sswilcox /* 1342355d6bb5Sswilcox * Normally, only use the stock versions of the utilities. 1343355d6bb5Sswilcox * However, if we're debugging, the odds are that we're 1344355d6bb5Sswilcox * using experimental versions of them as well, so allow 1345355d6bb5Sswilcox * some flexibility. 1346355d6bb5Sswilcox */ 1347355d6bb5Sswilcox mkfs_path = getenv("MKFS_PATH"); 1348355d6bb5Sswilcox if (!debug || (mkfs_path == NULL)) 1349355d6bb5Sswilcox mkfs_path = MKFS_PATH; 1350355d6bb5Sswilcox 1351355d6bb5Sswilcox newfs_path = getenv("NEWFS_PATH"); 1352355d6bb5Sswilcox if (!debug || (newfs_path == NULL)) 1353355d6bb5Sswilcox newfs_path = NEWFS_PATH; 1354355d6bb5Sswilcox 1355355d6bb5Sswilcox if (style == MKFS_STYLE) { 1356355d6bb5Sswilcox cmdline[CMD_IDX] = mkfs_path; 1357355d6bb5Sswilcox 1358355d6bb5Sswilcox size = getdisksize(dev, devfd); 1359355d6bb5Sswilcox if (size == 0) 1360355d6bb5Sswilcox return (0); 1361355d6bb5Sswilcox 1362355d6bb5Sswilcox (void) fsck_asprintf(&sizestr, "%lld", (longlong_t)size); 1363355d6bb5Sswilcox cmdline[SIZE_IDX] = sizestr; 1364355d6bb5Sswilcox } else if (style == NEWFS_STYLE) { 1365355d6bb5Sswilcox /* 1366355d6bb5Sswilcox * Make sure that newfs will find the right version of mkfs. 1367355d6bb5Sswilcox */ 1368355d6bb5Sswilcox cmdline[CMD_IDX] = newfs_path; 1369355d6bb5Sswilcox path_old = getenv("PATH"); 1370355d6bb5Sswilcox /* mkfs_path is always initialized, despite lint's concerns */ 1371355d6bb5Sswilcox mkfs_dir = strdup(mkfs_path); 1372355d6bb5Sswilcox if (mkfs_dir == NULL) 1373355d6bb5Sswilcox return (0); 1374355d6bb5Sswilcox /* 1375355d6bb5Sswilcox * If no location data for mkfs, don't need to do 1376355d6bb5Sswilcox * anything about PATH. 1377355d6bb5Sswilcox */ 1378355d6bb5Sswilcox slash = strrchr(mkfs_dir, '/'); 1379355d6bb5Sswilcox if (slash != NULL) { 1380355d6bb5Sswilcox /* 1381355d6bb5Sswilcox * Just want the dir, so discard the executable name. 1382355d6bb5Sswilcox */ 1383355d6bb5Sswilcox *slash = '\0'; 1384355d6bb5Sswilcox 1385355d6bb5Sswilcox /* 1386355d6bb5Sswilcox * newfs uses system() to find mkfs, so make sure 1387355d6bb5Sswilcox * that the one we want to use is first on the 1388355d6bb5Sswilcox * list. Don't free path_new upon success, as it 1389355d6bb5Sswilcox * has become part of the environment. 1390355d6bb5Sswilcox */ 1391355d6bb5Sswilcox (void) fsck_asprintf(&path_new, "PATH=%s:%s", 1392355d6bb5Sswilcox mkfs_dir, path_old); 1393355d6bb5Sswilcox if (putenv(path_new) != 0) { 1394355d6bb5Sswilcox free(mkfs_dir); 1395355d6bb5Sswilcox free(path_new); 1396355d6bb5Sswilcox return (0); 1397355d6bb5Sswilcox } 1398355d6bb5Sswilcox } 1399355d6bb5Sswilcox free(mkfs_dir); 1400355d6bb5Sswilcox } else { 1401355d6bb5Sswilcox /* 1402355d6bb5Sswilcox * Bad search style, quietly return failure. 1403355d6bb5Sswilcox */ 1404355d6bb5Sswilcox if (debug) { 1405355d6bb5Sswilcox (void) printf("calcsb: got bad style number %d\n", 1406355d6bb5Sswilcox (int)style); 1407355d6bb5Sswilcox } 1408355d6bb5Sswilcox return (0); 1409355d6bb5Sswilcox } 1410355d6bb5Sswilcox 1411355d6bb5Sswilcox if (pipe(child_pipe) < 0) { 1412355d6bb5Sswilcox pfatal("calcsb: could not create pipe: %s\n", strerror(errno)); 1413355d6bb5Sswilcox if (sizestr != NULL) 1414355d6bb5Sswilcox free(sizestr); 1415355d6bb5Sswilcox return (0); 1416355d6bb5Sswilcox } 1417355d6bb5Sswilcox 1418355d6bb5Sswilcox switch (fork()) { 1419355d6bb5Sswilcox case -1: 1420355d6bb5Sswilcox pfatal("calcsb: fork failed: %s\n", strerror(errno)); 1421355d6bb5Sswilcox if (sizestr != NULL) 1422355d6bb5Sswilcox free(sizestr); 1423355d6bb5Sswilcox return (0); 1424355d6bb5Sswilcox case 0: 1425355d6bb5Sswilcox if (dup2(child_pipe[TO_FSCK], fileno(stdout)) < 0) { 1426355d6bb5Sswilcox (void) printf( 1427355d6bb5Sswilcox "calcsb: could not rename file descriptor: %s\n", 1428355d6bb5Sswilcox strerror(errno)); 1429355d6bb5Sswilcox exit(EXBADPARM); 1430355d6bb5Sswilcox } 1431355d6bb5Sswilcox devnull = open("/dev/null", O_WRONLY); 1432355d6bb5Sswilcox if (devnull == -1) { 1433355d6bb5Sswilcox (void) printf("calcsb: could not open /dev/null: %s\n", 1434355d6bb5Sswilcox strerror(errno)); 1435355d6bb5Sswilcox exit(EXBADPARM); 1436355d6bb5Sswilcox } 1437355d6bb5Sswilcox if (dup2(devnull, fileno(stderr)) < 0) { 1438355d6bb5Sswilcox (void) printf( 1439355d6bb5Sswilcox "calcsb: could not rename file descriptor: %s\n", 1440355d6bb5Sswilcox strerror(errno)); 1441355d6bb5Sswilcox exit(EXBADPARM); 1442355d6bb5Sswilcox } 1443355d6bb5Sswilcox (void) close(child_pipe[FROM_CHILD]); 1444355d6bb5Sswilcox (void) execv(cmdline[CMD_IDX], cmdline); 1445355d6bb5Sswilcox (void) printf("calcsb: could not exec %s: %s\n", 1446355d6bb5Sswilcox cmdline[CMD_IDX], strerror(errno)); 1447355d6bb5Sswilcox exit(EXBADPARM); 1448355d6bb5Sswilcox /* NOTREACHED */ 1449355d6bb5Sswilcox default: 1450355d6bb5Sswilcox break; 1451355d6bb5Sswilcox } 1452355d6bb5Sswilcox 1453355d6bb5Sswilcox (void) close(child_pipe[TO_FSCK]); 1454355d6bb5Sswilcox if (sizestr != NULL) 1455355d6bb5Sswilcox free(sizestr); 1456355d6bb5Sswilcox 1457355d6bb5Sswilcox pending = sizeof (struct fs); 1458355d6bb5Sswilcox target = (caddr_t)fs; 1459355d6bb5Sswilcox do { 1460355d6bb5Sswilcox transferred = read(child_pipe[FROM_CHILD], target, pending); 1461355d6bb5Sswilcox pending -= transferred; 1462355d6bb5Sswilcox target += transferred; 1463355d6bb5Sswilcox } while ((pending > 0) && (transferred > 0)); 1464355d6bb5Sswilcox 1465355d6bb5Sswilcox if (pending > 0) { 1466355d6bb5Sswilcox if (transferred < 0) 1467355d6bb5Sswilcox pfatal( 1468355d6bb5Sswilcox "calcsb: binary read of superblock from %s failed: %s\n", 1469355d6bb5Sswilcox (style == MKFS_STYLE) ? "mkfs" : "newfs", 1470355d6bb5Sswilcox (transferred < 0) ? strerror(errno) : ""); 1471355d6bb5Sswilcox else 1472355d6bb5Sswilcox pfatal( 1473355d6bb5Sswilcox "calcsb: short read of superblock from %s\n", 1474355d6bb5Sswilcox (style == MKFS_STYLE) ? "mkfs" : "newfs"); 1475355d6bb5Sswilcox return (0); 1476355d6bb5Sswilcox } 1477355d6bb5Sswilcox 1478355d6bb5Sswilcox (void) close(child_pipe[FROM_CHILD]); 1479355d6bb5Sswilcox (void) wait(NULL); 1480355d6bb5Sswilcox 1481355d6bb5Sswilcox if ((fs->fs_magic != FS_MAGIC) && 1482355d6bb5Sswilcox (fs->fs_magic != MTB_UFS_MAGIC)) 1483355d6bb5Sswilcox return (0); 1484355d6bb5Sswilcox 1485355d6bb5Sswilcox return (1); 14867c478bd9Sstevel@tonic-gate } 1487