17c478bd9Sstevel@tonic-gate /*
223a1cceaSRoger A. Faulkner * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
348bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
47c478bd9Sstevel@tonic-gate */
57c478bd9Sstevel@tonic-gate
67c478bd9Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7*bfbf29e2SToomas Soome /* All Rights Reserved */
87c478bd9Sstevel@tonic-gate
97c478bd9Sstevel@tonic-gate /*
107c478bd9Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
117c478bd9Sstevel@tonic-gate * All rights reserved.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted
147c478bd9Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright
157c478bd9Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display
167c478bd9Sstevel@tonic-gate * the following acknowledgement: ``This product includes software
177c478bd9Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors''
187c478bd9Sstevel@tonic-gate * in the documentation or other materials provided with the distribution
197c478bd9Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this
207c478bd9Sstevel@tonic-gate * software. Neither the name of the University nor the names of its
217c478bd9Sstevel@tonic-gate * contributors may be used to endorse or promote products derived
227c478bd9Sstevel@tonic-gate * from this software without specific prior written permission.
237c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
247c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
257c478bd9Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #include <stdio.h>
29355d6bb5Sswilcox #include <stdlib.h>
30355d6bb5Sswilcox #include <unistd.h>
31355d6bb5Sswilcox #include <stdarg.h>
32355d6bb5Sswilcox #include <libadm.h>
33355d6bb5Sswilcox #include <note.h>
347c478bd9Sstevel@tonic-gate #include <sys/param.h>
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/mntent.h>
377c478bd9Sstevel@tonic-gate #include <sys/filio.h>
387c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
397c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
407c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h>
41355d6bb5Sswilcox #include <sys/fs/ufs_inode.h>
42355d6bb5Sswilcox #include <sys/fs/ufs_log.h>
437c478bd9Sstevel@tonic-gate #define _KERNEL
447c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
457c478bd9Sstevel@tonic-gate #undef _KERNEL
467c478bd9Sstevel@tonic-gate #include <sys/mnttab.h>
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
49355d6bb5Sswilcox #include <fcntl.h>
50355d6bb5Sswilcox #include <signal.h>
517c478bd9Sstevel@tonic-gate #include <string.h>
527c478bd9Sstevel@tonic-gate #include <ctype.h>
537c478bd9Sstevel@tonic-gate #include <sys/vfstab.h>
547c478bd9Sstevel@tonic-gate #include <sys/lockfs.h>
557c478bd9Sstevel@tonic-gate #include <errno.h>
56355d6bb5Sswilcox #include <sys/cmn_err.h>
57355d6bb5Sswilcox #include <sys/dkio.h>
58355d6bb5Sswilcox #include <sys/vtoc.h>
59355d6bb5Sswilcox #include <sys/efi_partition.h>
60355d6bb5Sswilcox #include <fslib.h>
61355d6bb5Sswilcox #include <inttypes.h>
62355d6bb5Sswilcox #include "fsck.h"
637c478bd9Sstevel@tonic-gate
64*bfbf29e2SToomas Soome struct bufarea *pbp;
65*bfbf29e2SToomas Soome struct bufarea *pdirbp;
66355d6bb5Sswilcox caddr_t mount_point = NULL;
67*bfbf29e2SToomas Soome static struct bufarea bufhead; /* head of list of other blks in filesys */
68*bfbf29e2SToomas Soome char *elock_combuf;
69*bfbf29e2SToomas Soome char *elock_mountp;
70*bfbf29e2SToomas Soome static struct lockfs *lfp; /* current lockfs status */
71355d6bb5Sswilcox
72355d6bb5Sswilcox static int64_t diskreads, totalreads; /* Disk cache statistics */
73355d6bb5Sswilcox
74355d6bb5Sswilcox static int log_checksum(int32_t *, int32_t *, int);
75355d6bb5Sswilcox static void vdirerror(fsck_ino_t, caddr_t, va_list);
76355d6bb5Sswilcox static struct mnttab *search_mnttab(caddr_t, caddr_t, caddr_t, size_t);
77355d6bb5Sswilcox static struct vfstab *search_vfstab(caddr_t, caddr_t, caddr_t, size_t);
78355d6bb5Sswilcox static void vpwarn(caddr_t, va_list);
7923a1cceaSRoger A. Faulkner static int getaline(FILE *, caddr_t, int);
80355d6bb5Sswilcox static struct bufarea *alloc_bufarea(void);
81355d6bb5Sswilcox static void rwerror(caddr_t, diskaddr_t, int rval);
82355d6bb5Sswilcox static void debugclean(void);
83355d6bb5Sswilcox static void report_io_prob(caddr_t, diskaddr_t, size_t, ssize_t);
84355d6bb5Sswilcox static void freelogblk(daddr32_t);
85355d6bb5Sswilcox static void verrexit(caddr_t, va_list);
86355d6bb5Sswilcox static void vpfatal(caddr_t, va_list);
87355d6bb5Sswilcox static diskaddr_t get_device_size(int, caddr_t);
88355d6bb5Sswilcox static diskaddr_t brute_force_get_device_size(int);
89355d6bb5Sswilcox static void cg_constants(int, daddr32_t *, daddr32_t *, daddr32_t *,
90355d6bb5Sswilcox daddr32_t *, daddr32_t *, daddr32_t *);
917c478bd9Sstevel@tonic-gate
92355d6bb5Sswilcox int
ftypeok(struct dinode * dp)93355d6bb5Sswilcox ftypeok(struct dinode *dp)
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate switch (dp->di_mode & IFMT) {
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate case IFDIR:
987c478bd9Sstevel@tonic-gate case IFREG:
997c478bd9Sstevel@tonic-gate case IFBLK:
1007c478bd9Sstevel@tonic-gate case IFCHR:
1017c478bd9Sstevel@tonic-gate case IFLNK:
1027c478bd9Sstevel@tonic-gate case IFSOCK:
1037c478bd9Sstevel@tonic-gate case IFIFO:
1047c478bd9Sstevel@tonic-gate case IFSHAD:
1057c478bd9Sstevel@tonic-gate case IFATTRDIR:
1067c478bd9Sstevel@tonic-gate return (1);
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate default:
1097c478bd9Sstevel@tonic-gate if (debug)
110355d6bb5Sswilcox (void) printf("bad file type 0%o\n", dp->di_mode);
1117c478bd9Sstevel@tonic-gate return (0);
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate }
1147c478bd9Sstevel@tonic-gate
115355d6bb5Sswilcox int
acltypeok(struct dinode * dp)116355d6bb5Sswilcox acltypeok(struct dinode *dp)
1177c478bd9Sstevel@tonic-gate {
1187c478bd9Sstevel@tonic-gate if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT))
1197c478bd9Sstevel@tonic-gate return (1);
1207c478bd9Sstevel@tonic-gate
1217c478bd9Sstevel@tonic-gate if (debug)
122355d6bb5Sswilcox (void) printf("bad file type for acl I=%d: 0%o\n",
123355d6bb5Sswilcox dp->di_shadow, dp->di_mode);
1247c478bd9Sstevel@tonic-gate return (0);
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate
127355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
128355d6bb5Sswilcox int
reply(caddr_t fmt,...)129355d6bb5Sswilcox reply(caddr_t fmt, ...)
1307c478bd9Sstevel@tonic-gate {
131355d6bb5Sswilcox va_list ap;
1327c478bd9Sstevel@tonic-gate char line[80];
1337c478bd9Sstevel@tonic-gate
1347c478bd9Sstevel@tonic-gate if (preen)
135355d6bb5Sswilcox pfatal("INTERNAL ERROR: GOT TO reply() in preen mode");
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate if (mflag) {
138355d6bb5Sswilcox /*
139355d6bb5Sswilcox * We don't know what's going on, so don't potentially
140355d6bb5Sswilcox * make things worse by having errexit() write stuff
141355d6bb5Sswilcox * out to disk.
142355d6bb5Sswilcox */
143355d6bb5Sswilcox (void) printf(
144355d6bb5Sswilcox "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
145355d6bb5Sswilcox devname);
146355d6bb5Sswilcox exit(EXERRFATAL);
1477c478bd9Sstevel@tonic-gate }
1487c478bd9Sstevel@tonic-gate
149355d6bb5Sswilcox va_start(ap, fmt);
150355d6bb5Sswilcox (void) putchar('\n');
151355d6bb5Sswilcox (void) vprintf(fmt, ap);
152355d6bb5Sswilcox (void) putchar('?');
153355d6bb5Sswilcox (void) putchar(' ');
154355d6bb5Sswilcox va_end(ap);
155355d6bb5Sswilcox
1567c478bd9Sstevel@tonic-gate if (nflag || fswritefd < 0) {
157355d6bb5Sswilcox (void) printf(" no\n\n");
1587c478bd9Sstevel@tonic-gate return (0);
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate if (yflag) {
161355d6bb5Sswilcox (void) printf(" yes\n\n");
1627c478bd9Sstevel@tonic-gate return (1);
1637c478bd9Sstevel@tonic-gate }
164355d6bb5Sswilcox (void) fflush(stdout);
16523a1cceaSRoger A. Faulkner if (getaline(stdin, line, sizeof (line)) == EOF)
1667c478bd9Sstevel@tonic-gate errexit("\n");
167355d6bb5Sswilcox (void) printf("\n");
168355d6bb5Sswilcox if (line[0] == 'y' || line[0] == 'Y') {
1697c478bd9Sstevel@tonic-gate return (1);
170355d6bb5Sswilcox } else {
1717c478bd9Sstevel@tonic-gate return (0);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate }
1747c478bd9Sstevel@tonic-gate
175355d6bb5Sswilcox int
getaline(FILE * fp,caddr_t loc,int maxlen)17623a1cceaSRoger A. Faulkner getaline(FILE *fp, caddr_t loc, int maxlen)
1777c478bd9Sstevel@tonic-gate {
1787c478bd9Sstevel@tonic-gate int n;
179355d6bb5Sswilcox caddr_t p, lastloc;
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate p = loc;
1827c478bd9Sstevel@tonic-gate lastloc = &p[maxlen-1];
1837c478bd9Sstevel@tonic-gate while ((n = getc(fp)) != '\n') {
1847c478bd9Sstevel@tonic-gate if (n == EOF)
1857c478bd9Sstevel@tonic-gate return (EOF);
1867c478bd9Sstevel@tonic-gate if (!isspace(n) && p < lastloc)
187355d6bb5Sswilcox *p++ = (char)n;
1887c478bd9Sstevel@tonic-gate }
189355d6bb5Sswilcox *p = '\0';
190355d6bb5Sswilcox /* LINTED pointer difference won't overflow */
1917c478bd9Sstevel@tonic-gate return (p - loc);
1927c478bd9Sstevel@tonic-gate }
193355d6bb5Sswilcox
1947c478bd9Sstevel@tonic-gate /*
1957c478bd9Sstevel@tonic-gate * Malloc buffers and set up cache.
1967c478bd9Sstevel@tonic-gate */
197355d6bb5Sswilcox void
bufinit(void)198355d6bb5Sswilcox bufinit(void)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate struct bufarea *bp;
2017c478bd9Sstevel@tonic-gate int bufcnt, i;
202355d6bb5Sswilcox caddr_t bufp;
2037c478bd9Sstevel@tonic-gate
204355d6bb5Sswilcox bufp = malloc((size_t)sblock.fs_bsize);
205355d6bb5Sswilcox if (bufp == NULL)
206355d6bb5Sswilcox goto nomem;
2077c478bd9Sstevel@tonic-gate initbarea(&cgblk);
208355d6bb5Sswilcox cgblk.b_un.b_buf = bufp;
2097c478bd9Sstevel@tonic-gate bufhead.b_next = bufhead.b_prev = &bufhead;
2107c478bd9Sstevel@tonic-gate bufcnt = MAXBUFSPACE / sblock.fs_bsize;
2117c478bd9Sstevel@tonic-gate if (bufcnt < MINBUFS)
2127c478bd9Sstevel@tonic-gate bufcnt = MINBUFS;
2137c478bd9Sstevel@tonic-gate for (i = 0; i < bufcnt; i++) {
2147c478bd9Sstevel@tonic-gate bp = (struct bufarea *)malloc(sizeof (struct bufarea));
215355d6bb5Sswilcox if (bp == NULL) {
2167c478bd9Sstevel@tonic-gate if (i >= MINBUFS)
217355d6bb5Sswilcox goto noalloc;
218355d6bb5Sswilcox goto nomem;
219355d6bb5Sswilcox }
220355d6bb5Sswilcox
221355d6bb5Sswilcox bufp = malloc((size_t)sblock.fs_bsize);
222355d6bb5Sswilcox if (bufp == NULL) {
223355d6bb5Sswilcox free((void *)bp);
224355d6bb5Sswilcox if (i >= MINBUFS)
225355d6bb5Sswilcox goto noalloc;
226355d6bb5Sswilcox goto nomem;
2277c478bd9Sstevel@tonic-gate }
228355d6bb5Sswilcox initbarea(bp);
2297c478bd9Sstevel@tonic-gate bp->b_un.b_buf = bufp;
2307c478bd9Sstevel@tonic-gate bp->b_prev = &bufhead;
2317c478bd9Sstevel@tonic-gate bp->b_next = bufhead.b_next;
2327c478bd9Sstevel@tonic-gate bufhead.b_next->b_prev = bp;
2337c478bd9Sstevel@tonic-gate bufhead.b_next = bp;
2347c478bd9Sstevel@tonic-gate }
235355d6bb5Sswilcox noalloc:
2367c478bd9Sstevel@tonic-gate bufhead.b_size = i; /* save number of buffers */
2377c478bd9Sstevel@tonic-gate pbp = pdirbp = NULL;
238355d6bb5Sswilcox return;
239355d6bb5Sswilcox
240355d6bb5Sswilcox nomem:
241355d6bb5Sswilcox errexit("cannot allocate buffer pool\n");
242355d6bb5Sswilcox /* NOTREACHED */
243355d6bb5Sswilcox }
244355d6bb5Sswilcox
245355d6bb5Sswilcox /*
246355d6bb5Sswilcox * Undo a bufinit().
247355d6bb5Sswilcox */
248355d6bb5Sswilcox void
unbufinit(void)249355d6bb5Sswilcox unbufinit(void)
250355d6bb5Sswilcox {
251355d6bb5Sswilcox int cnt;
252355d6bb5Sswilcox struct bufarea *bp, *nbp;
253355d6bb5Sswilcox
254355d6bb5Sswilcox cnt = 0;
255355d6bb5Sswilcox for (bp = bufhead.b_prev; bp != NULL && bp != &bufhead; bp = nbp) {
256355d6bb5Sswilcox cnt++;
257355d6bb5Sswilcox flush(fswritefd, bp);
258355d6bb5Sswilcox nbp = bp->b_prev;
259355d6bb5Sswilcox /*
260355d6bb5Sswilcox * We're discarding the entire chain, so this isn't
261355d6bb5Sswilcox * technically necessary. However, it doesn't hurt
262355d6bb5Sswilcox * and lint's data flow analysis is much happier
263355d6bb5Sswilcox * (this prevents it from thinking there's a chance
264355d6bb5Sswilcox * of our using memory elsewhere after it's been released).
265355d6bb5Sswilcox */
266355d6bb5Sswilcox nbp->b_next = bp->b_next;
267355d6bb5Sswilcox bp->b_next->b_prev = nbp;
268355d6bb5Sswilcox free((void *)bp->b_un.b_buf);
269355d6bb5Sswilcox free((void *)bp);
270355d6bb5Sswilcox }
271355d6bb5Sswilcox
272355d6bb5Sswilcox if (bufhead.b_size != cnt)
273355d6bb5Sswilcox errexit("Panic: cache lost %d buffers\n",
2741493b746SMilan Cermak bufhead.b_size - cnt);
2757c478bd9Sstevel@tonic-gate }
2767c478bd9Sstevel@tonic-gate
2777c478bd9Sstevel@tonic-gate /*
2787c478bd9Sstevel@tonic-gate * Manage a cache of directory blocks.
2797c478bd9Sstevel@tonic-gate */
2807c478bd9Sstevel@tonic-gate struct bufarea *
getdatablk(daddr32_t blkno,size_t size)281355d6bb5Sswilcox getdatablk(daddr32_t blkno, size_t size)
2827c478bd9Sstevel@tonic-gate {
2837c478bd9Sstevel@tonic-gate struct bufarea *bp;
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next)
286355d6bb5Sswilcox if (bp->b_bno == fsbtodb(&sblock, blkno)) {
2877c478bd9Sstevel@tonic-gate goto foundit;
288355d6bb5Sswilcox }
2897c478bd9Sstevel@tonic-gate for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev)
2907c478bd9Sstevel@tonic-gate if ((bp->b_flags & B_INUSE) == 0)
2917c478bd9Sstevel@tonic-gate break;
2927c478bd9Sstevel@tonic-gate if (bp == &bufhead) {
2937c478bd9Sstevel@tonic-gate bp = alloc_bufarea();
294355d6bb5Sswilcox if (bp == NULL) {
2957c478bd9Sstevel@tonic-gate errexit("deadlocked buffer pool\n");
296355d6bb5Sswilcox /* NOTREACHED */
297355d6bb5Sswilcox }
2987c478bd9Sstevel@tonic-gate }
299355d6bb5Sswilcox /*
300355d6bb5Sswilcox * We're at the same logical level as getblk(), so if there
301355d6bb5Sswilcox * are any errors, we'll let our caller handle them.
302355d6bb5Sswilcox */
303355d6bb5Sswilcox diskreads++;
304355d6bb5Sswilcox (void) getblk(bp, blkno, size);
305355d6bb5Sswilcox
3067c478bd9Sstevel@tonic-gate foundit:
3077c478bd9Sstevel@tonic-gate totalreads++;
3087c478bd9Sstevel@tonic-gate bp->b_cnt++;
3097c478bd9Sstevel@tonic-gate /*
310355d6bb5Sswilcox * Move the buffer to head of linked list if it isn't
3117c478bd9Sstevel@tonic-gate * already there.
3127c478bd9Sstevel@tonic-gate */
3137c478bd9Sstevel@tonic-gate if (bufhead.b_next != bp) {
3147c478bd9Sstevel@tonic-gate bp->b_prev->b_next = bp->b_next;
3157c478bd9Sstevel@tonic-gate bp->b_next->b_prev = bp->b_prev;
3167c478bd9Sstevel@tonic-gate bp->b_prev = &bufhead;
3177c478bd9Sstevel@tonic-gate bp->b_next = bufhead.b_next;
3187c478bd9Sstevel@tonic-gate bufhead.b_next->b_prev = bp;
3197c478bd9Sstevel@tonic-gate bufhead.b_next = bp;
3207c478bd9Sstevel@tonic-gate }
3217c478bd9Sstevel@tonic-gate bp->b_flags |= B_INUSE;
3227c478bd9Sstevel@tonic-gate return (bp);
3237c478bd9Sstevel@tonic-gate }
3247c478bd9Sstevel@tonic-gate
325355d6bb5Sswilcox void
brelse(struct bufarea * bp)3267c478bd9Sstevel@tonic-gate brelse(struct bufarea *bp)
3277c478bd9Sstevel@tonic-gate {
3287c478bd9Sstevel@tonic-gate bp->b_cnt--;
3297c478bd9Sstevel@tonic-gate if (bp->b_cnt == 0) {
3307c478bd9Sstevel@tonic-gate bp->b_flags &= ~B_INUSE;
3317c478bd9Sstevel@tonic-gate }
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate
3347c478bd9Sstevel@tonic-gate struct bufarea *
getblk(struct bufarea * bp,daddr32_t blk,size_t size)335355d6bb5Sswilcox getblk(struct bufarea *bp, daddr32_t blk, size_t size)
3367c478bd9Sstevel@tonic-gate {
3377c478bd9Sstevel@tonic-gate diskaddr_t dblk;
3387c478bd9Sstevel@tonic-gate
3397c478bd9Sstevel@tonic-gate dblk = fsbtodb(&sblock, blk);
3407c478bd9Sstevel@tonic-gate if (bp->b_bno == dblk)
3417c478bd9Sstevel@tonic-gate return (bp);
3427c478bd9Sstevel@tonic-gate flush(fswritefd, bp);
343355d6bb5Sswilcox bp->b_errs = fsck_bread(fsreadfd, bp->b_un.b_buf, dblk, size);
3447c478bd9Sstevel@tonic-gate bp->b_bno = dblk;
3457c478bd9Sstevel@tonic-gate bp->b_size = size;
3467c478bd9Sstevel@tonic-gate return (bp);
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate
349355d6bb5Sswilcox void
flush(int fd,struct bufarea * bp)350355d6bb5Sswilcox flush(int fd, struct bufarea *bp)
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate int i, j;
3537c478bd9Sstevel@tonic-gate caddr_t sip;
3547c478bd9Sstevel@tonic-gate long size;
3557c478bd9Sstevel@tonic-gate
3567c478bd9Sstevel@tonic-gate if (!bp->b_dirty)
3577c478bd9Sstevel@tonic-gate return;
358355d6bb5Sswilcox
359355d6bb5Sswilcox /*
360355d6bb5Sswilcox * It's not our buf, so if there are errors, let whoever
361355d6bb5Sswilcox * acquired it deal with the actual problem.
362355d6bb5Sswilcox */
3637c478bd9Sstevel@tonic-gate if (bp->b_errs != 0)
3647c478bd9Sstevel@tonic-gate pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno);
3657c478bd9Sstevel@tonic-gate bp->b_dirty = 0;
3667c478bd9Sstevel@tonic-gate bp->b_errs = 0;
3677c478bd9Sstevel@tonic-gate bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size);
368355d6bb5Sswilcox if (bp != &sblk) {
3697c478bd9Sstevel@tonic-gate return;
370355d6bb5Sswilcox }
371355d6bb5Sswilcox
372355d6bb5Sswilcox /*
373355d6bb5Sswilcox * We're flushing the superblock, so make sure all the
374355d6bb5Sswilcox * ancillary bits go out as well.
375355d6bb5Sswilcox */
3767c478bd9Sstevel@tonic-gate sip = (caddr_t)sblock.fs_u.fs_csp;
3777c478bd9Sstevel@tonic-gate for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) {
3787c478bd9Sstevel@tonic-gate size = sblock.fs_cssize - i < sblock.fs_bsize ?
3797c478bd9Sstevel@tonic-gate sblock.fs_cssize - i : sblock.fs_bsize;
3807c478bd9Sstevel@tonic-gate bwrite(fswritefd, sip,
3817c478bd9Sstevel@tonic-gate fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag),
3827c478bd9Sstevel@tonic-gate size);
3837c478bd9Sstevel@tonic-gate sip += size;
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate }
3867c478bd9Sstevel@tonic-gate
387355d6bb5Sswilcox static void
rwerror(caddr_t mesg,diskaddr_t blk,int rval)388355d6bb5Sswilcox rwerror(caddr_t mesg, diskaddr_t blk, int rval)
3897c478bd9Sstevel@tonic-gate {
390355d6bb5Sswilcox int olderr = errno;
391355d6bb5Sswilcox
392355d6bb5Sswilcox if (!preen)
393355d6bb5Sswilcox (void) printf("\n");
3947c478bd9Sstevel@tonic-gate
395355d6bb5Sswilcox if (rval == -1)
396355d6bb5Sswilcox pfatal("CANNOT %s: DISK BLOCK %lld: %s",
397355d6bb5Sswilcox mesg, blk, strerror(olderr));
398355d6bb5Sswilcox else
399355d6bb5Sswilcox pfatal("CANNOT %s: DISK BLOCK %lld", mesg, blk);
400355d6bb5Sswilcox
401355d6bb5Sswilcox if (reply("CONTINUE") == 0) {
402355d6bb5Sswilcox exitstat = EXERRFATAL;
4037c478bd9Sstevel@tonic-gate errexit("Program terminated\n");
404355d6bb5Sswilcox }
4057c478bd9Sstevel@tonic-gate }
4067c478bd9Sstevel@tonic-gate
407355d6bb5Sswilcox void
ckfini(void)408355d6bb5Sswilcox ckfini(void)
4097c478bd9Sstevel@tonic-gate {
410355d6bb5Sswilcox int64_t percentage;
411355d6bb5Sswilcox
412355d6bb5Sswilcox if (fswritefd < 0)
413355d6bb5Sswilcox return;
4147c478bd9Sstevel@tonic-gate
415355d6bb5Sswilcox flush(fswritefd, &sblk);
4167c478bd9Sstevel@tonic-gate /*
417355d6bb5Sswilcox * Were we using a backup superblock?
4187c478bd9Sstevel@tonic-gate */
4197c478bd9Sstevel@tonic-gate if (havesb && sblk.b_bno != SBOFF / dev_bsize) {
420355d6bb5Sswilcox if (preen || reply("UPDATE STANDARD SUPERBLOCK") == 1) {
421355d6bb5Sswilcox sblk.b_bno = SBOFF / dev_bsize;
422355d6bb5Sswilcox sbdirty();
423355d6bb5Sswilcox flush(fswritefd, &sblk);
424355d6bb5Sswilcox }
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate flush(fswritefd, &cgblk);
427355d6bb5Sswilcox if (cgblk.b_un.b_buf != NULL) {
428355d6bb5Sswilcox free((void *)cgblk.b_un.b_buf);
4297c478bd9Sstevel@tonic-gate cgblk.b_un.b_buf = NULL;
4307c478bd9Sstevel@tonic-gate }
431355d6bb5Sswilcox unbufinit();
432355d6bb5Sswilcox pbp = NULL;
433355d6bb5Sswilcox pdirbp = NULL;
434355d6bb5Sswilcox if (debug) {
435355d6bb5Sswilcox /*
436355d6bb5Sswilcox * Note that we only count cache-related reads.
437355d6bb5Sswilcox * Anything that called fsck_bread() or getblk()
438355d6bb5Sswilcox * directly are explicitly not cached, so they're not
439355d6bb5Sswilcox * included here.
440355d6bb5Sswilcox */
441355d6bb5Sswilcox if (totalreads != 0)
442355d6bb5Sswilcox percentage = diskreads * 100 / totalreads;
443355d6bb5Sswilcox else
444355d6bb5Sswilcox percentage = 0;
445355d6bb5Sswilcox
446355d6bb5Sswilcox (void) printf("cache missed %lld of %lld reads (%lld%%)\n",
447355d6bb5Sswilcox (longlong_t)diskreads, (longlong_t)totalreads,
448355d6bb5Sswilcox (longlong_t)percentage);
4497c478bd9Sstevel@tonic-gate }
450355d6bb5Sswilcox
4517c478bd9Sstevel@tonic-gate (void) close(fsreadfd);
4527c478bd9Sstevel@tonic-gate (void) close(fswritefd);
453355d6bb5Sswilcox fsreadfd = -1;
454355d6bb5Sswilcox fswritefd = -1;
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate
457355d6bb5Sswilcox int
fsck_bread(int fd,caddr_t buf,diskaddr_t blk,size_t size)458355d6bb5Sswilcox fsck_bread(int fd, caddr_t buf, diskaddr_t blk, size_t size)
4597c478bd9Sstevel@tonic-gate {
460355d6bb5Sswilcox caddr_t cp;
461355d6bb5Sswilcox int i;
4627c478bd9Sstevel@tonic-gate int errs;
4637c478bd9Sstevel@tonic-gate offset_t offset = ldbtob(blk);
4647c478bd9Sstevel@tonic-gate offset_t addr;
4657c478bd9Sstevel@tonic-gate
466355d6bb5Sswilcox /*
467355d6bb5Sswilcox * In our universe, nothing exists before the superblock, so
468355d6bb5Sswilcox * just pretend it's always zeros. This is the complement of
469355d6bb5Sswilcox * bwrite()'s ignoring write requests into that space.
470355d6bb5Sswilcox */
471355d6bb5Sswilcox if (blk < SBLOCK) {
472355d6bb5Sswilcox if (debug)
473355d6bb5Sswilcox (void) printf(
474355d6bb5Sswilcox "WARNING: fsck_bread() passed blkno < %d (%lld)\n",
475355d6bb5Sswilcox SBLOCK, (longlong_t)blk);
476355d6bb5Sswilcox (void) memset(buf, 0, (size_t)size);
477355d6bb5Sswilcox return (1);
4787c478bd9Sstevel@tonic-gate }
479355d6bb5Sswilcox
480f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) {
481355d6bb5Sswilcox rwerror("SEEK", blk, -1);
482355d6bb5Sswilcox }
483355d6bb5Sswilcox
484355d6bb5Sswilcox if ((i = read(fd, buf, size)) == size) {
4857c478bd9Sstevel@tonic-gate return (0);
486355d6bb5Sswilcox }
487355d6bb5Sswilcox rwerror("READ", blk, i);
488f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) {
489355d6bb5Sswilcox rwerror("SEEK", blk, -1);
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate errs = 0;
492355d6bb5Sswilcox (void) memset(buf, 0, (size_t)size);
4937c478bd9Sstevel@tonic-gate pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:");
4947c478bd9Sstevel@tonic-gate for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
4957c478bd9Sstevel@tonic-gate addr = ldbtob(blk + i);
496f763a6cdScasper if (llseek(fd, addr, SEEK_SET) < 0 ||
4977c478bd9Sstevel@tonic-gate read(fd, cp, (int)secsize) < 0) {
498355d6bb5Sswilcox iscorrupt = 1;
499355d6bb5Sswilcox (void) printf(" %llu", blk + (u_longlong_t)i);
5007c478bd9Sstevel@tonic-gate errs++;
5017c478bd9Sstevel@tonic-gate }
5027c478bd9Sstevel@tonic-gate }
503355d6bb5Sswilcox (void) printf("\n");
5047c478bd9Sstevel@tonic-gate return (errs);
5057c478bd9Sstevel@tonic-gate }
5067c478bd9Sstevel@tonic-gate
507355d6bb5Sswilcox void
bwrite(int fd,caddr_t buf,diskaddr_t blk,int64_t size)508355d6bb5Sswilcox bwrite(int fd, caddr_t buf, diskaddr_t blk, int64_t size)
5097c478bd9Sstevel@tonic-gate {
510355d6bb5Sswilcox int i;
5117c478bd9Sstevel@tonic-gate int n;
512355d6bb5Sswilcox caddr_t cp;
5137c478bd9Sstevel@tonic-gate offset_t offset = ldbtob(blk);
5147c478bd9Sstevel@tonic-gate offset_t addr;
5157c478bd9Sstevel@tonic-gate
5167c478bd9Sstevel@tonic-gate if (fd < 0)
5177c478bd9Sstevel@tonic-gate return;
5187c478bd9Sstevel@tonic-gate if (blk < SBLOCK) {
5197c478bd9Sstevel@tonic-gate if (debug)
520355d6bb5Sswilcox (void) printf(
521355d6bb5Sswilcox "WARNING: Attempt to write illegal blkno %lld on %s\n",
522355d6bb5Sswilcox (longlong_t)blk, devname);
5237c478bd9Sstevel@tonic-gate return;
5247c478bd9Sstevel@tonic-gate }
525f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) {
526355d6bb5Sswilcox rwerror("SEEK", blk, -1);
527355d6bb5Sswilcox }
528355d6bb5Sswilcox if ((i = write(fd, buf, (int)size)) == size) {
5297c478bd9Sstevel@tonic-gate fsmodified = 1;
5307c478bd9Sstevel@tonic-gate return;
5317c478bd9Sstevel@tonic-gate }
532355d6bb5Sswilcox rwerror("WRITE", blk, i);
533f763a6cdScasper if (llseek(fd, offset, SEEK_SET) < 0) {
534355d6bb5Sswilcox rwerror("SEEK", blk, -1);
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:");
5377c478bd9Sstevel@tonic-gate for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) {
5387c478bd9Sstevel@tonic-gate n = 0;
5397c478bd9Sstevel@tonic-gate addr = ldbtob(blk + i);
540f763a6cdScasper if (llseek(fd, addr, SEEK_SET) < 0 ||
5417c478bd9Sstevel@tonic-gate (n = write(fd, cp, DEV_BSIZE)) < 0) {
542355d6bb5Sswilcox iscorrupt = 1;
543355d6bb5Sswilcox (void) printf(" %llu", blk + (u_longlong_t)i);
5447c478bd9Sstevel@tonic-gate } else if (n > 0) {
5457c478bd9Sstevel@tonic-gate fsmodified = 1;
5467c478bd9Sstevel@tonic-gate }
5477c478bd9Sstevel@tonic-gate
5487c478bd9Sstevel@tonic-gate }
549355d6bb5Sswilcox (void) printf("\n");
5507c478bd9Sstevel@tonic-gate }
5517c478bd9Sstevel@tonic-gate
5527c478bd9Sstevel@tonic-gate /*
553355d6bb5Sswilcox * Allocates the specified number of contiguous fragments.
5547c478bd9Sstevel@tonic-gate */
5557c478bd9Sstevel@tonic-gate daddr32_t
allocblk(int wantedfrags)556355d6bb5Sswilcox allocblk(int wantedfrags)
5577c478bd9Sstevel@tonic-gate {
558355d6bb5Sswilcox int block, leadfrag, tailfrag;
559355d6bb5Sswilcox daddr32_t selected;
560355d6bb5Sswilcox size_t size;
561355d6bb5Sswilcox struct bufarea *bp;
5627c478bd9Sstevel@tonic-gate
563355d6bb5Sswilcox /*
564355d6bb5Sswilcox * It's arguable whether we should just fail, or instead
565355d6bb5Sswilcox * error out here. Since we should only ever be asked for
566355d6bb5Sswilcox * a single fragment or an entire block (i.e., sblock.fs_frag),
567355d6bb5Sswilcox * we'll fail out because anything else means somebody
568355d6bb5Sswilcox * changed code without considering all of the ramifications.
569355d6bb5Sswilcox */
570355d6bb5Sswilcox if (wantedfrags <= 0 || wantedfrags > sblock.fs_frag) {
571355d6bb5Sswilcox exitstat = EXERRFATAL;
572355d6bb5Sswilcox errexit("allocblk() asked for %d frags. "
5731493b746SMilan Cermak "Legal range is 1 to %d",
5741493b746SMilan Cermak wantedfrags, sblock.fs_frag);
575355d6bb5Sswilcox }
576355d6bb5Sswilcox
577355d6bb5Sswilcox /*
578355d6bb5Sswilcox * For each filesystem block, look at every possible starting
579355d6bb5Sswilcox * offset within the block such that we can get the number of
580355d6bb5Sswilcox * contiguous fragments that we need. This is a drastically
581355d6bb5Sswilcox * simplified version of the kernel's mapsearch() and alloc*().
582355d6bb5Sswilcox * It's also correspondingly slower.
583355d6bb5Sswilcox */
584355d6bb5Sswilcox for (block = 0; block < maxfsblock - sblock.fs_frag;
585355d6bb5Sswilcox block += sblock.fs_frag) {
586355d6bb5Sswilcox for (leadfrag = 0; leadfrag <= sblock.fs_frag - wantedfrags;
587355d6bb5Sswilcox leadfrag++) {
588355d6bb5Sswilcox /*
589355d6bb5Sswilcox * Is first fragment of candidate run available?
590355d6bb5Sswilcox */
591355d6bb5Sswilcox if (testbmap(block + leadfrag))
5927c478bd9Sstevel@tonic-gate continue;
593355d6bb5Sswilcox /*
594355d6bb5Sswilcox * Are the rest of them available?
595355d6bb5Sswilcox */
596355d6bb5Sswilcox for (tailfrag = 1; tailfrag < wantedfrags; tailfrag++)
597355d6bb5Sswilcox if (testbmap(block + leadfrag + tailfrag))
5987c478bd9Sstevel@tonic-gate break;
599355d6bb5Sswilcox if (tailfrag < wantedfrags) {
600355d6bb5Sswilcox /*
601355d6bb5Sswilcox * No, skip the known-unusable run.
602355d6bb5Sswilcox */
603355d6bb5Sswilcox leadfrag += tailfrag;
6047c478bd9Sstevel@tonic-gate continue;
6057c478bd9Sstevel@tonic-gate }
606355d6bb5Sswilcox /*
607355d6bb5Sswilcox * Found what we need, so claim them.
608355d6bb5Sswilcox */
609355d6bb5Sswilcox for (tailfrag = 0; tailfrag < wantedfrags; tailfrag++)
610355d6bb5Sswilcox setbmap(block + leadfrag + tailfrag);
611355d6bb5Sswilcox n_blks += wantedfrags;
612355d6bb5Sswilcox size = wantedfrags * sblock.fs_fsize;
613355d6bb5Sswilcox selected = block + leadfrag;
614355d6bb5Sswilcox bp = getdatablk(selected, size);
615355d6bb5Sswilcox (void) memset((void *)bp->b_un.b_buf, 0, size);
616355d6bb5Sswilcox dirty(bp);
617355d6bb5Sswilcox brelse(bp);
618355d6bb5Sswilcox if (debug)
619355d6bb5Sswilcox (void) printf(
620355d6bb5Sswilcox "allocblk: selected %d (in block %d), frags %d, size %d\n",
621355d6bb5Sswilcox selected, selected % sblock.fs_bsize,
622355d6bb5Sswilcox wantedfrags, (int)size);
623355d6bb5Sswilcox return (selected);
6247c478bd9Sstevel@tonic-gate }
6257c478bd9Sstevel@tonic-gate }
6267c478bd9Sstevel@tonic-gate return (0);
6277c478bd9Sstevel@tonic-gate }
6287c478bd9Sstevel@tonic-gate
6297c478bd9Sstevel@tonic-gate /*
6307c478bd9Sstevel@tonic-gate * Free a previously allocated block
6317c478bd9Sstevel@tonic-gate */
632355d6bb5Sswilcox void
freeblk(fsck_ino_t ino,daddr32_t blkno,int frags)633355d6bb5Sswilcox freeblk(fsck_ino_t ino, daddr32_t blkno, int frags)
6347c478bd9Sstevel@tonic-gate {
6357c478bd9Sstevel@tonic-gate struct inodesc idesc;
6367c478bd9Sstevel@tonic-gate
637355d6bb5Sswilcox if (debug)
638355d6bb5Sswilcox (void) printf("debug: freeing %d fragments starting at %d\n",
639355d6bb5Sswilcox frags, blkno);
640355d6bb5Sswilcox
641355d6bb5Sswilcox init_inodesc(&idesc);
642355d6bb5Sswilcox
643355d6bb5Sswilcox idesc.id_number = ino;
6447c478bd9Sstevel@tonic-gate idesc.id_blkno = blkno;
6457c478bd9Sstevel@tonic-gate idesc.id_numfrags = frags;
646355d6bb5Sswilcox idesc.id_truncto = -1;
647355d6bb5Sswilcox
648355d6bb5Sswilcox /*
649355d6bb5Sswilcox * Nothing in the return status has any relevance to how
650355d6bb5Sswilcox * we're using pass4check(), so just ignore it.
651355d6bb5Sswilcox */
652355d6bb5Sswilcox (void) pass4check(&idesc);
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate
6557c478bd9Sstevel@tonic-gate /*
656355d6bb5Sswilcox * Fill NAMEBUF with a path starting in CURDIR for INO. Assumes
657355d6bb5Sswilcox * that the given buffer is at least MAXPATHLEN + 1 characters.
6587c478bd9Sstevel@tonic-gate */
659355d6bb5Sswilcox void
getpathname(caddr_t namebuf,fsck_ino_t curdir,fsck_ino_t ino)660355d6bb5Sswilcox getpathname(caddr_t namebuf, fsck_ino_t curdir, fsck_ino_t ino)
6617c478bd9Sstevel@tonic-gate {
6627c478bd9Sstevel@tonic-gate int len;
663355d6bb5Sswilcox caddr_t cp;
664355d6bb5Sswilcox struct dinode *dp;
6657c478bd9Sstevel@tonic-gate struct inodesc idesc;
6667c478bd9Sstevel@tonic-gate struct inoinfo *inp;
6677c478bd9Sstevel@tonic-gate
668355d6bb5Sswilcox if (debug)
669355d6bb5Sswilcox (void) printf("debug: getpathname(curdir %d, ino %d)\n",
670355d6bb5Sswilcox curdir, ino);
671355d6bb5Sswilcox
672355d6bb5Sswilcox if ((curdir == 0) || (!INO_IS_DVALID(curdir))) {
673355d6bb5Sswilcox (void) strcpy(namebuf, "?");
674355d6bb5Sswilcox return;
675355d6bb5Sswilcox }
676355d6bb5Sswilcox
677355d6bb5Sswilcox if ((curdir == UFSROOTINO) && (ino == UFSROOTINO)) {
678355d6bb5Sswilcox (void) strcpy(namebuf, "/");
6797c478bd9Sstevel@tonic-gate return;
6807c478bd9Sstevel@tonic-gate }
681355d6bb5Sswilcox
682355d6bb5Sswilcox init_inodesc(&idesc);
6837c478bd9Sstevel@tonic-gate idesc.id_type = DATA;
6847c478bd9Sstevel@tonic-gate cp = &namebuf[MAXPATHLEN - 1];
6857c478bd9Sstevel@tonic-gate *cp = '\0';
686355d6bb5Sswilcox
687355d6bb5Sswilcox /*
688355d6bb5Sswilcox * In the case of extended attributes, our
689355d6bb5Sswilcox * parent won't necessarily be a directory, so just
690355d6bb5Sswilcox * return what we've found with a prefix indicating
691355d6bb5Sswilcox * that it's an XATTR. Presumably our caller will
692355d6bb5Sswilcox * know what's going on and do something useful, like
693355d6bb5Sswilcox * work out the path of the parent and then combine
694355d6bb5Sswilcox * the two names.
695355d6bb5Sswilcox *
696355d6bb5Sswilcox * Can't use strcpy(), etc, because we've probably
697355d6bb5Sswilcox * already got some name information in the buffer and
698355d6bb5Sswilcox * the usual trailing \0 would lose it.
699355d6bb5Sswilcox */
700355d6bb5Sswilcox dp = ginode(curdir);
701355d6bb5Sswilcox if ((dp->di_mode & IFMT) == IFATTRDIR) {
702355d6bb5Sswilcox idesc.id_number = curdir;
703355d6bb5Sswilcox idesc.id_parent = ino;
704355d6bb5Sswilcox idesc.id_func = findname;
705355d6bb5Sswilcox idesc.id_name = namebuf;
706355d6bb5Sswilcox idesc.id_fix = NOFIX;
707355d6bb5Sswilcox if ((ckinode(dp, &idesc, CKI_TRAVERSE) & FOUND) == 0) {
708355d6bb5Sswilcox *cp-- = '?';
709355d6bb5Sswilcox }
710355d6bb5Sswilcox
711355d6bb5Sswilcox len = sizeof (XATTR_DIR_NAME) - 1;
712355d6bb5Sswilcox cp -= len;
713355d6bb5Sswilcox (void) memmove(cp, XATTR_DIR_NAME, len);
714355d6bb5Sswilcox goto attrname;
715355d6bb5Sswilcox }
716355d6bb5Sswilcox
717355d6bb5Sswilcox /*
718355d6bb5Sswilcox * If curdir == ino, need to get a handle on .. so we
719355d6bb5Sswilcox * can search it for ino's name. Otherwise, just search
720355d6bb5Sswilcox * the given directory for ino. Repeat until out of space
721355d6bb5Sswilcox * or a full path has been built.
722355d6bb5Sswilcox */
7237c478bd9Sstevel@tonic-gate if (curdir != ino) {
7247c478bd9Sstevel@tonic-gate idesc.id_parent = curdir;
7257c478bd9Sstevel@tonic-gate goto namelookup;
7267c478bd9Sstevel@tonic-gate }
727355d6bb5Sswilcox while (ino != UFSROOTINO && ino != 0) {
7287c478bd9Sstevel@tonic-gate idesc.id_number = ino;
7297c478bd9Sstevel@tonic-gate idesc.id_func = findino;
7307c478bd9Sstevel@tonic-gate idesc.id_name = "..";
7317c478bd9Sstevel@tonic-gate idesc.id_fix = NOFIX;
732355d6bb5Sswilcox if ((ckinode(ginode(ino), &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7337c478bd9Sstevel@tonic-gate inp = getinoinfo(ino);
734355d6bb5Sswilcox if ((inp == NULL) || (inp->i_parent == 0)) {
7357c478bd9Sstevel@tonic-gate break;
736355d6bb5Sswilcox }
7377c478bd9Sstevel@tonic-gate idesc.id_parent = inp->i_parent;
7387c478bd9Sstevel@tonic-gate }
739355d6bb5Sswilcox
740355d6bb5Sswilcox /*
741355d6bb5Sswilcox * To get this far, id_parent must have the inode
742355d6bb5Sswilcox * number for `..' in it. By definition, that's got
743355d6bb5Sswilcox * to be a directory, so search it for the inode of
744355d6bb5Sswilcox * interest.
745355d6bb5Sswilcox */
746355d6bb5Sswilcox namelookup:
7477c478bd9Sstevel@tonic-gate idesc.id_number = idesc.id_parent;
7487c478bd9Sstevel@tonic-gate idesc.id_parent = ino;
7497c478bd9Sstevel@tonic-gate idesc.id_func = findname;
7507c478bd9Sstevel@tonic-gate idesc.id_name = namebuf;
7517c478bd9Sstevel@tonic-gate idesc.id_fix = NOFIX;
752355d6bb5Sswilcox if ((ckinode(ginode(idesc.id_number),
753355d6bb5Sswilcox &idesc, CKI_TRAVERSE) & FOUND) == 0) {
7547c478bd9Sstevel@tonic-gate break;
755355d6bb5Sswilcox }
756355d6bb5Sswilcox /*
757355d6bb5Sswilcox * Prepend to what we've accumulated so far. If
758355d6bb5Sswilcox * there's not enough room for even one more path element
759355d6bb5Sswilcox * (of the worst-case length), then bail out.
760355d6bb5Sswilcox */
7617c478bd9Sstevel@tonic-gate len = strlen(namebuf);
7627c478bd9Sstevel@tonic-gate cp -= len;
7637c478bd9Sstevel@tonic-gate if (cp < &namebuf[MAXNAMLEN])
7647c478bd9Sstevel@tonic-gate break;
765355d6bb5Sswilcox (void) memmove(cp, namebuf, len);
7667c478bd9Sstevel@tonic-gate *--cp = '/';
767355d6bb5Sswilcox
768355d6bb5Sswilcox /*
769355d6bb5Sswilcox * Corner case for a looped-to-itself directory.
770355d6bb5Sswilcox */
771355d6bb5Sswilcox if (ino == idesc.id_number)
772355d6bb5Sswilcox break;
773355d6bb5Sswilcox
774355d6bb5Sswilcox /*
775355d6bb5Sswilcox * Climb one level of the hierarchy. In other words,
776355d6bb5Sswilcox * the current .. becomes the inode to search for and
777355d6bb5Sswilcox * its parent becomes the directory to search in.
778355d6bb5Sswilcox */
7797c478bd9Sstevel@tonic-gate ino = idesc.id_number;
7807c478bd9Sstevel@tonic-gate }
781355d6bb5Sswilcox
782355d6bb5Sswilcox /*
783355d6bb5Sswilcox * If we hit a discontinuity in the hierarchy, indicate it by
784355d6bb5Sswilcox * prefixing the path so far with `?'. Otherwise, the first
785355d6bb5Sswilcox * character will be `/' as a side-effect of the *--cp above.
786355d6bb5Sswilcox *
787355d6bb5Sswilcox * The special case is to handle the situation where we're
788355d6bb5Sswilcox * trying to look something up in UFSROOTINO, but didn't find
789355d6bb5Sswilcox * it.
790355d6bb5Sswilcox */
791355d6bb5Sswilcox if (ino != UFSROOTINO || cp == &namebuf[MAXPATHLEN - 1]) {
792355d6bb5Sswilcox if (cp > namebuf)
793355d6bb5Sswilcox cp--;
794355d6bb5Sswilcox *cp = '?';
7957c478bd9Sstevel@tonic-gate }
796355d6bb5Sswilcox
797355d6bb5Sswilcox /*
798355d6bb5Sswilcox * The invariants being used for buffer integrity are:
799355d6bb5Sswilcox * - namebuf[] is terminated with \0 before anything else
800355d6bb5Sswilcox * - cp is always <= the last element of namebuf[]
801355d6bb5Sswilcox * - the new path element is always stored at the
802355d6bb5Sswilcox * beginning of namebuf[], and is no more than MAXNAMLEN-1
803355d6bb5Sswilcox * characters
804355d6bb5Sswilcox * - cp is is decremented by the number of characters in
805355d6bb5Sswilcox * the new path element
806355d6bb5Sswilcox * - if, after the above accounting for the new element's
807355d6bb5Sswilcox * size, there is no longer enough room at the beginning of
808355d6bb5Sswilcox * namebuf[] for a full-sized path element and a slash,
809355d6bb5Sswilcox * terminate the loop. cp is in the range
810355d6bb5Sswilcox * &namebuf[0]..&namebuf[MAXNAMLEN - 1]
811355d6bb5Sswilcox */
812355d6bb5Sswilcox attrname:
813355d6bb5Sswilcox /* LINTED per the above discussion */
814355d6bb5Sswilcox (void) memmove(namebuf, cp, &namebuf[MAXPATHLEN] - cp);
8157c478bd9Sstevel@tonic-gate }
8167c478bd9Sstevel@tonic-gate
817355d6bb5Sswilcox /* ARGSUSED */
8187c478bd9Sstevel@tonic-gate void
catch(int dummy)819355d6bb5Sswilcox catch(int dummy)
8207c478bd9Sstevel@tonic-gate {
8217c478bd9Sstevel@tonic-gate ckfini();
822355d6bb5Sswilcox exit(EXSIGNAL);
8237c478bd9Sstevel@tonic-gate }
8247c478bd9Sstevel@tonic-gate
8257c478bd9Sstevel@tonic-gate /*
8267c478bd9Sstevel@tonic-gate * When preening, allow a single quit to signal
8277c478bd9Sstevel@tonic-gate * a special exit after filesystem checks complete
8287c478bd9Sstevel@tonic-gate * so that reboot sequence may be interrupted.
8297c478bd9Sstevel@tonic-gate */
830355d6bb5Sswilcox /* ARGSUSED */
8317c478bd9Sstevel@tonic-gate void
catchquit(int dummy)832355d6bb5Sswilcox catchquit(int dummy)
8337c478bd9Sstevel@tonic-gate {
834355d6bb5Sswilcox (void) printf("returning to single-user after filesystem check\n");
835355d6bb5Sswilcox interrupted = 1;
8367c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL);
8377c478bd9Sstevel@tonic-gate }
8387c478bd9Sstevel@tonic-gate
8397c478bd9Sstevel@tonic-gate
8407c478bd9Sstevel@tonic-gate /*
8417c478bd9Sstevel@tonic-gate * determine whether an inode should be fixed.
8427c478bd9Sstevel@tonic-gate */
843355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
844355d6bb5Sswilcox int
dofix(struct inodesc * idesc,caddr_t msg,...)845355d6bb5Sswilcox dofix(struct inodesc *idesc, caddr_t msg, ...)
8467c478bd9Sstevel@tonic-gate {
847355d6bb5Sswilcox int rval = 0;
848355d6bb5Sswilcox va_list ap;
849355d6bb5Sswilcox
850355d6bb5Sswilcox va_start(ap, msg);
8517c478bd9Sstevel@tonic-gate
8527c478bd9Sstevel@tonic-gate switch (idesc->id_fix) {
8537c478bd9Sstevel@tonic-gate
8547c478bd9Sstevel@tonic-gate case DONTKNOW:
8557c478bd9Sstevel@tonic-gate if (idesc->id_type == DATA)
856355d6bb5Sswilcox vdirerror(idesc->id_number, msg, ap);
8577c478bd9Sstevel@tonic-gate else
858355d6bb5Sswilcox vpwarn(msg, ap);
8597c478bd9Sstevel@tonic-gate if (preen) {
8607c478bd9Sstevel@tonic-gate idesc->id_fix = FIX;
861355d6bb5Sswilcox rval = ALTERED;
862355d6bb5Sswilcox break;
8637c478bd9Sstevel@tonic-gate }
8647c478bd9Sstevel@tonic-gate if (reply("SALVAGE") == 0) {
8657c478bd9Sstevel@tonic-gate idesc->id_fix = NOFIX;
866355d6bb5Sswilcox break;
8677c478bd9Sstevel@tonic-gate }
8687c478bd9Sstevel@tonic-gate idesc->id_fix = FIX;
869355d6bb5Sswilcox rval = ALTERED;
870355d6bb5Sswilcox break;
8717c478bd9Sstevel@tonic-gate
8727c478bd9Sstevel@tonic-gate case FIX:
873355d6bb5Sswilcox rval = ALTERED;
874355d6bb5Sswilcox break;
8757c478bd9Sstevel@tonic-gate
8767c478bd9Sstevel@tonic-gate case NOFIX:
877355d6bb5Sswilcox break;
8787c478bd9Sstevel@tonic-gate
8797c478bd9Sstevel@tonic-gate default:
880355d6bb5Sswilcox errexit("UNKNOWN INODESC FIX MODE %d\n", (int)idesc->id_fix);
8817c478bd9Sstevel@tonic-gate }
882355d6bb5Sswilcox
883355d6bb5Sswilcox va_end(ap);
884355d6bb5Sswilcox return (rval);
8857c478bd9Sstevel@tonic-gate }
8867c478bd9Sstevel@tonic-gate
887355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
888355d6bb5Sswilcox void
errexit(caddr_t fmt,...)889355d6bb5Sswilcox errexit(caddr_t fmt, ...)
8907c478bd9Sstevel@tonic-gate {
891355d6bb5Sswilcox va_list ap;
8927c478bd9Sstevel@tonic-gate
893355d6bb5Sswilcox va_start(ap, fmt);
894355d6bb5Sswilcox verrexit(fmt, ap);
895355d6bb5Sswilcox /* NOTREACHED */
896355d6bb5Sswilcox }
897355d6bb5Sswilcox
898355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
899355d6bb5Sswilcox static void
verrexit(caddr_t fmt,va_list ap)900355d6bb5Sswilcox verrexit(caddr_t fmt, va_list ap)
901355d6bb5Sswilcox {
902355d6bb5Sswilcox static int recursing = 0;
903355d6bb5Sswilcox
904355d6bb5Sswilcox if (!recursing) {
905355d6bb5Sswilcox recursing = 1;
906355d6bb5Sswilcox if (errorlocked || iscorrupt) {
90773f4c598SMilan Cermak if (havesb && fswritefd >= 0) {
908355d6bb5Sswilcox sblock.fs_clean = FSBAD;
909355d6bb5Sswilcox sblock.fs_state = FSOKAY - (long)sblock.fs_time;
910355d6bb5Sswilcox sblock.fs_state = -sblock.fs_state;
911355d6bb5Sswilcox sbdirty();
912355d6bb5Sswilcox write_altsb(fswritefd);
913355d6bb5Sswilcox flush(fswritefd, &sblk);
914355d6bb5Sswilcox }
9157c478bd9Sstevel@tonic-gate }
916355d6bb5Sswilcox ckfini();
917355d6bb5Sswilcox recursing = 0;
9187c478bd9Sstevel@tonic-gate }
919355d6bb5Sswilcox (void) vprintf(fmt, ap);
920355d6bb5Sswilcox if (fmt[strlen(fmt) - 1] != '\n')
921355d6bb5Sswilcox (void) putchar('\n');
922355d6bb5Sswilcox exit((exitstat != 0) ? exitstat : EXERRFATAL);
9237c478bd9Sstevel@tonic-gate }
9247c478bd9Sstevel@tonic-gate
9257c478bd9Sstevel@tonic-gate /*
9267c478bd9Sstevel@tonic-gate * An unexpected inconsistency occured.
9277c478bd9Sstevel@tonic-gate * Die if preening, otherwise just print message and continue.
9287c478bd9Sstevel@tonic-gate */
929355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
930355d6bb5Sswilcox void
pfatal(caddr_t fmt,...)931355d6bb5Sswilcox pfatal(caddr_t fmt, ...)
932355d6bb5Sswilcox {
933355d6bb5Sswilcox va_list ap;
934355d6bb5Sswilcox
935355d6bb5Sswilcox va_start(ap, fmt);
936355d6bb5Sswilcox vpfatal(fmt, ap);
937355d6bb5Sswilcox va_end(ap);
938355d6bb5Sswilcox }
939355d6bb5Sswilcox
940355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
941355d6bb5Sswilcox static void
vpfatal(caddr_t fmt,va_list ap)942355d6bb5Sswilcox vpfatal(caddr_t fmt, va_list ap)
9437c478bd9Sstevel@tonic-gate {
9447c478bd9Sstevel@tonic-gate if (preen) {
945355d6bb5Sswilcox if (*fmt != '\0') {
946355d6bb5Sswilcox (void) printf("%s: ", devname);
947355d6bb5Sswilcox (void) vprintf(fmt, ap);
948355d6bb5Sswilcox (void) printf("\n");
949355d6bb5Sswilcox }
950355d6bb5Sswilcox (void) printf(
951355d6bb5Sswilcox "%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n",
952355d6bb5Sswilcox devname);
95373f4c598SMilan Cermak if (havesb && fswritefd >= 0) {
9547c478bd9Sstevel@tonic-gate sblock.fs_clean = FSBAD;
9557c478bd9Sstevel@tonic-gate sblock.fs_state = -(FSOKAY - (long)sblock.fs_time);
9567c478bd9Sstevel@tonic-gate sbdirty();
9577c478bd9Sstevel@tonic-gate flush(fswritefd, &sblk);
9587c478bd9Sstevel@tonic-gate }
959355d6bb5Sswilcox /*
960355d6bb5Sswilcox * We're exiting, it doesn't really matter that our
961355d6bb5Sswilcox * caller doesn't get to call va_end().
962355d6bb5Sswilcox */
963355d6bb5Sswilcox if (exitstat == 0)
964355d6bb5Sswilcox exitstat = EXFNDERRS;
965355d6bb5Sswilcox exit(exitstat);
966355d6bb5Sswilcox }
967355d6bb5Sswilcox if (*fmt != '\0') {
968355d6bb5Sswilcox (void) vprintf(fmt, ap);
9697c478bd9Sstevel@tonic-gate }
9707c478bd9Sstevel@tonic-gate }
9717c478bd9Sstevel@tonic-gate
9727c478bd9Sstevel@tonic-gate /*
9737c478bd9Sstevel@tonic-gate * Pwarn just prints a message when not preening,
9747c478bd9Sstevel@tonic-gate * or a warning (preceded by filename) when preening.
9757c478bd9Sstevel@tonic-gate */
976355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
977355d6bb5Sswilcox void
pwarn(caddr_t fmt,...)978355d6bb5Sswilcox pwarn(caddr_t fmt, ...)
9797c478bd9Sstevel@tonic-gate {
980355d6bb5Sswilcox va_list ap;
9817c478bd9Sstevel@tonic-gate
982355d6bb5Sswilcox va_start(ap, fmt);
983355d6bb5Sswilcox vpwarn(fmt, ap);
984355d6bb5Sswilcox va_end(ap);
985355d6bb5Sswilcox }
986355d6bb5Sswilcox
987355d6bb5Sswilcox NOTE(PRINTFLIKE(1))
988355d6bb5Sswilcox static void
vpwarn(caddr_t fmt,va_list ap)989355d6bb5Sswilcox vpwarn(caddr_t fmt, va_list ap)
990355d6bb5Sswilcox {
991355d6bb5Sswilcox if (*fmt != '\0') {
992355d6bb5Sswilcox if (preen)
993355d6bb5Sswilcox (void) printf("%s: ", devname);
994355d6bb5Sswilcox (void) vprintf(fmt, ap);
995355d6bb5Sswilcox }
9967c478bd9Sstevel@tonic-gate }
9977c478bd9Sstevel@tonic-gate
9987c478bd9Sstevel@tonic-gate /*
999355d6bb5Sswilcox * Like sprintf(), except the buffer is dynamically allocated
1000355d6bb5Sswilcox * and returned, instead of being passed in. A pointer to the
1001355d6bb5Sswilcox * buffer is stored in *RET, and FMT is the usual format string.
1002355d6bb5Sswilcox * The number of characters in *RET (excluding the trailing \0,
1003355d6bb5Sswilcox * to be consistent with the other *printf() routines) is returned.
1004355d6bb5Sswilcox *
1005355d6bb5Sswilcox * Solaris doesn't have asprintf(3C) yet, unfortunately.
10067c478bd9Sstevel@tonic-gate */
1007355d6bb5Sswilcox NOTE(PRINTFLIKE(2))
1008355d6bb5Sswilcox int
fsck_asprintf(caddr_t * ret,caddr_t fmt,...)1009355d6bb5Sswilcox fsck_asprintf(caddr_t *ret, caddr_t fmt, ...)
10107c478bd9Sstevel@tonic-gate {
1011355d6bb5Sswilcox int len;
1012355d6bb5Sswilcox caddr_t buffer;
1013355d6bb5Sswilcox va_list ap;
10147c478bd9Sstevel@tonic-gate
1015355d6bb5Sswilcox va_start(ap, fmt);
1016355d6bb5Sswilcox len = vsnprintf(NULL, 0, fmt, ap);
1017355d6bb5Sswilcox va_end(ap);
1018355d6bb5Sswilcox
1019355d6bb5Sswilcox buffer = malloc((len + 1) * sizeof (char));
1020355d6bb5Sswilcox if (buffer == NULL) {
1021355d6bb5Sswilcox errexit("Out of memory in asprintf\n");
1022355d6bb5Sswilcox /* NOTREACHED */
1023355d6bb5Sswilcox }
1024355d6bb5Sswilcox
1025355d6bb5Sswilcox va_start(ap, fmt);
1026355d6bb5Sswilcox (void) vsnprintf(buffer, len + 1, fmt, ap);
1027355d6bb5Sswilcox va_end(ap);
1028355d6bb5Sswilcox
1029355d6bb5Sswilcox *ret = buffer;
1030355d6bb5Sswilcox return (len);
10317c478bd9Sstevel@tonic-gate }
1032355d6bb5Sswilcox
1033355d6bb5Sswilcox /*
1034355d6bb5Sswilcox * So we can take advantage of kernel routines in ufs_subr.c.
1035355d6bb5Sswilcox */
1036355d6bb5Sswilcox /* PRINTFLIKE2 */
10377c478bd9Sstevel@tonic-gate void
cmn_err(int level,caddr_t fmt,...)1038355d6bb5Sswilcox cmn_err(int level, caddr_t fmt, ...)
10397c478bd9Sstevel@tonic-gate {
1040355d6bb5Sswilcox va_list ap;
10417c478bd9Sstevel@tonic-gate
1042355d6bb5Sswilcox va_start(ap, fmt);
10437c478bd9Sstevel@tonic-gate if (level == CE_PANIC) {
1044355d6bb5Sswilcox (void) printf("INTERNAL INCONSISTENCY:");
1045355d6bb5Sswilcox verrexit(fmt, ap);
1046355d6bb5Sswilcox } else {
1047355d6bb5Sswilcox (void) vprintf(fmt, ap);
10487c478bd9Sstevel@tonic-gate }
1049355d6bb5Sswilcox va_end(ap);
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate
10527c478bd9Sstevel@tonic-gate /*
10537c478bd9Sstevel@tonic-gate * Check to see if unraw version of name is already mounted.
1054355d6bb5Sswilcox * Updates devstr with the device name if devstr is not NULL
1055355d6bb5Sswilcox * and str_size is positive.
10567c478bd9Sstevel@tonic-gate */
1057355d6bb5Sswilcox int
mounted(caddr_t name,caddr_t devstr,size_t str_size)1058355d6bb5Sswilcox mounted(caddr_t name, caddr_t devstr, size_t str_size)
10597c478bd9Sstevel@tonic-gate {
1060355d6bb5Sswilcox int found;
1061355d6bb5Sswilcox struct mnttab *mntent;
10627c478bd9Sstevel@tonic-gate
1063355d6bb5Sswilcox mntent = search_mnttab(NULL, unrawname(name), devstr, str_size);
1064355d6bb5Sswilcox if (mntent == NULL)
1065355d6bb5Sswilcox return (M_NOMNT);
1066355d6bb5Sswilcox
1067355d6bb5Sswilcox /*
1068355d6bb5Sswilcox * It's mounted. With or without write access?
1069355d6bb5Sswilcox */
1070355d6bb5Sswilcox if (hasmntopt(mntent, MNTOPT_RO) != 0)
1071355d6bb5Sswilcox found = M_RO; /* mounted as RO */
1072355d6bb5Sswilcox else
1073*bfbf29e2SToomas Soome found = M_RW; /* mounted as R/W */
1074355d6bb5Sswilcox
1075355d6bb5Sswilcox if (mount_point == NULL) {
1076355d6bb5Sswilcox mount_point = strdup(mntent->mnt_mountp);
1077355d6bb5Sswilcox if (mount_point == NULL) {
1078355d6bb5Sswilcox errexit("fsck: memory allocation failure: %s",
10791493b746SMilan Cermak strerror(errno));
1080355d6bb5Sswilcox /* NOTREACHED */
10817c478bd9Sstevel@tonic-gate }
1082355d6bb5Sswilcox
1083355d6bb5Sswilcox if (devstr != NULL && str_size > 0)
1084355d6bb5Sswilcox (void) strlcpy(devstr, mntent->mnt_special, str_size);
10857c478bd9Sstevel@tonic-gate }
1086355d6bb5Sswilcox
10877c478bd9Sstevel@tonic-gate return (found);
10887c478bd9Sstevel@tonic-gate }
10897c478bd9Sstevel@tonic-gate
10907c478bd9Sstevel@tonic-gate /*
10917c478bd9Sstevel@tonic-gate * Check to see if name corresponds to an entry in vfstab, and that the entry
10927c478bd9Sstevel@tonic-gate * does not have option ro.
10937c478bd9Sstevel@tonic-gate */
1094355d6bb5Sswilcox int
writable(caddr_t name)1095355d6bb5Sswilcox writable(caddr_t name)
10967c478bd9Sstevel@tonic-gate {
10977c478bd9Sstevel@tonic-gate int rw = 1;
1098355d6bb5Sswilcox struct vfstab vfsbuf, vfskey;
10997c478bd9Sstevel@tonic-gate FILE *vfstab;
11007c478bd9Sstevel@tonic-gate
11017c478bd9Sstevel@tonic-gate vfstab = fopen(VFSTAB, "r");
11027c478bd9Sstevel@tonic-gate if (vfstab == NULL) {
1103355d6bb5Sswilcox (void) printf("can't open %s\n", VFSTAB);
11047c478bd9Sstevel@tonic-gate return (1);
11057c478bd9Sstevel@tonic-gate }
1106355d6bb5Sswilcox (void) memset((void *)&vfskey, 0, sizeof (vfskey));
1107355d6bb5Sswilcox vfsnull(&vfskey);
1108355d6bb5Sswilcox vfskey.vfs_special = unrawname(name);
1109355d6bb5Sswilcox vfskey.vfs_fstype = MNTTYPE_UFS;
1110355d6bb5Sswilcox if ((getvfsany(vfstab, &vfsbuf, &vfskey) == 0) &&
11117c478bd9Sstevel@tonic-gate (hasvfsopt(&vfsbuf, MNTOPT_RO))) {
11127c478bd9Sstevel@tonic-gate rw = 0;
11137c478bd9Sstevel@tonic-gate }
1114355d6bb5Sswilcox (void) fclose(vfstab);
11157c478bd9Sstevel@tonic-gate return (rw);
11167c478bd9Sstevel@tonic-gate }
11177c478bd9Sstevel@tonic-gate
11187c478bd9Sstevel@tonic-gate /*
11197c478bd9Sstevel@tonic-gate * debugclean
11207c478bd9Sstevel@tonic-gate */
1121355d6bb5Sswilcox static void
debugclean(void)1122355d6bb5Sswilcox debugclean(void)
11237c478bd9Sstevel@tonic-gate {
1124355d6bb5Sswilcox if (!debug)
11257c478bd9Sstevel@tonic-gate return;
11267c478bd9Sstevel@tonic-gate
11277c478bd9Sstevel@tonic-gate if ((iscorrupt == 0) && (isdirty == 0))
11287c478bd9Sstevel@tonic-gate return;
11297c478bd9Sstevel@tonic-gate
1130355d6bb5Sswilcox if ((sblock.fs_clean == FSSTABLE) || (sblock.fs_clean == FSCLEAN) ||
1131355d6bb5Sswilcox (sblock.fs_clean == FSLOG && islog && islogok) ||
1132355d6bb5Sswilcox ((FSOKAY == (sblock.fs_state + sblock.fs_time)) && !errorlocked))
11337c478bd9Sstevel@tonic-gate return;
11347c478bd9Sstevel@tonic-gate
1135355d6bb5Sswilcox (void) printf("WARNING: inconsistencies detected on %s filesystem %s\n",
11367c478bd9Sstevel@tonic-gate sblock.fs_clean == FSSTABLE ? "stable" :
11377c478bd9Sstevel@tonic-gate sblock.fs_clean == FSLOG ? "logging" :
1138355d6bb5Sswilcox sblock.fs_clean == FSFIX ? "being fixed" : "clean",
1139355d6bb5Sswilcox devname);
11407c478bd9Sstevel@tonic-gate }
11417c478bd9Sstevel@tonic-gate
11427c478bd9Sstevel@tonic-gate /*
11437c478bd9Sstevel@tonic-gate * updateclean
11447c478bd9Sstevel@tonic-gate * Carefully and transparently update the clean flag.
1145355d6bb5Sswilcox *
1146355d6bb5Sswilcox * `iscorrupt' has to be in its final state before this is called.
11477c478bd9Sstevel@tonic-gate */
1148355d6bb5Sswilcox int
updateclean(void)1149355d6bb5Sswilcox updateclean(void)
1150355d6bb5Sswilcox {
1151355d6bb5Sswilcox int freedlog = 0;
1152355d6bb5Sswilcox struct bufarea cleanbuf;
1153355d6bb5Sswilcox size_t size;
1154355d6bb5Sswilcox ssize_t io_res;
1155355d6bb5Sswilcox diskaddr_t bno;
1156355d6bb5Sswilcox char fsclean;
1157355d6bb5Sswilcox int fsreclaim;
1158355d6bb5Sswilcox char fsflags;
115920a1ae8aSjkennedy int flags_ok = 1;
1160355d6bb5Sswilcox daddr32_t fslogbno;
11617c478bd9Sstevel@tonic-gate offset_t sblkoff;
11627c478bd9Sstevel@tonic-gate time_t t;
11637c478bd9Sstevel@tonic-gate
11647c478bd9Sstevel@tonic-gate /*
11657c478bd9Sstevel@tonic-gate * debug stuff
11667c478bd9Sstevel@tonic-gate */
11677c478bd9Sstevel@tonic-gate debugclean();
11687c478bd9Sstevel@tonic-gate
11697c478bd9Sstevel@tonic-gate /*
11707c478bd9Sstevel@tonic-gate * set fsclean to its appropriate value
11717c478bd9Sstevel@tonic-gate */
11727c478bd9Sstevel@tonic-gate fslogbno = sblock.fs_logbno;
11737c478bd9Sstevel@tonic-gate fsclean = sblock.fs_clean;
11747c478bd9Sstevel@tonic-gate fsreclaim = sblock.fs_reclaim;
11757c478bd9Sstevel@tonic-gate fsflags = sblock.fs_flags;
1176355d6bb5Sswilcox if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) {
11777c478bd9Sstevel@tonic-gate fsclean = FSACTIVE;
1178355d6bb5Sswilcox }
1179355d6bb5Sswilcox /*
1180355d6bb5Sswilcox * If ufs log is not okay, note that we need to clear it.
1181355d6bb5Sswilcox */
118239542a18Sabalfour examinelog(NULL);
11837c478bd9Sstevel@tonic-gate if (fslogbno && !(islog && islogok)) {
11847c478bd9Sstevel@tonic-gate fsclean = FSACTIVE;
11857c478bd9Sstevel@tonic-gate fslogbno = 0;
11867c478bd9Sstevel@tonic-gate }
11877c478bd9Sstevel@tonic-gate
11887c478bd9Sstevel@tonic-gate /*
11897c478bd9Sstevel@tonic-gate * if necessary, update fs_clean and fs_state
11907c478bd9Sstevel@tonic-gate */
11917c478bd9Sstevel@tonic-gate switch (fsclean) {
11927c478bd9Sstevel@tonic-gate
11937c478bd9Sstevel@tonic-gate case FSACTIVE:
11947c478bd9Sstevel@tonic-gate if (!iscorrupt) {
11957c478bd9Sstevel@tonic-gate fsclean = FSSTABLE;
11967c478bd9Sstevel@tonic-gate fsreclaim = 0;
11977c478bd9Sstevel@tonic-gate }
11987c478bd9Sstevel@tonic-gate break;
11997c478bd9Sstevel@tonic-gate
12007c478bd9Sstevel@tonic-gate case FSCLEAN:
12017c478bd9Sstevel@tonic-gate case FSSTABLE:
1202355d6bb5Sswilcox if (iscorrupt) {
12037c478bd9Sstevel@tonic-gate fsclean = FSACTIVE;
1204355d6bb5Sswilcox } else {
12057c478bd9Sstevel@tonic-gate fsreclaim = 0;
1206355d6bb5Sswilcox }
12077c478bd9Sstevel@tonic-gate break;
12087c478bd9Sstevel@tonic-gate
12097c478bd9Sstevel@tonic-gate case FSLOG:
1210355d6bb5Sswilcox if (iscorrupt) {
12117c478bd9Sstevel@tonic-gate fsclean = FSACTIVE;
1212355d6bb5Sswilcox } else if (!islog || fslogbno == 0) {
12137c478bd9Sstevel@tonic-gate fsclean = FSSTABLE;
12147c478bd9Sstevel@tonic-gate fsreclaim = 0;
1215355d6bb5Sswilcox } else if (fflag) {
1216355d6bb5Sswilcox fsreclaim = 0;
1217355d6bb5Sswilcox }
12187c478bd9Sstevel@tonic-gate break;
12197c478bd9Sstevel@tonic-gate
12207c478bd9Sstevel@tonic-gate case FSFIX:
12217c478bd9Sstevel@tonic-gate fsclean = FSBAD;
12227c478bd9Sstevel@tonic-gate if (errorlocked && !iscorrupt) {
1223355d6bb5Sswilcox fsclean = islog ? FSLOG : FSCLEAN;
12247c478bd9Sstevel@tonic-gate }
12257c478bd9Sstevel@tonic-gate break;
12267c478bd9Sstevel@tonic-gate
12277c478bd9Sstevel@tonic-gate default:
1228355d6bb5Sswilcox if (iscorrupt) {
12297c478bd9Sstevel@tonic-gate fsclean = FSACTIVE;
1230355d6bb5Sswilcox } else {
12317c478bd9Sstevel@tonic-gate fsclean = FSSTABLE;
12327c478bd9Sstevel@tonic-gate fsreclaim = 0;
12337c478bd9Sstevel@tonic-gate }
12347c478bd9Sstevel@tonic-gate }
12357c478bd9Sstevel@tonic-gate
12367c478bd9Sstevel@tonic-gate if (largefile_count > 0)
12377c478bd9Sstevel@tonic-gate fsflags |= FSLARGEFILES;
12387c478bd9Sstevel@tonic-gate else
12397c478bd9Sstevel@tonic-gate fsflags &= ~FSLARGEFILES;
12407c478bd9Sstevel@tonic-gate
12417c478bd9Sstevel@tonic-gate /*
124220a1ae8aSjkennedy * There can be two discrepencies here. A) The superblock
124320a1ae8aSjkennedy * shows no largefiles but we found some while scanning.
124420a1ae8aSjkennedy * B) The superblock indicates the presence of largefiles,
124520a1ae8aSjkennedy * but none are present. Note that if preening, the superblock
124620a1ae8aSjkennedy * is silently corrected.
12477c478bd9Sstevel@tonic-gate */
124820a1ae8aSjkennedy if ((fsflags == FSLARGEFILES && sblock.fs_flags != FSLARGEFILES) ||
124920a1ae8aSjkennedy (fsflags != FSLARGEFILES && sblock.fs_flags == FSLARGEFILES))
1250355d6bb5Sswilcox flags_ok = 0;
1251355d6bb5Sswilcox
12527c478bd9Sstevel@tonic-gate if (debug)
1253355d6bb5Sswilcox (void) printf(
1254355d6bb5Sswilcox "** largefile count=%d, fs.fs_flags=%x, flags_ok %d\n",
1255355d6bb5Sswilcox largefile_count, sblock.fs_flags, flags_ok);
12567c478bd9Sstevel@tonic-gate
1257355d6bb5Sswilcox /*
1258355d6bb5Sswilcox * If fs is unchanged, do nothing.
1259355d6bb5Sswilcox */
1260355d6bb5Sswilcox if ((!isdirty) && (flags_ok) &&
12617c478bd9Sstevel@tonic-gate (fslogbno == sblock.fs_logbno) &&
1262355d6bb5Sswilcox (sblock.fs_clean == fsclean) &&
1263355d6bb5Sswilcox (sblock.fs_reclaim == fsreclaim) &&
12647c478bd9Sstevel@tonic-gate (FSOKAY == (sblock.fs_state + sblock.fs_time))) {
12657c478bd9Sstevel@tonic-gate if (errorlocked) {
12667c478bd9Sstevel@tonic-gate if (!do_errorlock(LOCKFS_ULOCK))
12677c478bd9Sstevel@tonic-gate pwarn(
12687c478bd9Sstevel@tonic-gate "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n");
12697c478bd9Sstevel@tonic-gate }
1270355d6bb5Sswilcox return (freedlog);
12717c478bd9Sstevel@tonic-gate }
12727c478bd9Sstevel@tonic-gate
12737c478bd9Sstevel@tonic-gate /*
12747c478bd9Sstevel@tonic-gate * if user allows, update superblock state
12757c478bd9Sstevel@tonic-gate */
1276355d6bb5Sswilcox if (debug) {
1277355d6bb5Sswilcox (void) printf(
1278355d6bb5Sswilcox "superblock: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1279355d6bb5Sswilcox sblock.fs_flags, sblock.fs_logbno,
1280355d6bb5Sswilcox sblock.fs_clean, sblock.fs_reclaim,
1281355d6bb5Sswilcox sblock.fs_state + sblock.fs_time);
1282355d6bb5Sswilcox (void) printf(
1283355d6bb5Sswilcox "calculated: flags 0x%x logbno %d clean %d reclaim %d state 0x%x\n",
1284355d6bb5Sswilcox fsflags, fslogbno, fsclean, fsreclaim, FSOKAY);
1285355d6bb5Sswilcox }
1286355d6bb5Sswilcox if (!isdirty && !preen && !rerun &&
12877c478bd9Sstevel@tonic-gate (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0))
1288355d6bb5Sswilcox return (freedlog);
12897c478bd9Sstevel@tonic-gate
12907c478bd9Sstevel@tonic-gate (void) time(&t);
12917c478bd9Sstevel@tonic-gate sblock.fs_time = (time32_t)t;
12927c478bd9Sstevel@tonic-gate if (debug)
12937c478bd9Sstevel@tonic-gate printclean();
1294355d6bb5Sswilcox
1295355d6bb5Sswilcox if (sblock.fs_logbno != fslogbno) {
129639542a18Sabalfour examinelog(&freelogblk);
1297355d6bb5Sswilcox freedlog++;
1298355d6bb5Sswilcox }
1299355d6bb5Sswilcox
13007c478bd9Sstevel@tonic-gate sblock.fs_logbno = fslogbno;
13017c478bd9Sstevel@tonic-gate sblock.fs_clean = fsclean;
13027c478bd9Sstevel@tonic-gate sblock.fs_state = FSOKAY - (long)sblock.fs_time;
13037c478bd9Sstevel@tonic-gate sblock.fs_reclaim = fsreclaim;
13047c478bd9Sstevel@tonic-gate sblock.fs_flags = fsflags;
13057c478bd9Sstevel@tonic-gate
13067c478bd9Sstevel@tonic-gate /*
13077c478bd9Sstevel@tonic-gate * if superblock can't be written, return
13087c478bd9Sstevel@tonic-gate */
13097c478bd9Sstevel@tonic-gate if (fswritefd < 0)
1310355d6bb5Sswilcox return (freedlog);
13117c478bd9Sstevel@tonic-gate
13127c478bd9Sstevel@tonic-gate /*
1313355d6bb5Sswilcox * Read private copy of superblock, update clean flag, and write it.
13147c478bd9Sstevel@tonic-gate */
13157c478bd9Sstevel@tonic-gate bno = sblk.b_bno;
13167c478bd9Sstevel@tonic-gate size = sblk.b_size;
13177c478bd9Sstevel@tonic-gate
13187c478bd9Sstevel@tonic-gate sblkoff = ldbtob(bno);
13197c478bd9Sstevel@tonic-gate
13207c478bd9Sstevel@tonic-gate if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL)
13217c478bd9Sstevel@tonic-gate errexit("out of memory");
1322f763a6cdScasper if (llseek(fsreadfd, sblkoff, SEEK_SET) == -1) {
1323355d6bb5Sswilcox (void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1324355d6bb5Sswilcox (longlong_t)bno, strerror(errno));
1325355d6bb5Sswilcox goto out;
1326355d6bb5Sswilcox }
13277c478bd9Sstevel@tonic-gate
1328355d6bb5Sswilcox if ((io_res = read(fsreadfd, cleanbuf.b_un.b_buf, size)) != size) {
1329355d6bb5Sswilcox report_io_prob("READ FROM", bno, size, io_res);
1330355d6bb5Sswilcox goto out;
1331355d6bb5Sswilcox }
13327c478bd9Sstevel@tonic-gate
13337c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_logbno = sblock.fs_logbno;
13347c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_clean = sblock.fs_clean;
13357c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_state = sblock.fs_state;
13367c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_time = sblock.fs_time;
13377c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim;
13387c478bd9Sstevel@tonic-gate cleanbuf.b_un.b_fs->fs_flags = sblock.fs_flags;
13397c478bd9Sstevel@tonic-gate
1340f763a6cdScasper if (llseek(fswritefd, sblkoff, SEEK_SET) == -1) {
1341355d6bb5Sswilcox (void) printf("COULD NOT SEEK TO SUPERBLOCK AT %lld: %s\n",
1342355d6bb5Sswilcox (longlong_t)bno, strerror(errno));
1343355d6bb5Sswilcox goto out;
1344355d6bb5Sswilcox }
1345355d6bb5Sswilcox
1346355d6bb5Sswilcox if ((io_res = write(fswritefd, cleanbuf.b_un.b_buf, size)) != size) {
1347355d6bb5Sswilcox report_io_prob("WRITE TO", bno, size, io_res);
1348355d6bb5Sswilcox goto out;
1349355d6bb5Sswilcox }
13507c478bd9Sstevel@tonic-gate
13517c478bd9Sstevel@tonic-gate /*
13527c478bd9Sstevel@tonic-gate * 1208040
13537c478bd9Sstevel@tonic-gate * If we had to use -b to grab an alternate superblock, then we
13547c478bd9Sstevel@tonic-gate * likely had to do so because of unacceptable differences between
1355355d6bb5Sswilcox * the main and alternate superblocks. So, we had better update
13567c478bd9Sstevel@tonic-gate * the alternate superblock as well, or we'll just fail again
13577c478bd9Sstevel@tonic-gate * the next time we attempt to run fsck!
13587c478bd9Sstevel@tonic-gate */
1359355d6bb5Sswilcox if (bflag != 0) {
1360355d6bb5Sswilcox write_altsb(fswritefd);
13617c478bd9Sstevel@tonic-gate }
13627c478bd9Sstevel@tonic-gate
13637c478bd9Sstevel@tonic-gate if (errorlocked) {
13647c478bd9Sstevel@tonic-gate if (!do_errorlock(LOCKFS_ULOCK))
13657c478bd9Sstevel@tonic-gate pwarn(
13667c478bd9Sstevel@tonic-gate "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n");
13677c478bd9Sstevel@tonic-gate }
1368355d6bb5Sswilcox
1369355d6bb5Sswilcox out:
1370355d6bb5Sswilcox if (cleanbuf.b_un.b_buf != NULL) {
1371355d6bb5Sswilcox free((void *)cleanbuf.b_un.b_buf);
1372355d6bb5Sswilcox }
1373355d6bb5Sswilcox
1374355d6bb5Sswilcox return (freedlog);
1375355d6bb5Sswilcox }
1376355d6bb5Sswilcox
1377355d6bb5Sswilcox static void
report_io_prob(caddr_t what,diskaddr_t bno,size_t expected,ssize_t failure)1378355d6bb5Sswilcox report_io_prob(caddr_t what, diskaddr_t bno, size_t expected, ssize_t failure)
1379355d6bb5Sswilcox {
1380355d6bb5Sswilcox if (failure < 0)
1381355d6bb5Sswilcox (void) printf("COULD NOT %s SUPERBLOCK AT %d: %s\n",
1382355d6bb5Sswilcox what, (int)bno, strerror(errno));
1383355d6bb5Sswilcox else if (failure == 0)
1384355d6bb5Sswilcox (void) printf("COULD NOT %s SUPERBLOCK AT %d: EOF\n",
1385355d6bb5Sswilcox what, (int)bno);
1386355d6bb5Sswilcox else
1387355d6bb5Sswilcox (void) printf("SHORT %s SUPERBLOCK AT %d: %u out of %u bytes\n",
1388355d6bb5Sswilcox what, (int)bno, (unsigned)failure, (unsigned)expected);
13897c478bd9Sstevel@tonic-gate }
13907c478bd9Sstevel@tonic-gate
13917c478bd9Sstevel@tonic-gate /*
13927c478bd9Sstevel@tonic-gate * print out clean info
13937c478bd9Sstevel@tonic-gate */
1394355d6bb5Sswilcox void
printclean(void)1395355d6bb5Sswilcox printclean(void)
13967c478bd9Sstevel@tonic-gate {
1397355d6bb5Sswilcox caddr_t s;
13987c478bd9Sstevel@tonic-gate
13997c478bd9Sstevel@tonic-gate if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked)
14007c478bd9Sstevel@tonic-gate s = "unknown";
14017c478bd9Sstevel@tonic-gate else
14027c478bd9Sstevel@tonic-gate switch (sblock.fs_clean) {
14037c478bd9Sstevel@tonic-gate
14047c478bd9Sstevel@tonic-gate case FSACTIVE:
14057c478bd9Sstevel@tonic-gate s = "active";
14067c478bd9Sstevel@tonic-gate break;
14077c478bd9Sstevel@tonic-gate
14087c478bd9Sstevel@tonic-gate case FSCLEAN:
14097c478bd9Sstevel@tonic-gate s = "clean";
14107c478bd9Sstevel@tonic-gate break;
14117c478bd9Sstevel@tonic-gate
14127c478bd9Sstevel@tonic-gate case FSSTABLE:
14137c478bd9Sstevel@tonic-gate s = "stable";
14147c478bd9Sstevel@tonic-gate break;
14157c478bd9Sstevel@tonic-gate
14167c478bd9Sstevel@tonic-gate case FSLOG:
14177c478bd9Sstevel@tonic-gate s = "logging";
14187c478bd9Sstevel@tonic-gate break;
14197c478bd9Sstevel@tonic-gate
14207c478bd9Sstevel@tonic-gate case FSBAD:
14217c478bd9Sstevel@tonic-gate s = "is bad";
14227c478bd9Sstevel@tonic-gate break;
14237c478bd9Sstevel@tonic-gate
14247c478bd9Sstevel@tonic-gate case FSFIX:
14257c478bd9Sstevel@tonic-gate s = "being fixed";
14267c478bd9Sstevel@tonic-gate break;
14277c478bd9Sstevel@tonic-gate
14287c478bd9Sstevel@tonic-gate default:
14297c478bd9Sstevel@tonic-gate s = "unknown";
14307c478bd9Sstevel@tonic-gate }
14317c478bd9Sstevel@tonic-gate
14327c478bd9Sstevel@tonic-gate if (preen)
14337c478bd9Sstevel@tonic-gate pwarn("is %s.\n", s);
14347c478bd9Sstevel@tonic-gate else
1435355d6bb5Sswilcox (void) printf("** %s is %s.\n", devname, s);
14367c478bd9Sstevel@tonic-gate }
14377c478bd9Sstevel@tonic-gate
1438355d6bb5Sswilcox int
is_errorlocked(caddr_t fs)1439355d6bb5Sswilcox is_errorlocked(caddr_t fs)
14407c478bd9Sstevel@tonic-gate {
1441355d6bb5Sswilcox int retval;
1442355d6bb5Sswilcox struct stat64 statb;
1443355d6bb5Sswilcox caddr_t mountp;
1444355d6bb5Sswilcox struct mnttab *mntent;
14457c478bd9Sstevel@tonic-gate
1446355d6bb5Sswilcox retval = 0;
14477c478bd9Sstevel@tonic-gate
14487c478bd9Sstevel@tonic-gate if (!fs)
14497c478bd9Sstevel@tonic-gate return (0);
14507c478bd9Sstevel@tonic-gate
14517c478bd9Sstevel@tonic-gate if (stat64(fs, &statb) < 0)
14527c478bd9Sstevel@tonic-gate return (0);
14537c478bd9Sstevel@tonic-gate
14547c478bd9Sstevel@tonic-gate if (S_ISDIR(statb.st_mode)) {
14557c478bd9Sstevel@tonic-gate mountp = fs;
14567c478bd9Sstevel@tonic-gate } else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) {
1457355d6bb5Sswilcox mntent = search_mnttab(NULL, fs, NULL, 0);
1458355d6bb5Sswilcox if (mntent == NULL)
1459355d6bb5Sswilcox return (0);
1460355d6bb5Sswilcox mountp = mntent->mnt_mountp;
1461355d6bb5Sswilcox if (mountp == NULL) /* theoretically a can't-happen */
14627c478bd9Sstevel@tonic-gate return (0);
14637c478bd9Sstevel@tonic-gate } else {
14647c478bd9Sstevel@tonic-gate return (0);
14657c478bd9Sstevel@tonic-gate }
14667c478bd9Sstevel@tonic-gate
1467355d6bb5Sswilcox /*
1468355d6bb5Sswilcox * From here on, must `goto out' to avoid memory leakage.
1469355d6bb5Sswilcox */
1470355d6bb5Sswilcox
1471355d6bb5Sswilcox if (elock_combuf == NULL)
14727c478bd9Sstevel@tonic-gate elock_combuf =
14731493b746SMilan Cermak (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char));
1474355d6bb5Sswilcox else
14757c478bd9Sstevel@tonic-gate elock_combuf =
14761493b746SMilan Cermak (caddr_t)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN);
14777c478bd9Sstevel@tonic-gate
14787c478bd9Sstevel@tonic-gate if (elock_combuf == NULL)
1479355d6bb5Sswilcox goto out;
14807c478bd9Sstevel@tonic-gate
1481355d6bb5Sswilcox (void) memset((void *)elock_combuf, 0, LOCKFS_MAXCOMMENTLEN);
1482355d6bb5Sswilcox
1483355d6bb5Sswilcox if (elock_mountp != NULL) {
1484355d6bb5Sswilcox free(elock_mountp);
1485355d6bb5Sswilcox }
14867c478bd9Sstevel@tonic-gate
14877c478bd9Sstevel@tonic-gate elock_mountp = strdup(mountp);
1488355d6bb5Sswilcox if (elock_mountp == NULL)
1489355d6bb5Sswilcox goto out;
14907c478bd9Sstevel@tonic-gate
14917c478bd9Sstevel@tonic-gate if (mountfd < 0) {
14927c478bd9Sstevel@tonic-gate if ((mountfd = open64(mountp, O_RDONLY)) == -1)
1493355d6bb5Sswilcox goto out;
14947c478bd9Sstevel@tonic-gate }
14957c478bd9Sstevel@tonic-gate
1496355d6bb5Sswilcox if (lfp == NULL) {
14977c478bd9Sstevel@tonic-gate lfp = (struct lockfs *)malloc(sizeof (struct lockfs));
1498355d6bb5Sswilcox if (lfp == NULL)
1499355d6bb5Sswilcox goto out;
1500355d6bb5Sswilcox (void) memset((void *)lfp, 0, sizeof (struct lockfs));
15017c478bd9Sstevel@tonic-gate }
15027c478bd9Sstevel@tonic-gate
15037c478bd9Sstevel@tonic-gate lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
15047c478bd9Sstevel@tonic-gate lfp->lf_comment = elock_combuf;
15057c478bd9Sstevel@tonic-gate
15067c478bd9Sstevel@tonic-gate if (ioctl(mountfd, _FIOLFSS, lfp) == -1)
1507355d6bb5Sswilcox goto out;
1508355d6bb5Sswilcox
1509355d6bb5Sswilcox /*
1510355d6bb5Sswilcox * lint believes that the ioctl() (or any other function
1511355d6bb5Sswilcox * taking lfp as an arg) could free lfp. This is not the
1512355d6bb5Sswilcox * case, however.
1513355d6bb5Sswilcox */
1514355d6bb5Sswilcox retval = LOCKFS_IS_ELOCK(lfp);
15157c478bd9Sstevel@tonic-gate
1516355d6bb5Sswilcox out:
1517355d6bb5Sswilcox return (retval);
15187c478bd9Sstevel@tonic-gate }
15197c478bd9Sstevel@tonic-gate
1520355d6bb5Sswilcox /*
1521355d6bb5Sswilcox * Given a name which is known to be a directory, see if it appears
1522355d6bb5Sswilcox * in the vfstab. If so, return the entry's block (special) device
1523355d6bb5Sswilcox * field via devstr.
1524355d6bb5Sswilcox */
1525355d6bb5Sswilcox int
check_vfstab(caddr_t name,caddr_t devstr,size_t str_size)1526355d6bb5Sswilcox check_vfstab(caddr_t name, caddr_t devstr, size_t str_size)
1527355d6bb5Sswilcox {
1528355d6bb5Sswilcox return (NULL != search_vfstab(name, NULL, devstr, str_size));
1529355d6bb5Sswilcox }
15307c478bd9Sstevel@tonic-gate
1531355d6bb5Sswilcox /*
1532355d6bb5Sswilcox * Given a name which is known to be a directory, see if it appears
1533355d6bb5Sswilcox * in the mnttab. If so, return the entry's block (special) device
1534355d6bb5Sswilcox * field via devstr.
1535355d6bb5Sswilcox */
1536355d6bb5Sswilcox int
check_mnttab(caddr_t name,caddr_t devstr,size_t str_size)1537355d6bb5Sswilcox check_mnttab(caddr_t name, caddr_t devstr, size_t str_size)
1538355d6bb5Sswilcox {
1539355d6bb5Sswilcox return (NULL != search_mnttab(name, NULL, devstr, str_size));
15407c478bd9Sstevel@tonic-gate }
15417c478bd9Sstevel@tonic-gate
1542355d6bb5Sswilcox /*
1543355d6bb5Sswilcox * Search for mount point and/or special device in the given file.
1544355d6bb5Sswilcox * The first matching entry is returned.
1545355d6bb5Sswilcox *
1546355d6bb5Sswilcox * If an entry is found and str_size is greater than zero, then
1547355d6bb5Sswilcox * up to size_str bytes of the special device name from the entry
1548355d6bb5Sswilcox * are copied to devstr.
1549355d6bb5Sswilcox */
1550355d6bb5Sswilcox
1551355d6bb5Sswilcox #define SEARCH_TAB_BODY(st_type, st_file, st_mount, st_special, \
1552355d6bb5Sswilcox st_nuller, st_init, st_searcher) \
1553355d6bb5Sswilcox { \
1554355d6bb5Sswilcox FILE *fp; \
1555355d6bb5Sswilcox struct st_type *retval = NULL; \
1556355d6bb5Sswilcox struct st_type key; \
1557355d6bb5Sswilcox static struct st_type buffer; \
1558355d6bb5Sswilcox \
1559355d6bb5Sswilcox /* LINTED ``assigned value never used'' */ \
1560355d6bb5Sswilcox st_nuller(&key); \
1561355d6bb5Sswilcox key.st_mount = mountp; \
1562355d6bb5Sswilcox key.st_special = special; \
1563355d6bb5Sswilcox st_init; \
1564355d6bb5Sswilcox \
1565355d6bb5Sswilcox if ((fp = fopen(st_file, "r")) == NULL) \
1566355d6bb5Sswilcox return (NULL); \
1567355d6bb5Sswilcox \
1568355d6bb5Sswilcox if (st_searcher(fp, &buffer, &key) == 0) { \
1569355d6bb5Sswilcox retval = &buffer; \
1570355d6bb5Sswilcox if (devstr != NULL && str_size > 0 && \
1571355d6bb5Sswilcox buffer.st_special != NULL) { \
1572355d6bb5Sswilcox (void) strlcpy(devstr, buffer.st_special, \
1573355d6bb5Sswilcox str_size); \
1574355d6bb5Sswilcox } \
1575355d6bb5Sswilcox } \
1576355d6bb5Sswilcox (void) fclose(fp); \
1577355d6bb5Sswilcox return (retval); \
1578355d6bb5Sswilcox }
1579355d6bb5Sswilcox
1580355d6bb5Sswilcox static struct vfstab *
search_vfstab(caddr_t mountp,caddr_t special,caddr_t devstr,size_t str_size)1581355d6bb5Sswilcox search_vfstab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1582355d6bb5Sswilcox SEARCH_TAB_BODY(vfstab, VFSTAB, vfs_mountp, vfs_special, vfsnull,
1583355d6bb5Sswilcox (retval = retval), getvfsany)
1584355d6bb5Sswilcox
1585355d6bb5Sswilcox static struct mnttab *
1586355d6bb5Sswilcox search_mnttab(caddr_t mountp, caddr_t special, caddr_t devstr, size_t str_size)
1587355d6bb5Sswilcox SEARCH_TAB_BODY(mnttab, MNTTAB, mnt_mountp, mnt_special, mntnull,
1588355d6bb5Sswilcox (key.mnt_fstype = MNTTYPE_UFS), getmntany)
1589355d6bb5Sswilcox
1590355d6bb5Sswilcox int
15917c478bd9Sstevel@tonic-gate do_errorlock(int lock_type)
15927c478bd9Sstevel@tonic-gate {
1593355d6bb5Sswilcox caddr_t buf;
1594355d6bb5Sswilcox time_t now;
1595355d6bb5Sswilcox struct tm *local;
1596355d6bb5Sswilcox int rc;
15977c478bd9Sstevel@tonic-gate
1598355d6bb5Sswilcox if (elock_combuf == NULL)
15997c478bd9Sstevel@tonic-gate errexit("do_errorlock(%s, %d): unallocated elock_combuf\n",
16001493b746SMilan Cermak elock_mountp ? elock_mountp : "<null>",
16011493b746SMilan Cermak lock_type);
16027c478bd9Sstevel@tonic-gate
1603355d6bb5Sswilcox if ((buf = (caddr_t)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char))) ==
1604355d6bb5Sswilcox NULL) {
16057c478bd9Sstevel@tonic-gate errexit("Couldn't alloc memory for temp. lock status buffer\n");
1606355d6bb5Sswilcox }
1607355d6bb5Sswilcox if (lfp == NULL) {
16087c478bd9Sstevel@tonic-gate errexit("do_errorlock(%s, %d): lockfs status unallocated\n",
16091493b746SMilan Cermak elock_mountp, lock_type);
16107c478bd9Sstevel@tonic-gate }
16117c478bd9Sstevel@tonic-gate
1612355d6bb5Sswilcox (void) memmove((void *)buf, (void *)elock_combuf,
1613355d6bb5Sswilcox LOCKFS_MAXCOMMENTLEN-1);
16147c478bd9Sstevel@tonic-gate
16157c478bd9Sstevel@tonic-gate switch (lock_type) {
16167c478bd9Sstevel@tonic-gate case LOCKFS_ELOCK:
1617355d6bb5Sswilcox /*
1618355d6bb5Sswilcox * Note that if it is error-locked, we won't get an
1619355d6bb5Sswilcox * error back if we try to error-lock it again.
1620355d6bb5Sswilcox */
16217c478bd9Sstevel@tonic-gate if (time(&now) != (time_t)-1) {
16227c478bd9Sstevel@tonic-gate if ((local = localtime(&now)) != NULL)
1623355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16247c478bd9Sstevel@tonic-gate "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d",
1625355d6bb5Sswilcox elock_combuf, (int)pid,
1626355d6bb5Sswilcox local->tm_mon + 1, local->tm_mday,
16277c478bd9Sstevel@tonic-gate (local->tm_year % 100), local->tm_hour,
16287c478bd9Sstevel@tonic-gate local->tm_min, local->tm_sec);
16297c478bd9Sstevel@tonic-gate else
1630355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1631355d6bb5Sswilcox "%s [fsck pid %d", elock_combuf, pid);
16327c478bd9Sstevel@tonic-gate
16337c478bd9Sstevel@tonic-gate } else {
1634355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1635355d6bb5Sswilcox "%s [fsck pid %d", elock_combuf, pid);
16367c478bd9Sstevel@tonic-gate }
16377c478bd9Sstevel@tonic-gate break;
16387c478bd9Sstevel@tonic-gate
16397c478bd9Sstevel@tonic-gate case LOCKFS_ULOCK:
16407c478bd9Sstevel@tonic-gate if (time(&now) != (time_t)-1) {
16417c478bd9Sstevel@tonic-gate if ((local = localtime(&now)) != NULL) {
1642355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
16437c478bd9Sstevel@tonic-gate "%s, done:%02d/%02d/%02d %02d:%02d:%02d]",
16447c478bd9Sstevel@tonic-gate elock_combuf,
1645355d6bb5Sswilcox local->tm_mon + 1, local->tm_mday,
16467c478bd9Sstevel@tonic-gate (local->tm_year % 100), local->tm_hour,
16477c478bd9Sstevel@tonic-gate local->tm_min, local->tm_sec);
16487c478bd9Sstevel@tonic-gate } else {
1649355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1650355d6bb5Sswilcox "%s]", elock_combuf);
16517c478bd9Sstevel@tonic-gate }
16527c478bd9Sstevel@tonic-gate } else {
1653355d6bb5Sswilcox (void) snprintf(buf, LOCKFS_MAXCOMMENTLEN,
1654355d6bb5Sswilcox "%s]", elock_combuf);
16557c478bd9Sstevel@tonic-gate }
16567c478bd9Sstevel@tonic-gate if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) {
1657355d6bb5Sswilcox pwarn("do_errorlock: unlock failed: %s\n",
1658355d6bb5Sswilcox strerror(errno));
16597c478bd9Sstevel@tonic-gate goto out;
16607c478bd9Sstevel@tonic-gate }
16617c478bd9Sstevel@tonic-gate break;
16627c478bd9Sstevel@tonic-gate
16637c478bd9Sstevel@tonic-gate default:
16647c478bd9Sstevel@tonic-gate break;
16657c478bd9Sstevel@tonic-gate }
16667c478bd9Sstevel@tonic-gate
1667355d6bb5Sswilcox (void) memmove((void *)elock_combuf, (void *)buf,
1668355d6bb5Sswilcox LOCKFS_MAXCOMMENTLEN - 1);
16697c478bd9Sstevel@tonic-gate
1670355d6bb5Sswilcox lfp->lf_lock = lock_type;
1671355d6bb5Sswilcox lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN;
1672355d6bb5Sswilcox lfp->lf_comment = elock_combuf;
1673355d6bb5Sswilcox lfp->lf_flags = 0;
1674355d6bb5Sswilcox errno = 0;
16757c478bd9Sstevel@tonic-gate
16767c478bd9Sstevel@tonic-gate if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) {
16777c478bd9Sstevel@tonic-gate if (errno == EINVAL) {
16787c478bd9Sstevel@tonic-gate pwarn("Another fsck active?\n");
16797c478bd9Sstevel@tonic-gate iscorrupt = 0; /* don't go away mad, just go away */
16807c478bd9Sstevel@tonic-gate } else {
1681355d6bb5Sswilcox pwarn("do_errorlock(lock_type:%d, %s) failed: %s\n",
1682355d6bb5Sswilcox lock_type, elock_combuf, strerror(errno));
16837c478bd9Sstevel@tonic-gate }
16847c478bd9Sstevel@tonic-gate }
16857c478bd9Sstevel@tonic-gate out:
1686355d6bb5Sswilcox if (buf != NULL) {
1687355d6bb5Sswilcox free((void *)buf);
1688355d6bb5Sswilcox }
16897c478bd9Sstevel@tonic-gate
16907c478bd9Sstevel@tonic-gate return (rc != -1);
16917c478bd9Sstevel@tonic-gate }
16927c478bd9Sstevel@tonic-gate
16937c478bd9Sstevel@tonic-gate /*
1694355d6bb5Sswilcox * Shadow inode support. To register a shadow with a client is to note
1695355d6bb5Sswilcox * that an inode (the client) refers to the shadow.
16967c478bd9Sstevel@tonic-gate */
16977c478bd9Sstevel@tonic-gate
16987c478bd9Sstevel@tonic-gate static struct shadowclients *
newshadowclient(struct shadowclients * prev)16997c478bd9Sstevel@tonic-gate newshadowclient(struct shadowclients *prev)
17007c478bd9Sstevel@tonic-gate {
17017c478bd9Sstevel@tonic-gate struct shadowclients *rc;
17027c478bd9Sstevel@tonic-gate
17037c478bd9Sstevel@tonic-gate rc = (struct shadowclients *)malloc(sizeof (*rc));
17047c478bd9Sstevel@tonic-gate if (rc == NULL)
1705355d6bb5Sswilcox errexit("newshadowclient: cannot malloc shadow client");
17067c478bd9Sstevel@tonic-gate rc->next = prev;
17077c478bd9Sstevel@tonic-gate rc->nclients = 0;
17087c478bd9Sstevel@tonic-gate
1709355d6bb5Sswilcox rc->client = (fsck_ino_t *)malloc(sizeof (fsck_ino_t) *
1710355d6bb5Sswilcox maxshadowclients);
17117c478bd9Sstevel@tonic-gate if (rc->client == NULL)
1712355d6bb5Sswilcox errexit("newshadowclient: cannot malloc client array");
17137c478bd9Sstevel@tonic-gate return (rc);
17147c478bd9Sstevel@tonic-gate }
17157c478bd9Sstevel@tonic-gate
17167c478bd9Sstevel@tonic-gate void
registershadowclient(fsck_ino_t shadow,fsck_ino_t client,struct shadowclientinfo ** info)1717355d6bb5Sswilcox registershadowclient(fsck_ino_t shadow, fsck_ino_t client,
1718355d6bb5Sswilcox struct shadowclientinfo **info)
17197c478bd9Sstevel@tonic-gate {
17207c478bd9Sstevel@tonic-gate struct shadowclientinfo *sci;
17217c478bd9Sstevel@tonic-gate struct shadowclients *scc;
17227c478bd9Sstevel@tonic-gate
1723355d6bb5Sswilcox /*
1724355d6bb5Sswilcox * Already have a record for this shadow?
1725355d6bb5Sswilcox */
1726355d6bb5Sswilcox for (sci = *info; sci != NULL; sci = sci->next)
17277c478bd9Sstevel@tonic-gate if (sci->shadow == shadow)
17287c478bd9Sstevel@tonic-gate break;
17297c478bd9Sstevel@tonic-gate if (sci == NULL) {
1730355d6bb5Sswilcox /*
1731355d6bb5Sswilcox * It's a new shadow, add it to the list
1732355d6bb5Sswilcox */
17337c478bd9Sstevel@tonic-gate sci = (struct shadowclientinfo *)malloc(sizeof (*sci));
17347c478bd9Sstevel@tonic-gate if (sci == NULL)
17357c478bd9Sstevel@tonic-gate errexit("registershadowclient: cannot malloc");
17367c478bd9Sstevel@tonic-gate sci->next = *info;
17377c478bd9Sstevel@tonic-gate *info = sci;
17387c478bd9Sstevel@tonic-gate sci->shadow = shadow;
17397c478bd9Sstevel@tonic-gate sci->totalClients = 0;
17407c478bd9Sstevel@tonic-gate sci->clients = newshadowclient(NULL);
17417c478bd9Sstevel@tonic-gate }
17427c478bd9Sstevel@tonic-gate
17437c478bd9Sstevel@tonic-gate sci->totalClients++;
17447c478bd9Sstevel@tonic-gate scc = sci->clients;
17457c478bd9Sstevel@tonic-gate if (scc->nclients >= maxshadowclients) {
17467c478bd9Sstevel@tonic-gate scc = newshadowclient(sci->clients);
17477c478bd9Sstevel@tonic-gate sci->clients = scc;
17487c478bd9Sstevel@tonic-gate }
17497c478bd9Sstevel@tonic-gate
17507c478bd9Sstevel@tonic-gate scc->client[scc->nclients++] = client;
17517c478bd9Sstevel@tonic-gate }
17527c478bd9Sstevel@tonic-gate
1753355d6bb5Sswilcox /*
1754355d6bb5Sswilcox * Locate and discard a shadow.
1755355d6bb5Sswilcox */
1756355d6bb5Sswilcox void
clearshadow(fsck_ino_t shadow,struct shadowclientinfo ** info)1757355d6bb5Sswilcox clearshadow(fsck_ino_t shadow, struct shadowclientinfo **info)
1758355d6bb5Sswilcox {
1759355d6bb5Sswilcox struct shadowclientinfo *sci, *prev;
1760355d6bb5Sswilcox
1761355d6bb5Sswilcox /*
1762355d6bb5Sswilcox * Do we have a record for this shadow?
1763355d6bb5Sswilcox */
1764355d6bb5Sswilcox prev = NULL;
1765355d6bb5Sswilcox for (sci = *info; sci != NULL; sci = sci->next) {
1766355d6bb5Sswilcox if (sci->shadow == shadow)
1767355d6bb5Sswilcox break;
1768355d6bb5Sswilcox prev = sci;
1769355d6bb5Sswilcox }
1770355d6bb5Sswilcox
1771355d6bb5Sswilcox if (sci != NULL) {
1772355d6bb5Sswilcox /*
1773355d6bb5Sswilcox * First, pull it off the list, since we know there
1774355d6bb5Sswilcox * shouldn't be any future references to this one.
1775355d6bb5Sswilcox */
1776355d6bb5Sswilcox if (prev == NULL)
1777355d6bb5Sswilcox *info = sci->next;
1778355d6bb5Sswilcox else
1779355d6bb5Sswilcox prev->next = sci->next;
1780355d6bb5Sswilcox deshadow(sci, clearattrref);
1781355d6bb5Sswilcox }
1782355d6bb5Sswilcox }
1783355d6bb5Sswilcox
1784355d6bb5Sswilcox /*
1785355d6bb5Sswilcox * Discard all memory used to track clients of a shadow.
1786355d6bb5Sswilcox */
1787355d6bb5Sswilcox void
deshadow(struct shadowclientinfo * sci,void (* cb)(fsck_ino_t))1788355d6bb5Sswilcox deshadow(struct shadowclientinfo *sci, void (*cb)(fsck_ino_t))
1789355d6bb5Sswilcox {
1790355d6bb5Sswilcox struct shadowclients *clients, *discard;
1791355d6bb5Sswilcox int idx;
1792355d6bb5Sswilcox
1793355d6bb5Sswilcox clients = sci->clients;
1794355d6bb5Sswilcox while (clients != NULL) {
1795355d6bb5Sswilcox discard = clients;
1796355d6bb5Sswilcox clients = clients->next;
1797355d6bb5Sswilcox if (discard->client != NULL) {
1798355d6bb5Sswilcox if (cb != NULL) {
1799355d6bb5Sswilcox for (idx = 0; idx < discard->nclients; idx++)
1800355d6bb5Sswilcox (*cb)(discard->client[idx]);
1801355d6bb5Sswilcox }
1802355d6bb5Sswilcox free((void *)discard->client);
1803355d6bb5Sswilcox }
1804355d6bb5Sswilcox free((void *)discard);
1805355d6bb5Sswilcox }
1806355d6bb5Sswilcox
1807355d6bb5Sswilcox free((void *)sci);
1808355d6bb5Sswilcox }
1809355d6bb5Sswilcox
18107c478bd9Sstevel@tonic-gate /*
18117c478bd9Sstevel@tonic-gate * Allocate more buffer as need arises but allocate one at a time.
18127c478bd9Sstevel@tonic-gate * This is done to make sure that fsck does not exit with error if it
1813355d6bb5Sswilcox * needs more buffer to complete its task.
18147c478bd9Sstevel@tonic-gate */
18157c478bd9Sstevel@tonic-gate static struct bufarea *
alloc_bufarea(void)1816355d6bb5Sswilcox alloc_bufarea(void)
18177c478bd9Sstevel@tonic-gate {
1818355d6bb5Sswilcox struct bufarea *newbp;
1819355d6bb5Sswilcox caddr_t bufp;
18207c478bd9Sstevel@tonic-gate
18217c478bd9Sstevel@tonic-gate bufp = malloc((unsigned int)sblock.fs_bsize);
1822355d6bb5Sswilcox if (bufp == NULL)
1823355d6bb5Sswilcox return (NULL);
1824355d6bb5Sswilcox
1825355d6bb5Sswilcox newbp = (struct bufarea *)malloc(sizeof (struct bufarea));
1826355d6bb5Sswilcox if (newbp == NULL) {
1827355d6bb5Sswilcox free((void *)bufp);
18287c478bd9Sstevel@tonic-gate return (NULL);
18297c478bd9Sstevel@tonic-gate }
1830355d6bb5Sswilcox
1831355d6bb5Sswilcox initbarea(newbp);
1832355d6bb5Sswilcox newbp->b_un.b_buf = bufp;
1833355d6bb5Sswilcox newbp->b_prev = &bufhead;
1834355d6bb5Sswilcox newbp->b_next = bufhead.b_next;
1835355d6bb5Sswilcox bufhead.b_next->b_prev = newbp;
1836355d6bb5Sswilcox bufhead.b_next = newbp;
18377c478bd9Sstevel@tonic-gate bufhead.b_size++;
1838355d6bb5Sswilcox return (newbp);
1839355d6bb5Sswilcox }
1840355d6bb5Sswilcox
1841355d6bb5Sswilcox /*
1842355d6bb5Sswilcox * We length-limit in both unrawname() and rawname() to avoid
1843355d6bb5Sswilcox * overflowing our arrays or those of our naive, trusting callers.
1844355d6bb5Sswilcox */
1845355d6bb5Sswilcox
1846355d6bb5Sswilcox caddr_t
unrawname(caddr_t name)1847355d6bb5Sswilcox unrawname(caddr_t name)
1848355d6bb5Sswilcox {
1849355d6bb5Sswilcox caddr_t dp;
1850355d6bb5Sswilcox static char fullname[MAXPATHLEN + 1];
1851355d6bb5Sswilcox
1852355d6bb5Sswilcox if ((dp = getfullblkname(name)) == NULL)
1853355d6bb5Sswilcox return ("");
1854355d6bb5Sswilcox
1855355d6bb5Sswilcox (void) strlcpy(fullname, dp, sizeof (fullname));
1856355d6bb5Sswilcox /*
1857355d6bb5Sswilcox * Not reporting under debug, as the allocation isn't
1858355d6bb5Sswilcox * reported by getfullblkname. The idea is that we
1859355d6bb5Sswilcox * produce balanced alloc/free instances.
1860355d6bb5Sswilcox */
1861355d6bb5Sswilcox free(dp);
1862355d6bb5Sswilcox
1863355d6bb5Sswilcox return (fullname);
1864355d6bb5Sswilcox }
1865355d6bb5Sswilcox
1866355d6bb5Sswilcox caddr_t
rawname(caddr_t name)1867355d6bb5Sswilcox rawname(caddr_t name)
1868355d6bb5Sswilcox {
1869355d6bb5Sswilcox caddr_t dp;
1870355d6bb5Sswilcox static char fullname[MAXPATHLEN + 1];
1871355d6bb5Sswilcox
1872355d6bb5Sswilcox if ((dp = getfullrawname(name)) == NULL)
1873355d6bb5Sswilcox return ("");
1874355d6bb5Sswilcox
1875355d6bb5Sswilcox (void) strlcpy(fullname, dp, sizeof (fullname));
1876355d6bb5Sswilcox /*
1877355d6bb5Sswilcox * Not reporting under debug, as the allocation isn't
1878355d6bb5Sswilcox * reported by getfullblkname. The idea is that we
1879355d6bb5Sswilcox * produce balanced alloc/free instances.
1880355d6bb5Sswilcox */
1881355d6bb5Sswilcox free(dp);
1882355d6bb5Sswilcox
1883355d6bb5Sswilcox return (fullname);
1884355d6bb5Sswilcox }
1885355d6bb5Sswilcox
1886355d6bb5Sswilcox /*
1887355d6bb5Sswilcox * Make sure that a cg header looks at least moderately reasonable.
1888355d6bb5Sswilcox * We want to be able to trust the contents enough to be able to use
1889355d6bb5Sswilcox * the standard accessor macros. So, besides looking at the obvious
1890355d6bb5Sswilcox * such as the magic number, we verify that the offset field values
1891355d6bb5Sswilcox * are properly aligned and not too big or small.
1892355d6bb5Sswilcox *
1893355d6bb5Sswilcox * Returns a NULL pointer if the cg is sane enough for our needs, else
1894355d6bb5Sswilcox * a dynamically-allocated string describing all of its faults.
1895355d6bb5Sswilcox */
1896355d6bb5Sswilcox #define Append_Error(full, full_len, addition, addition_len) \
1897355d6bb5Sswilcox if (full == NULL) { \
1898355d6bb5Sswilcox full = addition; \
1899355d6bb5Sswilcox full_len = addition_len; \
1900355d6bb5Sswilcox } else { \
1901355d6bb5Sswilcox /* lint doesn't think realloc() understands NULLs */ \
1902355d6bb5Sswilcox full = realloc(full, full_len + addition_len + 1); \
1903355d6bb5Sswilcox if (full == NULL) { \
1904355d6bb5Sswilcox errexit("Out of memory in cg_sanity"); \
1905355d6bb5Sswilcox /* NOTREACHED */ \
1906355d6bb5Sswilcox } \
1907355d6bb5Sswilcox (void) strcpy(full + full_len, addition); \
1908355d6bb5Sswilcox full_len += addition_len; \
1909355d6bb5Sswilcox free(addition); \
1910355d6bb5Sswilcox }
1911355d6bb5Sswilcox
1912355d6bb5Sswilcox caddr_t
cg_sanity(struct cg * cgp,int cgno)191377a343abSabalfour cg_sanity(struct cg *cgp, int cgno)
1914355d6bb5Sswilcox {
1915355d6bb5Sswilcox caddr_t full_err;
1916355d6bb5Sswilcox caddr_t this_err = NULL;
1917355d6bb5Sswilcox int full_len, this_len;
1918355d6bb5Sswilcox daddr32_t ndblk;
1919355d6bb5Sswilcox daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
1920355d6bb5Sswilcox daddr32_t exp_freeoff, exp_nextfreeoff;
1921355d6bb5Sswilcox
1922355d6bb5Sswilcox cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
1923355d6bb5Sswilcox &exp_freeoff, &exp_nextfreeoff, &ndblk);
1924355d6bb5Sswilcox
1925355d6bb5Sswilcox full_err = NULL;
1926355d6bb5Sswilcox full_len = 0;
1927355d6bb5Sswilcox
1928355d6bb5Sswilcox if (!cg_chkmagic(cgp)) {
1929355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1930355d6bb5Sswilcox "BAD CG MAGIC NUMBER (0x%x should be 0x%x)\n",
1931355d6bb5Sswilcox cgp->cg_magic, CG_MAGIC);
1932355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1933355d6bb5Sswilcox }
1934355d6bb5Sswilcox
1935355d6bb5Sswilcox if (cgp->cg_cgx != cgno) {
1936355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1937355d6bb5Sswilcox "WRONG CG NUMBER (%d should be %d)\n",
1938355d6bb5Sswilcox cgp->cg_cgx, cgno);
1939355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1940355d6bb5Sswilcox }
1941355d6bb5Sswilcox
1942355d6bb5Sswilcox if ((cgp->cg_btotoff & 3) != 0) {
1943355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1944355d6bb5Sswilcox "BLOCK TOTALS OFFSET %d NOT FOUR-BYTE ALIGNED\n",
1945355d6bb5Sswilcox cgp->cg_btotoff);
1946355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1947355d6bb5Sswilcox }
1948355d6bb5Sswilcox
1949355d6bb5Sswilcox if ((cgp->cg_boff & 1) != 0) {
1950355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1951355d6bb5Sswilcox "FREE BLOCK POSITIONS TABLE OFFSET %d NOT TWO-BYTE ALIGNED\n",
1952355d6bb5Sswilcox cgp->cg_boff);
1953355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1954355d6bb5Sswilcox }
1955355d6bb5Sswilcox
1956355d6bb5Sswilcox if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
1957355d6bb5Sswilcox if (cgp->cg_ncyl < 1) {
1958355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1959355d6bb5Sswilcox "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is less than 1)\n",
1960355d6bb5Sswilcox cgp->cg_ncyl);
1961355d6bb5Sswilcox } else {
1962355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1963355d6bb5Sswilcox "IMPOSSIBLE NUMBER OF CYLINDERS IN GROUP (%d is greater than %d)\n",
1964355d6bb5Sswilcox cgp->cg_ncyl, sblock.fs_cpg);
1965355d6bb5Sswilcox }
1966355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1967355d6bb5Sswilcox }
1968355d6bb5Sswilcox
1969355d6bb5Sswilcox if (cgp->cg_niblk != sblock.fs_ipg) {
1970355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1971355d6bb5Sswilcox "INCORRECT NUMBER OF INODES IN GROUP (%d should be %d)\n",
1972355d6bb5Sswilcox cgp->cg_niblk, sblock.fs_ipg);
1973355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1974355d6bb5Sswilcox }
1975355d6bb5Sswilcox
1976355d6bb5Sswilcox if (cgp->cg_ndblk != ndblk) {
1977355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1978355d6bb5Sswilcox "INCORRECT NUMBER OF DATA BLOCKS IN GROUP (%d should be %d)\n",
1979355d6bb5Sswilcox cgp->cg_ndblk, ndblk);
1980355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1981355d6bb5Sswilcox }
1982355d6bb5Sswilcox
1983355d6bb5Sswilcox if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= ndblk)) {
1984355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1985355d6bb5Sswilcox "IMPOSSIBLE BLOCK ALLOCATION ROTOR POSITION "
1986355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n",
1987355d6bb5Sswilcox cgp->cg_rotor, ndblk);
1988355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1989355d6bb5Sswilcox }
1990355d6bb5Sswilcox
1991355d6bb5Sswilcox if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= ndblk)) {
1992355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
1993355d6bb5Sswilcox "IMPOSSIBLE FRAGMENT ALLOCATION ROTOR POSITION "
1994355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n",
1995355d6bb5Sswilcox cgp->cg_frotor, ndblk);
1996355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
1997355d6bb5Sswilcox }
1998355d6bb5Sswilcox
1999355d6bb5Sswilcox if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
2000355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2001355d6bb5Sswilcox "IMPOSSIBLE INODE ALLOCATION ROTOR POSITION "
2002355d6bb5Sswilcox "(%d should be at least 0 and less than %d)\n",
2003355d6bb5Sswilcox cgp->cg_irotor, sblock.fs_ipg);
2004355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2005355d6bb5Sswilcox }
2006355d6bb5Sswilcox
2007355d6bb5Sswilcox if (cgp->cg_btotoff != exp_btotoff) {
2008355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2009355d6bb5Sswilcox "INCORRECT BLOCK TOTALS OFFSET (%d should be %d)\n",
2010355d6bb5Sswilcox cgp->cg_btotoff, exp_btotoff);
2011355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2012355d6bb5Sswilcox }
2013355d6bb5Sswilcox
2014355d6bb5Sswilcox if (cgp->cg_boff != exp_boff) {
2015355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2016355d6bb5Sswilcox "BAD FREE BLOCK POSITIONS TABLE OFFSET (%d should %d)\n",
2017355d6bb5Sswilcox cgp->cg_boff, exp_boff);
2018355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2019355d6bb5Sswilcox }
2020355d6bb5Sswilcox
2021355d6bb5Sswilcox if (cgp->cg_iusedoff != exp_iusedoff) {
2022355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2023355d6bb5Sswilcox "INCORRECT USED INODE MAP OFFSET (%d should be %d)\n",
2024355d6bb5Sswilcox cgp->cg_iusedoff, exp_iusedoff);
2025355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2026355d6bb5Sswilcox }
2027355d6bb5Sswilcox
2028355d6bb5Sswilcox if (cgp->cg_freeoff != exp_freeoff) {
2029355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2030355d6bb5Sswilcox "INCORRECT FREE FRAGMENT MAP OFFSET (%d should be %d)\n",
2031355d6bb5Sswilcox cgp->cg_freeoff, exp_freeoff);
2032355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2033355d6bb5Sswilcox }
2034355d6bb5Sswilcox
2035355d6bb5Sswilcox if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2036355d6bb5Sswilcox this_len = fsck_asprintf(&this_err,
2037355d6bb5Sswilcox "END OF HEADER POSITION INCORRECT (%d should be %d)\n",
2038355d6bb5Sswilcox cgp->cg_nextfreeoff, exp_nextfreeoff);
2039355d6bb5Sswilcox Append_Error(full_err, full_len, this_err, this_len);
2040355d6bb5Sswilcox }
2041355d6bb5Sswilcox
2042355d6bb5Sswilcox return (full_err);
2043355d6bb5Sswilcox }
2044355d6bb5Sswilcox
2045355d6bb5Sswilcox #undef Append_Error
2046355d6bb5Sswilcox
2047355d6bb5Sswilcox /*
2048355d6bb5Sswilcox * This is taken from mkfs, and is what is used to come up with the
2049355d6bb5Sswilcox * original values for a struct cg. This implies that, since these
2050355d6bb5Sswilcox * are all constants, recalculating them now should give us the same
2051355d6bb5Sswilcox * thing as what's on disk.
2052355d6bb5Sswilcox */
2053355d6bb5Sswilcox static void
cg_constants(int cgno,daddr32_t * btotoff,daddr32_t * boff,daddr32_t * iusedoff,daddr32_t * freeoff,daddr32_t * nextfreeoff,daddr32_t * ndblk)2054355d6bb5Sswilcox cg_constants(int cgno, daddr32_t *btotoff, daddr32_t *boff,
2055355d6bb5Sswilcox daddr32_t *iusedoff, daddr32_t *freeoff, daddr32_t *nextfreeoff,
2056355d6bb5Sswilcox daddr32_t *ndblk)
2057355d6bb5Sswilcox {
2058355d6bb5Sswilcox daddr32_t cbase, dmax;
2059355d6bb5Sswilcox struct cg *cgp;
2060355d6bb5Sswilcox
2061355d6bb5Sswilcox (void) getblk(&cgblk, (diskaddr_t)cgtod(&sblock, cgno),
2062355d6bb5Sswilcox (size_t)sblock.fs_cgsize);
2063355d6bb5Sswilcox cgp = cgblk.b_un.b_cg;
2064355d6bb5Sswilcox
2065355d6bb5Sswilcox cbase = cgbase(&sblock, cgno);
2066355d6bb5Sswilcox dmax = cbase + sblock.fs_fpg;
2067355d6bb5Sswilcox if (dmax > sblock.fs_size)
2068355d6bb5Sswilcox dmax = sblock.fs_size;
2069355d6bb5Sswilcox
2070355d6bb5Sswilcox /* LINTED pointer difference won't overflow */
2071355d6bb5Sswilcox *btotoff = &cgp->cg_space[0] - (uchar_t *)(&cgp->cg_link);
2072355d6bb5Sswilcox *boff = *btotoff + sblock.fs_cpg * sizeof (daddr32_t);
2073355d6bb5Sswilcox *iusedoff = *boff + sblock.fs_cpg * sblock.fs_nrpos * sizeof (int16_t);
2074355d6bb5Sswilcox *freeoff = *iusedoff + howmany(sblock.fs_ipg, NBBY);
2075355d6bb5Sswilcox *nextfreeoff = *freeoff +
20761493b746SMilan Cermak howmany(sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY);
2077355d6bb5Sswilcox *ndblk = dmax - cbase;
2078355d6bb5Sswilcox }
2079355d6bb5Sswilcox
2080355d6bb5Sswilcox /*
2081355d6bb5Sswilcox * Corrects all fields in the cg that can be done with the available
2082355d6bb5Sswilcox * redundant data.
2083355d6bb5Sswilcox */
2084355d6bb5Sswilcox void
fix_cg(struct cg * cgp,int cgno)2085355d6bb5Sswilcox fix_cg(struct cg *cgp, int cgno)
2086355d6bb5Sswilcox {
2087355d6bb5Sswilcox daddr32_t exp_btotoff, exp_boff, exp_iusedoff;
2088355d6bb5Sswilcox daddr32_t exp_freeoff, exp_nextfreeoff;
2089355d6bb5Sswilcox daddr32_t ndblk;
2090355d6bb5Sswilcox
2091355d6bb5Sswilcox cg_constants(cgno, &exp_btotoff, &exp_boff, &exp_iusedoff,
2092355d6bb5Sswilcox &exp_freeoff, &exp_nextfreeoff, &ndblk);
2093355d6bb5Sswilcox
2094355d6bb5Sswilcox if (cgp->cg_cgx != cgno) {
2095355d6bb5Sswilcox cgp->cg_cgx = cgno;
2096355d6bb5Sswilcox }
2097355d6bb5Sswilcox
2098355d6bb5Sswilcox if ((cgp->cg_ncyl < 1) || (cgp->cg_ncyl > sblock.fs_cpg)) {
2099ef31a55cSvk if (cgno == (sblock.fs_ncg - 1)) {
2100355d6bb5Sswilcox cgp->cg_ncyl = sblock.fs_ncyl -
21011493b746SMilan Cermak (sblock.fs_cpg * cgno);
2102355d6bb5Sswilcox } else {
2103355d6bb5Sswilcox cgp->cg_ncyl = sblock.fs_cpg;
2104355d6bb5Sswilcox }
2105355d6bb5Sswilcox }
2106355d6bb5Sswilcox
2107355d6bb5Sswilcox if (cgp->cg_niblk != sblock.fs_ipg) {
2108355d6bb5Sswilcox /*
2109355d6bb5Sswilcox * This is not used by the kernel, so it's pretty
2110355d6bb5Sswilcox * harmless if it's wrong.
2111355d6bb5Sswilcox */
2112355d6bb5Sswilcox cgp->cg_niblk = sblock.fs_ipg;
2113355d6bb5Sswilcox }
2114355d6bb5Sswilcox
2115355d6bb5Sswilcox if (cgp->cg_ndblk != ndblk) {
2116355d6bb5Sswilcox cgp->cg_ndblk = ndblk;
2117355d6bb5Sswilcox }
2118355d6bb5Sswilcox
2119355d6bb5Sswilcox /*
2120355d6bb5Sswilcox * For the rotors, any position's valid, so pick the one we know
2121355d6bb5Sswilcox * will always exist.
2122355d6bb5Sswilcox */
2123355d6bb5Sswilcox if ((cgp->cg_rotor < 0) || (cgp->cg_rotor >= cgp->cg_ndblk)) {
2124355d6bb5Sswilcox cgp->cg_rotor = 0;
2125355d6bb5Sswilcox }
2126355d6bb5Sswilcox
2127355d6bb5Sswilcox if ((cgp->cg_frotor < 0) || (cgp->cg_frotor >= cgp->cg_ndblk)) {
2128355d6bb5Sswilcox cgp->cg_frotor = 0;
2129355d6bb5Sswilcox }
2130355d6bb5Sswilcox
2131355d6bb5Sswilcox if ((cgp->cg_irotor < 0) || (cgp->cg_irotor >= sblock.fs_ipg)) {
2132355d6bb5Sswilcox cgp->cg_irotor = 0;
2133355d6bb5Sswilcox }
2134355d6bb5Sswilcox
2135355d6bb5Sswilcox /*
2136355d6bb5Sswilcox * For btotoff and boff, if they're misaligned they won't
2137355d6bb5Sswilcox * match the expected values, so we're catching both cases
2138355d6bb5Sswilcox * here. Of course, if any of these are off, it seems likely
2139355d6bb5Sswilcox * that the tables really won't be where we calculate they
2140355d6bb5Sswilcox * should be anyway.
2141355d6bb5Sswilcox */
2142355d6bb5Sswilcox if (cgp->cg_btotoff != exp_btotoff) {
2143355d6bb5Sswilcox cgp->cg_btotoff = exp_btotoff;
2144355d6bb5Sswilcox }
2145355d6bb5Sswilcox
2146355d6bb5Sswilcox if (cgp->cg_boff != exp_boff) {
2147355d6bb5Sswilcox cgp->cg_boff = exp_boff;
2148355d6bb5Sswilcox }
2149355d6bb5Sswilcox
2150355d6bb5Sswilcox if (cgp->cg_iusedoff != exp_iusedoff) {
2151355d6bb5Sswilcox cgp->cg_iusedoff = exp_iusedoff;
2152355d6bb5Sswilcox }
2153355d6bb5Sswilcox
2154355d6bb5Sswilcox if (cgp->cg_freeoff != exp_freeoff) {
2155355d6bb5Sswilcox cgp->cg_freeoff = exp_freeoff;
2156355d6bb5Sswilcox }
2157355d6bb5Sswilcox
2158355d6bb5Sswilcox if (cgp->cg_nextfreeoff != exp_nextfreeoff) {
2159355d6bb5Sswilcox cgp->cg_nextfreeoff = exp_nextfreeoff;
2160355d6bb5Sswilcox }
2161355d6bb5Sswilcox
216277a343abSabalfour /*
216377a343abSabalfour * Reset the magic, as we've recreated this cg, also
216477a343abSabalfour * update the cg_time, as we're writing out the cg
216577a343abSabalfour */
216677a343abSabalfour cgp->cg_magic = CG_MAGIC;
216777a343abSabalfour cgp->cg_time = time(NULL);
216877a343abSabalfour
2169355d6bb5Sswilcox /*
2170355d6bb5Sswilcox * We know there was at least one correctable problem,
2171355d6bb5Sswilcox * or else we wouldn't have been called. So instead of
2172355d6bb5Sswilcox * marking the buffer dirty N times above, just do it
2173355d6bb5Sswilcox * once here.
2174355d6bb5Sswilcox */
2175355d6bb5Sswilcox cgdirty();
2176355d6bb5Sswilcox }
2177355d6bb5Sswilcox
2178355d6bb5Sswilcox void
examinelog(void (* cb)(daddr32_t))217939542a18Sabalfour examinelog(void (*cb)(daddr32_t))
2180355d6bb5Sswilcox {
2181355d6bb5Sswilcox struct bufarea *bp;
2182355d6bb5Sswilcox extent_block_t *ebp;
2183355d6bb5Sswilcox extent_t *ep;
2184355d6bb5Sswilcox daddr32_t nfno, fno;
2185355d6bb5Sswilcox int i;
2186355d6bb5Sswilcox int j;
2187355d6bb5Sswilcox
218839542a18Sabalfour /*
218939542a18Sabalfour * Since ufs stores fs_logbno as blocks and MTBufs stores it as frags
219039542a18Sabalfour * we need to translate accordingly using logbtodb()
219139542a18Sabalfour */
219239542a18Sabalfour
219339542a18Sabalfour if (logbtodb(&sblock, sblock.fs_logbno) < SBLOCK) {
219439542a18Sabalfour if (debug) {
219539542a18Sabalfour (void) printf("fs_logbno < SBLOCK: %ld < %ld\n" \
219639542a18Sabalfour "Aborting log examination\n", \
219739542a18Sabalfour logbtodb(&sblock, sblock.fs_logbno), SBLOCK);
219839542a18Sabalfour }
2199355d6bb5Sswilcox return;
220039542a18Sabalfour }
2201355d6bb5Sswilcox
2202355d6bb5Sswilcox /*
2203355d6bb5Sswilcox * Read errors will return zeros, which will cause us
2204355d6bb5Sswilcox * to do nothing harmful, so don't need to handle it.
2205355d6bb5Sswilcox */
2206355d6bb5Sswilcox bp = getdatablk(logbtofrag(&sblock, sblock.fs_logbno),
22071493b746SMilan Cermak (size_t)sblock.fs_bsize);
2208355d6bb5Sswilcox ebp = (void *)bp->b_un.b_buf;
2209355d6bb5Sswilcox
2210355d6bb5Sswilcox /*
2211355d6bb5Sswilcox * Does it look like a log allocation table?
2212355d6bb5Sswilcox */
2213355d6bb5Sswilcox /* LINTED pointer cast is aligned */
2214355d6bb5Sswilcox if (!log_checksum(&ebp->chksum, (int32_t *)bp->b_un.b_buf,
2215355d6bb5Sswilcox sblock.fs_bsize))
2216355d6bb5Sswilcox return;
2217355d6bb5Sswilcox if (ebp->type != LUFS_EXTENTS || ebp->nextents == 0)
2218355d6bb5Sswilcox return;
2219355d6bb5Sswilcox
2220355d6bb5Sswilcox ep = &ebp->extents[0];
2221355d6bb5Sswilcox for (i = 0; i < ebp->nextents; ++i, ++ep) {
2222355d6bb5Sswilcox fno = logbtofrag(&sblock, ep->pbno);
2223355d6bb5Sswilcox nfno = dbtofsb(&sblock, ep->nbno);
2224355d6bb5Sswilcox for (j = 0; j < nfno; ++j, ++fno) {
2225355d6bb5Sswilcox /*
2226355d6bb5Sswilcox * Invoke the callback first, so that pass1 can
2227355d6bb5Sswilcox * mark the log blocks in-use. Then, if any
2228355d6bb5Sswilcox * subsequent pass over the log shows us that a
2229355d6bb5Sswilcox * block got freed (say, it was also claimed by
2230355d6bb5Sswilcox * an inode that we cleared), we can safely declare
2231355d6bb5Sswilcox * the log bad.
2232355d6bb5Sswilcox */
2233355d6bb5Sswilcox if (cb != NULL)
2234355d6bb5Sswilcox (*cb)(fno);
2235355d6bb5Sswilcox if (!testbmap(fno))
2236355d6bb5Sswilcox islogok = 0;
2237355d6bb5Sswilcox }
2238355d6bb5Sswilcox }
2239355d6bb5Sswilcox brelse(bp);
2240355d6bb5Sswilcox
2241355d6bb5Sswilcox if (cb != NULL) {
2242355d6bb5Sswilcox fno = logbtofrag(&sblock, sblock.fs_logbno);
2243355d6bb5Sswilcox for (j = 0; j < sblock.fs_frag; ++j, ++fno)
2244355d6bb5Sswilcox (*cb)(fno);
2245355d6bb5Sswilcox }
2246355d6bb5Sswilcox }
2247355d6bb5Sswilcox
2248355d6bb5Sswilcox static void
freelogblk(daddr32_t frag)2249355d6bb5Sswilcox freelogblk(daddr32_t frag)
2250355d6bb5Sswilcox {
2251355d6bb5Sswilcox freeblk(sblock.fs_logbno, frag, 1);
2252355d6bb5Sswilcox }
2253355d6bb5Sswilcox
2254355d6bb5Sswilcox caddr_t
file_id(fsck_ino_t inum,mode_t mode)2255355d6bb5Sswilcox file_id(fsck_ino_t inum, mode_t mode)
2256355d6bb5Sswilcox {
2257355d6bb5Sswilcox static char name[MAXPATHLEN + 1];
2258355d6bb5Sswilcox
2259355d6bb5Sswilcox if (lfdir == inum) {
2260355d6bb5Sswilcox return (lfname);
2261355d6bb5Sswilcox }
2262355d6bb5Sswilcox
2263355d6bb5Sswilcox if ((mode & IFMT) == IFDIR) {
2264355d6bb5Sswilcox (void) strcpy(name, "DIR");
2265355d6bb5Sswilcox } else if ((mode & IFMT) == IFATTRDIR) {
2266355d6bb5Sswilcox (void) strcpy(name, "ATTR DIR");
2267355d6bb5Sswilcox } else if ((mode & IFMT) == IFSHAD) {
2268355d6bb5Sswilcox (void) strcpy(name, "ACL");
2269355d6bb5Sswilcox } else {
2270355d6bb5Sswilcox (void) strcpy(name, "FILE");
2271355d6bb5Sswilcox }
2272355d6bb5Sswilcox
2273355d6bb5Sswilcox return (name);
2274355d6bb5Sswilcox }
2275355d6bb5Sswilcox
2276355d6bb5Sswilcox /*
2277355d6bb5Sswilcox * Simple initializer for inodesc structures, so users of only a few
2278355d6bb5Sswilcox * fields don't have to worry about getting the right defaults for
2279355d6bb5Sswilcox * everything out.
2280355d6bb5Sswilcox */
2281355d6bb5Sswilcox void
init_inodesc(struct inodesc * idesc)2282355d6bb5Sswilcox init_inodesc(struct inodesc *idesc)
2283355d6bb5Sswilcox {
2284355d6bb5Sswilcox /*
2285355d6bb5Sswilcox * Most fields should be zero, just hit the special cases.
2286355d6bb5Sswilcox */
2287355d6bb5Sswilcox (void) memset((void *)idesc, 0, sizeof (struct inodesc));
2288355d6bb5Sswilcox idesc->id_fix = DONTKNOW;
2289355d6bb5Sswilcox idesc->id_lbn = -1;
2290355d6bb5Sswilcox idesc->id_truncto = -1;
2291355d6bb5Sswilcox idesc->id_firsthole = -1;
2292355d6bb5Sswilcox }
2293355d6bb5Sswilcox
2294355d6bb5Sswilcox /*
2295355d6bb5Sswilcox * Compare routine for tsearch(C) to use on ino_t instances.
2296355d6bb5Sswilcox */
2297355d6bb5Sswilcox int
ino_t_cmp(const void * left,const void * right)2298355d6bb5Sswilcox ino_t_cmp(const void *left, const void *right)
2299355d6bb5Sswilcox {
2300355d6bb5Sswilcox const fsck_ino_t lino = (const fsck_ino_t)left;
2301355d6bb5Sswilcox const fsck_ino_t rino = (const fsck_ino_t)right;
2302355d6bb5Sswilcox
2303355d6bb5Sswilcox return (lino - rino);
2304355d6bb5Sswilcox }
2305355d6bb5Sswilcox
2306355d6bb5Sswilcox int
cgisdirty(void)2307355d6bb5Sswilcox cgisdirty(void)
2308355d6bb5Sswilcox {
2309355d6bb5Sswilcox return (cgblk.b_dirty);
2310355d6bb5Sswilcox }
2311355d6bb5Sswilcox
2312355d6bb5Sswilcox void
cgflush(void)2313355d6bb5Sswilcox cgflush(void)
2314355d6bb5Sswilcox {
2315355d6bb5Sswilcox flush(fswritefd, &cgblk);
2316355d6bb5Sswilcox }
2317355d6bb5Sswilcox
2318355d6bb5Sswilcox void
dirty(struct bufarea * bp)2319355d6bb5Sswilcox dirty(struct bufarea *bp)
2320355d6bb5Sswilcox {
2321355d6bb5Sswilcox if (fswritefd < 0) {
23221493b746SMilan Cermak /*
23231493b746SMilan Cermak * No one should call dirty() in read only mode.
232448bbca81SDaniel Hoffman * But if one does, it's not fatal issue. Just warn them.
23251493b746SMilan Cermak */
23261493b746SMilan Cermak pwarn("WON'T SET DIRTY FLAG IN READ_ONLY MODE\n");
2327355d6bb5Sswilcox } else {
2328355d6bb5Sswilcox (bp)->b_dirty = 1;
2329355d6bb5Sswilcox isdirty = 1;
2330355d6bb5Sswilcox }
2331355d6bb5Sswilcox }
2332355d6bb5Sswilcox
2333355d6bb5Sswilcox void
initbarea(struct bufarea * bp)2334355d6bb5Sswilcox initbarea(struct bufarea *bp)
2335355d6bb5Sswilcox {
2336355d6bb5Sswilcox (bp)->b_dirty = 0;
2337355d6bb5Sswilcox (bp)->b_bno = (diskaddr_t)-1LL;
2338355d6bb5Sswilcox (bp)->b_flags = 0;
2339355d6bb5Sswilcox (bp)->b_cnt = 0;
2340355d6bb5Sswilcox (bp)->b_errs = 0;
2341355d6bb5Sswilcox }
2342355d6bb5Sswilcox
2343355d6bb5Sswilcox /*
2344355d6bb5Sswilcox * Partition-sizing routines adapted from ../newfs/newfs.c.
2345355d6bb5Sswilcox * Needed because calcsb() needs to use mkfs to work out what the
2346355d6bb5Sswilcox * superblock should be, and mkfs insists on being told how many
2347355d6bb5Sswilcox * sectors to use.
2348355d6bb5Sswilcox *
2349355d6bb5Sswilcox * Error handling assumes we're never called while preening.
2350355d6bb5Sswilcox *
2351355d6bb5Sswilcox * XXX This should be extracted into a ../ufslib.{c,h},
2352355d6bb5Sswilcox * in the same spirit to ../../fslib.{c,h}. Once that is
2353355d6bb5Sswilcox * done, both fsck and newfs should be modified to link
2354355d6bb5Sswilcox * against it.
2355355d6bb5Sswilcox */
2356355d6bb5Sswilcox
2357355d6bb5Sswilcox static int label_type;
2358355d6bb5Sswilcox
2359355d6bb5Sswilcox #define LABEL_TYPE_VTOC 1
2360355d6bb5Sswilcox #define LABEL_TYPE_EFI 2
2361355d6bb5Sswilcox #define LABEL_TYPE_OTHER 3
2362355d6bb5Sswilcox
2363355d6bb5Sswilcox #define MB (1024 * 1024)
2364355d6bb5Sswilcox #define SECTORS_PER_TERABYTE (1LL << 31)
2365355d6bb5Sswilcox #define FS_SIZE_UPPER_LIMIT 0x100000000000LL
2366355d6bb5Sswilcox
2367355d6bb5Sswilcox diskaddr_t
getdisksize(caddr_t disk,int fd)2368355d6bb5Sswilcox getdisksize(caddr_t disk, int fd)
2369355d6bb5Sswilcox {
2370355d6bb5Sswilcox int rpm;
2371355d6bb5Sswilcox struct dk_geom g;
2372355d6bb5Sswilcox struct dk_cinfo ci;
2373355d6bb5Sswilcox diskaddr_t actual_size;
2374355d6bb5Sswilcox
2375355d6bb5Sswilcox /*
2376355d6bb5Sswilcox * get_device_size() determines the actual size of the
2377355d6bb5Sswilcox * device, and also the disk's attributes, such as geometry.
2378355d6bb5Sswilcox */
2379355d6bb5Sswilcox actual_size = get_device_size(fd, disk);
2380355d6bb5Sswilcox
2381355d6bb5Sswilcox if (label_type == LABEL_TYPE_VTOC) {
2382355d6bb5Sswilcox if (ioctl(fd, DKIOCGGEOM, &g)) {
2383355d6bb5Sswilcox pwarn("%s: Unable to read Disk geometry", disk);
2384355d6bb5Sswilcox return (0);
2385355d6bb5Sswilcox }
2386355d6bb5Sswilcox if (sblock.fs_nsect == 0)
2387355d6bb5Sswilcox sblock.fs_nsect = g.dkg_nsect;
2388355d6bb5Sswilcox if (sblock.fs_ntrak == 0)
2389355d6bb5Sswilcox sblock.fs_ntrak = g.dkg_nhead;
2390355d6bb5Sswilcox if (sblock.fs_rps == 0) {
2391355d6bb5Sswilcox rpm = ((int)g.dkg_rpm <= 0) ? 3600: g.dkg_rpm;
2392355d6bb5Sswilcox sblock.fs_rps = rpm / 60;
2393355d6bb5Sswilcox }
2394355d6bb5Sswilcox }
2395355d6bb5Sswilcox
2396355d6bb5Sswilcox if (sblock.fs_bsize == 0)
2397355d6bb5Sswilcox sblock.fs_bsize = MAXBSIZE;
2398355d6bb5Sswilcox
2399355d6bb5Sswilcox /*
2400355d6bb5Sswilcox * Adjust maxcontig by the device's maxtransfer. If maxtransfer
2401355d6bb5Sswilcox * information is not available, default to the min of a MB and
2402355d6bb5Sswilcox * maxphys.
2403355d6bb5Sswilcox */
2404355d6bb5Sswilcox if (sblock.fs_maxcontig == -1 && ioctl(fd, DKIOCINFO, &ci) == 0) {
2405355d6bb5Sswilcox sblock.fs_maxcontig = ci.dki_maxtransfer * DEV_BSIZE;
2406355d6bb5Sswilcox if (sblock.fs_maxcontig < 0) {
2407355d6bb5Sswilcox int gotit, maxphys;
2408355d6bb5Sswilcox
2409355d6bb5Sswilcox gotit = fsgetmaxphys(&maxphys, NULL);
2410355d6bb5Sswilcox
2411355d6bb5Sswilcox /*
2412355d6bb5Sswilcox * If we cannot get the maxphys value, default
2413355d6bb5Sswilcox * to ufs_maxmaxphys (MB).
2414355d6bb5Sswilcox */
2415355d6bb5Sswilcox if (gotit) {
2416355d6bb5Sswilcox sblock.fs_maxcontig = MIN(maxphys, MB);
2417355d6bb5Sswilcox } else {
2418355d6bb5Sswilcox sblock.fs_maxcontig = MB;
2419355d6bb5Sswilcox }
2420355d6bb5Sswilcox }
2421355d6bb5Sswilcox sblock.fs_maxcontig /= sblock.fs_bsize;
2422355d6bb5Sswilcox }
2423355d6bb5Sswilcox
2424355d6bb5Sswilcox return (actual_size);
2425355d6bb5Sswilcox }
2426355d6bb5Sswilcox
2427355d6bb5Sswilcox /*
2428355d6bb5Sswilcox * Figure out how big the partition we're dealing with is.
2429355d6bb5Sswilcox */
2430355d6bb5Sswilcox static diskaddr_t
get_device_size(int fd,caddr_t name)2431355d6bb5Sswilcox get_device_size(int fd, caddr_t name)
2432355d6bb5Sswilcox {
2433342440ecSPrasad Singamsetty struct extvtoc vtoc;
2434355d6bb5Sswilcox struct dk_gpt *efi_vtoc;
2435355d6bb5Sswilcox diskaddr_t slicesize = 0;
2436355d6bb5Sswilcox
2437342440ecSPrasad Singamsetty int index = read_extvtoc(fd, &vtoc);
2438355d6bb5Sswilcox
2439355d6bb5Sswilcox if (index >= 0) {
2440355d6bb5Sswilcox label_type = LABEL_TYPE_VTOC;
2441355d6bb5Sswilcox } else {
2442355d6bb5Sswilcox if (index == VT_ENOTSUP || index == VT_ERROR) {
2443355d6bb5Sswilcox /* it might be an EFI label */
2444355d6bb5Sswilcox index = efi_alloc_and_read(fd, &efi_vtoc);
2445355d6bb5Sswilcox if (index >= 0)
2446355d6bb5Sswilcox label_type = LABEL_TYPE_EFI;
2447355d6bb5Sswilcox }
2448355d6bb5Sswilcox }
2449355d6bb5Sswilcox
2450355d6bb5Sswilcox if (index < 0) {
2451355d6bb5Sswilcox /*
2452355d6bb5Sswilcox * Since both attempts to read the label failed, we're
2453355d6bb5Sswilcox * going to fall back to a brute force approach to
2454355d6bb5Sswilcox * determining the device's size: see how far out we can
2455355d6bb5Sswilcox * perform reads on the device.
2456355d6bb5Sswilcox */
2457355d6bb5Sswilcox
2458355d6bb5Sswilcox slicesize = brute_force_get_device_size(fd);
2459355d6bb5Sswilcox if (slicesize == 0) {
2460355d6bb5Sswilcox switch (index) {
2461355d6bb5Sswilcox case VT_ERROR:
2462355d6bb5Sswilcox pwarn("%s: %s\n", name, strerror(errno));
2463355d6bb5Sswilcox break;
2464355d6bb5Sswilcox case VT_EIO:
2465355d6bb5Sswilcox pwarn("%s: I/O error accessing VTOC", name);
2466355d6bb5Sswilcox break;
2467355d6bb5Sswilcox case VT_EINVAL:
2468355d6bb5Sswilcox pwarn("%s: Invalid field in VTOC", name);
2469355d6bb5Sswilcox break;
2470355d6bb5Sswilcox default:
2471355d6bb5Sswilcox pwarn("%s: unknown error %d accessing VTOC",
2472355d6bb5Sswilcox name, index);
2473355d6bb5Sswilcox break;
2474355d6bb5Sswilcox }
2475355d6bb5Sswilcox return (0);
2476355d6bb5Sswilcox } else {
2477355d6bb5Sswilcox label_type = LABEL_TYPE_OTHER;
2478355d6bb5Sswilcox }
2479355d6bb5Sswilcox }
2480355d6bb5Sswilcox
2481355d6bb5Sswilcox if (label_type == LABEL_TYPE_EFI) {
2482355d6bb5Sswilcox slicesize = efi_vtoc->efi_parts[index].p_size;
2483355d6bb5Sswilcox efi_free(efi_vtoc);
2484355d6bb5Sswilcox } else if (label_type == LABEL_TYPE_VTOC) {
2485342440ecSPrasad Singamsetty slicesize = vtoc.v_part[index].p_size;
2486355d6bb5Sswilcox }
2487355d6bb5Sswilcox
2488355d6bb5Sswilcox return (slicesize);
2489355d6bb5Sswilcox }
2490355d6bb5Sswilcox
2491355d6bb5Sswilcox /*
2492355d6bb5Sswilcox * brute_force_get_device_size
2493355d6bb5Sswilcox *
2494355d6bb5Sswilcox * Determine the size of the device by seeing how far we can
2495355d6bb5Sswilcox * read. Doing an llseek( , , SEEK_END) would probably work
2496355d6bb5Sswilcox * in most cases, but we've seen at least one third-party driver
2497355d6bb5Sswilcox * which doesn't correctly support the SEEK_END option when the
2498355d6bb5Sswilcox * the device is greater than a terabyte.
2499355d6bb5Sswilcox */
2500355d6bb5Sswilcox
2501355d6bb5Sswilcox static diskaddr_t
brute_force_get_device_size(int fd)2502355d6bb5Sswilcox brute_force_get_device_size(int fd)
2503355d6bb5Sswilcox {
2504355d6bb5Sswilcox diskaddr_t min_fail = 0;
2505355d6bb5Sswilcox diskaddr_t max_succeed = 0;
2506355d6bb5Sswilcox diskaddr_t cur_db_off;
2507*bfbf29e2SToomas Soome char buf[DEV_BSIZE];
2508355d6bb5Sswilcox
2509355d6bb5Sswilcox /*
2510355d6bb5Sswilcox * First, see if we can read the device at all, just to
2511355d6bb5Sswilcox * eliminate errors that have nothing to do with the
2512355d6bb5Sswilcox * device's size.
2513355d6bb5Sswilcox */
2514355d6bb5Sswilcox
2515355d6bb5Sswilcox if (((llseek(fd, (offset_t)0, SEEK_SET)) == -1) ||
2516355d6bb5Sswilcox ((read(fd, buf, DEV_BSIZE)) == -1))
2517355d6bb5Sswilcox return (0); /* can't determine size */
2518355d6bb5Sswilcox
2519355d6bb5Sswilcox /*
2520355d6bb5Sswilcox * Now, go sequentially through the multiples of 4TB
2521355d6bb5Sswilcox * to find the first read that fails (this isn't strictly
2522355d6bb5Sswilcox * the most efficient way to find the actual size if the
2523355d6bb5Sswilcox * size really could be anything between 0 and 2**64 bytes.
2524355d6bb5Sswilcox * We expect the sizes to be less than 16 TB for some time,
2525355d6bb5Sswilcox * so why do a bunch of reads that are larger than that?
2526355d6bb5Sswilcox * However, this algorithm *will* work for sizes of greater
2527355d6bb5Sswilcox * than 16 TB. We're just not optimizing for those sizes.)
2528355d6bb5Sswilcox */
2529355d6bb5Sswilcox
2530355d6bb5Sswilcox /*
2531355d6bb5Sswilcox * XXX lint uses 32-bit arithmetic for doing flow analysis.
2532355d6bb5Sswilcox * We're using > 32-bit constants here. Therefore, its flow
2533355d6bb5Sswilcox * analysis is wrong. For the time being, ignore complaints
2534355d6bb5Sswilcox * from it about the body of the for() being unreached.
2535355d6bb5Sswilcox */
2536355d6bb5Sswilcox for (cur_db_off = SECTORS_PER_TERABYTE * 4;
2537355d6bb5Sswilcox (min_fail == 0) && (cur_db_off < FS_SIZE_UPPER_LIMIT);
2538355d6bb5Sswilcox cur_db_off += 4 * SECTORS_PER_TERABYTE) {
2539355d6bb5Sswilcox if ((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2540355d6bb5Sswilcox SEEK_SET) == -1) ||
2541355d6bb5Sswilcox (read(fd, buf, DEV_BSIZE) != DEV_BSIZE))
2542355d6bb5Sswilcox min_fail = cur_db_off;
2543355d6bb5Sswilcox else
2544355d6bb5Sswilcox max_succeed = cur_db_off;
2545355d6bb5Sswilcox }
2546355d6bb5Sswilcox
2547355d6bb5Sswilcox /*
2548355d6bb5Sswilcox * XXX Same lint flow analysis problem as above.
2549355d6bb5Sswilcox */
2550355d6bb5Sswilcox if (min_fail == 0)
2551355d6bb5Sswilcox return (0);
2552355d6bb5Sswilcox
2553355d6bb5Sswilcox /*
2554355d6bb5Sswilcox * We now know that the size of the device is less than
2555355d6bb5Sswilcox * min_fail and greater than or equal to max_succeed. Now
2556355d6bb5Sswilcox * keep splitting the difference until the actual size in
2557355d6bb5Sswilcox * sectors in known. We also know that the difference
2558355d6bb5Sswilcox * between max_succeed and min_fail at this time is
2559355d6bb5Sswilcox * 4 * SECTORS_PER_TERABYTE, which is a power of two, which
2560355d6bb5Sswilcox * simplifies the math below.
2561355d6bb5Sswilcox */
2562355d6bb5Sswilcox
2563355d6bb5Sswilcox while (min_fail - max_succeed > 1) {
2564355d6bb5Sswilcox cur_db_off = max_succeed + (min_fail - max_succeed)/2;
2565355d6bb5Sswilcox if (((llseek(fd, (offset_t)(cur_db_off * DEV_BSIZE),
2566355d6bb5Sswilcox SEEK_SET)) == -1) ||
2567355d6bb5Sswilcox ((read(fd, buf, DEV_BSIZE)) != DEV_BSIZE))
2568355d6bb5Sswilcox min_fail = cur_db_off;
2569355d6bb5Sswilcox else
2570355d6bb5Sswilcox max_succeed = cur_db_off;
2571355d6bb5Sswilcox }
2572355d6bb5Sswilcox
2573355d6bb5Sswilcox /* the size is the last successfully read sector offset plus one */
2574355d6bb5Sswilcox return (max_succeed + 1);
2575355d6bb5Sswilcox }
2576355d6bb5Sswilcox
2577355d6bb5Sswilcox static void
vfileerror(fsck_ino_t cwd,fsck_ino_t ino,caddr_t fmt,va_list ap)2578355d6bb5Sswilcox vfileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, va_list ap)
2579355d6bb5Sswilcox {
2580355d6bb5Sswilcox struct dinode *dp;
2581355d6bb5Sswilcox char pathbuf[MAXPATHLEN + 1];
2582355d6bb5Sswilcox
2583355d6bb5Sswilcox vpwarn(fmt, ap);
2584355d6bb5Sswilcox (void) putchar(' ');
2585355d6bb5Sswilcox pinode(ino);
2586355d6bb5Sswilcox (void) printf("\n");
2587355d6bb5Sswilcox getpathname(pathbuf, cwd, ino);
2588355d6bb5Sswilcox if (ino < UFSROOTINO || ino > maxino) {
2589355d6bb5Sswilcox pfatal("NAME=%s\n", pathbuf);
2590355d6bb5Sswilcox return;
2591355d6bb5Sswilcox }
2592355d6bb5Sswilcox dp = ginode(ino);
2593355d6bb5Sswilcox if (ftypeok(dp))
2594355d6bb5Sswilcox pfatal("%s=%s\n", file_id(ino, dp->di_mode), pathbuf);
2595355d6bb5Sswilcox else
2596355d6bb5Sswilcox pfatal("NAME=%s\n", pathbuf);
2597355d6bb5Sswilcox }
2598355d6bb5Sswilcox
2599355d6bb5Sswilcox void
direrror(fsck_ino_t ino,caddr_t fmt,...)2600355d6bb5Sswilcox direrror(fsck_ino_t ino, caddr_t fmt, ...)
2601355d6bb5Sswilcox {
2602355d6bb5Sswilcox va_list ap;
2603355d6bb5Sswilcox
2604355d6bb5Sswilcox va_start(ap, fmt);
2605355d6bb5Sswilcox vfileerror(ino, ino, fmt, ap);
2606355d6bb5Sswilcox va_end(ap);
2607355d6bb5Sswilcox }
2608355d6bb5Sswilcox
2609355d6bb5Sswilcox static void
vdirerror(fsck_ino_t ino,caddr_t fmt,va_list ap)2610355d6bb5Sswilcox vdirerror(fsck_ino_t ino, caddr_t fmt, va_list ap)
2611355d6bb5Sswilcox {
2612355d6bb5Sswilcox vfileerror(ino, ino, fmt, ap);
2613355d6bb5Sswilcox }
2614355d6bb5Sswilcox
2615355d6bb5Sswilcox void
fileerror(fsck_ino_t cwd,fsck_ino_t ino,caddr_t fmt,...)2616355d6bb5Sswilcox fileerror(fsck_ino_t cwd, fsck_ino_t ino, caddr_t fmt, ...)
2617355d6bb5Sswilcox {
2618355d6bb5Sswilcox va_list ap;
2619355d6bb5Sswilcox
2620355d6bb5Sswilcox va_start(ap, fmt);
2621355d6bb5Sswilcox vfileerror(cwd, ino, fmt, ap);
2622355d6bb5Sswilcox va_end(ap);
2623355d6bb5Sswilcox }
2624355d6bb5Sswilcox
2625355d6bb5Sswilcox /*
2626355d6bb5Sswilcox * Adds the given inode to the orphaned-directories list, limbo_dirs.
2627355d6bb5Sswilcox * Assumes that the caller has set INCLEAR in the inode's statemap[]
2628355d6bb5Sswilcox * entry.
2629355d6bb5Sswilcox *
2630355d6bb5Sswilcox * With INCLEAR set, the inode will get ignored by passes 2 and 3,
2631355d6bb5Sswilcox * meaning it's effectively an orphan. It needs to be noted now, so
2632355d6bb5Sswilcox * it will be remembered in pass 4.
2633355d6bb5Sswilcox */
2634355d6bb5Sswilcox
2635355d6bb5Sswilcox void
add_orphan_dir(fsck_ino_t ino)2636355d6bb5Sswilcox add_orphan_dir(fsck_ino_t ino)
2637355d6bb5Sswilcox {
2638355d6bb5Sswilcox if (tsearch((void *)ino, &limbo_dirs, ino_t_cmp) == NULL)
2639355d6bb5Sswilcox errexit("add_orphan_dir: out of memory");
2640355d6bb5Sswilcox }
2641355d6bb5Sswilcox
2642355d6bb5Sswilcox /*
2643355d6bb5Sswilcox * Remove an inode from the orphaned-directories list, presumably
2644355d6bb5Sswilcox * because it's been cleared.
2645355d6bb5Sswilcox */
2646355d6bb5Sswilcox void
remove_orphan_dir(fsck_ino_t ino)2647355d6bb5Sswilcox remove_orphan_dir(fsck_ino_t ino)
2648355d6bb5Sswilcox {
2649355d6bb5Sswilcox (void) tdelete((void *)ino, &limbo_dirs, ino_t_cmp);
2650355d6bb5Sswilcox }
2651355d6bb5Sswilcox
2652355d6bb5Sswilcox /*
2653355d6bb5Sswilcox * log_setsum() and log_checksum() are equivalent to lufs.c:setsum()
2654355d6bb5Sswilcox * and lufs.c:checksum().
2655355d6bb5Sswilcox */
2656355d6bb5Sswilcox static void
log_setsum(int32_t * sp,int32_t * lp,int nb)2657355d6bb5Sswilcox log_setsum(int32_t *sp, int32_t *lp, int nb)
2658355d6bb5Sswilcox {
2659355d6bb5Sswilcox int32_t csum = 0;
2660355d6bb5Sswilcox
2661355d6bb5Sswilcox *sp = 0;
2662355d6bb5Sswilcox nb /= sizeof (int32_t);
2663355d6bb5Sswilcox while (nb--)
2664355d6bb5Sswilcox csum += *lp++;
2665355d6bb5Sswilcox *sp = csum;
2666355d6bb5Sswilcox }
2667355d6bb5Sswilcox
2668355d6bb5Sswilcox static int
log_checksum(int32_t * sp,int32_t * lp,int nb)2669355d6bb5Sswilcox log_checksum(int32_t *sp, int32_t *lp, int nb)
2670355d6bb5Sswilcox {
2671355d6bb5Sswilcox int32_t ssum = *sp;
2672355d6bb5Sswilcox
2673355d6bb5Sswilcox log_setsum(sp, lp, nb);
2674355d6bb5Sswilcox if (ssum != *sp) {
2675355d6bb5Sswilcox *sp = ssum;
2676355d6bb5Sswilcox return (0);
2677355d6bb5Sswilcox }
2678355d6bb5Sswilcox return (1);
26797c478bd9Sstevel@tonic-gate }
2680