17c478bd9Sstevel@tonic-gate /* 2*23a1cceaSRoger A. Faulkner * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. 37c478bd9Sstevel@tonic-gate */ 47c478bd9Sstevel@tonic-gate 57c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 67c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 77c478bd9Sstevel@tonic-gate 87c478bd9Sstevel@tonic-gate /* 97c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. 107c478bd9Sstevel@tonic-gate * All rights reserved. 117c478bd9Sstevel@tonic-gate * 127c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted 137c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright 147c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display 157c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software 167c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors'' 177c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution 187c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this 197c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its 207c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived 217c478bd9Sstevel@tonic-gate * from this software without specific prior written permission. 227c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 237c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 247c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate #include <stdio.h> 28355d6bb5Sswilcox #include <stdlib.h> 29355d6bb5Sswilcox #include <unistd.h> 30355d6bb5Sswilcox #include <stdarg.h> 31355d6bb5Sswilcox #include <libadm.h> 32355d6bb5Sswilcox #include <note.h> 337c478bd9Sstevel@tonic-gate #include <sys/param.h> 347c478bd9Sstevel@tonic-gate #include <sys/types.h> 357c478bd9Sstevel@tonic-gate #include <sys/mntent.h> 367c478bd9Sstevel@tonic-gate #include <sys/filio.h> 377c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h> 387c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 397c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h> 40355d6bb5Sswilcox #include <sys/fs/ufs_inode.h> 41355d6bb5Sswilcox #include <sys/fs/ufs_log.h> 427c478bd9Sstevel@tonic-gate #define _KERNEL 437c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h> 447c478bd9Sstevel@tonic-gate #undef _KERNEL 457c478bd9Sstevel@tonic-gate #include <sys/mnttab.h> 467c478bd9Sstevel@tonic-gate #include <sys/types.h> 477c478bd9Sstevel@tonic-gate #include <sys/stat.h> 48355d6bb5Sswilcox #include <fcntl.h> 49355d6bb5Sswilcox #include <signal.h> 507c478bd9Sstevel@tonic-gate #include <string.h> 517c478bd9Sstevel@tonic-gate #include <ctype.h> 527c478bd9Sstevel@tonic-gate #include <sys/vfstab.h> 537c478bd9Sstevel@tonic-gate #include <sys/lockfs.h> 547c478bd9Sstevel@tonic-gate #include <errno.h> 55355d6bb5Sswilcox #include <sys/cmn_err.h> 56355d6bb5Sswilcox #include <sys/dkio.h> 57355d6bb5Sswilcox #include <sys/vtoc.h> 58355d6bb5Sswilcox #include <sys/efi_partition.h> 59355d6bb5Sswilcox #include <fslib.h> 60355d6bb5Sswilcox #include <inttypes.h> 61355d6bb5Sswilcox #include "fsck.h" 627c478bd9Sstevel@tonic-gate 63355d6bb5Sswilcox caddr_t mount_point = NULL; 647c478bd9Sstevel@tonic-gate 65355d6bb5Sswilcox static int64_t diskreads, totalreads; /* Disk cache statistics */ 667c478bd9Sstevel@tonic-gate 67355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int); 68355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list); 69355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t); 70355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t); 71355d6bb5Sswilcox static void vpwarn(caddr_t, va_list); 72*23a1cceaSRoger A. Faulkner static int getaline(FILE *, caddr_t, int); 73355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void); 74355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval); 75355d6bb5Sswilcox static void debugclean(void); 76355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t); 77355d6bb5Sswilcox static void freelogblk(daddr32_t); 78355d6bb5Sswilcox static void verrexit(caddr_t, va_list); 79355d6bb5Sswilcox static void vpfatal(caddr_t, va_list); 80355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t); 81355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int); 82355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *, 83355d6bb5Sswilcox daddr32_t *, daddr32_t *, daddr32_t *); 847c478bd9Sstevel@tonic-gate 85355d6bb5Sswilcox int 86355d6bb5Sswilcox ftypeok(struct dinode *dp) 877c478bd9Sstevel@tonic-gate { 887c478bd9Sstevel@tonic-gate switch (dp->di_mode & IFMT) { 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate case IFDIR: 917c478bd9Sstevel@tonic-gate case IFREG: 927c478bd9Sstevel@tonic-gate case IFBLK: 937c478bd9Sstevel@tonic-gate case IFCHR: 947c478bd9Sstevel@tonic-gate case IFLNK: 957c478bd9Sstevel@tonic-gate case IFSOCK: 967c478bd9Sstevel@tonic-gate case IFIFO: 977c478bd9Sstevel@tonic-gate case IFSHAD: 987c478bd9Sstevel@tonic-gate case IFATTRDIR: 997c478bd9Sstevel@tonic-gate return (1); 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate default: 1027c478bd9Sstevel@tonic-gate if (debug) 103355d6bb5Sswilcox (void) printf("bad file type 0%o\n", dp->di_mode); 1047c478bd9Sstevel@tonic-gate return (0); 1057c478bd9Sstevel@tonic-gate } 1067c478bd9Sstevel@tonic-gate } 1077c478bd9Sstevel@tonic-gate 108355d6bb5Sswilcox int 109355d6bb5Sswilcox acltypeok(struct dinode *dp) 1107c478bd9Sstevel@tonic-gate { 1117c478bd9Sstevel@tonic-gate if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT)) 1127c478bd9Sstevel@tonic-gate return (1); 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate if (debug) 115355d6bb5Sswilcox (void) printf("bad file type for acl I=%d: 0%o\n", 116355d6bb5Sswilcox dp->di_shadow, dp->di_mode); 1177c478bd9Sstevel@tonic-gate return (0); 1187c478bd9Sstevel@tonic-gate } 1197c478bd9Sstevel@tonic-gate 120355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 121355d6bb5Sswilcox int 122355d6bb5Sswilcox reply(caddr_t fmt, ...) 1237c478bd9Sstevel@tonic-gate { 124355d6bb5Sswilcox va_list ap; 1257c478bd9Sstevel@tonic-gate char line[80]; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate if (preen) 128355d6bb5Sswilcox pfatal("INTERNAL ERROR: GOT TO reply() in preen mode"); 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate if (mflag) { 131355d6bb5Sswilcox /* 132355d6bb5Sswilcox * We don't know what's going on, so don't potentially 133355d6bb5Sswilcox * make things worse by having errexit() write stuff 134355d6bb5Sswilcox * out to disk. 135355d6bb5Sswilcox */ 136355d6bb5Sswilcox (void) printf( 137355d6bb5Sswilcox "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 1387c478bd9Sstevel@tonic-gate devname); 139355d6bb5Sswilcox exit(EXERRFATAL); 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate 142355d6bb5Sswilcox va_start(ap, fmt); 143355d6bb5Sswilcox (void) putchar('\n'); 144355d6bb5Sswilcox (void) vprintf(fmt, ap); 145355d6bb5Sswilcox (void) putchar('?'); 146355d6bb5Sswilcox (void) putchar(' '); 147355d6bb5Sswilcox va_end(ap); 148355d6bb5Sswilcox 1497c478bd9Sstevel@tonic-gate if (nflag || fswritefd < 0) { 150355d6bb5Sswilcox (void) printf(" no\n\n"); 1517c478bd9Sstevel@tonic-gate return (0); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate if (yflag) { 154355d6bb5Sswilcox (void) printf(" yes\n\n"); 1557c478bd9Sstevel@tonic-gate return (1); 1567c478bd9Sstevel@tonic-gate } 157355d6bb5Sswilcox (void) fflush(stdout); 158*23a1cceaSRoger A. Faulkner if (getaline(stdin, line, sizeof (line)) == EOF) 1597c478bd9Sstevel@tonic-gate errexit("\n"); 160355d6bb5Sswilcox (void) printf("\n"); 161355d6bb5Sswilcox if (line[0] == 'y' || line[0] == 'Y') { 1627c478bd9Sstevel@tonic-gate return (1); 163355d6bb5Sswilcox } else { 1647c478bd9Sstevel@tonic-gate return (0); 1657c478bd9Sstevel@tonic-gate } 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 168355d6bb5Sswilcox int 169*23a1cceaSRoger A. Faulkner getaline(FILE *fp, caddr_t loc, int maxlen) 1707c478bd9Sstevel@tonic-gate { 1717c478bd9Sstevel@tonic-gate int n; 172355d6bb5Sswilcox caddr_t p, lastloc; 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate p = loc; 1757c478bd9Sstevel@tonic-gate lastloc = &p[maxlen-1]; 1767c478bd9Sstevel@tonic-gate while ((n = getc(fp)) != '\n') { 1777c478bd9Sstevel@tonic-gate if (n == EOF) 1787c478bd9Sstevel@tonic-gate return (EOF); 1797c478bd9Sstevel@tonic-gate if (!isspace(n) && p < lastloc) 180355d6bb5Sswilcox *p++ = (char)n; 1817c478bd9Sstevel@tonic-gate } 182355d6bb5Sswilcox *p = '\0'; 183355d6bb5Sswilcox /* LINTED pointer difference won't overflow */ 1847c478bd9Sstevel@tonic-gate return (p - loc); 1857c478bd9Sstevel@tonic-gate } 186355d6bb5Sswilcox 1877c478bd9Sstevel@tonic-gate /* 1887c478bd9Sstevel@tonic-gate * Malloc buffers and set up cache. 1897c478bd9Sstevel@tonic-gate */ 190355d6bb5Sswilcox void 191355d6bb5Sswilcox bufinit(void) 1927c478bd9Sstevel@tonic-gate { 1937c478bd9Sstevel@tonic-gate struct bufarea *bp; 1947c478bd9Sstevel@tonic-gate int bufcnt, i; 195355d6bb5Sswilcox caddr_t bufp; 1967c478bd9Sstevel@tonic-gate 197355d6bb5Sswilcox bufp = malloc((size_t)sblock.fs_bsize); 198355d6bb5Sswilcox if (bufp == NULL) 199355d6bb5Sswilcox goto nomem; 2007c478bd9Sstevel@tonic-gate initbarea(&cgblk); 201355d6bb5Sswilcox cgblk.b_un.b_buf = bufp; 2027c478bd9Sstevel@tonic-gate bufhead.b_next = bufhead.b_prev = &bufhead; 2037c478bd9Sstevel@tonic-gate bufcnt = MAXBUFSPACE / sblock.fs_bsize; 2047c478bd9Sstevel@tonic-gate if (bufcnt < MINBUFS) 2057c478bd9Sstevel@tonic-gate bufcnt = MINBUFS; 2067c478bd9Sstevel@tonic-gate for (i = 0; i < bufcnt; i++) { 2077c478bd9Sstevel@tonic-gate bp = (struct bufarea *)malloc(sizeof (struct bufarea)); 208355d6bb5Sswilcox if (bp == NULL) { 2097c478bd9Sstevel@tonic-gate if (i >= MINBUFS) 210355d6bb5Sswilcox goto noalloc; 211355d6bb5Sswilcox goto nomem; 2127c478bd9Sstevel@tonic-gate } 213355d6bb5Sswilcox 214355d6bb5Sswilcox bufp = malloc((size_t)sblock.fs_bsize); 215355d6bb5Sswilcox if (bufp == NULL) { 216355d6bb5Sswilcox free((void *)bp); 217355d6bb5Sswilcox if (i >= MINBUFS) 218355d6bb5Sswilcox goto noalloc; 219355d6bb5Sswilcox goto nomem; 220355d6bb5Sswilcox } 221355d6bb5Sswilcox initbarea(bp); 2227c478bd9Sstevel@tonic-gate bp->b_un.b_buf = bufp; 2237c478bd9Sstevel@tonic-gate bp->b_prev = &bufhead; 2247c478bd9Sstevel@tonic-gate bp->b_next = bufhead.b_next; 2257c478bd9Sstevel@tonic-gate bufhead.b_next->b_prev = bp; 2267c478bd9Sstevel@tonic-gate bufhead.b_next = bp; 2277c478bd9Sstevel@tonic-gate } 228355d6bb5Sswilcox noalloc: 2297c478bd9Sstevel@tonic-gate bufhead.b_size = i; /* save number of buffers */ 2307c478bd9Sstevel@tonic-gate pbp = pdirbp = NULL; 231355d6bb5Sswilcox return; 232355d6bb5Sswilcox 233355d6bb5Sswilcox nomem: 234355d6bb5Sswilcox errexit("cannot allocate buffer pool\n"); 235355d6bb5Sswilcox /* NOTREACHED */ 236355d6bb5Sswilcox } 237355d6bb5Sswilcox 238355d6bb5Sswilcox /* 239355d6bb5Sswilcox * Undo a bufinit(). 240355d6bb5Sswilcox */ 241355d6bb5Sswilcox void 242355d6bb5Sswilcox unbufinit(void) 243355d6bb5Sswilcox { 244355d6bb5Sswilcox int cnt; 245355d6bb5Sswilcox struct bufarea *bp, *nbp; 246355d6bb5Sswilcox 247355d6bb5Sswilcox cnt = 0; 248355d6bb5Sswilcox for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) { 249355d6bb5Sswilcox cnt++; 250355d6bb5Sswilcox flush(fswritefd, bp); 251355d6bb5Sswilcox nbp = bp->b_prev; 252355d6bb5Sswilcox /* 253355d6bb5Sswilcox * We're discarding the entire chain, so this isn't 254355d6bb5Sswilcox * technically necessary. However, it doesn't hurt 255355d6bb5Sswilcox * and lint's data flow analysis is much happier 256355d6bb5Sswilcox * (this prevents it from thinking there's a chance 257355d6bb5Sswilcox * of our using memory elsewhere after it's been released). 258355d6bb5Sswilcox */ 259355d6bb5Sswilcox nbp->b_next = bp->b_next; 260355d6bb5Sswilcox bp->b_next->b_prev = nbp; 261355d6bb5Sswilcox free((void *)bp->b_un.b_buf); 262355d6bb5Sswilcox free((void *)bp); 263355d6bb5Sswilcox } 264355d6bb5Sswilcox 265355d6bb5Sswilcox if (bufhead.b_size != cnt) 266355d6bb5Sswilcox errexit("Panic: cache lost %d buffers\n", 267355d6bb5Sswilcox bufhead.b_size - cnt); 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate /* 2717c478bd9Sstevel@tonic-gate * Manage a cache of directory blocks. 2727c478bd9Sstevel@tonic-gate */ 2737c478bd9Sstevel@tonic-gate struct bufarea * 274355d6bb5Sswilcox getdatablk(daddr32_t blkno, size_t size) 2757c478bd9Sstevel@tonic-gate { 2767c478bd9Sstevel@tonic-gate struct bufarea *bp; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) 279355d6bb5Sswilcox if (bp->b_bno == fsbtodb(&sblock, blkno)) { 2807c478bd9Sstevel@tonic-gate goto foundit; 281355d6bb5Sswilcox } 2827c478bd9Sstevel@tonic-gate for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) 2837c478bd9Sstevel@tonic-gate if ((bp->b_flags & B_INUSE) == 0) 2847c478bd9Sstevel@tonic-gate break; 2857c478bd9Sstevel@tonic-gate if (bp == &bufhead) { 2867c478bd9Sstevel@tonic-gate bp = alloc_bufarea(); 287355d6bb5Sswilcox if (bp == NULL) { 2887c478bd9Sstevel@tonic-gate errexit("deadlocked buffer pool\n"); 289355d6bb5Sswilcox /* NOTREACHED */ 2907c478bd9Sstevel@tonic-gate } 291355d6bb5Sswilcox } 292355d6bb5Sswilcox /* 293355d6bb5Sswilcox * We're at the same logical level as getblk(), so if there 294355d6bb5Sswilcox * are any errors, we'll let our caller handle them. 295355d6bb5Sswilcox */ 296355d6bb5Sswilcox diskreads++; 297355d6bb5Sswilcox (void) getblk(bp, blkno, size); 298355d6bb5Sswilcox 2997c478bd9Sstevel@tonic-gate foundit: 3007c478bd9Sstevel@tonic-gate totalreads++; 3017c478bd9Sstevel@tonic-gate bp->b_cnt++; 3027c478bd9Sstevel@tonic-gate /* 303355d6bb5Sswilcox * Move the buffer to head of linked list if it isn't 3047c478bd9Sstevel@tonic-gate * already there. 3057c478bd9Sstevel@tonic-gate */ 3067c478bd9Sstevel@tonic-gate if (bufhead.b_next != bp) { 3077c478bd9Sstevel@tonic-gate bp->b_prev->b_next = bp->b_next; 3087c478bd9Sstevel@tonic-gate bp->b_next->b_prev = bp->b_prev; 3097c478bd9Sstevel@tonic-gate bp->b_prev = &bufhead; 3107c478bd9Sstevel@tonic-gate bp->b_next = bufhead.b_next; 3117c478bd9Sstevel@tonic-gate bufhead.b_next->b_prev = bp; 3127c478bd9Sstevel@tonic-gate bufhead.b_next = bp; 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate bp->b_flags |= B_INUSE; 3157c478bd9Sstevel@tonic-gate return (bp); 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate 318355d6bb5Sswilcox void 3197c478bd9Sstevel@tonic-gate brelse(struct bufarea *bp) 3207c478bd9Sstevel@tonic-gate { 3217c478bd9Sstevel@tonic-gate bp->b_cnt--; 3227c478bd9Sstevel@tonic-gate if (bp->b_cnt == 0) { 3237c478bd9Sstevel@tonic-gate bp->b_flags &= ~B_INUSE; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate struct bufarea * 328355d6bb5Sswilcox getblk(struct bufarea *bp, daddr32_t blk, size_t size) 3297c478bd9Sstevel@tonic-gate { 3307c478bd9Sstevel@tonic-gate diskaddr_t dblk; 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate dblk = fsbtodb(&sblock, blk); 3337c478bd9Sstevel@tonic-gate if (bp->b_bno == dblk) 3347c478bd9Sstevel@tonic-gate return (bp); 3357c478bd9Sstevel@tonic-gate flush(fswritefd, bp); 336355d6bb5Sswilcox bp->b_errs = fsck_bread(fsreadfd, bp->b_un.b_buf, dblk, size); 3377c478bd9Sstevel@tonic-gate bp->b_bno = dblk; 3387c478bd9Sstevel@tonic-gate bp->b_size = size; 3397c478bd9Sstevel@tonic-gate return (bp); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 342355d6bb5Sswilcox void 343355d6bb5Sswilcox flush(int fd, struct bufarea *bp) 3447c478bd9Sstevel@tonic-gate { 3457c478bd9Sstevel@tonic-gate int i, j; 3467c478bd9Sstevel@tonic-gate caddr_t sip; 3477c478bd9Sstevel@tonic-gate long size; 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate if (!bp->b_dirty) 3507c478bd9Sstevel@tonic-gate return; 351355d6bb5Sswilcox 352355d6bb5Sswilcox /* 353355d6bb5Sswilcox * It's not our buf, so if there are errors, let whoever 354355d6bb5Sswilcox * acquired it deal with the actual problem. 355355d6bb5Sswilcox */ 3567c478bd9Sstevel@tonic-gate if (bp->b_errs != 0) 3577c478bd9Sstevel@tonic-gate pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno); 3587c478bd9Sstevel@tonic-gate bp->b_dirty = 0; 3597c478bd9Sstevel@tonic-gate bp->b_errs = 0; 3607c478bd9Sstevel@tonic-gate bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); 361355d6bb5Sswilcox if (bp != &sblk) { 3627c478bd9Sstevel@tonic-gate return; 363355d6bb5Sswilcox } 364355d6bb5Sswilcox 365355d6bb5Sswilcox /* 366355d6bb5Sswilcox * We're flushing the superblock, so make sure all the 367355d6bb5Sswilcox * ancillary bits go out as well. 368355d6bb5Sswilcox */ 3697c478bd9Sstevel@tonic-gate sip = (caddr_t)sblock.fs_u.fs_csp; 3707c478bd9Sstevel@tonic-gate for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { 3717c478bd9Sstevel@tonic-gate size = sblock.fs_cssize - i < sblock.fs_bsize ? 3727c478bd9Sstevel@tonic-gate sblock.fs_cssize - i : sblock.fs_bsize; 3737c478bd9Sstevel@tonic-gate bwrite(fswritefd, sip, 3747c478bd9Sstevel@tonic-gate fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), 3757c478bd9Sstevel@tonic-gate size); 3767c478bd9Sstevel@tonic-gate sip += size; 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate 380355d6bb5Sswilcox static void 381355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval) 3827c478bd9Sstevel@tonic-gate { 383355d6bb5Sswilcox int olderr = errno; 3847c478bd9Sstevel@tonic-gate 385355d6bb5Sswilcox if (!preen) 386355d6bb5Sswilcox (void) printf("\n"); 387355d6bb5Sswilcox 388355d6bb5Sswilcox if (rval == -1) 389355d6bb5Sswilcox pfatal("CANNOT %s: DISK BLOCK %lld: %s", 390355d6bb5Sswilcox mesg, blk, strerror(olderr)); 391355d6bb5Sswilcox else 392355d6bb5Sswilcox pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk); 393355d6bb5Sswilcox 394355d6bb5Sswilcox if (reply("CONTINUE") == 0) { 395355d6bb5Sswilcox exitstat = EXERRFATAL; 3967c478bd9Sstevel@tonic-gate errexit("Program terminated\n"); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate } 399355d6bb5Sswilcox 400355d6bb5Sswilcox void 401355d6bb5Sswilcox ckfini(void) 402355d6bb5Sswilcox { 403355d6bb5Sswilcox int64_t percentage; 404355d6bb5Sswilcox 405355d6bb5Sswilcox if (fswritefd < 0) 406355d6bb5Sswilcox return; 407355d6bb5Sswilcox 4087c478bd9Sstevel@tonic-gate flush(fswritefd, &sblk); 409355d6bb5Sswilcox /* 410355d6bb5Sswilcox * Were we using a backup superblock? 411355d6bb5Sswilcox */ 4127c478bd9Sstevel@tonic-gate if (havesb && sblk.b_bno != SBOFF / dev_bsize) { 413355d6bb5Sswilcox if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) { 4147c478bd9Sstevel@tonic-gate sblk.b_bno = SBOFF / dev_bsize; 4157c478bd9Sstevel@tonic-gate sbdirty(); 4167c478bd9Sstevel@tonic-gate flush(fswritefd, &sblk); 4177c478bd9Sstevel@tonic-gate } 418355d6bb5Sswilcox } 4197c478bd9Sstevel@tonic-gate flush(fswritefd, &cgblk); 420355d6bb5Sswilcox if (cgblk.b_un.b_buf != NULL) { 421355d6bb5Sswilcox free((void *)cgblk.b_un.b_buf); 4227c478bd9Sstevel@tonic-gate cgblk.b_un.b_buf = NULL; 4237c478bd9Sstevel@tonic-gate } 424355d6bb5Sswilcox unbufinit(); 425355d6bb5Sswilcox pbp = NULL; 426355d6bb5Sswilcox pdirbp = NULL; 427355d6bb5Sswilcox if (debug) { 428355d6bb5Sswilcox /* 429355d6bb5Sswilcox * Note that we only count cache-related reads. 430355d6bb5Sswilcox * Anything that called fsck_bread() or getblk() 431355d6bb5Sswilcox * directly are explicitly not cached, so they're not 432355d6bb5Sswilcox * included here. 433355d6bb5Sswilcox */ 434355d6bb5Sswilcox if (totalreads != 0) 435355d6bb5Sswilcox percentage = diskreads * 100 / totalreads; 436355d6bb5Sswilcox else 437355d6bb5Sswilcox percentage = 0; 438355d6bb5Sswilcox 439355d6bb5Sswilcox (void) printf("cache missed %lld of %lld reads (%lld%%)\n", 440355d6bb5Sswilcox (longlong_t)diskreads, (longlong_t)totalreads, 441355d6bb5Sswilcox (longlong_t)percentage); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 444355d6bb5Sswilcox (void) close(fsreadfd); 445355d6bb5Sswilcox (void) close(fswritefd); 446355d6bb5Sswilcox fsreadfd = -1; 447355d6bb5Sswilcox fswritefd = -1; 448355d6bb5Sswilcox } 449355d6bb5Sswilcox 450355d6bb5Sswilcox int 451355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size) 4527c478bd9Sstevel@tonic-gate { 453355d6bb5Sswilcox caddr_t cp; 4547c478bd9Sstevel@tonic-gate int i; 4557c478bd9Sstevel@tonic-gate int errs; 4567c478bd9Sstevel@tonic-gate offset_t offset = ldbtob(blk); 4577c478bd9Sstevel@tonic-gate offset_t addr; 4587c478bd9Sstevel@tonic-gate 459355d6bb5Sswilcox /* 460355d6bb5Sswilcox * In our universe, nothing exists before the superblock, so 461355d6bb5Sswilcox * just pretend it's always zeros. This is the complement of 462355d6bb5Sswilcox * bwrite()'s ignoring write requests into that space. 463355d6bb5Sswilcox */ 464355d6bb5Sswilcox if (blk < SBLOCK) { 465355d6bb5Sswilcox if (debug) 466355d6bb5Sswilcox (void) printf( 467355d6bb5Sswilcox "WARNING: fsck_bread() passed blkno < %d (%lld)\n", 468355d6bb5Sswilcox SBLOCK, (longlong_t)blk); 469355d6bb5Sswilcox (void) memset(buf, 0, (size_t)size); 470355d6bb5Sswilcox return (1); 4717c478bd9Sstevel@tonic-gate } 472355d6bb5Sswilcox 473f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) { 474355d6bb5Sswilcox rwerror("SEEK", blk, -1); 475355d6bb5Sswilcox } 476355d6bb5Sswilcox 477355d6bb5Sswilcox if ((i = read(fd, buf, size)) == size) { 4787c478bd9Sstevel@tonic-gate return (0); 479355d6bb5Sswilcox } 480355d6bb5Sswilcox rwerror("READ", blk, i); 481f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) { 482355d6bb5Sswilcox rwerror("SEEK", blk, -1); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate errs = 0; 485355d6bb5Sswilcox (void) memset(buf, 0, (size_t)size); 4867c478bd9Sstevel@tonic-gate pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:"); 4877c478bd9Sstevel@tonic-gate for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { 4887c478bd9Sstevel@tonic-gate addr = ldbtob(blk + i); 489f763a6cdScasper if (llseek(fd, addr, SEEK_SET) < 0 || 4907c478bd9Sstevel@tonic-gate read(fd, cp, (int)secsize) < 0) { 491355d6bb5Sswilcox iscorrupt = 1; 492355d6bb5Sswilcox (void) printf(" %llu", blk + (u_longlong_t)i); 4937c478bd9Sstevel@tonic-gate errs++; 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate } 496355d6bb5Sswilcox (void) printf("\n"); 4977c478bd9Sstevel@tonic-gate return (errs); 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate 500355d6bb5Sswilcox void 501355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size) 5027c478bd9Sstevel@tonic-gate { 5037c478bd9Sstevel@tonic-gate int i; 5047c478bd9Sstevel@tonic-gate int n; 505355d6bb5Sswilcox caddr_t cp; 5067c478bd9Sstevel@tonic-gate offset_t offset = ldbtob(blk); 5077c478bd9Sstevel@tonic-gate offset_t addr; 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate if (fd < 0) 5107c478bd9Sstevel@tonic-gate return; 5117c478bd9Sstevel@tonic-gate if (blk < SBLOCK) { 5127c478bd9Sstevel@tonic-gate if (debug) 513355d6bb5Sswilcox (void) printf( 514355d6bb5Sswilcox "WARNING: Attempt to write illegal blkno %lld on %s\n", 515355d6bb5Sswilcox (longlong_t)blk, devname); 5167c478bd9Sstevel@tonic-gate return; 5177c478bd9Sstevel@tonic-gate } 518f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) { 519355d6bb5Sswilcox rwerror("SEEK", blk, -1); 520355d6bb5Sswilcox } 521355d6bb5Sswilcox if ((i = write(fd, buf, (int)size)) == size) { 5227c478bd9Sstevel@tonic-gate fsmodified = 1; 5237c478bd9Sstevel@tonic-gate return; 5247c478bd9Sstevel@tonic-gate } 525355d6bb5Sswilcox rwerror("WRITE", blk, i); 526f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) { 527355d6bb5Sswilcox rwerror("SEEK", blk, -1); 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); 5307c478bd9Sstevel@tonic-gate for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { 5317c478bd9Sstevel@tonic-gate n = 0; 5327c478bd9Sstevel@tonic-gate addr = ldbtob(blk + i); 533f763a6cdScasper if (llseek(fd, addr, SEEK_SET) < 0 || 5347c478bd9Sstevel@tonic-gate (n = write(fd, cp, DEV_BSIZE)) < 0) { 535355d6bb5Sswilcox iscorrupt = 1; 536355d6bb5Sswilcox (void) printf(" %llu", blk + (u_longlong_t)i); 5377c478bd9Sstevel@tonic-gate } else if (n > 0) { 5387c478bd9Sstevel@tonic-gate fsmodified = 1; 5397c478bd9Sstevel@tonic-gate } 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate } 542355d6bb5Sswilcox (void) printf("\n"); 5437c478bd9Sstevel@tonic-gate } 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate /* 546355d6bb5Sswilcox * Allocates the specified number of contiguous fragments. 5477c478bd9Sstevel@tonic-gate */ 5487c478bd9Sstevel@tonic-gate daddr32_t 549355d6bb5Sswilcox allocblk(int wantedfrags) 5507c478bd9Sstevel@tonic-gate { 551355d6bb5Sswilcox int block, leadfrag, tailfrag; 552355d6bb5Sswilcox daddr32_t selected; 553355d6bb5Sswilcox size_t size; 554355d6bb5Sswilcox struct bufarea *bp; 5557c478bd9Sstevel@tonic-gate 556355d6bb5Sswilcox /* 557355d6bb5Sswilcox * It's arguable whether we should just fail, or instead 558355d6bb5Sswilcox * error out here. Since we should only ever be asked for 559355d6bb5Sswilcox * a single fragment or an entire block (i.e., sblock.fs_frag), 560355d6bb5Sswilcox * we'll fail out because anything else means somebody 561355d6bb5Sswilcox * changed code without considering all of the ramifications. 562355d6bb5Sswilcox */ 563355d6bb5Sswilcox if (wantedfrags <= 0 || wantedfrags > sblock.fs_frag) { 564355d6bb5Sswilcox exitstat = EXERRFATAL; 565355d6bb5Sswilcox errexit("allocblk() asked for %d frags. " 566355d6bb5Sswilcox "Legal range is 1 to %d", 567355d6bb5Sswilcox wantedfrags, sblock.fs_frag); 568355d6bb5Sswilcox } 569355d6bb5Sswilcox 570355d6bb5Sswilcox /* 571355d6bb5Sswilcox * For each filesystem block, look at every possible starting 572355d6bb5Sswilcox * offset within the block such that we can get the number of 573355d6bb5Sswilcox * contiguous fragments that we need. This is a drastically 574355d6bb5Sswilcox * simplified version of the kernel's mapsearch() and alloc*(). 575355d6bb5Sswilcox * It's also correspondingly slower. 576355d6bb5Sswilcox */ 577355d6bb5Sswilcox for (block = 0; block < maxfsblock - sblock.fs_frag; 578355d6bb5Sswilcox block += sblock.fs_frag) { 579355d6bb5Sswilcox for (leadfrag = 0; leadfrag <= sblock.fs_frag - wantedfrags; 580355d6bb5Sswilcox leadfrag++) { 581355d6bb5Sswilcox /* 582355d6bb5Sswilcox * Is first fragment of candidate run available? 583355d6bb5Sswilcox */ 584355d6bb5Sswilcox if (testbmap(block + leadfrag)) 5857c478bd9Sstevel@tonic-gate continue; 586355d6bb5Sswilcox /* 587355d6bb5Sswilcox * Are the rest of them available? 588355d6bb5Sswilcox */ 589355d6bb5Sswilcox for (tailfrag = 1; tailfrag < wantedfrags; tailfrag++) 590355d6bb5Sswilcox if (testbmap(block + leadfrag + tailfrag)) 5917c478bd9Sstevel@tonic-gate break; 592355d6bb5Sswilcox if (tailfrag < wantedfrags) { 593355d6bb5Sswilcox /* 594355d6bb5Sswilcox * No, skip the known-unusable run. 595355d6bb5Sswilcox */ 596355d6bb5Sswilcox leadfrag += tailfrag; 5977c478bd9Sstevel@tonic-gate continue; 5987c478bd9Sstevel@tonic-gate } 599355d6bb5Sswilcox /* 600355d6bb5Sswilcox * Found what we need, so claim them. 601355d6bb5Sswilcox */ 602355d6bb5Sswilcox for (tailfrag = 0; tailfrag < wantedfrags; tailfrag++) 603355d6bb5Sswilcox setbmap(block + leadfrag + tailfrag); 604355d6bb5Sswilcox n_blks += wantedfrags; 605355d6bb5Sswilcox size = wantedfrags * sblock.fs_fsize; 606355d6bb5Sswilcox selected = block + leadfrag; 607355d6bb5Sswilcox bp = getdatablk(selected, size); 608355d6bb5Sswilcox (void) memset((void *)bp->b_un.b_buf, 0, size); 609355d6bb5Sswilcox dirty(bp); 610355d6bb5Sswilcox brelse(bp); 611355d6bb5Sswilcox if (debug) 612355d6bb5Sswilcox (void) printf( 613355d6bb5Sswilcox "allocblk: selected %d (in block %d), frags %d, size %d\n", 614355d6bb5Sswilcox selected, selected % sblock.fs_bsize, 615355d6bb5Sswilcox wantedfrags, (int)size); 616355d6bb5Sswilcox return (selected); 6177c478bd9Sstevel@tonic-gate } 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate return (0); 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate /* 6237c478bd9Sstevel@tonic-gate * Free a previously allocated block 6247c478bd9Sstevel@tonic-gate */ 625355d6bb5Sswilcox void 626355d6bb5Sswilcox freeblk(fsck_ino_t ino, daddr32_t blkno, int frags) 6277c478bd9Sstevel@tonic-gate { 6287c478bd9Sstevel@tonic-gate struct inodesc idesc; 6297c478bd9Sstevel@tonic-gate 630355d6bb5Sswilcox if (debug) 631355d6bb5Sswilcox (void) printf("debug: freeing %d fragments starting at %d\n", 632355d6bb5Sswilcox frags, blkno); 633355d6bb5Sswilcox 634355d6bb5Sswilcox init_inodesc(&idesc); 635355d6bb5Sswilcox 636355d6bb5Sswilcox idesc.id_number = ino; 6377c478bd9Sstevel@tonic-gate idesc.id_blkno = blkno; 6387c478bd9Sstevel@tonic-gate idesc.id_numfrags = frags; 639355d6bb5Sswilcox idesc.id_truncto = -1; 640355d6bb5Sswilcox 641355d6bb5Sswilcox /* 642355d6bb5Sswilcox * Nothing in the return status has any relevance to how 643355d6bb5Sswilcox * we're using pass4check(), so just ignore it. 644355d6bb5Sswilcox */ 645355d6bb5Sswilcox (void) pass4check(&idesc); 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate /* 649355d6bb5Sswilcox * Fill NAMEBUF with a path starting in CURDIR for INO. Assumes 650355d6bb5Sswilcox * that the given buffer is at least MAXPATHLEN + 1 characters. 6517c478bd9Sstevel@tonic-gate */ 652355d6bb5Sswilcox void 653355d6bb5Sswilcox getpathname(caddr_t namebuf, fsck_ino_t curdir, fsck_ino_t ino) 6547c478bd9Sstevel@tonic-gate { 6557c478bd9Sstevel@tonic-gate int len; 656355d6bb5Sswilcox caddr_t cp; 657355d6bb5Sswilcox struct dinode *dp; 6587c478bd9Sstevel@tonic-gate struct inodesc idesc; 6597c478bd9Sstevel@tonic-gate struct inoinfo *inp; 6607c478bd9Sstevel@tonic-gate 661355d6bb5Sswilcox if (debug) 662355d6bb5Sswilcox (void) printf("debug: getpathname(curdir %d, ino %d)\n", 663355d6bb5Sswilcox curdir, ino); 664355d6bb5Sswilcox 665355d6bb5Sswilcox if ((curdir == 0) || (!INO_IS_DVALID(curdir))) { 666355d6bb5Sswilcox (void) strcpy(namebuf, "?"); 6677c478bd9Sstevel@tonic-gate return; 6687c478bd9Sstevel@tonic-gate } 669355d6bb5Sswilcox 670355d6bb5Sswilcox if ((curdir == UFSROOTINO) && (ino == UFSROOTINO)) { 671355d6bb5Sswilcox (void) strcpy(namebuf, "/"); 672355d6bb5Sswilcox return; 673355d6bb5Sswilcox } 674355d6bb5Sswilcox 675355d6bb5Sswilcox init_inodesc(&idesc); 6767c478bd9Sstevel@tonic-gate idesc.id_type = DATA; 6777c478bd9Sstevel@tonic-gate cp = &namebuf[MAXPATHLEN - 1]; 6787c478bd9Sstevel@tonic-gate *cp = '\0'; 679355d6bb5Sswilcox 680355d6bb5Sswilcox /* 681355d6bb5Sswilcox * In the case of extended attributes, our 682355d6bb5Sswilcox * parent won't necessarily be a directory, so just 683355d6bb5Sswilcox * return what we've found with a prefix indicating 684355d6bb5Sswilcox * that it's an XATTR. Presumably our caller will 685355d6bb5Sswilcox * know what's going on and do something useful, like 686355d6bb5Sswilcox * work out the path of the parent and then combine 687355d6bb5Sswilcox * the two names. 688355d6bb5Sswilcox * 689355d6bb5Sswilcox * Can't use strcpy(), etc, because we've probably 690355d6bb5Sswilcox * already got some name information in the buffer and 691355d6bb5Sswilcox * the usual trailing \0 would lose it. 692355d6bb5Sswilcox */ 693355d6bb5Sswilcox dp = ginode(curdir); 694355d6bb5Sswilcox if ((dp->di_mode & IFMT) == IFATTRDIR) { 695355d6bb5Sswilcox idesc.id_number = curdir; 696355d6bb5Sswilcox idesc.id_parent = ino; 697355d6bb5Sswilcox idesc.id_func = findname; 698355d6bb5Sswilcox idesc.id_name = namebuf; 699355d6bb5Sswilcox idesc.id_fix = NOFIX; 700355d6bb5Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) == 0) { 701355d6bb5Sswilcox *cp-- = '?'; 702355d6bb5Sswilcox } 703355d6bb5Sswilcox 704355d6bb5Sswilcox len = sizeof (XATTR_DIR_NAME) - 1; 705355d6bb5Sswilcox cp -= len; 706355d6bb5Sswilcox (void) memmove(cp, XATTR_DIR_NAME, len); 707355d6bb5Sswilcox goto attrname; 708355d6bb5Sswilcox } 709355d6bb5Sswilcox 710355d6bb5Sswilcox /* 711355d6bb5Sswilcox * If curdir == ino, need to get a handle on .. so we 712355d6bb5Sswilcox * can search it for ino's name. Otherwise, just search 713355d6bb5Sswilcox * the given directory for ino. Repeat until out of space 714355d6bb5Sswilcox * or a full path has been built. 715355d6bb5Sswilcox */ 7167c478bd9Sstevel@tonic-gate if (curdir != ino) { 7177c478bd9Sstevel@tonic-gate idesc.id_parent = curdir; 7187c478bd9Sstevel@tonic-gate goto namelookup; 7197c478bd9Sstevel@tonic-gate } 720355d6bb5Sswilcox while (ino != UFSROOTINO && ino != 0) { 7217c478bd9Sstevel@tonic-gate idesc.id_number = ino; 7227c478bd9Sstevel@tonic-gate idesc.id_func = findino; 7237c478bd9Sstevel@tonic-gate idesc.id_name = ".."; 7247c478bd9Sstevel@tonic-gate idesc.id_fix = NOFIX; 725355d6bb5Sswilcox if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) == 0) { 7267c478bd9Sstevel@tonic-gate inp = getinoinfo(ino); 727355d6bb5Sswilcox if ((inp == NULL) || (inp->i_parent == 0)) { 7287c478bd9Sstevel@tonic-gate break; 729355d6bb5Sswilcox } 7307c478bd9Sstevel@tonic-gate idesc.id_parent = inp->i_parent; 7317c478bd9Sstevel@tonic-gate } 732355d6bb5Sswilcox 733355d6bb5Sswilcox /* 734355d6bb5Sswilcox * To get this far, id_parent must have the inode 735355d6bb5Sswilcox * number for `..' in it. By definition, that's got 736355d6bb5Sswilcox * to be a directory, so search it for the inode of 737355d6bb5Sswilcox * interest. 738355d6bb5Sswilcox */ 7397c478bd9Sstevel@tonic-gate namelookup: 7407c478bd9Sstevel@tonic-gate idesc.id_number = idesc.id_parent; 7417c478bd9Sstevel@tonic-gate idesc.id_parent = ino; 7427c478bd9Sstevel@tonic-gate idesc.id_func = findname; 7437c478bd9Sstevel@tonic-gate idesc.id_name = namebuf; 7447c478bd9Sstevel@tonic-gate idesc.id_fix = NOFIX; 745355d6bb5Sswilcox if ((ckinode(ginode(idesc.id_number), 746355d6bb5Sswilcox &idesc, CKI_TRAVERSE) & FOUND) == 0) { 7477c478bd9Sstevel@tonic-gate break; 748355d6bb5Sswilcox } 749355d6bb5Sswilcox /* 750355d6bb5Sswilcox * Prepend to what we've accumulated so far. If 751355d6bb5Sswilcox * there's not enough room for even one more path element 752355d6bb5Sswilcox * (of the worst-case length), then bail out. 753355d6bb5Sswilcox */ 7547c478bd9Sstevel@tonic-gate len = strlen(namebuf); 7557c478bd9Sstevel@tonic-gate cp -= len; 7567c478bd9Sstevel@tonic-gate if (cp < &namebuf[MAXNAMLEN]) 7577c478bd9Sstevel@tonic-gate break; 758355d6bb5Sswilcox (void) memmove(cp, namebuf, len); 7597c478bd9Sstevel@tonic-gate *--cp = '/'; 760355d6bb5Sswilcox 761355d6bb5Sswilcox /* 762355d6bb5Sswilcox * Corner case for a looped-to-itself directory. 763355d6bb5Sswilcox */ 764355d6bb5Sswilcox if (ino == idesc.id_number) 765355d6bb5Sswilcox break; 766355d6bb5Sswilcox 767355d6bb5Sswilcox /* 768355d6bb5Sswilcox * Climb one level of the hierarchy. In other words, 769355d6bb5Sswilcox * the current .. becomes the inode to search for and 770355d6bb5Sswilcox * its parent becomes the directory to search in. 771355d6bb5Sswilcox */ 7727c478bd9Sstevel@tonic-gate ino = idesc.id_number; 7737c478bd9Sstevel@tonic-gate } 774355d6bb5Sswilcox 775355d6bb5Sswilcox /* 776355d6bb5Sswilcox * If we hit a discontinuity in the hierarchy, indicate it by 777355d6bb5Sswilcox * prefixing the path so far with `?'. Otherwise, the first 778355d6bb5Sswilcox * character will be `/' as a side-effect of the *--cp above. 779355d6bb5Sswilcox * 780355d6bb5Sswilcox * The special case is to handle the situation where we're 781355d6bb5Sswilcox * trying to look something up in UFSROOTINO, but didn't find 782355d6bb5Sswilcox * it. 783355d6bb5Sswilcox */ 784355d6bb5Sswilcox if (ino != UFSROOTINO || cp == &namebuf[MAXPATHLEN - 1]) { 785355d6bb5Sswilcox if (cp > namebuf) 786355d6bb5Sswilcox cp--; 787355d6bb5Sswilcox *cp = '?'; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate 790355d6bb5Sswilcox /* 791355d6bb5Sswilcox * The invariants being used for buffer integrity are: 792355d6bb5Sswilcox * - namebuf[] is terminated with \0 before anything else 793355d6bb5Sswilcox * - cp is always <= the last element of namebuf[] 794355d6bb5Sswilcox * - the new path element is always stored at the 795355d6bb5Sswilcox * beginning of namebuf[], and is no more than MAXNAMLEN-1 796355d6bb5Sswilcox * characters 797355d6bb5Sswilcox * - cp is is decremented by the number of characters in 798355d6bb5Sswilcox * the new path element 799355d6bb5Sswilcox * - if, after the above accounting for the new element's 800355d6bb5Sswilcox * size, there is no longer enough room at the beginning of 801355d6bb5Sswilcox * namebuf[] for a full-sized path element and a slash, 802355d6bb5Sswilcox * terminate the loop. cp is in the range 803355d6bb5Sswilcox * &namebuf[0]..&namebuf[MAXNAMLEN - 1] 804355d6bb5Sswilcox */ 805355d6bb5Sswilcox attrname: 806355d6bb5Sswilcox /* LINTED per the above discussion */ 807355d6bb5Sswilcox (void) memmove(namebuf, cp, &namebuf[MAXPATHLEN] - cp); 808355d6bb5Sswilcox } 809355d6bb5Sswilcox 810355d6bb5Sswilcox /* ARGSUSED */ 8117c478bd9Sstevel@tonic-gate void 812355d6bb5Sswilcox catch(int dummy) 8137c478bd9Sstevel@tonic-gate { 8147c478bd9Sstevel@tonic-gate ckfini(); 815355d6bb5Sswilcox exit(EXSIGNAL); 8167c478bd9Sstevel@tonic-gate } 8177c478bd9Sstevel@tonic-gate 8187c478bd9Sstevel@tonic-gate /* 8197c478bd9Sstevel@tonic-gate * When preening, allow a single quit to signal 8207c478bd9Sstevel@tonic-gate * a special exit after filesystem checks complete 8217c478bd9Sstevel@tonic-gate * so that reboot sequence may be interrupted. 8227c478bd9Sstevel@tonic-gate */ 823355d6bb5Sswilcox /* ARGSUSED */ 8247c478bd9Sstevel@tonic-gate void 825355d6bb5Sswilcox catchquit(int dummy) 8267c478bd9Sstevel@tonic-gate { 827355d6bb5Sswilcox (void) printf("returning to single-user after filesystem check\n"); 828355d6bb5Sswilcox interrupted = 1; 8297c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL); 8307c478bd9Sstevel@tonic-gate } 8317c478bd9Sstevel@tonic-gate 8327c478bd9Sstevel@tonic-gate 8337c478bd9Sstevel@tonic-gate /* 8347c478bd9Sstevel@tonic-gate * determine whether an inode should be fixed. 8357c478bd9Sstevel@tonic-gate */ 836355d6bb5Sswilcox NOTE(PRINTFLIKE(2)) 837355d6bb5Sswilcox int 838355d6bb5Sswilcox dofix(struct inodesc *idesc, caddr_t msg, ...) 8397c478bd9Sstevel@tonic-gate { 840355d6bb5Sswilcox int rval = 0; 841355d6bb5Sswilcox va_list ap; 842355d6bb5Sswilcox 843355d6bb5Sswilcox va_start(ap, msg); 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate switch (idesc->id_fix) { 8467c478bd9Sstevel@tonic-gate 8477c478bd9Sstevel@tonic-gate case DONTKNOW: 8487c478bd9Sstevel@tonic-gate if (idesc->id_type == DATA) 849355d6bb5Sswilcox vdirerror(idesc->id_number, msg, ap); 8507c478bd9Sstevel@tonic-gate else 851355d6bb5Sswilcox vpwarn(msg, ap); 8527c478bd9Sstevel@tonic-gate if (preen) { 8537c478bd9Sstevel@tonic-gate idesc->id_fix = FIX; 854355d6bb5Sswilcox rval = ALTERED; 855355d6bb5Sswilcox break; 8567c478bd9Sstevel@tonic-gate } 8577c478bd9Sstevel@tonic-gate if (reply("SALVAGE") == 0) { 8587c478bd9Sstevel@tonic-gate idesc->id_fix = NOFIX; 859355d6bb5Sswilcox break; 8607c478bd9Sstevel@tonic-gate } 8617c478bd9Sstevel@tonic-gate idesc->id_fix = FIX; 862355d6bb5Sswilcox rval = ALTERED; 863355d6bb5Sswilcox break; 8647c478bd9Sstevel@tonic-gate 8657c478bd9Sstevel@tonic-gate case FIX: 866355d6bb5Sswilcox rval = ALTERED; 867355d6bb5Sswilcox break; 8687c478bd9Sstevel@tonic-gate 8697c478bd9Sstevel@tonic-gate case NOFIX: 870355d6bb5Sswilcox break; 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate default: 873355d6bb5Sswilcox errexit("UNKNOWN INODESC FIX MODE %d\n", (int)idesc->id_fix); 8747c478bd9Sstevel@tonic-gate } 875355d6bb5Sswilcox 876355d6bb5Sswilcox va_end(ap); 877355d6bb5Sswilcox return (rval); 878355d6bb5Sswilcox } 879355d6bb5Sswilcox 880355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 881355d6bb5Sswilcox void 882355d6bb5Sswilcox errexit(caddr_t fmt, ...) 883355d6bb5Sswilcox { 884355d6bb5Sswilcox va_list ap; 885355d6bb5Sswilcox 886355d6bb5Sswilcox va_start(ap, fmt); 887355d6bb5Sswilcox verrexit(fmt, ap); 8887c478bd9Sstevel@tonic-gate /* NOTREACHED */ 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate 891355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 892355d6bb5Sswilcox static void 893355d6bb5Sswilcox verrexit(caddr_t fmt, va_list ap) 8947c478bd9Sstevel@tonic-gate { 895355d6bb5Sswilcox static int recursing = 0; 8967c478bd9Sstevel@tonic-gate 897355d6bb5Sswilcox if (!recursing) { 898355d6bb5Sswilcox recursing = 1; 899355d6bb5Sswilcox if (errorlocked || iscorrupt) { 90073f4c598SMilan Cermak if (havesb && fswritefd >= 0) { 9017c478bd9Sstevel@tonic-gate sblock.fs_clean = FSBAD; 902355d6bb5Sswilcox sblock.fs_state = FSOKAY - (long)sblock.fs_time; 903355d6bb5Sswilcox sblock.fs_state = -sblock.fs_state; 9047c478bd9Sstevel@tonic-gate sbdirty(); 9057c478bd9Sstevel@tonic-gate write_altsb(fswritefd); 9067c478bd9Sstevel@tonic-gate flush(fswritefd, &sblk); 9077c478bd9Sstevel@tonic-gate } 9087c478bd9Sstevel@tonic-gate } 909355d6bb5Sswilcox ckfini(); 910355d6bb5Sswilcox recursing = 0; 911355d6bb5Sswilcox } 912355d6bb5Sswilcox (void) vprintf(fmt, ap); 913355d6bb5Sswilcox if (fmt[strlen(fmt) - 1] != '\n') 914355d6bb5Sswilcox (void) putchar('\n'); 915355d6bb5Sswilcox exit((exitstat != 0) ? exitstat : EXERRFATAL); 9167c478bd9Sstevel@tonic-gate } 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate /* 9197c478bd9Sstevel@tonic-gate * An unexpected inconsistency occured. 9207c478bd9Sstevel@tonic-gate * Die if preening, otherwise just print message and continue. 9217c478bd9Sstevel@tonic-gate */ 922355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 923355d6bb5Sswilcox void 924355d6bb5Sswilcox pfatal(caddr_t fmt, ...) 925355d6bb5Sswilcox { 926355d6bb5Sswilcox va_list ap; 927355d6bb5Sswilcox 928355d6bb5Sswilcox va_start(ap, fmt); 929355d6bb5Sswilcox vpfatal(fmt, ap); 930355d6bb5Sswilcox va_end(ap); 931355d6bb5Sswilcox } 932355d6bb5Sswilcox 933355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 934355d6bb5Sswilcox static void 935355d6bb5Sswilcox vpfatal(caddr_t fmt, va_list ap) 9367c478bd9Sstevel@tonic-gate { 9377c478bd9Sstevel@tonic-gate if (preen) { 938355d6bb5Sswilcox if (*fmt != '\0') { 939355d6bb5Sswilcox (void) printf("%s: ", devname); 940355d6bb5Sswilcox (void) vprintf(fmt, ap); 941355d6bb5Sswilcox (void) printf("\n"); 942355d6bb5Sswilcox } 943355d6bb5Sswilcox (void) printf( 944355d6bb5Sswilcox "%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", 9457c478bd9Sstevel@tonic-gate devname); 94673f4c598SMilan Cermak if (havesb && fswritefd >= 0) { 9477c478bd9Sstevel@tonic-gate sblock.fs_clean = FSBAD; 9487c478bd9Sstevel@tonic-gate sblock.fs_state = -(FSOKAY - (long)sblock.fs_time); 9497c478bd9Sstevel@tonic-gate sbdirty(); 9507c478bd9Sstevel@tonic-gate flush(fswritefd, &sblk); 9517c478bd9Sstevel@tonic-gate } 952355d6bb5Sswilcox /* 953355d6bb5Sswilcox * We're exiting, it doesn't really matter that our 954355d6bb5Sswilcox * caller doesn't get to call va_end(). 955355d6bb5Sswilcox */ 956355d6bb5Sswilcox if (exitstat == 0) 957355d6bb5Sswilcox exitstat = EXFNDERRS; 958355d6bb5Sswilcox exit(exitstat); 9597c478bd9Sstevel@tonic-gate } 960355d6bb5Sswilcox if (*fmt != '\0') { 961355d6bb5Sswilcox (void) vprintf(fmt, ap); 962355d6bb5Sswilcox } 9637c478bd9Sstevel@tonic-gate } 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate /* 9667c478bd9Sstevel@tonic-gate * Pwarn just prints a message when not preening, 9677c478bd9Sstevel@tonic-gate * or a warning (preceded by filename) when preening. 9687c478bd9Sstevel@tonic-gate */ 969355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 9707c478bd9Sstevel@tonic-gate void 971355d6bb5Sswilcox pwarn(caddr_t fmt, ...) 9727c478bd9Sstevel@tonic-gate { 973355d6bb5Sswilcox va_list ap; 9747c478bd9Sstevel@tonic-gate 975355d6bb5Sswilcox va_start(ap, fmt); 976355d6bb5Sswilcox vpwarn(fmt, ap); 977355d6bb5Sswilcox va_end(ap); 978355d6bb5Sswilcox } 979355d6bb5Sswilcox 980355d6bb5Sswilcox NOTE(PRINTFLIKE(1)) 981355d6bb5Sswilcox static void 982355d6bb5Sswilcox vpwarn(caddr_t fmt, va_list ap) 983355d6bb5Sswilcox { 984355d6bb5Sswilcox if (*fmt != '\0') { 985355d6bb5Sswilcox if (preen) 986355d6bb5Sswilcox (void) printf("%s: ", devname); 987355d6bb5Sswilcox (void) vprintf(fmt, ap); 988355d6bb5Sswilcox } 989355d6bb5Sswilcox } 990355d6bb5Sswilcox 991355d6bb5Sswilcox /* 992355d6bb5Sswilcox * Like sprintf(), except the buffer is dynamically allocated 993355d6bb5Sswilcox * and returned, instead of being passed in. A pointer to the 994355d6bb5Sswilcox * buffer is stored in *RET, and FMT is the usual format string. 995355d6bb5Sswilcox * The number of characters in *RET (excluding the trailing \0, 996355d6bb5Sswilcox * to be consistent with the other *printf() routines) is returned. 997355d6bb5Sswilcox * 998355d6bb5Sswilcox * Solaris doesn't have asprintf(3C) yet, unfortunately. 999355d6bb5Sswilcox */ 1000355d6bb5Sswilcox NOTE(PRINTFLIKE(2)) 1001355d6bb5Sswilcox int 1002355d6bb5Sswilcox fsck_asprintf(caddr_t *ret, caddr_t fmt, ...) 1003355d6bb5Sswilcox { 1004355d6bb5Sswilcox int len; 1005355d6bb5Sswilcox caddr_t buffer; 1006355d6bb5Sswilcox va_list ap; 1007355d6bb5Sswilcox 1008355d6bb5Sswilcox va_start(ap, fmt); 1009355d6bb5Sswilcox len = vsnprintf(NULL, 0, fmt, ap); 1010355d6bb5Sswilcox va_end(ap); 1011355d6bb5Sswilcox 1012355d6bb5Sswilcox buffer = malloc((len + 1) * sizeof (char)); 1013355d6bb5Sswilcox if (buffer == NULL) { 1014355d6bb5Sswilcox errexit("Out of memory in asprintf\n"); 1015355d6bb5Sswilcox /* NOTREACHED */ 1016355d6bb5Sswilcox } 1017355d6bb5Sswilcox 1018355d6bb5Sswilcox va_start(ap, fmt); 1019355d6bb5Sswilcox (void) vsnprintf(buffer, len + 1, fmt, ap); 1020355d6bb5Sswilcox va_end(ap); 1021355d6bb5Sswilcox 1022355d6bb5Sswilcox *ret = buffer; 1023355d6bb5Sswilcox return (len); 1024355d6bb5Sswilcox } 1025355d6bb5Sswilcox 1026355d6bb5Sswilcox /* 1027355d6bb5Sswilcox * So we can take advantage of kernel routines in ufs_subr.c. 1028355d6bb5Sswilcox */ 1029355d6bb5Sswilcox /* PRINTFLIKE2 */ 1030355d6bb5Sswilcox void 1031355d6bb5Sswilcox cmn_err(int level, caddr_t fmt, ...) 1032355d6bb5Sswilcox { 1033355d6bb5Sswilcox va_list ap; 1034355d6bb5Sswilcox 1035355d6bb5Sswilcox va_start(ap, fmt); 10367c478bd9Sstevel@tonic-gate if (level == CE_PANIC) { 1037355d6bb5Sswilcox (void) printf("INTERNAL INCONSISTENCY:"); 1038355d6bb5Sswilcox verrexit(fmt, ap); 1039355d6bb5Sswilcox } else { 1040355d6bb5Sswilcox (void) vprintf(fmt, ap); 10417c478bd9Sstevel@tonic-gate } 1042355d6bb5Sswilcox va_end(ap); 10437c478bd9Sstevel@tonic-gate } 10447c478bd9Sstevel@tonic-gate 10457c478bd9Sstevel@tonic-gate /* 10467c478bd9Sstevel@tonic-gate * Check to see if unraw version of name is already mounted. 1047355d6bb5Sswilcox * Updates devstr with the device name if devstr is not NULL 1048355d6bb5Sswilcox * and str_size is positive. 10497c478bd9Sstevel@tonic-gate */ 1050355d6bb5Sswilcox int 1051355d6bb5Sswilcox mounted(caddr_t name, caddr_t devstr, size_t str_size) 10527c478bd9Sstevel@tonic-gate { 1053355d6bb5Sswilcox int found; 1054355d6bb5Sswilcox struct mnttab *mntent; 10557c478bd9Sstevel@tonic-gate 1056355d6bb5Sswilcox mntent = search_mnttab(NULL, unrawname(name), devstr, str_size); 1057355d6bb5Sswilcox if (mntent == NULL) 1058355d6bb5Sswilcox return (M_NOMNT); 1059355d6bb5Sswilcox 1060355d6bb5Sswilcox /* 1061355d6bb5Sswilcox * It's mounted. With or without write access? 1062355d6bb5Sswilcox */ 1063355d6bb5Sswilcox if (hasmntopt(mntent, MNTOPT_RO) != 0) 1064355d6bb5Sswilcox found = M_RO; /* mounted as RO */ 10657c478bd9Sstevel@tonic-gate else 1066355d6bb5Sswilcox found = M_RW; /* mounted as R/W */ 1067355d6bb5Sswilcox 10687c478bd9Sstevel@tonic-gate if (mount_point == NULL) { 1069355d6bb5Sswilcox mount_point = strdup(mntent->mnt_mountp); 10707c478bd9Sstevel@tonic-gate if (mount_point == NULL) { 1071355d6bb5Sswilcox errexit("fsck: memory allocation failure: %s", 1072355d6bb5Sswilcox strerror(errno)); 1073355d6bb5Sswilcox /* NOTREACHED */ 10747c478bd9Sstevel@tonic-gate } 1075355d6bb5Sswilcox 1076355d6bb5Sswilcox if (devstr != NULL && str_size > 0) 1077355d6bb5Sswilcox (void) strlcpy(devstr, mntent->mnt_special, str_size); 10787c478bd9Sstevel@tonic-gate } 1079355d6bb5Sswilcox 10807c478bd9Sstevel@tonic-gate return (found); 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate /* 10847c478bd9Sstevel@tonic-gate * Check to see if name corresponds to an entry in vfstab, and that the entry 10857c478bd9Sstevel@tonic-gate * does not have option ro. 10867c478bd9Sstevel@tonic-gate */ 1087355d6bb5Sswilcox int 1088355d6bb5Sswilcox writable(caddr_t name) 10897c478bd9Sstevel@tonic-gate { 10907c478bd9Sstevel@tonic-gate int rw = 1; 1091355d6bb5Sswilcox struct vfstab vfsbuf, vfskey; 10927c478bd9Sstevel@tonic-gate FILE *vfstab; 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate vfstab = fopen(VFSTAB, "r"); 10957c478bd9Sstevel@tonic-gate if (vfstab == NULL) { 1096355d6bb5Sswilcox (void) printf("can't open %s\n", VFSTAB); 10977c478bd9Sstevel@tonic-gate return (1); 10987c478bd9Sstevel@tonic-gate } 1099355d6bb5Sswilcox (void) memset((void *)&vfskey, 0, sizeof (vfskey)); 1100355d6bb5Sswilcox vfsnull(&vfskey); 1101355d6bb5Sswilcox vfskey.vfs_special = unrawname(name); 1102355d6bb5Sswilcox vfskey.vfs_fstype = MNTTYPE_UFS; 1103355d6bb5Sswilcox if ((getvfsany(vfstab, &vfsbuf, &vfskey) == 0) && 11047c478bd9Sstevel@tonic-gate (hasvfsopt(&vfsbuf, MNTOPT_RO))) { 11057c478bd9Sstevel@tonic-gate rw = 0; 11067c478bd9Sstevel@tonic-gate } 1107355d6bb5Sswilcox (void) fclose(vfstab); 11087c478bd9Sstevel@tonic-gate return (rw); 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate /* 11127c478bd9Sstevel@tonic-gate * debugclean 11137c478bd9Sstevel@tonic-gate */ 1114355d6bb5Sswilcox static void 1115355d6bb5Sswilcox debugclean(void) 11167c478bd9Sstevel@tonic-gate { 1117355d6bb5Sswilcox if (!debug) 11187c478bd9Sstevel@tonic-gate return; 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate if ((iscorrupt == 0) && (isdirty == 0)) 11217c478bd9Sstevel@tonic-gate return; 11227c478bd9Sstevel@tonic-gate 1123355d6bb5Sswilcox if ((sblock.fs_clean == FSSTABLE) || (sblock.fs_clean == FSCLEAN) || 1124355d6bb5Sswilcox (sblock.fs_clean == FSLOG && islog && islogok) || 1125355d6bb5Sswilcox ((FSOKAY == (sblock.fs_state + sblock.fs_time)) && !errorlocked)) 11267c478bd9Sstevel@tonic-gate return; 11277c478bd9Sstevel@tonic-gate 1128355d6bb5Sswilcox (void) printf("WARNING: inconsistencies detected on %s filesystem %s\n", 11297c478bd9Sstevel@tonic-gate sblock.fs_clean == FSSTABLE ? "stable" : 11307c478bd9Sstevel@tonic-gate sblock.fs_clean == FSLOG ? "logging" : 1131355d6bb5Sswilcox sblock.fs_clean == FSFIX ? "being fixed" : "clean", 1132355d6bb5Sswilcox devname); 11337c478bd9Sstevel@tonic-gate } 11347c478bd9Sstevel@tonic-gate 11357c478bd9Sstevel@tonic-gate /* 11367c478bd9Sstevel@tonic-gate * updateclean 11377c478bd9Sstevel@tonic-gate * Carefully and transparently update the clean flag. 1138355d6bb5Sswilcox * 1139355d6bb5Sswilcox * `iscorrupt' has to be in its final state before this is called. 11407c478bd9Sstevel@tonic-gate */ 1141355d6bb5Sswilcox int 1142355d6bb5Sswilcox updateclean(void) 11437c478bd9Sstevel@tonic-gate { 1144355d6bb5Sswilcox int freedlog = 0; 11457c478bd9Sstevel@tonic-gate struct bufarea cleanbuf; 1146355d6bb5Sswilcox size_t size; 1147355d6bb5Sswilcox ssize_t io_res; 1148355d6bb5Sswilcox diskaddr_t bno; 1149355d6bb5Sswilcox char fsclean; 11507c478bd9Sstevel@tonic-gate int fsreclaim; 1151355d6bb5Sswilcox char fsflags; 115220a1ae8aSjkennedy int flags_ok = 1; 11537c478bd9Sstevel@tonic-gate daddr32_t fslogbno; 11547c478bd9Sstevel@tonic-gate offset_t sblkoff; 11557c478bd9Sstevel@tonic-gate time_t t; 11567c478bd9Sstevel@tonic-gate 11577c478bd9Sstevel@tonic-gate /* 11587c478bd9Sstevel@tonic-gate * debug stuff 11597c478bd9Sstevel@tonic-gate */ 11607c478bd9Sstevel@tonic-gate debugclean(); 11617c478bd9Sstevel@tonic-gate 11627c478bd9Sstevel@tonic-gate /* 11637c478bd9Sstevel@tonic-gate * set fsclean to its appropriate value 11647c478bd9Sstevel@tonic-gate */ 11657c478bd9Sstevel@tonic-gate fslogbno = sblock.fs_logbno; 11667c478bd9Sstevel@tonic-gate fsclean = sblock.fs_clean; 11677c478bd9Sstevel@tonic-gate fsreclaim = sblock.fs_reclaim; 11687c478bd9Sstevel@tonic-gate fsflags = sblock.fs_flags; 1169355d6bb5Sswilcox if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) { 11707c478bd9Sstevel@tonic-gate fsclean = FSACTIVE; 1171355d6bb5Sswilcox } 1172355d6bb5Sswilcox /* 1173355d6bb5Sswilcox * If ufs log is not okay, note that we need to clear it. 1174355d6bb5Sswilcox */ 117539542a18Sabalfour examinelog(NULL); 11767c478bd9Sstevel@tonic-gate if (fslogbno && !(islog && islogok)) { 11777c478bd9Sstevel@tonic-gate fsclean = FSACTIVE; 11787c478bd9Sstevel@tonic-gate fslogbno = 0; 11797c478bd9Sstevel@tonic-gate } 11807c478bd9Sstevel@tonic-gate 11817c478bd9Sstevel@tonic-gate /* 11827c478bd9Sstevel@tonic-gate * if necessary, update fs_clean and fs_state 11837c478bd9Sstevel@tonic-gate */ 11847c478bd9Sstevel@tonic-gate switch (fsclean) { 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate case FSACTIVE: 11877c478bd9Sstevel@tonic-gate if (!iscorrupt) { 11887c478bd9Sstevel@tonic-gate fsclean = FSSTABLE; 11897c478bd9Sstevel@tonic-gate fsreclaim = 0; 11907c478bd9Sstevel@tonic-gate } 11917c478bd9Sstevel@tonic-gate break; 11927c478bd9Sstevel@tonic-gate 11937c478bd9Sstevel@tonic-gate case FSCLEAN: 11947c478bd9Sstevel@tonic-gate case FSSTABLE: 1195355d6bb5Sswilcox if (iscorrupt) { 11967c478bd9Sstevel@tonic-gate fsclean = FSACTIVE; 1197355d6bb5Sswilcox } else { 11987c478bd9Sstevel@tonic-gate fsreclaim = 0; 1199355d6bb5Sswilcox } 12007c478bd9Sstevel@tonic-gate break; 12017c478bd9Sstevel@tonic-gate 12027c478bd9Sstevel@tonic-gate case FSLOG: 1203355d6bb5Sswilcox if (iscorrupt) { 12047c478bd9Sstevel@tonic-gate fsclean = FSACTIVE; 1205355d6bb5Sswilcox } else if (!islog || fslogbno == 0) { 12067c478bd9Sstevel@tonic-gate fsclean = FSSTABLE; 12077c478bd9Sstevel@tonic-gate fsreclaim = 0; 1208355d6bb5Sswilcox } else if (fflag) { 1209355d6bb5Sswilcox fsreclaim = 0; 1210355d6bb5Sswilcox } 12117c478bd9Sstevel@tonic-gate break; 12127c478bd9Sstevel@tonic-gate 12137c478bd9Sstevel@tonic-gate case FSFIX: 12147c478bd9Sstevel@tonic-gate fsclean = FSBAD; 12157c478bd9Sstevel@tonic-gate if (errorlocked && !iscorrupt) { 12167c478bd9Sstevel@tonic-gate fsclean = islog ? FSLOG : FSCLEAN; 12177c478bd9Sstevel@tonic-gate } 12187c478bd9Sstevel@tonic-gate break; 12197c478bd9Sstevel@tonic-gate 12207c478bd9Sstevel@tonic-gate default: 1221355d6bb5Sswilcox if (iscorrupt) { 12227c478bd9Sstevel@tonic-gate fsclean = FSACTIVE; 1223355d6bb5Sswilcox } else { 12247c478bd9Sstevel@tonic-gate fsclean = FSSTABLE; 12257c478bd9Sstevel@tonic-gate fsreclaim = 0; 12267c478bd9Sstevel@tonic-gate } 12277c478bd9Sstevel@tonic-gate } 12287c478bd9Sstevel@tonic-gate 12297c478bd9Sstevel@tonic-gate if (largefile_count > 0) 12307c478bd9Sstevel@tonic-gate fsflags |= FSLARGEFILES; 12317c478bd9Sstevel@tonic-gate else 12327c478bd9Sstevel@tonic-gate fsflags &= ~FSLARGEFILES; 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate /* 123520a1ae8aSjkennedy * There can be two discrepencies here. A) The superblock 123620a1ae8aSjkennedy * shows no largefiles but we found some while scanning. 123720a1ae8aSjkennedy * B) The superblock indicates the presence of largefiles, 123820a1ae8aSjkennedy * but none are present. Note that if preening, the superblock 123920a1ae8aSjkennedy * is silently corrected. 12407c478bd9Sstevel@tonic-gate */ 124120a1ae8aSjkennedy if ((fsflags == FSLARGEFILES && sblock.fs_flags != FSLARGEFILES) || 124220a1ae8aSjkennedy (fsflags != FSLARGEFILES && sblock.fs_flags == FSLARGEFILES)) 1243355d6bb5Sswilcox flags_ok = 0; 1244355d6bb5Sswilcox 12457c478bd9Sstevel@tonic-gate if (debug) 1246355d6bb5Sswilcox (void) printf( 1247355d6bb5Sswilcox "** largefile count=%d, fs.fs_flags=%x, flags_ok %d\n", 1248355d6bb5Sswilcox largefile_count, sblock.fs_flags, flags_ok); 12497c478bd9Sstevel@tonic-gate 1250355d6bb5Sswilcox /* 1251355d6bb5Sswilcox * If fs is unchanged, do nothing. 1252355d6bb5Sswilcox */ 1253355d6bb5Sswilcox if ((!isdirty) && (flags_ok) && 12547c478bd9Sstevel@tonic-gate (fslogbno == sblock.fs_logbno) && 1255355d6bb5Sswilcox (sblock.fs_clean == fsclean) && 1256355d6bb5Sswilcox (sblock.fs_reclaim == fsreclaim) && 12577c478bd9Sstevel@tonic-gate (FSOKAY == (sblock.fs_state + sblock.fs_time))) { 12587c478bd9Sstevel@tonic-gate if (errorlocked) { 12597c478bd9Sstevel@tonic-gate if (!do_errorlock(LOCKFS_ULOCK)) 12607c478bd9Sstevel@tonic-gate pwarn( 12617c478bd9Sstevel@tonic-gate "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n"); 12627c478bd9Sstevel@tonic-gate } 1263355d6bb5Sswilcox return (freedlog); 12647c478bd9Sstevel@tonic-gate } 12657c478bd9Sstevel@tonic-gate 12667c478bd9Sstevel@tonic-gate /* 12677c478bd9Sstevel@tonic-gate * if user allows, update superblock state 12687c478bd9Sstevel@tonic-gate */ 1269355d6bb5Sswilcox if (debug) { 1270355d6bb5Sswilcox (void) printf( 1271355d6bb5Sswilcox "superblock: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n", 1272355d6bb5Sswilcox sblock.fs_flags, sblock.fs_logbno, 1273355d6bb5Sswilcox sblock.fs_clean, sblock.fs_reclaim, 1274355d6bb5Sswilcox sblock.fs_state + sblock.fs_time); 1275355d6bb5Sswilcox (void) printf( 1276355d6bb5Sswilcox "calculated: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n", 1277355d6bb5Sswilcox fsflags, fslogbno, fsclean, fsreclaim, FSOKAY); 1278355d6bb5Sswilcox } 1279355d6bb5Sswilcox if (!isdirty && !preen && !rerun && 12807c478bd9Sstevel@tonic-gate (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0)) 1281355d6bb5Sswilcox return (freedlog); 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate (void) time(&t); 12847c478bd9Sstevel@tonic-gate sblock.fs_time = (time32_t)t; 12857c478bd9Sstevel@tonic-gate if (debug) 12867c478bd9Sstevel@tonic-gate printclean(); 1287355d6bb5Sswilcox 1288355d6bb5Sswilcox if (sblock.fs_logbno != fslogbno) { 128939542a18Sabalfour examinelog(&freelogblk); 1290355d6bb5Sswilcox freedlog++; 1291355d6bb5Sswilcox } 1292355d6bb5Sswilcox 12937c478bd9Sstevel@tonic-gate sblock.fs_logbno = fslogbno; 12947c478bd9Sstevel@tonic-gate sblock.fs_clean = fsclean; 12957c478bd9Sstevel@tonic-gate sblock.fs_state = FSOKAY - (long)sblock.fs_time; 12967c478bd9Sstevel@tonic-gate sblock.fs_reclaim = fsreclaim; 12977c478bd9Sstevel@tonic-gate sblock.fs_flags = fsflags; 12987c478bd9Sstevel@tonic-gate 12997c478bd9Sstevel@tonic-gate /* 13007c478bd9Sstevel@tonic-gate * if superblock can't be written, return 13017c478bd9Sstevel@tonic-gate */ 13027c478bd9Sstevel@tonic-gate if (fswritefd < 0) 1303355d6bb5Sswilcox return (freedlog); 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate /* 1306355d6bb5Sswilcox * Read private copy of superblock, update clean flag, and write it. 13077c478bd9Sstevel@tonic-gate */ 13087c478bd9Sstevel@tonic-gate bno = sblk.b_bno; 13097c478bd9Sstevel@tonic-gate size = sblk.b_size; 13107c478bd9Sstevel@tonic-gate 13117c478bd9Sstevel@tonic-gate sblkoff = ldbtob(bno); 13127c478bd9Sstevel@tonic-gate 13137c478bd9Sstevel@tonic-gate if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL) 13147c478bd9Sstevel@tonic-gate errexit("out of memory"); 1315f763a6cdScasper if (llseek(fsreadfd, sblkoff, SEEK_SET) == -1) { 1316355d6bb5Sswilcox (void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n", 1317355d6bb5Sswilcox (longlong_t)bno, strerror(errno)); 1318355d6bb5Sswilcox goto out; 1319355d6bb5Sswilcox } 13207c478bd9Sstevel@tonic-gate 1321355d6bb5Sswilcox if ((io_res = read(fsreadfd, cleanbuf.b_un.b_buf, size)) != size) { 1322355d6bb5Sswilcox report_io_prob("READ FROM", bno, size, io_res); 1323355d6bb5Sswilcox goto out; 1324355d6bb5Sswilcox } 13257c478bd9Sstevel@tonic-gate 13267c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_logbno = sblock.fs_logbno; 13277c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_clean = sblock.fs_clean; 13287c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_state = sblock.fs_state; 13297c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_time = sblock.fs_time; 13307c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim; 13317c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_flags = sblock.fs_flags; 13327c478bd9Sstevel@tonic-gate 1333f763a6cdScasper if (llseek(fswritefd, sblkoff, SEEK_SET) == -1) { 1334355d6bb5Sswilcox (void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n", 1335355d6bb5Sswilcox (longlong_t)bno, strerror(errno)); 1336355d6bb5Sswilcox goto out; 1337355d6bb5Sswilcox } 1338355d6bb5Sswilcox 1339355d6bb5Sswilcox if ((io_res = write(fswritefd, cleanbuf.b_un.b_buf, size)) != size) { 1340355d6bb5Sswilcox report_io_prob("WRITE TO", bno, size, io_res); 1341355d6bb5Sswilcox goto out; 1342355d6bb5Sswilcox } 13437c478bd9Sstevel@tonic-gate 13447c478bd9Sstevel@tonic-gate /* 13457c478bd9Sstevel@tonic-gate * 1208040 13467c478bd9Sstevel@tonic-gate * If we had to use -b to grab an alternate superblock, then we 13477c478bd9Sstevel@tonic-gate * likely had to do so because of unacceptable differences between 1348355d6bb5Sswilcox * the main and alternate superblocks. So, we had better update 13497c478bd9Sstevel@tonic-gate * the alternate superblock as well, or we'll just fail again 13507c478bd9Sstevel@tonic-gate * the next time we attempt to run fsck! 13517c478bd9Sstevel@tonic-gate */ 1352355d6bb5Sswilcox if (bflag != 0) { 1353355d6bb5Sswilcox write_altsb(fswritefd); 13547c478bd9Sstevel@tonic-gate } 13557c478bd9Sstevel@tonic-gate 13567c478bd9Sstevel@tonic-gate if (errorlocked) { 13577c478bd9Sstevel@tonic-gate if (!do_errorlock(LOCKFS_ULOCK)) 13587c478bd9Sstevel@tonic-gate pwarn( 13597c478bd9Sstevel@tonic-gate "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n"); 13607c478bd9Sstevel@tonic-gate } 1361355d6bb5Sswilcox 1362355d6bb5Sswilcox out: 1363355d6bb5Sswilcox if (cleanbuf.b_un.b_buf != NULL) { 1364355d6bb5Sswilcox free((void *)cleanbuf.b_un.b_buf); 1365355d6bb5Sswilcox } 1366355d6bb5Sswilcox 1367355d6bb5Sswilcox return (freedlog); 1368355d6bb5Sswilcox } 1369355d6bb5Sswilcox 1370355d6bb5Sswilcox static void 1371355d6bb5Sswilcox report_io_prob(caddr_t what, diskaddr_t bno, size_t expected, ssize_t failure) 1372355d6bb5Sswilcox { 1373355d6bb5Sswilcox if (failure < 0) 1374355d6bb5Sswilcox (void) printf("COULD NOT %s SUPERBLOCK AT %d: %s\n", 1375355d6bb5Sswilcox what, (int)bno, strerror(errno)); 1376355d6bb5Sswilcox else if (failure == 0) 1377355d6bb5Sswilcox (void) printf("COULD NOT %s SUPERBLOCK AT %d: EOF\n", 1378355d6bb5Sswilcox what, (int)bno); 1379355d6bb5Sswilcox else 1380355d6bb5Sswilcox (void) printf("SHORT %s SUPERBLOCK AT %d: %u out of %u bytes\n", 1381355d6bb5Sswilcox what, (int)bno, (unsigned)failure, (unsigned)expected); 13827c478bd9Sstevel@tonic-gate } 13837c478bd9Sstevel@tonic-gate 13847c478bd9Sstevel@tonic-gate /* 13857c478bd9Sstevel@tonic-gate * print out clean info 13867c478bd9Sstevel@tonic-gate */ 1387355d6bb5Sswilcox void 1388355d6bb5Sswilcox printclean(void) 13897c478bd9Sstevel@tonic-gate { 1390355d6bb5Sswilcox caddr_t s; 13917c478bd9Sstevel@tonic-gate 13927c478bd9Sstevel@tonic-gate if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) 13937c478bd9Sstevel@tonic-gate s = "unknown"; 13947c478bd9Sstevel@tonic-gate else 13957c478bd9Sstevel@tonic-gate switch (sblock.fs_clean) { 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate case FSACTIVE: 13987c478bd9Sstevel@tonic-gate s = "active"; 13997c478bd9Sstevel@tonic-gate break; 14007c478bd9Sstevel@tonic-gate 14017c478bd9Sstevel@tonic-gate case FSCLEAN: 14027c478bd9Sstevel@tonic-gate s = "clean"; 14037c478bd9Sstevel@tonic-gate break; 14047c478bd9Sstevel@tonic-gate 14057c478bd9Sstevel@tonic-gate case FSSTABLE: 14067c478bd9Sstevel@tonic-gate s = "stable"; 14077c478bd9Sstevel@tonic-gate break; 14087c478bd9Sstevel@tonic-gate 14097c478bd9Sstevel@tonic-gate case FSLOG: 14107c478bd9Sstevel@tonic-gate s = "logging"; 14117c478bd9Sstevel@tonic-gate break; 14127c478bd9Sstevel@tonic-gate 14137c478bd9Sstevel@tonic-gate case FSBAD: 14147c478bd9Sstevel@tonic-gate s = "is bad"; 14157c478bd9Sstevel@tonic-gate break; 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate case FSFIX: 14187c478bd9Sstevel@tonic-gate s = "being fixed"; 14197c478bd9Sstevel@tonic-gate break; 14207c478bd9Sstevel@tonic-gate 14217c478bd9Sstevel@tonic-gate default: 14227c478bd9Sstevel@tonic-gate s = "unknown"; 14237c478bd9Sstevel@tonic-gate } 14247c478bd9Sstevel@tonic-gate 14257c478bd9Sstevel@tonic-gate if (preen) 14267c478bd9Sstevel@tonic-gate pwarn("is %s.\n", s); 14277c478bd9Sstevel@tonic-gate else 1428355d6bb5Sswilcox (void) printf("** %s is %s.\n", devname, s); 14297c478bd9Sstevel@tonic-gate } 14307c478bd9Sstevel@tonic-gate 1431355d6bb5Sswilcox int 1432355d6bb5Sswilcox is_errorlocked(caddr_t fs) 14337c478bd9Sstevel@tonic-gate { 1434355d6bb5Sswilcox int retval; 14357c478bd9Sstevel@tonic-gate struct stat64 statb; 1436355d6bb5Sswilcox caddr_t mountp; 1437355d6bb5Sswilcox struct mnttab *mntent; 14387c478bd9Sstevel@tonic-gate 1439355d6bb5Sswilcox retval = 0; 14407c478bd9Sstevel@tonic-gate 14417c478bd9Sstevel@tonic-gate if (!fs) 14427c478bd9Sstevel@tonic-gate return (0); 14437c478bd9Sstevel@tonic-gate 14447c478bd9Sstevel@tonic-gate if (stat64(fs, &statb) < 0) 14457c478bd9Sstevel@tonic-gate return (0); 14467c478bd9Sstevel@tonic-gate 14477c478bd9Sstevel@tonic-gate if (S_ISDIR(statb.st_mode)) { 14487c478bd9Sstevel@tonic-gate mountp = fs; 14497c478bd9Sstevel@tonic-gate } else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) { 1450355d6bb5Sswilcox mntent = search_mnttab(NULL, fs, NULL, 0); 1451355d6bb5Sswilcox if (mntent == NULL) 14527c478bd9Sstevel@tonic-gate return (0); 1453355d6bb5Sswilcox mountp = mntent->mnt_mountp; 1454355d6bb5Sswilcox if (mountp == NULL) /* theoretically a can't-happen */ 1455355d6bb5Sswilcox return (0); 14567c478bd9Sstevel@tonic-gate } else { 14577c478bd9Sstevel@tonic-gate return (0); 14587c478bd9Sstevel@tonic-gate } 14597c478bd9Sstevel@tonic-gate 1460355d6bb5Sswilcox /* 1461355d6bb5Sswilcox * From here on, must `goto out' to avoid memory leakage. 1462355d6bb5Sswilcox */ 14637c478bd9Sstevel@tonic-gate 14647c478bd9Sstevel@tonic-gate if (elock_combuf == NULL) 1465355d6bb5Sswilcox elock_combuf = 1466355d6bb5Sswilcox (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char)); 1467355d6bb5Sswilcox else 1468355d6bb5Sswilcox elock_combuf = 1469355d6bb5Sswilcox (caddr_t)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN); 14707c478bd9Sstevel@tonic-gate 1471355d6bb5Sswilcox if (elock_combuf == NULL) 1472355d6bb5Sswilcox goto out; 1473355d6bb5Sswilcox 1474355d6bb5Sswilcox (void) memset((void *)elock_combuf, 0, LOCKFS_MAXCOMMENTLEN); 1475355d6bb5Sswilcox 1476355d6bb5Sswilcox if (elock_mountp != NULL) { 1477355d6bb5Sswilcox free(elock_mountp); 1478355d6bb5Sswilcox } 14797c478bd9Sstevel@tonic-gate 14807c478bd9Sstevel@tonic-gate elock_mountp = strdup(mountp); 1481355d6bb5Sswilcox if (elock_mountp == NULL) 1482355d6bb5Sswilcox goto out; 14837c478bd9Sstevel@tonic-gate 14847c478bd9Sstevel@tonic-gate if (mountfd < 0) { 14857c478bd9Sstevel@tonic-gate if ((mountfd = open64(mountp, O_RDONLY)) == -1) 1486355d6bb5Sswilcox goto out; 14877c478bd9Sstevel@tonic-gate } 14887c478bd9Sstevel@tonic-gate 1489355d6bb5Sswilcox if (lfp == NULL) { 14907c478bd9Sstevel@tonic-gate lfp = (struct lockfs *)malloc(sizeof (struct lockfs)); 1491355d6bb5Sswilcox if (lfp == NULL) 1492355d6bb5Sswilcox goto out; 1493355d6bb5Sswilcox (void) memset((void *)lfp, 0, sizeof (struct lockfs)); 14947c478bd9Sstevel@tonic-gate } 14957c478bd9Sstevel@tonic-gate 14967c478bd9Sstevel@tonic-gate lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN; 14977c478bd9Sstevel@tonic-gate lfp->lf_comment = elock_combuf; 14987c478bd9Sstevel@tonic-gate 14997c478bd9Sstevel@tonic-gate if (ioctl(mountfd, _FIOLFSS, lfp) == -1) 1500355d6bb5Sswilcox goto out; 15017c478bd9Sstevel@tonic-gate 1502355d6bb5Sswilcox /* 1503355d6bb5Sswilcox * lint believes that the ioctl() (or any other function 1504355d6bb5Sswilcox * taking lfp as an arg) could free lfp. This is not the 1505355d6bb5Sswilcox * case, however. 1506355d6bb5Sswilcox */ 1507355d6bb5Sswilcox retval = LOCKFS_IS_ELOCK(lfp); 1508355d6bb5Sswilcox 1509355d6bb5Sswilcox out: 1510355d6bb5Sswilcox return (retval); 15117c478bd9Sstevel@tonic-gate } 15127c478bd9Sstevel@tonic-gate 1513355d6bb5Sswilcox /* 1514355d6bb5Sswilcox * Given a name which is known to be a directory, see if it appears 1515355d6bb5Sswilcox * in the vfstab. If so, return the entry's block (special) device 1516355d6bb5Sswilcox * field via devstr. 1517355d6bb5Sswilcox */ 1518355d6bb5Sswilcox int 1519355d6bb5Sswilcox check_vfstab(caddr_t name, caddr_t devstr, size_t str_size) 1520355d6bb5Sswilcox { 1521355d6bb5Sswilcox return (NULL != search_vfstab(name, NULL, devstr, str_size)); 15227c478bd9Sstevel@tonic-gate } 15237c478bd9Sstevel@tonic-gate 1524355d6bb5Sswilcox /* 1525355d6bb5Sswilcox * Given a name which is known to be a directory, see if it appears 1526355d6bb5Sswilcox * in the mnttab. If so, return the entry's block (special) device 1527355d6bb5Sswilcox * field via devstr. 1528355d6bb5Sswilcox */ 1529355d6bb5Sswilcox int 1530355d6bb5Sswilcox check_mnttab(caddr_t name, caddr_t devstr, size_t str_size) 1531355d6bb5Sswilcox { 1532355d6bb5Sswilcox return (NULL != search_mnttab(name, NULL, devstr, str_size)); 1533355d6bb5Sswilcox } 1534355d6bb5Sswilcox 1535355d6bb5Sswilcox /* 1536355d6bb5Sswilcox * Search for mount point and/or special device in the given file. 1537355d6bb5Sswilcox * The first matching entry is returned. 1538355d6bb5Sswilcox * 1539355d6bb5Sswilcox * If an entry is found and str_size is greater than zero, then 1540355d6bb5Sswilcox * up to size_str bytes of the special device name from the entry 1541355d6bb5Sswilcox * are copied to devstr. 1542355d6bb5Sswilcox */ 1543355d6bb5Sswilcox 1544355d6bb5Sswilcox #define SEARCH_TAB_BODY(st_type, st_file, st_mount, st_special, \ 1545355d6bb5Sswilcox st_nuller, st_init, st_searcher) \ 1546355d6bb5Sswilcox { \ 1547355d6bb5Sswilcox FILE *fp; \ 1548355d6bb5Sswilcox struct st_type *retval = NULL; \ 1549355d6bb5Sswilcox struct st_type key; \ 1550355d6bb5Sswilcox static struct st_type buffer; \ 1551355d6bb5Sswilcox \ 1552355d6bb5Sswilcox /* LINTED ``assigned value never used'' */ \ 1553355d6bb5Sswilcox st_nuller(&key); \ 1554355d6bb5Sswilcox key.st_mount = mountp; \ 1555355d6bb5Sswilcox key.st_special = special; \ 1556355d6bb5Sswilcox st_init; \ 1557355d6bb5Sswilcox \ 1558355d6bb5Sswilcox if ((fp = fopen(st_file, "r")) == NULL) \ 1559355d6bb5Sswilcox return (NULL); \ 1560355d6bb5Sswilcox \ 1561355d6bb5Sswilcox if (st_searcher(fp, &buffer, &key) == 0) { \ 1562355d6bb5Sswilcox retval = &buffer; \ 1563355d6bb5Sswilcox if (devstr != NULL && str_size > 0 && \ 1564355d6bb5Sswilcox buffer.st_special != NULL) { \ 1565355d6bb5Sswilcox (void) strlcpy(devstr, buffer.st_special, \ 1566355d6bb5Sswilcox str_size); \ 1567355d6bb5Sswilcox } \ 1568355d6bb5Sswilcox } \ 1569355d6bb5Sswilcox (void) fclose(fp); \ 1570355d6bb5Sswilcox return (retval); \ 1571355d6bb5Sswilcox } 1572355d6bb5Sswilcox 1573355d6bb5Sswilcox static struct vfstab * 1574355d6bb5Sswilcox search_vfstab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size) 1575355d6bb5Sswilcox SEARCH_TAB_BODY(vfstab, VFSTAB, vfs_mountp, vfs_special, vfsnull, 1576355d6bb5Sswilcox (retval = retval), getvfsany) 1577355d6bb5Sswilcox 1578355d6bb5Sswilcox static struct mnttab * 1579355d6bb5Sswilcox search_mnttab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size) 1580355d6bb5Sswilcox SEARCH_TAB_BODY(mnttab, MNTTAB, mnt_mountp, mnt_special, mntnull, 1581355d6bb5Sswilcox (key.mnt_fstype = MNTTYPE_UFS), getmntany) 1582355d6bb5Sswilcox 1583355d6bb5Sswilcox int 15847c478bd9Sstevel@tonic-gate do_errorlock(int lock_type) 15857c478bd9Sstevel@tonic-gate { 1586355d6bb5Sswilcox caddr_t buf; 15877c478bd9Sstevel@tonic-gate time_t now; 15887c478bd9Sstevel@tonic-gate struct tm *local; 1589355d6bb5Sswilcox int rc; 15907c478bd9Sstevel@tonic-gate 1591355d6bb5Sswilcox if (elock_combuf == NULL) 15927c478bd9Sstevel@tonic-gate errexit("do_errorlock(%s, %d): unallocated elock_combuf\n", 1593355d6bb5Sswilcox elock_mountp ? elock_mountp : "<null>", 1594355d6bb5Sswilcox lock_type); 15957c478bd9Sstevel@tonic-gate 1596355d6bb5Sswilcox if ((buf = (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char))) == 1597355d6bb5Sswilcox NULL) { 15987c478bd9Sstevel@tonic-gate errexit("Couldn't alloc memory for temp. lock status buffer\n"); 1599355d6bb5Sswilcox } 1600355d6bb5Sswilcox if (lfp == NULL) { 16017c478bd9Sstevel@tonic-gate errexit("do_errorlock(%s, %d): lockfs status unallocated\n", 16027c478bd9Sstevel@tonic-gate elock_mountp, lock_type); 16037c478bd9Sstevel@tonic-gate } 16047c478bd9Sstevel@tonic-gate 1605355d6bb5Sswilcox (void) memmove((void *)buf, (void *)elock_combuf, 1606355d6bb5Sswilcox LOCKFS_MAXCOMMENTLEN-1); 16077c478bd9Sstevel@tonic-gate 16087c478bd9Sstevel@tonic-gate switch (lock_type) { 16097c478bd9Sstevel@tonic-gate case LOCKFS_ELOCK: 1610355d6bb5Sswilcox /* 1611355d6bb5Sswilcox * Note that if it is error-locked, we won't get an 1612355d6bb5Sswilcox * error back if we try to error-lock it again. 1613355d6bb5Sswilcox */ 16147c478bd9Sstevel@tonic-gate if (time(&now) != (time_t)-1) { 16157c478bd9Sstevel@tonic-gate if ((local = localtime(&now)) != NULL) 1616355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 16177c478bd9Sstevel@tonic-gate "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d", 1618355d6bb5Sswilcox elock_combuf, (int)pid, 16197c478bd9Sstevel@tonic-gate local->tm_mon + 1, local->tm_mday, 16207c478bd9Sstevel@tonic-gate (local->tm_year % 100), local->tm_hour, 16217c478bd9Sstevel@tonic-gate local->tm_min, local->tm_sec); 16227c478bd9Sstevel@tonic-gate else 1623355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 1624355d6bb5Sswilcox "%s [fsck pid %d", elock_combuf, pid); 16257c478bd9Sstevel@tonic-gate 16267c478bd9Sstevel@tonic-gate } else { 1627355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 1628355d6bb5Sswilcox "%s [fsck pid %d", elock_combuf, pid); 16297c478bd9Sstevel@tonic-gate } 16307c478bd9Sstevel@tonic-gate break; 16317c478bd9Sstevel@tonic-gate 16327c478bd9Sstevel@tonic-gate case LOCKFS_ULOCK: 16337c478bd9Sstevel@tonic-gate if (time(&now) != (time_t)-1) { 16347c478bd9Sstevel@tonic-gate if ((local = localtime(&now)) != NULL) { 1635355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 16367c478bd9Sstevel@tonic-gate "%s, done:%02d/%02d/%02d %02d:%02d:%02d]", 16377c478bd9Sstevel@tonic-gate elock_combuf, 16387c478bd9Sstevel@tonic-gate local->tm_mon + 1, local->tm_mday, 16397c478bd9Sstevel@tonic-gate (local->tm_year % 100), local->tm_hour, 16407c478bd9Sstevel@tonic-gate local->tm_min, local->tm_sec); 16417c478bd9Sstevel@tonic-gate } else { 1642355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 1643355d6bb5Sswilcox "%s]", elock_combuf); 16447c478bd9Sstevel@tonic-gate } 16457c478bd9Sstevel@tonic-gate } else { 1646355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN, 1647355d6bb5Sswilcox "%s]", elock_combuf); 16487c478bd9Sstevel@tonic-gate } 16497c478bd9Sstevel@tonic-gate if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) { 1650355d6bb5Sswilcox pwarn("do_errorlock: unlock failed: %s\n", 1651355d6bb5Sswilcox strerror(errno)); 16527c478bd9Sstevel@tonic-gate goto out; 16537c478bd9Sstevel@tonic-gate } 16547c478bd9Sstevel@tonic-gate break; 16557c478bd9Sstevel@tonic-gate 16567c478bd9Sstevel@tonic-gate default: 16577c478bd9Sstevel@tonic-gate break; 16587c478bd9Sstevel@tonic-gate } 16597c478bd9Sstevel@tonic-gate 1660355d6bb5Sswilcox (void) memmove((void *)elock_combuf, (void *)buf, 1661355d6bb5Sswilcox LOCKFS_MAXCOMMENTLEN - 1); 16627c478bd9Sstevel@tonic-gate 16637c478bd9Sstevel@tonic-gate lfp->lf_lock = lock_type; 16647c478bd9Sstevel@tonic-gate lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN; 16657c478bd9Sstevel@tonic-gate lfp->lf_comment = elock_combuf; 16667c478bd9Sstevel@tonic-gate lfp->lf_flags = 0; 16677c478bd9Sstevel@tonic-gate errno = 0; 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) { 16707c478bd9Sstevel@tonic-gate if (errno == EINVAL) { 16717c478bd9Sstevel@tonic-gate pwarn("Another fsck active?\n"); 16727c478bd9Sstevel@tonic-gate iscorrupt = 0; /* don't go away mad, just go away */ 16737c478bd9Sstevel@tonic-gate } else { 1674355d6bb5Sswilcox pwarn("do_errorlock(lock_type:%d, %s) failed: %s\n", 1675355d6bb5Sswilcox lock_type, elock_combuf, strerror(errno)); 16767c478bd9Sstevel@tonic-gate } 16777c478bd9Sstevel@tonic-gate } 16787c478bd9Sstevel@tonic-gate out: 1679355d6bb5Sswilcox if (buf != NULL) { 1680355d6bb5Sswilcox free((void *)buf); 1681355d6bb5Sswilcox } 16827c478bd9Sstevel@tonic-gate 16837c478bd9Sstevel@tonic-gate return (rc != -1); 16847c478bd9Sstevel@tonic-gate } 16857c478bd9Sstevel@tonic-gate 16867c478bd9Sstevel@tonic-gate /* 1687355d6bb5Sswilcox * Shadow inode support. To register a shadow with a client is to note 1688355d6bb5Sswilcox * that an inode (the client) refers to the shadow. 16897c478bd9Sstevel@tonic-gate */ 16907c478bd9Sstevel@tonic-gate 16917c478bd9Sstevel@tonic-gate static struct shadowclients * 16927c478bd9Sstevel@tonic-gate newshadowclient(struct shadowclients *prev) 16937c478bd9Sstevel@tonic-gate { 16947c478bd9Sstevel@tonic-gate struct shadowclients *rc; 16957c478bd9Sstevel@tonic-gate 16967c478bd9Sstevel@tonic-gate rc = (struct shadowclients *)malloc(sizeof (*rc)); 16977c478bd9Sstevel@tonic-gate if (rc == NULL) 1698355d6bb5Sswilcox errexit("newshadowclient: cannot malloc shadow client"); 16997c478bd9Sstevel@tonic-gate rc->next = prev; 17007c478bd9Sstevel@tonic-gate rc->nclients = 0; 17017c478bd9Sstevel@tonic-gate 1702355d6bb5Sswilcox rc->client = (fsck_ino_t *)malloc(sizeof (fsck_ino_t) * 1703355d6bb5Sswilcox maxshadowclients); 17047c478bd9Sstevel@tonic-gate if (rc->client == NULL) 1705355d6bb5Sswilcox errexit("newshadowclient: cannot malloc client array"); 17067c478bd9Sstevel@tonic-gate return (rc); 17077c478bd9Sstevel@tonic-gate } 17087c478bd9Sstevel@tonic-gate 17097c478bd9Sstevel@tonic-gate void 1710355d6bb5Sswilcox registershadowclient(fsck_ino_t shadow, fsck_ino_t client, 1711355d6bb5Sswilcox struct shadowclientinfo **info) 17127c478bd9Sstevel@tonic-gate { 17137c478bd9Sstevel@tonic-gate struct shadowclientinfo *sci; 17147c478bd9Sstevel@tonic-gate struct shadowclients *scc; 17157c478bd9Sstevel@tonic-gate 1716355d6bb5Sswilcox /* 1717355d6bb5Sswilcox * Already have a record for this shadow? 1718355d6bb5Sswilcox */ 1719355d6bb5Sswilcox for (sci = *info; sci != NULL; sci = sci->next) 17207c478bd9Sstevel@tonic-gate if (sci->shadow == shadow) 17217c478bd9Sstevel@tonic-gate break; 17227c478bd9Sstevel@tonic-gate if (sci == NULL) { 1723355d6bb5Sswilcox /* 1724355d6bb5Sswilcox * It's a new shadow, add it to the list 1725355d6bb5Sswilcox */ 17267c478bd9Sstevel@tonic-gate sci = (struct shadowclientinfo *)malloc(sizeof (*sci)); 17277c478bd9Sstevel@tonic-gate if (sci == NULL) 17287c478bd9Sstevel@tonic-gate errexit("registershadowclient: cannot malloc"); 17297c478bd9Sstevel@tonic-gate sci->next = *info; 17307c478bd9Sstevel@tonic-gate *info = sci; 17317c478bd9Sstevel@tonic-gate sci->shadow = shadow; 17327c478bd9Sstevel@tonic-gate sci->totalClients = 0; 17337c478bd9Sstevel@tonic-gate sci->clients = newshadowclient(NULL); 17347c478bd9Sstevel@tonic-gate } 17357c478bd9Sstevel@tonic-gate 17367c478bd9Sstevel@tonic-gate sci->totalClients++; 17377c478bd9Sstevel@tonic-gate scc = sci->clients; 17387c478bd9Sstevel@tonic-gate if (scc->nclients >= maxshadowclients) { 17397c478bd9Sstevel@tonic-gate scc = newshadowclient(sci->clients); 17407c478bd9Sstevel@tonic-gate sci->clients = scc; 17417c478bd9Sstevel@tonic-gate } 17427c478bd9Sstevel@tonic-gate 17437c478bd9Sstevel@tonic-gate scc->client[scc->nclients++] = client; 17447c478bd9Sstevel@tonic-gate } 17457c478bd9Sstevel@tonic-gate 17467c478bd9Sstevel@tonic-gate /* 1747355d6bb5Sswilcox * Locate and discard a shadow. 1748355d6bb5Sswilcox */ 1749355d6bb5Sswilcox void 1750355d6bb5Sswilcox clearshadow(fsck_ino_t shadow, struct shadowclientinfo **info) 1751355d6bb5Sswilcox { 1752355d6bb5Sswilcox struct shadowclientinfo *sci, *prev; 1753355d6bb5Sswilcox 1754355d6bb5Sswilcox /* 1755355d6bb5Sswilcox * Do we have a record for this shadow? 1756355d6bb5Sswilcox */ 1757355d6bb5Sswilcox prev = NULL; 1758355d6bb5Sswilcox for (sci = *info; sci != NULL; sci = sci->next) { 1759355d6bb5Sswilcox if (sci->shadow == shadow) 1760355d6bb5Sswilcox break; 1761355d6bb5Sswilcox prev = sci; 1762355d6bb5Sswilcox } 1763355d6bb5Sswilcox 1764355d6bb5Sswilcox if (sci != NULL) { 1765355d6bb5Sswilcox /* 1766355d6bb5Sswilcox * First, pull it off the list, since we know there 1767355d6bb5Sswilcox * shouldn't be any future references to this one. 1768355d6bb5Sswilcox */ 1769355d6bb5Sswilcox if (prev == NULL) 1770355d6bb5Sswilcox *info = sci->next; 1771355d6bb5Sswilcox else 1772355d6bb5Sswilcox prev->next = sci->next; 1773355d6bb5Sswilcox deshadow(sci, clearattrref); 1774355d6bb5Sswilcox } 1775355d6bb5Sswilcox } 1776355d6bb5Sswilcox 1777355d6bb5Sswilcox /* 1778355d6bb5Sswilcox * Discard all memory used to track clients of a shadow. 1779355d6bb5Sswilcox */ 1780355d6bb5Sswilcox void 1781355d6bb5Sswilcox deshadow(struct shadowclientinfo *sci, void (*cb)(fsck_ino_t)) 1782355d6bb5Sswilcox { 1783355d6bb5Sswilcox struct shadowclients *clients, *discard; 1784355d6bb5Sswilcox int idx; 1785355d6bb5Sswilcox 1786355d6bb5Sswilcox clients = sci->clients; 1787355d6bb5Sswilcox while (clients != NULL) { 1788355d6bb5Sswilcox discard = clients; 1789355d6bb5Sswilcox clients = clients->next; 1790355d6bb5Sswilcox if (discard->client != NULL) { 1791355d6bb5Sswilcox if (cb != NULL) { 1792355d6bb5Sswilcox for (idx = 0; idx < discard->nclients; idx++) 1793355d6bb5Sswilcox (*cb)(discard->client[idx]); 1794355d6bb5Sswilcox } 1795355d6bb5Sswilcox free((void *)discard->client); 1796355d6bb5Sswilcox } 1797355d6bb5Sswilcox free((void *)discard); 1798355d6bb5Sswilcox } 1799355d6bb5Sswilcox 1800355d6bb5Sswilcox free((void *)sci); 1801355d6bb5Sswilcox } 1802355d6bb5Sswilcox 1803355d6bb5Sswilcox /* 18047c478bd9Sstevel@tonic-gate * Allocate more buffer as need arises but allocate one at a time. 18057c478bd9Sstevel@tonic-gate * This is done to make sure that fsck does not exit with error if it 1806355d6bb5Sswilcox * needs more buffer to complete its task. 18077c478bd9Sstevel@tonic-gate */ 18087c478bd9Sstevel@tonic-gate static struct bufarea * 1809355d6bb5Sswilcox alloc_bufarea(void) 18107c478bd9Sstevel@tonic-gate { 1811355d6bb5Sswilcox struct bufarea *newbp; 1812355d6bb5Sswilcox caddr_t bufp; 18137c478bd9Sstevel@tonic-gate 18147c478bd9Sstevel@tonic-gate bufp = malloc((unsigned int)sblock.fs_bsize); 1815355d6bb5Sswilcox if (bufp == NULL) 1816355d6bb5Sswilcox return (NULL); 1817355d6bb5Sswilcox 1818355d6bb5Sswilcox newbp = (struct bufarea *)malloc(sizeof (struct bufarea)); 1819355d6bb5Sswilcox if (newbp == NULL) { 1820355d6bb5Sswilcox free((void *)bufp); 18217c478bd9Sstevel@tonic-gate return (NULL); 18227c478bd9Sstevel@tonic-gate } 1823355d6bb5Sswilcox 1824355d6bb5Sswilcox initbarea(newbp); 1825355d6bb5Sswilcox newbp->b_un.b_buf = bufp; 1826355d6bb5Sswilcox newbp->b_prev = &bufhead; 1827355d6bb5Sswilcox newbp->b_next = bufhead.b_next; 1828355d6bb5Sswilcox bufhead.b_next->b_prev = newbp; 1829355d6bb5Sswilcox bufhead.b_next = newbp; 18307c478bd9Sstevel@tonic-gate bufhead.b_size++; 1831355d6bb5Sswilcox return (newbp); 1832355d6bb5Sswilcox } 1833355d6bb5Sswilcox 1834355d6bb5Sswilcox /* 1835355d6bb5Sswilcox * We length-limit in both unrawname() and rawname() to avoid 1836355d6bb5Sswilcox * overflowing our arrays or those of our naive, trusting callers. 1837355d6bb5Sswilcox */ 1838355d6bb5Sswilcox 1839355d6bb5Sswilcox caddr_t 1840355d6bb5Sswilcox unrawname(caddr_t name) 1841355d6bb5Sswilcox { 1842355d6bb5Sswilcox caddr_t dp; 1843355d6bb5Sswilcox static char fullname[MAXPATHLEN + 1]; 1844355d6bb5Sswilcox 1845355d6bb5Sswilcox if ((dp = getfullblkname(name)) == NULL) 1846355d6bb5Sswilcox return (""); 1847355d6bb5Sswilcox 1848355d6bb5Sswilcox (void) strlcpy(fullname, dp, sizeof (fullname)); 1849355d6bb5Sswilcox /* 1850355d6bb5Sswilcox * Not reporting under debug, as the allocation isn't 1851355d6bb5Sswilcox * reported by getfullblkname. The idea is that we 1852355d6bb5Sswilcox * produce balanced alloc/free instances. 1853355d6bb5Sswilcox */ 1854355d6bb5Sswilcox free(dp); 1855355d6bb5Sswilcox 1856355d6bb5Sswilcox return (fullname); 1857355d6bb5Sswilcox } 1858355d6bb5Sswilcox 1859355d6bb5Sswilcox caddr_t 1860355d6bb5Sswilcox rawname(caddr_t name) 1861355d6bb5Sswilcox { 1862355d6bb5Sswilcox caddr_t dp; 1863355d6bb5Sswilcox static char fullname[MAXPATHLEN + 1]; 1864355d6bb5Sswilcox 1865355d6bb5Sswilcox if ((dp = getfullrawname(name)) == NULL) 1866355d6bb5Sswilcox return (""); 1867355d6bb5Sswilcox 1868355d6bb5Sswilcox (void) strlcpy(fullname, dp, sizeof (fullname)); 1869355d6bb5Sswilcox /* 1870355d6bb5Sswilcox * Not reporting under debug, as the allocation isn't 1871355d6bb5Sswilcox * reported by getfullblkname. The idea is that we 1872355d6bb5Sswilcox * produce balanced alloc/free instances. 1873355d6bb5Sswilcox */ 1874355d6bb5Sswilcox free(dp); 1875355d6bb5Sswilcox 1876355d6bb5Sswilcox return (fullname); 1877355d6bb5Sswilcox } 1878355d6bb5Sswilcox 1879355d6bb5Sswilcox /* 1880355d6bb5Sswilcox * Make sure that a cg header looks at least moderately reasonable. 1881355d6bb5Sswilcox * We want to be able to trust the contents enough to be able to use 1882355d6bb5Sswilcox * the standard accessor macros. So, besides looking at the obvious 1883355d6bb5Sswilcox * such as the magic number, we verify that the offset field values 1884355d6bb5Sswilcox * are properly aligned and not too big or small. 1885355d6bb5Sswilcox * 1886355d6bb5Sswilcox * Returns a NULL pointer if the cg is sane enough for our needs, else 1887355d6bb5Sswilcox * a dynamically-allocated string describing all of its faults. 1888355d6bb5Sswilcox */ 1889355d6bb5Sswilcox #define Append_Error(full, full_len, addition, addition_len) \ 1890355d6bb5Sswilcox if (full == NULL) { \ 1891355d6bb5Sswilcox full = addition; \ 1892355d6bb5Sswilcox full_len = addition_len; \ 1893355d6bb5Sswilcox } else { \ 1894355d6bb5Sswilcox /* lint doesn't think realloc() understands NULLs */ \ 1895355d6bb5Sswilcox full = realloc(full, full_len + addition_len + 1); \ 1896355d6bb5Sswilcox if (full == NULL) { \ 1897355d6bb5Sswilcox errexit("Out of memory in cg_sanity"); \ 1898355d6bb5Sswilcox /* NOTREACHED */ \ 1899355d6bb5Sswilcox } \ 1900355d6bb5Sswilcox (void) strcpy(full + full_len, addition); \ 1901355d6bb5Sswilcox full_len += addition_len; \ 1902355d6bb5Sswilcox free(addition); \ 1903355d6bb5Sswilcox } 1904355d6bb5Sswilcox 1905355d6bb5Sswilcox caddr_t 190677a343abSabalfour cg_sanity(struct cg *cgp, int cgno) 1907355d6bb5Sswilcox { 1908355d6bb5Sswilcox caddr_t full_err; 1909355d6bb5Sswilcox caddr_t this_err = NULL; 1910355d6bb5Sswilcox int full_len, this_len; 1911355d6bb5Sswilcox daddr32_t ndblk; 1912355d6bb5Sswilcox daddr32_t exp_btotoff, exp_boff, exp_iusedoff; 1913355d6bb5Sswilcox daddr32_t exp_freeoff, exp_nextfreeoff; 1914355d6bb5Sswilcox 1915355d6bb5Sswilcox cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff, 1916355d6bb5Sswilcox &exp_freeoff, &exp_nextfreeoff, &ndblk); 1917355d6bb5Sswilcox 1918355d6bb5Sswilcox full_err = NULL; 1919355d6bb5Sswilcox full_len = 0; 1920355d6bb5Sswilcox 1921355d6bb5Sswilcox if (!cg_chkmagic(cgp)) { 1922355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1923355d6bb5Sswilcox "BAD CG MAGIC NUMBER (0x%x should be 0x%x)\n", 1924355d6bb5Sswilcox cgp->cg_magic, CG_MAGIC); 1925355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1926355d6bb5Sswilcox } 1927355d6bb5Sswilcox 1928355d6bb5Sswilcox if (cgp->cg_cgx != cgno) { 1929355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1930355d6bb5Sswilcox "WRONG CG NUMBER (%d should be %d)\n", 1931355d6bb5Sswilcox cgp->cg_cgx, cgno); 1932355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1933355d6bb5Sswilcox } 1934355d6bb5Sswilcox 1935355d6bb5Sswilcox if ((cgp->cg_btotoff & 3) != 0) { 1936355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1937355d6bb5Sswilcox "BLOCK TOTALS OFFSET %d NOT FOUR-BYTE ALIGNED\n", 1938355d6bb5Sswilcox cgp->cg_btotoff); 1939355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1940355d6bb5Sswilcox } 1941355d6bb5Sswilcox 1942355d6bb5Sswilcox if ((cgp->cg_boff & 1) != 0) { 1943355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1944355d6bb5Sswilcox "FREE BLOCK POSITIONS TABLE OFFSET %d NOT TWO-BYTE ALIGNED\n", 1945355d6bb5Sswilcox cgp->cg_boff); 1946355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1947355d6bb5Sswilcox } 1948355d6bb5Sswilcox 1949355d6bb5Sswilcox if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) { 1950355d6bb5Sswilcox if (cgp->cg_ncyl < 1) { 1951355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1952355d6bb5Sswilcox "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is less than 1)\n", 1953355d6bb5Sswilcox cgp->cg_ncyl); 1954355d6bb5Sswilcox } else { 1955355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1956355d6bb5Sswilcox "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is greater than %d)\n", 1957355d6bb5Sswilcox cgp->cg_ncyl, sblock.fs_cpg); 1958355d6bb5Sswilcox } 1959355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1960355d6bb5Sswilcox } 1961355d6bb5Sswilcox 1962355d6bb5Sswilcox if (cgp->cg_niblk != sblock.fs_ipg) { 1963355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1964355d6bb5Sswilcox "INCORRECT NUMBER OF INODES IN GROUP (%d should be %d)\n", 1965355d6bb5Sswilcox cgp->cg_niblk, sblock.fs_ipg); 1966355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1967355d6bb5Sswilcox } 1968355d6bb5Sswilcox 1969355d6bb5Sswilcox if (cgp->cg_ndblk != ndblk) { 1970355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1971355d6bb5Sswilcox "INCORRECT NUMBER OF DATA BLOCKS IN GROUP (%d should be %d)\n", 1972355d6bb5Sswilcox cgp->cg_ndblk, ndblk); 1973355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1974355d6bb5Sswilcox } 1975355d6bb5Sswilcox 1976355d6bb5Sswilcox if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= ndblk)) { 1977355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1978355d6bb5Sswilcox "IMPOSSIBLE BLOCK ALLOCATION ROTOR POSITION " 1979355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n", 1980355d6bb5Sswilcox cgp->cg_rotor, ndblk); 1981355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1982355d6bb5Sswilcox } 1983355d6bb5Sswilcox 1984355d6bb5Sswilcox if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= ndblk)) { 1985355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1986355d6bb5Sswilcox "IMPOSSIBLE FRAGMENT ALLOCATION ROTOR POSITION " 1987355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n", 1988355d6bb5Sswilcox cgp->cg_frotor, ndblk); 1989355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1990355d6bb5Sswilcox } 1991355d6bb5Sswilcox 1992355d6bb5Sswilcox if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) { 1993355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 1994355d6bb5Sswilcox "IMPOSSIBLE INODE ALLOCATION ROTOR POSITION " 1995355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n", 1996355d6bb5Sswilcox cgp->cg_irotor, sblock.fs_ipg); 1997355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 1998355d6bb5Sswilcox } 1999355d6bb5Sswilcox 2000355d6bb5Sswilcox if (cgp->cg_btotoff != exp_btotoff) { 2001355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 2002355d6bb5Sswilcox "INCORRECT BLOCK TOTALS OFFSET (%d should be %d)\n", 2003355d6bb5Sswilcox cgp->cg_btotoff, exp_btotoff); 2004355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 2005355d6bb5Sswilcox } 2006355d6bb5Sswilcox 2007355d6bb5Sswilcox if (cgp->cg_boff != exp_boff) { 2008355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 2009355d6bb5Sswilcox "BAD FREE BLOCK POSITIONS TABLE OFFSET (%d should %d)\n", 2010355d6bb5Sswilcox cgp->cg_boff, exp_boff); 2011355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 2012355d6bb5Sswilcox } 2013355d6bb5Sswilcox 2014355d6bb5Sswilcox if (cgp->cg_iusedoff != exp_iusedoff) { 2015355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 2016355d6bb5Sswilcox "INCORRECT USED INODE MAP OFFSET (%d should be %d)\n", 2017355d6bb5Sswilcox cgp->cg_iusedoff, exp_iusedoff); 2018355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 2019355d6bb5Sswilcox } 2020355d6bb5Sswilcox 2021355d6bb5Sswilcox if (cgp->cg_freeoff != exp_freeoff) { 2022355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 2023355d6bb5Sswilcox "INCORRECT FREE FRAGMENT MAP OFFSET (%d should be %d)\n", 2024355d6bb5Sswilcox cgp->cg_freeoff, exp_freeoff); 2025355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 2026355d6bb5Sswilcox } 2027355d6bb5Sswilcox 2028355d6bb5Sswilcox if (cgp->cg_nextfreeoff != exp_nextfreeoff) { 2029355d6bb5Sswilcox this_len = fsck_asprintf(&this_err, 2030355d6bb5Sswilcox "END OF HEADER POSITION INCORRECT (%d should be %d)\n", 2031355d6bb5Sswilcox cgp->cg_nextfreeoff, exp_nextfreeoff); 2032355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len); 2033355d6bb5Sswilcox } 2034355d6bb5Sswilcox 2035355d6bb5Sswilcox return (full_err); 2036355d6bb5Sswilcox } 2037355d6bb5Sswilcox 2038355d6bb5Sswilcox #undef Append_Error 2039355d6bb5Sswilcox 2040355d6bb5Sswilcox /* 2041355d6bb5Sswilcox * This is taken from mkfs, and is what is used to come up with the 2042355d6bb5Sswilcox * original values for a struct cg. This implies that, since these 2043355d6bb5Sswilcox * are all constants, recalculating them now should give us the same 2044355d6bb5Sswilcox * thing as what's on disk. 2045355d6bb5Sswilcox */ 2046355d6bb5Sswilcox static void 2047355d6bb5Sswilcox cg_constants(int cgno, daddr32_t *btotoff, daddr32_t *boff, 2048355d6bb5Sswilcox daddr32_t *iusedoff, daddr32_t *freeoff, daddr32_t *nextfreeoff, 2049355d6bb5Sswilcox daddr32_t *ndblk) 2050355d6bb5Sswilcox { 2051355d6bb5Sswilcox daddr32_t cbase, dmax; 2052355d6bb5Sswilcox struct cg *cgp; 2053355d6bb5Sswilcox 2054355d6bb5Sswilcox (void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cgno), 2055355d6bb5Sswilcox (size_t)sblock.fs_cgsize); 2056355d6bb5Sswilcox cgp = cgblk.b_un.b_cg; 2057355d6bb5Sswilcox 2058355d6bb5Sswilcox cbase = cgbase(&sblock, cgno); 2059355d6bb5Sswilcox dmax = cbase + sblock.fs_fpg; 2060355d6bb5Sswilcox if (dmax > sblock.fs_size) 2061355d6bb5Sswilcox dmax = sblock.fs_size; 2062355d6bb5Sswilcox 2063355d6bb5Sswilcox /* LINTED pointer difference won't overflow */ 2064355d6bb5Sswilcox *btotoff = &cgp->cg_space[0] - (uchar_t *)(&cgp->cg_link); 2065355d6bb5Sswilcox *boff = *btotoff + sblock.fs_cpg * sizeof (daddr32_t); 2066355d6bb5Sswilcox *iusedoff = *boff + sblock.fs_cpg * sblock.fs_nrpos * sizeof (int16_t); 2067355d6bb5Sswilcox *freeoff = *iusedoff + howmany(sblock.fs_ipg, NBBY); 2068355d6bb5Sswilcox *nextfreeoff = *freeoff + 2069355d6bb5Sswilcox howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY); 2070355d6bb5Sswilcox *ndblk = dmax - cbase; 2071355d6bb5Sswilcox } 2072355d6bb5Sswilcox 2073355d6bb5Sswilcox /* 2074355d6bb5Sswilcox * Corrects all fields in the cg that can be done with the available 2075355d6bb5Sswilcox * redundant data. 2076355d6bb5Sswilcox */ 2077355d6bb5Sswilcox void 2078355d6bb5Sswilcox fix_cg(struct cg *cgp, int cgno) 2079355d6bb5Sswilcox { 2080355d6bb5Sswilcox daddr32_t exp_btotoff, exp_boff, exp_iusedoff; 2081355d6bb5Sswilcox daddr32_t exp_freeoff, exp_nextfreeoff; 2082355d6bb5Sswilcox daddr32_t ndblk; 2083355d6bb5Sswilcox 2084355d6bb5Sswilcox cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff, 2085355d6bb5Sswilcox &exp_freeoff, &exp_nextfreeoff, &ndblk); 2086355d6bb5Sswilcox 2087355d6bb5Sswilcox if (cgp->cg_cgx != cgno) { 2088355d6bb5Sswilcox cgp->cg_cgx = cgno; 2089355d6bb5Sswilcox } 2090355d6bb5Sswilcox 2091355d6bb5Sswilcox if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) { 2092ef31a55cSvk154806 if (cgno == (sblock.fs_ncg - 1)) { 2093355d6bb5Sswilcox cgp->cg_ncyl = sblock.fs_ncyl - 2094ef31a55cSvk154806 (sblock.fs_cpg * cgno); 2095355d6bb5Sswilcox } else { 2096355d6bb5Sswilcox cgp->cg_ncyl = sblock.fs_cpg; 2097355d6bb5Sswilcox } 2098355d6bb5Sswilcox } 2099355d6bb5Sswilcox 2100355d6bb5Sswilcox if (cgp->cg_niblk != sblock.fs_ipg) { 2101355d6bb5Sswilcox /* 2102355d6bb5Sswilcox * This is not used by the kernel, so it's pretty 2103355d6bb5Sswilcox * harmless if it's wrong. 2104355d6bb5Sswilcox */ 2105355d6bb5Sswilcox cgp->cg_niblk = sblock.fs_ipg; 2106355d6bb5Sswilcox } 2107355d6bb5Sswilcox 2108355d6bb5Sswilcox if (cgp->cg_ndblk != ndblk) { 2109355d6bb5Sswilcox cgp->cg_ndblk = ndblk; 2110355d6bb5Sswilcox } 2111355d6bb5Sswilcox 2112355d6bb5Sswilcox /* 2113355d6bb5Sswilcox * For the rotors, any position's valid, so pick the one we know 2114355d6bb5Sswilcox * will always exist. 2115355d6bb5Sswilcox */ 2116355d6bb5Sswilcox if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= cgp->cg_ndblk)) { 2117355d6bb5Sswilcox cgp->cg_rotor = 0; 2118355d6bb5Sswilcox } 2119355d6bb5Sswilcox 2120355d6bb5Sswilcox if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= cgp->cg_ndblk)) { 2121355d6bb5Sswilcox cgp->cg_frotor = 0; 2122355d6bb5Sswilcox } 2123355d6bb5Sswilcox 2124355d6bb5Sswilcox if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) { 2125355d6bb5Sswilcox cgp->cg_irotor = 0; 2126355d6bb5Sswilcox } 2127355d6bb5Sswilcox 2128355d6bb5Sswilcox /* 2129355d6bb5Sswilcox * For btotoff and boff, if they're misaligned they won't 2130355d6bb5Sswilcox * match the expected values, so we're catching both cases 2131355d6bb5Sswilcox * here. Of course, if any of these are off, it seems likely 2132355d6bb5Sswilcox * that the tables really won't be where we calculate they 2133355d6bb5Sswilcox * should be anyway. 2134355d6bb5Sswilcox */ 2135355d6bb5Sswilcox if (cgp->cg_btotoff != exp_btotoff) { 2136355d6bb5Sswilcox cgp->cg_btotoff = exp_btotoff; 2137355d6bb5Sswilcox } 2138355d6bb5Sswilcox 2139355d6bb5Sswilcox if (cgp->cg_boff != exp_boff) { 2140355d6bb5Sswilcox cgp->cg_boff = exp_boff; 2141355d6bb5Sswilcox } 2142355d6bb5Sswilcox 2143355d6bb5Sswilcox if (cgp->cg_iusedoff != exp_iusedoff) { 2144355d6bb5Sswilcox cgp->cg_iusedoff = exp_iusedoff; 2145355d6bb5Sswilcox } 2146355d6bb5Sswilcox 2147355d6bb5Sswilcox if (cgp->cg_freeoff != exp_freeoff) { 2148355d6bb5Sswilcox cgp->cg_freeoff = exp_freeoff; 2149355d6bb5Sswilcox } 2150355d6bb5Sswilcox 2151355d6bb5Sswilcox if (cgp->cg_nextfreeoff != exp_nextfreeoff) { 2152355d6bb5Sswilcox cgp->cg_nextfreeoff = exp_nextfreeoff; 2153355d6bb5Sswilcox } 2154355d6bb5Sswilcox 2155355d6bb5Sswilcox /* 215677a343abSabalfour * Reset the magic, as we've recreated this cg, also 215777a343abSabalfour * update the cg_time, as we're writing out the cg 215877a343abSabalfour */ 215977a343abSabalfour cgp->cg_magic = CG_MAGIC; 216077a343abSabalfour cgp->cg_time = time(NULL); 216177a343abSabalfour 216277a343abSabalfour /* 2163355d6bb5Sswilcox * We know there was at least one correctable problem, 2164355d6bb5Sswilcox * or else we wouldn't have been called. So instead of 2165355d6bb5Sswilcox * marking the buffer dirty N times above, just do it 2166355d6bb5Sswilcox * once here. 2167355d6bb5Sswilcox */ 2168355d6bb5Sswilcox cgdirty(); 2169355d6bb5Sswilcox } 2170355d6bb5Sswilcox 2171355d6bb5Sswilcox void 217239542a18Sabalfour examinelog(void (*cb)(daddr32_t)) 2173355d6bb5Sswilcox { 2174355d6bb5Sswilcox struct bufarea *bp; 2175355d6bb5Sswilcox extent_block_t *ebp; 2176355d6bb5Sswilcox extent_t *ep; 2177355d6bb5Sswilcox daddr32_t nfno, fno; 2178355d6bb5Sswilcox int i; 2179355d6bb5Sswilcox int j; 2180355d6bb5Sswilcox 218139542a18Sabalfour /* 218239542a18Sabalfour * Since ufs stores fs_logbno as blocks and MTBufs stores it as frags 218339542a18Sabalfour * we need to translate accordingly using logbtodb() 218439542a18Sabalfour */ 218539542a18Sabalfour 218639542a18Sabalfour if (logbtodb(&sblock, sblock.fs_logbno) < SBLOCK) { 218739542a18Sabalfour if (debug) { 218839542a18Sabalfour (void) printf("fs_logbno < SBLOCK: %ld < %ld\n" \ 218939542a18Sabalfour "Aborting log examination\n", \ 219039542a18Sabalfour logbtodb(&sblock, sblock.fs_logbno), SBLOCK); 219139542a18Sabalfour } 2192355d6bb5Sswilcox return; 219339542a18Sabalfour } 2194355d6bb5Sswilcox 2195355d6bb5Sswilcox /* 2196355d6bb5Sswilcox * Read errors will return zeros, which will cause us 2197355d6bb5Sswilcox * to do nothing harmful, so don't need to handle it. 2198355d6bb5Sswilcox */ 2199355d6bb5Sswilcox bp = getdatablk(logbtofrag(&sblock, sblock.fs_logbno), 2200355d6bb5Sswilcox (size_t)sblock.fs_bsize); 2201355d6bb5Sswilcox ebp = (void *)bp->b_un.b_buf; 2202355d6bb5Sswilcox 2203355d6bb5Sswilcox /* 2204355d6bb5Sswilcox * Does it look like a log allocation table? 2205355d6bb5Sswilcox */ 2206355d6bb5Sswilcox /* LINTED pointer cast is aligned */ 2207355d6bb5Sswilcox if (!log_checksum(&ebp->chksum, (int32_t *)bp->b_un.b_buf, 2208355d6bb5Sswilcox sblock.fs_bsize)) 2209355d6bb5Sswilcox return; 2210355d6bb5Sswilcox if (ebp->type != LUFS_EXTENTS || ebp->nextents == 0) 2211355d6bb5Sswilcox return; 2212355d6bb5Sswilcox 2213355d6bb5Sswilcox ep = &ebp->extents[0]; 2214355d6bb5Sswilcox for (i = 0; i < ebp->nextents; ++i, ++ep) { 2215355d6bb5Sswilcox fno = logbtofrag(&sblock, ep->pbno); 2216355d6bb5Sswilcox nfno = dbtofsb(&sblock, ep->nbno); 2217355d6bb5Sswilcox for (j = 0; j < nfno; ++j, ++fno) { 2218355d6bb5Sswilcox /* 2219355d6bb5Sswilcox * Invoke the callback first, so that pass1 can 2220355d6bb5Sswilcox * mark the log blocks in-use. Then, if any 2221355d6bb5Sswilcox * subsequent pass over the log shows us that a 2222355d6bb5Sswilcox * block got freed (say, it was also claimed by 2223355d6bb5Sswilcox * an inode that we cleared), we can safely declare 2224355d6bb5Sswilcox * the log bad. 2225355d6bb5Sswilcox */ 2226355d6bb5Sswilcox if (cb != NULL) 2227355d6bb5Sswilcox (*cb)(fno); 2228355d6bb5Sswilcox if (!testbmap(fno)) 2229355d6bb5Sswilcox islogok = 0; 2230355d6bb5Sswilcox } 2231355d6bb5Sswilcox } 2232355d6bb5Sswilcox brelse(bp); 2233355d6bb5Sswilcox 2234355d6bb5Sswilcox if (cb != NULL) { 2235355d6bb5Sswilcox fno = logbtofrag(&sblock, sblock.fs_logbno); 2236355d6bb5Sswilcox for (j = 0; j < sblock.fs_frag; ++j, ++fno) 2237355d6bb5Sswilcox (*cb)(fno); 2238355d6bb5Sswilcox } 2239355d6bb5Sswilcox } 2240355d6bb5Sswilcox 2241355d6bb5Sswilcox static void 2242355d6bb5Sswilcox freelogblk(daddr32_t frag) 2243355d6bb5Sswilcox { 2244355d6bb5Sswilcox freeblk(sblock.fs_logbno, frag, 1); 2245355d6bb5Sswilcox } 2246355d6bb5Sswilcox 2247355d6bb5Sswilcox caddr_t 2248355d6bb5Sswilcox file_id(fsck_ino_t inum, mode_t mode) 2249355d6bb5Sswilcox { 2250355d6bb5Sswilcox static char name[MAXPATHLEN + 1]; 2251355d6bb5Sswilcox 2252355d6bb5Sswilcox if (lfdir == inum) { 2253355d6bb5Sswilcox return (lfname); 2254355d6bb5Sswilcox } 2255355d6bb5Sswilcox 2256355d6bb5Sswilcox if ((mode & IFMT) == IFDIR) { 2257355d6bb5Sswilcox (void) strcpy(name, "DIR"); 2258355d6bb5Sswilcox } else if ((mode & IFMT) == IFATTRDIR) { 2259355d6bb5Sswilcox (void) strcpy(name, "ATTR DIR"); 2260355d6bb5Sswilcox } else if ((mode & IFMT) == IFSHAD) { 2261355d6bb5Sswilcox (void) strcpy(name, "ACL"); 2262355d6bb5Sswilcox } else { 2263355d6bb5Sswilcox (void) strcpy(name, "FILE"); 2264355d6bb5Sswilcox } 2265355d6bb5Sswilcox 2266355d6bb5Sswilcox return (name); 2267355d6bb5Sswilcox } 2268355d6bb5Sswilcox 2269355d6bb5Sswilcox /* 2270355d6bb5Sswilcox * Simple initializer for inodesc structures, so users of only a few 2271355d6bb5Sswilcox * fields don't have to worry about getting the right defaults for 2272355d6bb5Sswilcox * everything out. 2273355d6bb5Sswilcox */ 2274355d6bb5Sswilcox void 2275355d6bb5Sswilcox init_inodesc(struct inodesc *idesc) 2276355d6bb5Sswilcox { 2277355d6bb5Sswilcox /* 2278355d6bb5Sswilcox * Most fields should be zero, just hit the special cases. 2279355d6bb5Sswilcox */ 2280355d6bb5Sswilcox (void) memset((void *)idesc, 0, sizeof (struct inodesc)); 2281355d6bb5Sswilcox idesc->id_fix = DONTKNOW; 2282355d6bb5Sswilcox idesc->id_lbn = -1; 2283355d6bb5Sswilcox idesc->id_truncto = -1; 2284355d6bb5Sswilcox idesc->id_firsthole = -1; 2285355d6bb5Sswilcox } 2286355d6bb5Sswilcox 2287355d6bb5Sswilcox /* 2288355d6bb5Sswilcox * Compare routine for tsearch(C) to use on ino_t instances. 2289355d6bb5Sswilcox */ 2290355d6bb5Sswilcox int 2291355d6bb5Sswilcox ino_t_cmp(const void *left, const void *right) 2292355d6bb5Sswilcox { 2293355d6bb5Sswilcox const fsck_ino_t lino = (const fsck_ino_t)left; 2294355d6bb5Sswilcox const fsck_ino_t rino = (const fsck_ino_t)right; 2295355d6bb5Sswilcox 2296355d6bb5Sswilcox return (lino - rino); 2297355d6bb5Sswilcox } 2298355d6bb5Sswilcox 2299355d6bb5Sswilcox int 2300355d6bb5Sswilcox cgisdirty(void) 2301355d6bb5Sswilcox { 2302355d6bb5Sswilcox return (cgblk.b_dirty); 2303355d6bb5Sswilcox } 2304355d6bb5Sswilcox 2305355d6bb5Sswilcox void 2306355d6bb5Sswilcox cgflush(void) 2307355d6bb5Sswilcox { 2308355d6bb5Sswilcox flush(fswritefd, &cgblk); 2309355d6bb5Sswilcox } 2310355d6bb5Sswilcox 2311355d6bb5Sswilcox void 2312355d6bb5Sswilcox dirty(struct bufarea *bp) 2313355d6bb5Sswilcox { 2314355d6bb5Sswilcox if (fswritefd < 0) { 23151493b746SMilan Cermak /* 23161493b746SMilan Cermak * No one should call dirty() in read only mode. 23171493b746SMilan Cermak * But if one does, it's not fatal issue. Just warn him. 23181493b746SMilan Cermak */ 23191493b746SMilan Cermak pwarn("WON'T SET DIRTY FLAG IN READ_ONLY MODE\n"); 2320355d6bb5Sswilcox } else { 2321355d6bb5Sswilcox (bp)->b_dirty = 1; 2322355d6bb5Sswilcox isdirty = 1; 2323355d6bb5Sswilcox } 2324355d6bb5Sswilcox } 2325355d6bb5Sswilcox 2326355d6bb5Sswilcox void 2327355d6bb5Sswilcox initbarea(struct bufarea *bp) 2328355d6bb5Sswilcox { 2329355d6bb5Sswilcox (bp)->b_dirty = 0; 2330355d6bb5Sswilcox (bp)->b_bno = (diskaddr_t)-1LL; 2331355d6bb5Sswilcox (bp)->b_flags = 0; 2332355d6bb5Sswilcox (bp)->b_cnt = 0; 2333355d6bb5Sswilcox (bp)->b_errs = 0; 2334355d6bb5Sswilcox } 2335355d6bb5Sswilcox 2336355d6bb5Sswilcox /* 2337355d6bb5Sswilcox * Partition-sizing routines adapted from ../newfs/newfs.c. 2338355d6bb5Sswilcox * Needed because calcsb() needs to use mkfs to work out what the 2339355d6bb5Sswilcox * superblock should be, and mkfs insists on being told how many 2340355d6bb5Sswilcox * sectors to use. 2341355d6bb5Sswilcox * 2342355d6bb5Sswilcox * Error handling assumes we're never called while preening. 2343355d6bb5Sswilcox * 2344355d6bb5Sswilcox * XXX This should be extracted into a ../ufslib.{c,h}, 2345355d6bb5Sswilcox * in the same spirit to ../../fslib.{c,h}. Once that is 2346355d6bb5Sswilcox * done, both fsck and newfs should be modified to link 2347355d6bb5Sswilcox * against it. 2348355d6bb5Sswilcox */ 2349355d6bb5Sswilcox 2350355d6bb5Sswilcox static int label_type; 2351355d6bb5Sswilcox 2352355d6bb5Sswilcox #define LABEL_TYPE_VTOC 1 2353355d6bb5Sswilcox #define LABEL_TYPE_EFI 2 2354355d6bb5Sswilcox #define LABEL_TYPE_OTHER 3 2355355d6bb5Sswilcox 2356355d6bb5Sswilcox #define MB (1024 * 1024) 2357355d6bb5Sswilcox #define SECTORS_PER_TERABYTE (1LL << 31) 2358355d6bb5Sswilcox #define FS_SIZE_UPPER_LIMIT 0x100000000000LL 2359355d6bb5Sswilcox 2360355d6bb5Sswilcox diskaddr_t 2361355d6bb5Sswilcox getdisksize(caddr_t disk, int fd) 2362355d6bb5Sswilcox { 2363355d6bb5Sswilcox int rpm; 2364355d6bb5Sswilcox struct dk_geom g; 2365355d6bb5Sswilcox struct dk_cinfo ci; 2366355d6bb5Sswilcox diskaddr_t actual_size; 2367355d6bb5Sswilcox 2368355d6bb5Sswilcox /* 2369355d6bb5Sswilcox * get_device_size() determines the actual size of the 2370355d6bb5Sswilcox * device, and also the disk's attributes, such as geometry. 2371355d6bb5Sswilcox */ 2372355d6bb5Sswilcox actual_size = get_device_size(fd, disk); 2373355d6bb5Sswilcox 2374355d6bb5Sswilcox if (label_type == LABEL_TYPE_VTOC) { 2375355d6bb5Sswilcox if (ioctl(fd, DKIOCGGEOM, &g)) { 2376355d6bb5Sswilcox pwarn("%s: Unable to read Disk geometry", disk); 2377355d6bb5Sswilcox return (0); 2378355d6bb5Sswilcox } 2379355d6bb5Sswilcox if (sblock.fs_nsect == 0) 2380355d6bb5Sswilcox sblock.fs_nsect = g.dkg_nsect; 2381355d6bb5Sswilcox if (sblock.fs_ntrak == 0) 2382355d6bb5Sswilcox sblock.fs_ntrak = g.dkg_nhead; 2383355d6bb5Sswilcox if (sblock.fs_rps == 0) { 2384355d6bb5Sswilcox rpm = ((int)g.dkg_rpm <= 0) ? 3600: g.dkg_rpm; 2385355d6bb5Sswilcox sblock.fs_rps = rpm / 60; 2386355d6bb5Sswilcox } 2387355d6bb5Sswilcox } 2388355d6bb5Sswilcox 2389355d6bb5Sswilcox if (sblock.fs_bsize == 0) 2390355d6bb5Sswilcox sblock.fs_bsize = MAXBSIZE; 2391355d6bb5Sswilcox 2392355d6bb5Sswilcox /* 2393355d6bb5Sswilcox * Adjust maxcontig by the device's maxtransfer. If maxtransfer 2394355d6bb5Sswilcox * information is not available, default to the min of a MB and 2395355d6bb5Sswilcox * maxphys. 2396355d6bb5Sswilcox */ 2397355d6bb5Sswilcox if (sblock.fs_maxcontig == -1 && ioctl(fd, DKIOCINFO, &ci) == 0) { 2398355d6bb5Sswilcox sblock.fs_maxcontig = ci.dki_maxtransfer * DEV_BSIZE; 2399355d6bb5Sswilcox if (sblock.fs_maxcontig < 0) { 2400355d6bb5Sswilcox int gotit, maxphys; 2401355d6bb5Sswilcox 2402355d6bb5Sswilcox gotit = fsgetmaxphys(&maxphys, NULL); 2403355d6bb5Sswilcox 2404355d6bb5Sswilcox /* 2405355d6bb5Sswilcox * If we cannot get the maxphys value, default 2406355d6bb5Sswilcox * to ufs_maxmaxphys (MB). 2407355d6bb5Sswilcox */ 2408355d6bb5Sswilcox if (gotit) { 2409355d6bb5Sswilcox sblock.fs_maxcontig = MIN(maxphys, MB); 2410355d6bb5Sswilcox } else { 2411355d6bb5Sswilcox sblock.fs_maxcontig = MB; 2412355d6bb5Sswilcox } 2413355d6bb5Sswilcox } 2414355d6bb5Sswilcox sblock.fs_maxcontig /= sblock.fs_bsize; 2415355d6bb5Sswilcox } 2416355d6bb5Sswilcox 2417355d6bb5Sswilcox return (actual_size); 2418355d6bb5Sswilcox } 2419355d6bb5Sswilcox 2420355d6bb5Sswilcox /* 2421355d6bb5Sswilcox * Figure out how big the partition we're dealing with is. 2422355d6bb5Sswilcox */ 2423355d6bb5Sswilcox static diskaddr_t 2424355d6bb5Sswilcox get_device_size(int fd, caddr_t name) 2425355d6bb5Sswilcox { 2426342440ecSPrasad Singamsetty struct extvtoc vtoc; 2427355d6bb5Sswilcox struct dk_gpt *efi_vtoc; 2428355d6bb5Sswilcox diskaddr_t slicesize = 0; 2429355d6bb5Sswilcox 2430342440ecSPrasad Singamsetty int index = read_extvtoc(fd, &vtoc); 2431355d6bb5Sswilcox 2432355d6bb5Sswilcox if (index >= 0) { 2433355d6bb5Sswilcox label_type = LABEL_TYPE_VTOC; 2434355d6bb5Sswilcox } else { 2435355d6bb5Sswilcox if (index == VT_ENOTSUP || index == VT_ERROR) { 2436355d6bb5Sswilcox /* it might be an EFI label */ 2437355d6bb5Sswilcox index = efi_alloc_and_read(fd, &efi_vtoc); 2438355d6bb5Sswilcox if (index >= 0) 2439355d6bb5Sswilcox label_type = LABEL_TYPE_EFI; 2440355d6bb5Sswilcox } 2441355d6bb5Sswilcox } 2442355d6bb5Sswilcox 2443355d6bb5Sswilcox if (index < 0) { 2444355d6bb5Sswilcox /* 2445355d6bb5Sswilcox * Since both attempts to read the label failed, we're 2446355d6bb5Sswilcox * going to fall back to a brute force approach to 2447355d6bb5Sswilcox * determining the device's size: see how far out we can 2448355d6bb5Sswilcox * perform reads on the device. 2449355d6bb5Sswilcox */ 2450355d6bb5Sswilcox 2451355d6bb5Sswilcox slicesize = brute_force_get_device_size(fd); 2452355d6bb5Sswilcox if (slicesize == 0) { 2453355d6bb5Sswilcox switch (index) { 2454355d6bb5Sswilcox case VT_ERROR: 2455355d6bb5Sswilcox pwarn("%s: %s\n", name, strerror(errno)); 2456355d6bb5Sswilcox break; 2457355d6bb5Sswilcox case VT_EIO: 2458355d6bb5Sswilcox pwarn("%s: I/O error accessing VTOC", name); 2459355d6bb5Sswilcox break; 2460355d6bb5Sswilcox case VT_EINVAL: 2461355d6bb5Sswilcox pwarn("%s: Invalid field in VTOC", name); 2462355d6bb5Sswilcox break; 2463355d6bb5Sswilcox default: 2464355d6bb5Sswilcox pwarn("%s: unknown error %d accessing VTOC", 2465355d6bb5Sswilcox name, index); 2466355d6bb5Sswilcox break; 2467355d6bb5Sswilcox } 2468355d6bb5Sswilcox return (0); 2469355d6bb5Sswilcox } else { 2470355d6bb5Sswilcox label_type = LABEL_TYPE_OTHER; 2471355d6bb5Sswilcox } 2472355d6bb5Sswilcox } 2473355d6bb5Sswilcox 2474355d6bb5Sswilcox if (label_type == LABEL_TYPE_EFI) { 2475355d6bb5Sswilcox slicesize = efi_vtoc->efi_parts[index].p_size; 2476355d6bb5Sswilcox efi_free(efi_vtoc); 2477355d6bb5Sswilcox } else if (label_type == LABEL_TYPE_VTOC) { 2478342440ecSPrasad Singamsetty slicesize = vtoc.v_part[index].p_size; 2479355d6bb5Sswilcox } 2480355d6bb5Sswilcox 2481355d6bb5Sswilcox return (slicesize); 2482355d6bb5Sswilcox } 2483355d6bb5Sswilcox 2484355d6bb5Sswilcox /* 2485355d6bb5Sswilcox * brute_force_get_device_size 2486355d6bb5Sswilcox * 2487355d6bb5Sswilcox * Determine the size of the device by seeing how far we can 2488355d6bb5Sswilcox * read. Doing an llseek( , , SEEK_END) would probably work 2489355d6bb5Sswilcox * in most cases, but we've seen at least one third-party driver 2490355d6bb5Sswilcox * which doesn't correctly support the SEEK_END option when the 2491355d6bb5Sswilcox * the device is greater than a terabyte. 2492355d6bb5Sswilcox */ 2493355d6bb5Sswilcox 2494355d6bb5Sswilcox static diskaddr_t 2495355d6bb5Sswilcox brute_force_get_device_size(int fd) 2496355d6bb5Sswilcox { 2497355d6bb5Sswilcox diskaddr_t min_fail = 0; 2498355d6bb5Sswilcox diskaddr_t max_succeed = 0; 2499355d6bb5Sswilcox diskaddr_t cur_db_off; 2500355d6bb5Sswilcox char buf[DEV_BSIZE]; 2501355d6bb5Sswilcox 2502355d6bb5Sswilcox /* 2503355d6bb5Sswilcox * First, see if we can read the device at all, just to 2504355d6bb5Sswilcox * eliminate errors that have nothing to do with the 2505355d6bb5Sswilcox * device's size. 2506355d6bb5Sswilcox */ 2507355d6bb5Sswilcox 2508355d6bb5Sswilcox if (((llseek(fd, (offset_t)0, SEEK_SET)) == -1) || 2509355d6bb5Sswilcox ((read(fd, buf, DEV_BSIZE)) == -1)) 2510355d6bb5Sswilcox return (0); /* can't determine size */ 2511355d6bb5Sswilcox 2512355d6bb5Sswilcox /* 2513355d6bb5Sswilcox * Now, go sequentially through the multiples of 4TB 2514355d6bb5Sswilcox * to find the first read that fails (this isn't strictly 2515355d6bb5Sswilcox * the most efficient way to find the actual size if the 2516355d6bb5Sswilcox * size really could be anything between 0 and 2**64 bytes. 2517355d6bb5Sswilcox * We expect the sizes to be less than 16 TB for some time, 2518355d6bb5Sswilcox * so why do a bunch of reads that are larger than that? 2519355d6bb5Sswilcox * However, this algorithm *will* work for sizes of greater 2520355d6bb5Sswilcox * than 16 TB. We're just not optimizing for those sizes.) 2521355d6bb5Sswilcox */ 2522355d6bb5Sswilcox 2523355d6bb5Sswilcox /* 2524355d6bb5Sswilcox * XXX lint uses 32-bit arithmetic for doing flow analysis. 2525355d6bb5Sswilcox * We're using > 32-bit constants here. Therefore, its flow 2526355d6bb5Sswilcox * analysis is wrong. For the time being, ignore complaints 2527355d6bb5Sswilcox * from it about the body of the for() being unreached. 2528355d6bb5Sswilcox */ 2529355d6bb5Sswilcox for (cur_db_off = SECTORS_PER_TERABYTE * 4; 2530355d6bb5Sswilcox (min_fail == 0) && (cur_db_off < FS_SIZE_UPPER_LIMIT); 2531355d6bb5Sswilcox cur_db_off += 4 * SECTORS_PER_TERABYTE) { 2532355d6bb5Sswilcox if ((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE), 2533355d6bb5Sswilcox SEEK_SET) == -1) || 2534355d6bb5Sswilcox (read(fd, buf, DEV_BSIZE) != DEV_BSIZE)) 2535355d6bb5Sswilcox min_fail = cur_db_off; 2536355d6bb5Sswilcox else 2537355d6bb5Sswilcox max_succeed = cur_db_off; 2538355d6bb5Sswilcox } 2539355d6bb5Sswilcox 2540355d6bb5Sswilcox /* 2541355d6bb5Sswilcox * XXX Same lint flow analysis problem as above. 2542355d6bb5Sswilcox */ 2543355d6bb5Sswilcox if (min_fail == 0) 2544355d6bb5Sswilcox return (0); 2545355d6bb5Sswilcox 2546355d6bb5Sswilcox /* 2547355d6bb5Sswilcox * We now know that the size of the device is less than 2548355d6bb5Sswilcox * min_fail and greater than or equal to max_succeed. Now 2549355d6bb5Sswilcox * keep splitting the difference until the actual size in 2550355d6bb5Sswilcox * sectors in known. We also know that the difference 2551355d6bb5Sswilcox * between max_succeed and min_fail at this time is 2552355d6bb5Sswilcox * 4 * SECTORS_PER_TERABYTE, which is a power of two, which 2553355d6bb5Sswilcox * simplifies the math below. 2554355d6bb5Sswilcox */ 2555355d6bb5Sswilcox 2556355d6bb5Sswilcox while (min_fail - max_succeed > 1) { 2557355d6bb5Sswilcox cur_db_off = max_succeed + (min_fail - max_succeed)/2; 2558355d6bb5Sswilcox if (((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE), 2559355d6bb5Sswilcox SEEK_SET)) == -1) || 2560355d6bb5Sswilcox ((read(fd, buf, DEV_BSIZE)) != DEV_BSIZE)) 2561355d6bb5Sswilcox min_fail = cur_db_off; 2562355d6bb5Sswilcox else 2563355d6bb5Sswilcox max_succeed = cur_db_off; 2564355d6bb5Sswilcox } 2565355d6bb5Sswilcox 2566355d6bb5Sswilcox /* the size is the last successfully read sector offset plus one */ 2567355d6bb5Sswilcox return (max_succeed + 1); 2568355d6bb5Sswilcox } 2569355d6bb5Sswilcox 2570355d6bb5Sswilcox static void 2571355d6bb5Sswilcox vfileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, va_list ap) 2572355d6bb5Sswilcox { 2573355d6bb5Sswilcox struct dinode *dp; 2574355d6bb5Sswilcox char pathbuf[MAXPATHLEN + 1]; 2575355d6bb5Sswilcox 2576355d6bb5Sswilcox vpwarn(fmt, ap); 2577355d6bb5Sswilcox (void) putchar(' '); 2578355d6bb5Sswilcox pinode(ino); 2579355d6bb5Sswilcox (void) printf("\n"); 2580355d6bb5Sswilcox getpathname(pathbuf, cwd, ino); 2581355d6bb5Sswilcox if (ino < UFSROOTINO || ino > maxino) { 2582355d6bb5Sswilcox pfatal("NAME=%s\n", pathbuf); 2583355d6bb5Sswilcox return; 2584355d6bb5Sswilcox } 2585355d6bb5Sswilcox dp = ginode(ino); 2586355d6bb5Sswilcox if (ftypeok(dp)) 2587355d6bb5Sswilcox pfatal("%s=%s\n", file_id(ino, dp->di_mode), pathbuf); 2588355d6bb5Sswilcox else 2589355d6bb5Sswilcox pfatal("NAME=%s\n", pathbuf); 2590355d6bb5Sswilcox } 2591355d6bb5Sswilcox 2592355d6bb5Sswilcox void 2593355d6bb5Sswilcox direrror(fsck_ino_t ino, caddr_t fmt, ...) 2594355d6bb5Sswilcox { 2595355d6bb5Sswilcox va_list ap; 2596355d6bb5Sswilcox 2597355d6bb5Sswilcox va_start(ap, fmt); 2598355d6bb5Sswilcox vfileerror(ino, ino, fmt, ap); 2599355d6bb5Sswilcox va_end(ap); 2600355d6bb5Sswilcox } 2601355d6bb5Sswilcox 2602355d6bb5Sswilcox static void 2603355d6bb5Sswilcox vdirerror(fsck_ino_t ino, caddr_t fmt, va_list ap) 2604355d6bb5Sswilcox { 2605355d6bb5Sswilcox vfileerror(ino, ino, fmt, ap); 2606355d6bb5Sswilcox } 2607355d6bb5Sswilcox 2608355d6bb5Sswilcox void 2609355d6bb5Sswilcox fileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, ...) 2610355d6bb5Sswilcox { 2611355d6bb5Sswilcox va_list ap; 2612355d6bb5Sswilcox 2613355d6bb5Sswilcox va_start(ap, fmt); 2614355d6bb5Sswilcox vfileerror(cwd, ino, fmt, ap); 2615355d6bb5Sswilcox va_end(ap); 2616355d6bb5Sswilcox } 2617355d6bb5Sswilcox 2618355d6bb5Sswilcox /* 2619355d6bb5Sswilcox * Adds the given inode to the orphaned-directories list, limbo_dirs. 2620355d6bb5Sswilcox * Assumes that the caller has set INCLEAR in the inode's statemap[] 2621355d6bb5Sswilcox * entry. 2622355d6bb5Sswilcox * 2623355d6bb5Sswilcox * With INCLEAR set, the inode will get ignored by passes 2 and 3, 2624355d6bb5Sswilcox * meaning it's effectively an orphan. It needs to be noted now, so 2625355d6bb5Sswilcox * it will be remembered in pass 4. 2626355d6bb5Sswilcox */ 2627355d6bb5Sswilcox 2628355d6bb5Sswilcox void 2629355d6bb5Sswilcox add_orphan_dir(fsck_ino_t ino) 2630355d6bb5Sswilcox { 2631355d6bb5Sswilcox if (tsearch((void *)ino, &limbo_dirs, ino_t_cmp) == NULL) 2632355d6bb5Sswilcox errexit("add_orphan_dir: out of memory"); 2633355d6bb5Sswilcox } 2634355d6bb5Sswilcox 2635355d6bb5Sswilcox /* 2636355d6bb5Sswilcox * Remove an inode from the orphaned-directories list, presumably 2637355d6bb5Sswilcox * because it's been cleared. 2638355d6bb5Sswilcox */ 2639355d6bb5Sswilcox void 2640355d6bb5Sswilcox remove_orphan_dir(fsck_ino_t ino) 2641355d6bb5Sswilcox { 2642355d6bb5Sswilcox (void) tdelete((void *)ino, &limbo_dirs, ino_t_cmp); 2643355d6bb5Sswilcox } 2644355d6bb5Sswilcox 2645355d6bb5Sswilcox /* 2646355d6bb5Sswilcox * log_setsum() and log_checksum() are equivalent to lufs.c:setsum() 2647355d6bb5Sswilcox * and lufs.c:checksum(). 2648355d6bb5Sswilcox */ 2649355d6bb5Sswilcox static void 2650355d6bb5Sswilcox log_setsum(int32_t *sp, int32_t *lp, int nb) 2651355d6bb5Sswilcox { 2652355d6bb5Sswilcox int32_t csum = 0; 2653355d6bb5Sswilcox 2654355d6bb5Sswilcox *sp = 0; 2655355d6bb5Sswilcox nb /= sizeof (int32_t); 2656355d6bb5Sswilcox while (nb--) 2657355d6bb5Sswilcox csum += *lp++; 2658355d6bb5Sswilcox *sp = csum; 2659355d6bb5Sswilcox } 2660355d6bb5Sswilcox 2661355d6bb5Sswilcox static int 2662355d6bb5Sswilcox log_checksum(int32_t *sp, int32_t *lp, int nb) 2663355d6bb5Sswilcox { 2664355d6bb5Sswilcox int32_t ssum = *sp; 2665355d6bb5Sswilcox 2666355d6bb5Sswilcox log_setsum(sp, lp, nb); 2667355d6bb5Sswilcox if (ssum != *sp) { 2668355d6bb5Sswilcox *sp = ssum; 2669355d6bb5Sswilcox return (0); 2670355d6bb5Sswilcox } 2671355d6bb5Sswilcox return (1); 26727c478bd9Sstevel@tonic-gate } 2673