/* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980, 1986, 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by the University of California, Berkeley and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #define bcopy(f, t, n) memcpy(t, f, n) #define bzero(s, n) memset(s, 0, n) #define bcmp(s, d, n) memcmp(s, d, n) #define index(s, r) strchr(s, r) #define rindex(s, r) strrchr(s, r) #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #include #include #include #include #include "fsck.h" #include #include #include int64_t diskreads, totalreads; /* Disk cache statistics */ offset_t llseek(); char *malloc(); char *mount_point = NULL; extern int mflag; extern uint_t largefile_count; static struct bufarea *alloc_bufarea(); ftypeok(dp) struct dinode *dp; { switch (dp->di_mode & IFMT) { case IFDIR: case IFREG: case IFBLK: case IFCHR: case IFLNK: case IFSOCK: case IFIFO: case IFSHAD: case IFATTRDIR: return (1); default: if (debug) printf("bad file type 0%o\n", dp->di_mode); return (0); } } acltypeok(dp) struct dinode *dp; { if (CHECK_ACL_ALLOWED(dp->di_mode & IFMT)) return (1); if (debug) printf("bad file type for acl 0%o\n", dp->di_mode); return (0); } reply(question) char *question; { char line[80]; if (preen) pfatal("INTERNAL ERROR: GOT TO reply()"); if (mflag) { printf("\n"); printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname); exit(39); } printf("\n%s? ", question); if (nflag || fswritefd < 0) { printf(" no\n\n"); iscorrupt = 1; /* known to be corrupt */ return (0); } if (yflag) { printf(" yes\n\n"); return (1); } if (getline(stdin, line, sizeof (line)) == EOF) errexit("\n"); printf("\n"); if (line[0] == 'y' || line[0] == 'Y') return (1); else { iscorrupt = 1; /* known to be corrupt */ return (0); } } getline(fp, loc, maxlen) FILE *fp; char *loc; { int n; char *p, *lastloc; p = loc; lastloc = &p[maxlen-1]; while ((n = getc(fp)) != '\n') { if (n == EOF) return (EOF); if (!isspace(n) && p < lastloc) *p++ = n; } *p = 0; return (p - loc); } /* * Malloc buffers and set up cache. */ bufinit() { struct bufarea *bp; int bufcnt, i; char *bufp; bufp = malloc((unsigned int)sblock.fs_bsize); if (bufp == 0) errexit("cannot allocate buffer pool\n"); cgblk.b_un.b_buf = bufp; initbarea(&cgblk); bufhead.b_next = bufhead.b_prev = &bufhead; bufcnt = MAXBUFSPACE / sblock.fs_bsize; if (bufcnt < MINBUFS) bufcnt = MINBUFS; for (i = 0; i < bufcnt; i++) { bp = (struct bufarea *)malloc(sizeof (struct bufarea)); bufp = malloc((unsigned int)sblock.fs_bsize); if (bp == NULL || bufp == NULL) { if (bp) free((char *)bp); if (bufp) free(bufp); if (i >= MINBUFS) break; errexit("cannot allocate buffer pool\n"); } bp->b_un.b_buf = bufp; bp->b_prev = &bufhead; bp->b_next = bufhead.b_next; bufhead.b_next->b_prev = bp; bufhead.b_next = bp; initbarea(bp); } bufhead.b_size = i; /* save number of buffers */ pbp = pdirbp = NULL; } /* * Manage a cache of directory blocks. */ struct bufarea * getdatablk(blkno, size) daddr32_t blkno; int size; { struct bufarea *bp; for (bp = bufhead.b_next; bp != &bufhead; bp = bp->b_next) if (bp->b_bno == fsbtodb(&sblock, blkno)) goto foundit; for (bp = bufhead.b_prev; bp != &bufhead; bp = bp->b_prev) if ((bp->b_flags & B_INUSE) == 0) break; if (bp == &bufhead) { bp = alloc_bufarea(); if (bp == NULL) errexit("deadlocked buffer pool\n"); } getblk(bp, blkno, size); /* fall through */ foundit: totalreads++; bp->b_cnt++; /* * Move the buffer to head of link-list if it isn't * already there. */ if (bufhead.b_next != bp) { bp->b_prev->b_next = bp->b_next; bp->b_next->b_prev = bp->b_prev; bp->b_prev = &bufhead; bp->b_next = bufhead.b_next; bufhead.b_next->b_prev = bp; bufhead.b_next = bp; } bp->b_flags |= B_INUSE; return (bp); } int brelse(struct bufarea *bp) { bp->b_cnt--; if (bp->b_cnt == 0) { bp->b_flags &= ~B_INUSE; } } struct bufarea * getblk(bp, blk, size) struct bufarea *bp; daddr32_t blk; int size; { diskaddr_t dblk; dblk = fsbtodb(&sblock, blk); if (bp->b_bno == dblk) return (bp); flush(fswritefd, bp); diskreads++; bp->b_errs = bread(fsreadfd, bp->b_un.b_buf, dblk, (long)size); bp->b_bno = dblk; bp->b_size = size; return (bp); } flush(fd, bp) int fd; struct bufarea *bp; { int i, j; caddr_t sip; long size; if (!bp->b_dirty) return; if (bp->b_errs != 0) pfatal("WRITING ZERO'ED BLOCK %lld TO DISK\n", bp->b_bno); bp->b_dirty = 0; bp->b_errs = 0; bwrite(fd, bp->b_un.b_buf, bp->b_bno, (long)bp->b_size); if (bp != &sblk) return; sip = (caddr_t)sblock.fs_u.fs_csp; for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { size = sblock.fs_cssize - i < sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize; bwrite(fswritefd, sip, fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), size); sip += size; } } rwerror(mesg, blk) char *mesg; diskaddr_t blk; { if (preen == 0) printf("\n"); pfatal("CANNOT %s: BLK %lld", mesg, blk); if (reply("CONTINUE") == 0) errexit("Program terminated\n"); } ckfini() { struct bufarea *bp, *nbp; int cnt = 0; /* * Mark the filesystem bad if a re-check is required. */ if (dirholes && havesb) { sblock.fs_clean = FSBAD; sblock.fs_state = -(FSOKAY - (long)sblock.fs_time); sbdirty(); } flush(fswritefd, &sblk); if (havesb && sblk.b_bno != SBOFF / dev_bsize) { sblk.b_bno = SBOFF / dev_bsize; sbdirty(); flush(fswritefd, &sblk); } flush(fswritefd, &cgblk); if (cgblk.b_un.b_buf) { free(cgblk.b_un.b_buf); cgblk.b_un.b_buf = NULL; } for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { cnt++; flush(fswritefd, bp); nbp = bp->b_prev; free(bp->b_un.b_buf); free((char *)bp); } pbp = pdirbp = NULL; if (bufhead.b_size != cnt) errexit("Panic: lost %d buffers\n", bufhead.b_size - cnt); if (debug) printf("cache missed %d of %d (%d%%)\n", diskreads, totalreads, totalreads ? diskreads * 100 / totalreads : 0); (void) close(fsreadfd); (void) close(fswritefd); } bread(fd, buf, blk, size) int fd; char *buf; diskaddr_t blk; long size; { char *cp; int i; int errs; offset_t offset = ldbtob(blk); offset_t addr; if (debug && (blk < SBLOCK)) { char msg[256]; sprintf(msg, "WARNING: fsck bread() passed blkno < %d (%ld)\n", SBLOCK, blk); printf(msg); } if (llseek(fd, offset, 0) < 0) { rwerror("SEEK", blk); } else if (read(fd, buf, (int)size) == size) return (0); rwerror("READ", blk); if (llseek(fd, offset, 0) < 0) { rwerror("SEEK", blk); } errs = 0; bzero(buf, (size_t)size); pwarn("THE FOLLOWING SECTORS COULD NOT BE READ:"); for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { addr = ldbtob(blk + i); if (llseek(fd, addr, SEEK_CUR) < 0 || read(fd, cp, (int)secsize) < 0) { printf(" %d", blk + i); errs++; } } printf("\n"); return (errs); } bwrite(fd, buf, blk, size) int fd; char *buf; diskaddr_t blk; long size; { int i; int n; char *cp; offset_t offset = ldbtob(blk); offset_t addr; if (fd < 0) return; if (blk < SBLOCK) { char msg[256]; sprintf(msg, "WARNING: Attempt to write illegal blkno %lld on %s\n", blk, devname); if (debug) printf(msg); return; } if (llseek(fd, offset, 0) < 0) { rwerror("SEEK", blk); } else if (write(fd, buf, (int)size) == size) { fsmodified = 1; return; } rwerror("WRITE", blk); if (llseek(fd, offset, 0) < 0) { rwerror("SEEK", blk); } pwarn("THE FOLLOWING SECTORS COULD NOT BE WRITTEN:"); for (cp = buf, i = 0; i < btodb(size); i++, cp += DEV_BSIZE) { n = 0; addr = ldbtob(blk + i); if (llseek(fd, addr, SEEK_CUR) < 0 || (n = write(fd, cp, DEV_BSIZE)) < 0) { printf(" %d", blk + i); } else if (n > 0) { fsmodified = 1; } } printf("\n"); } /* * allocate a data block with the specified number of fragments */ daddr32_t allocblk(frags) int frags; { int i, j, k; if (frags <= 0 || frags > sblock.fs_frag) return (0); for (i = 0; i < maxfsblock - sblock.fs_frag; i += sblock.fs_frag) { for (j = 0; j <= sblock.fs_frag - frags; j++) { if (testbmap(i + j)) continue; for (k = 1; k < frags; k++) if (testbmap(i + j + k)) break; if (k < frags) { j += k; continue; } for (k = 0; k < frags; k++) setbmap(i + j + k); n_blks += frags; return (i + j); } } return (0); } /* * Free a previously allocated block */ freeblk(blkno, frags) daddr32_t blkno; int frags; { struct inodesc idesc; idesc.id_blkno = blkno; idesc.id_numfrags = frags; pass4check(&idesc); } /* * Find a pathname */ getpathname(namebuf, curdir, ino) char *namebuf; ino_t curdir, ino; { int len; char *cp; struct inodesc idesc; struct inoinfo *inp; extern int findname(); if (statemap[curdir] != DSTATE && statemap[curdir] != DFOUND) { strcpy(namebuf, "?"); return; } bzero((char *)&idesc, sizeof (struct inodesc)); idesc.id_type = DATA; cp = &namebuf[MAXPATHLEN - 1]; *cp = '\0'; if (curdir != ino) { idesc.id_parent = curdir; goto namelookup; } while (ino != UFSROOTINO) { idesc.id_number = ino; idesc.id_func = findino; idesc.id_name = ".."; idesc.id_fix = NOFIX; if ((ckinode(ginode(ino), &idesc) & FOUND) == 0) { inp = getinoinfo(ino); if (inp->i_parent == 0) break; idesc.id_parent = inp->i_parent; } namelookup: idesc.id_number = idesc.id_parent; idesc.id_parent = ino; idesc.id_func = findname; idesc.id_name = namebuf; idesc.id_fix = NOFIX; if ((ckinode(ginode(idesc.id_number), &idesc)&FOUND) == 0) break; len = strlen(namebuf); cp -= len; if (cp < &namebuf[MAXNAMLEN]) break; bcopy(namebuf, cp, len); *--cp = '/'; ino = idesc.id_number; } if (ino != UFSROOTINO) { strcpy(namebuf, "?"); return; } bcopy(cp, namebuf, &namebuf[MAXPATHLEN] - cp); } void catch() { ckfini(); exit(37); } /* * When preening, allow a single quit to signal * a special exit after filesystem checks complete * so that reboot sequence may be interrupted. */ void catchquit() { extern returntosingle; printf("returning to single-user after filesystem check\n"); returntosingle = 1; (void) signal(SIGQUIT, SIG_DFL); } /* * Ignore a single quit signal; wait and flush just in case. * Used by child processes in preen. */ void voidquit() { sleep(1); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGQUIT, SIG_DFL); } /* * determine whether an inode should be fixed. */ dofix(idesc, msg) struct inodesc *idesc; char *msg; { switch (idesc->id_fix) { case DONTKNOW: if (idesc->id_type == DATA) direrror(idesc->id_number, msg); else pwarn(msg); if (preen) { printf(" (SALVAGED)\n"); idesc->id_fix = FIX; return (ALTERED); } if (reply("SALVAGE") == 0) { idesc->id_fix = NOFIX; return (0); } idesc->id_fix = FIX; return (ALTERED); case FIX: return (ALTERED); case NOFIX: return (0); default: errexit("UNKNOWN INODESC FIX MODE %d\n", idesc->id_fix); } /* NOTREACHED */ } /* VARARGS1 */ errexit(s1, s2, s3, s4) char *s1; { extern void write_altsb(int); if (errorlocked) { if (havesb) { sblock.fs_clean = FSBAD; sblock.fs_state = -(FSOKAY - (long)sblock.fs_time); sbdirty(); write_altsb(fswritefd); flush(fswritefd, &sblk); } } printf(s1, s2, s3, s4); exit(39); } /* * An unexpected inconsistency occured. * Die if preening, otherwise just print message and continue. */ /* VARARGS1 */ pfatal(s, a1, a2, a3) char *s; { if (preen) { printf("%s: ", devname); printf(s, a1, a2, a3); printf("\n"); printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname); if (havesb) { sblock.fs_clean = FSBAD; sblock.fs_state = -(FSOKAY - (long)sblock.fs_time); sbdirty(); flush(fswritefd, &sblk); } exit(36); } printf(s, a1, a2, a3); } /* * Pwarn just prints a message when not preening, * or a warning (preceded by filename) when preening. */ /* VARARGS1 */ pwarn(s, a1, a2, a3, a4, a5, a6) char *s; { if (preen) printf("%s: ", devname); printf(s, a1, a2, a3, a4, a5, a6); } #ifndef lint /* * Stub for routines from kernel. */ panic(s) char *s; { pfatal("INTERNAL INCONSISTENCY:"); errexit(s); } #define CE_PANIC 3 void cmn_err(level, s) int level; char *s; { if (level == CE_PANIC) { pfatal("INTERNAL INCONSISTENCY:"); errexit(s); } else printf(s); } #endif /* * Check to see if unraw version of name is already mounted. * Since we do not believe /etc/mnttab, we stat the mount point * to see if it is really looks mounted. */ mounted(name) char *name; { int found = 0; struct mnttab mnt; FILE *mnttab; struct stat64 device_stat, mount_stat; char *blkname, *unrawname(); mnttab = fopen(MNTTAB, "r"); if (mnttab == NULL) { return (0); } blkname = unrawname(name); while ((getmntent(mnttab, &mnt)) == NULL) { if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0) { continue; } if (strcmp(blkname, mnt.mnt_special) == 0) { stat64(mnt.mnt_mountp, &mount_stat); stat64(mnt.mnt_special, &device_stat); if (device_stat.st_rdev == mount_stat.st_dev) { if (hasmntopt(&mnt, MNTOPT_RO) != 0) found = 2; /* mounted as RO */ else found = 1; /* mounted as R/W */ } if (mount_point == NULL) { mount_point = strdup(mnt.mnt_mountp); if (mount_point == NULL) { printf("fsck: memory allocation" " failure\n"); exit(39); } } break; } } fclose(mnttab); return (found); } /* * Check to see if name corresponds to an entry in vfstab, and that the entry * does not have option ro. */ writable(name) char *name; { int rw = 1; struct vfstab vfsbuf; FILE *vfstab; char *blkname, *unrawname(); vfstab = fopen(VFSTAB, "r"); if (vfstab == NULL) { printf("can't open %s\n", VFSTAB); return (1); } blkname = unrawname(name); if ((getvfsspec(vfstab, &vfsbuf, blkname) == 0) && (vfsbuf.vfs_fstype != NULL) && (strcmp(vfsbuf.vfs_fstype, MNTTYPE_UFS) == 0) && (hasvfsopt(&vfsbuf, MNTOPT_RO))) { rw = 0; } fclose(vfstab); return (rw); } /* * debugclean */ debugclean() { char s[256]; if (debug == 0) return; if ((iscorrupt == 0) && (isdirty == 0)) return; if ((sblock.fs_clean != FSSTABLE) && (sblock.fs_clean != FSCLEAN) && (sblock.fs_clean != FSLOG || !islog || !islogok)) return; if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) return; sprintf(s, "WARNING: inconsistencies detected on `%s' filesystem %s", sblock.fs_clean == FSSTABLE ? "stable" : sblock.fs_clean == FSLOG ? "logging" : sblock.fs_clean == FSFIX ? "being fixed" : "clean", devname); printf("%s\n", s); } /* * updateclean * Carefully and transparently update the clean flag. */ updateclean() { struct bufarea cleanbuf; int size; daddr32_t bno; int fsclean; int fsreclaim; int fsflags; int r; daddr32_t fslogbno; offset_t sblkoff; time_t t; /* * debug stuff */ debugclean(); /* * set fsclean to its appropriate value */ fslogbno = sblock.fs_logbno; fsclean = sblock.fs_clean; fsreclaim = sblock.fs_reclaim; fsflags = sblock.fs_flags; if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) fsclean = FSACTIVE; /* if ufs log is not okay, clear it */ if (fslogbno && !(islog && islogok)) { fsclean = FSACTIVE; fslogbno = 0; } /* * if necessary, update fs_clean and fs_state */ switch (fsclean) { case FSACTIVE: if (!iscorrupt) { fsclean = FSSTABLE; fsreclaim = 0; } break; case FSCLEAN: case FSSTABLE: if (iscorrupt) fsclean = FSACTIVE; else fsreclaim = 0; break; case FSLOG: if (iscorrupt) fsclean = FSACTIVE; else if (!islog) { fsreclaim = 0; fsclean = FSSTABLE; } else if (fflag) fsreclaim = 0; break; case FSFIX: fsreclaim = needs_reclaim; fsclean = FSBAD; if (errorlocked && !iscorrupt) { fsclean = islog? FSLOG: FSCLEAN; } break; default: if (iscorrupt) fsclean = FSACTIVE; else { fsclean = FSSTABLE; fsreclaim = 0; } } if (largefile_count > 0) fsflags |= FSLARGEFILES; else fsflags &= ~FSLARGEFILES; /* * fs is unchanged, do nothing */ if (debug) printf("** largefile count=%d, fs.fs_flags=%x\n", largefile_count, sblock.fs_flags); if ((!isdirty) && (fsflags == sblock.fs_flags) && (fslogbno == sblock.fs_logbno) && (sblock.fs_clean == fsclean) && (sblock.fs_reclaim == fsreclaim) && (FSOKAY == (sblock.fs_state + sblock.fs_time))) { if (islog && !islogok) (void) ioctl(fswritefd, _FIOLOGRESET, NULL); if (errorlocked) { if (!do_errorlock(LOCKFS_ULOCK)) pwarn( "updateclean(unchanged): unlock(LOCKFS_ULOCK) failed\n"); } return; } /* * if user allows, update superblock state */ if (!isdirty && !preen && (reply("FILE SYSTEM STATE IN SUPERBLOCK IS WRONG; FIX") == 0)) return; (void) time(&t); sblock.fs_time = (time32_t)t; if (debug) printclean(); sblock.fs_logbno = fslogbno; sblock.fs_clean = fsclean; sblock.fs_state = FSOKAY - (long)sblock.fs_time; sblock.fs_reclaim = fsreclaim; sblock.fs_flags = fsflags; /* * if superblock can't be written, return */ if (fswritefd < 0) return; /* * read private copy of superblock, update clean flag, and write it */ bno = sblk.b_bno; size = sblk.b_size; sblkoff = ldbtob(bno); if ((cleanbuf.b_un.b_buf = malloc(size)) == NULL) errexit("out of memory"); if (llseek(fsreadfd, sblkoff, 0) == -1) return; if (read(fsreadfd, cleanbuf.b_un.b_buf, (int)size) != size) return; cleanbuf.b_un.b_fs->fs_logbno = sblock.fs_logbno; cleanbuf.b_un.b_fs->fs_clean = sblock.fs_clean; cleanbuf.b_un.b_fs->fs_state = sblock.fs_state; cleanbuf.b_un.b_fs->fs_time = sblock.fs_time; cleanbuf.b_un.b_fs->fs_reclaim = sblock.fs_reclaim; cleanbuf.b_un.b_fs->fs_flags = sblock.fs_flags; if (llseek(fswritefd, sblkoff, 0) == -1) return; if (write(fswritefd, cleanbuf.b_un.b_buf, (int)size) != size) return; /* * 1208040 * If we had to use -b to grab an alternate superblock, then we * likely had to do so because of unacceptable differences between * the main and alternate superblocks. SO, we had better update * the alternate superblock as well, or we'll just fail again * the next time we attempt to run fsck! */ if (bflag) { extern struct bufarea asblk; if (llseek(fswritefd, ldbtob(asblk.b_bno), 0) == -1) return; if (write(fswritefd, cleanbuf.b_un.b_buf, (int)size) != size) return; } if (islog && !islogok) (void) ioctl(fswritefd, _FIOLOGRESET, NULL); if (errorlocked) { if (!do_errorlock(LOCKFS_ULOCK)) pwarn( "updateclean(changed): unlock(LOCKFS_ULOCK) failed\n"); } } /* * print out clean info */ printclean() { char *s; if (FSOKAY != (sblock.fs_state + sblock.fs_time) && !errorlocked) s = "unknown"; else switch (sblock.fs_clean) { case FSACTIVE: s = "active"; break; case FSCLEAN: s = "clean"; break; case FSSTABLE: s = "stable"; break; case FSLOG: s = "logging"; break; case FSBAD: s = "is bad"; break; case FSFIX: s = "being fixed"; break; default: s = "unknown"; } if (preen) pwarn("is %s.\n", s); else printf("** %s is %s.\n", devname, s); } /* see if all numbers */ numbers(yp) char *yp; { if (yp == NULL) return (0); while ('0' <= *yp && *yp <= '9') yp++; if (*yp) return (0); return (1); } is_errorlocked(char *fs) { struct stat64 statb; char *mountp; static char *getmountp(char *); char *unrawname(char *); mountp = NULL; if (!fs) return (0); if (stat64(fs, &statb) < 0) return (0); if (S_ISDIR(statb.st_mode)) { mountp = fs; } else if (S_ISBLK(statb.st_mode) || S_ISCHR(statb.st_mode)) { mountp = getmountp(S_ISCHR(statb.st_mode)? unrawname(fs): fs); if (!mountp) { return (0); } } else { return (0); } if (elock_combuf == NULL) { elock_combuf = (char *)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char)); } else { elock_combuf = (char *)realloc(elock_combuf, LOCKFS_MAXCOMMENTLEN); } if (elock_combuf == NULL) return (0); bzero((caddr_t)elock_combuf, LOCKFS_MAXCOMMENTLEN); elock_mountp = strdup(mountp); if (mountfd < 0) { if ((mountfd = open64(mountp, O_RDONLY)) == -1) return (0); } if (!lfp) { lfp = (struct lockfs *)malloc(sizeof (struct lockfs)); if (!lfp) return (0); bzero((caddr_t)lfp, sizeof (struct lockfs)); } lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN; lfp->lf_comment = elock_combuf; if (ioctl(mountfd, _FIOLFSS, lfp) == -1) return (0); return (LOCKFS_IS_ELOCK(lfp)); } static char * getmountp(char *dev) { FILE *vfstab; struct vfstab vfsbuf; char *mountp; int rc; mountp = NULL; if ((vfstab = fopen(VFSTAB, "r")) == NULL) { return (NULL); } if ((rc = getvfsspec(vfstab, &vfsbuf, dev)) == 0) { if (!(mountp = malloc(MAXPATHLEN)) || vfsbuf.vfs_mountp == NULL) return (NULL); strcpy(mountp, vfsbuf.vfs_mountp); } else if (rc == -1) { return (NULL); } fclose(vfstab); return (mountp); } do_errorlock(int lock_type) { char *buf; time_t now; struct tm *local; int rc = 0; if (!elock_combuf) errexit("do_errorlock(%s, %d): unallocated elock_combuf\n", elock_mountp? elock_mountp: "", lock_type); if (!(buf = (char *)calloc(LOCKFS_MAXCOMMENTLEN, sizeof (char)))) errexit("Couldn't alloc memory for temp. lock status buffer\n"); if (!lfp) { errexit("do_errorlock(%s, %d): lockfs status unallocated\n", elock_mountp, lock_type); } bcopy(elock_combuf, buf, LOCKFS_MAXCOMMENTLEN-1); switch (lock_type) { case LOCKFS_ELOCK: if (time(&now) != (time_t)-1) { if ((local = localtime(&now)) != NULL) sprintf(buf, "%s [pid:%d fsck start:%02d/%02d/%02d %02d:%02d:%02d", elock_combuf, pid, local->tm_mon+1, local->tm_mday, (local->tm_year % 100), local->tm_hour, local->tm_min, local->tm_sec); else sprintf(buf, "%s [fsck pid %d", elock_combuf, pid); } else { sprintf(buf, "%s [fsck pid %d", elock_combuf, pid); } break; case LOCKFS_ULOCK: if (time(&now) != (time_t)-1) { if ((local = localtime(&now)) != NULL) { sprintf(buf, "%s, done:%02d/%02d/%02d %02d:%02d:%02d]", elock_combuf, local->tm_mon+1, local->tm_mday, (local->tm_year % 100), local->tm_hour, local->tm_min, local->tm_sec); } else { sprintf(buf, "%s]", elock_combuf); } } else { sprintf(buf, "%s]", elock_combuf); } if ((rc = ioctl(mountfd, _FIOLFSS, lfp)) == -1) { goto out; } break; default: break; } bcopy(buf, elock_combuf, LOCKFS_MAXCOMMENTLEN-1); lfp->lf_lock = lock_type; lfp->lf_comlen = LOCKFS_MAXCOMMENTLEN; lfp->lf_comment = elock_combuf; lfp->lf_flags = 0; errno = 0; if ((rc = ioctl(mountfd, _FIOLFS, lfp)) == -1) { if (errno == EINVAL) { pwarn("Another fsck active?\n"); iscorrupt = 0; /* don't go away mad, just go away */ } else { pwarn( "do_errorlock(lock_type:%d, %s) failed: errno:%d\n", lock_type, elock_combuf, errno); } } out: if (buf) free(buf); return (rc != -1); } /* * Shadow inode support. To `register' a shadow with a client is to note * that an inode (the `client') refers to the shadow. See fsck.h for more * on how the shadowclientinfo and shadowclients structures are used. */ static struct shadowclients * newshadowclient(struct shadowclients *prev) { struct shadowclients *rc; rc = (struct shadowclients *)malloc(sizeof (*rc)); if (rc == NULL) errexit("newshadowclient: cannot malloc (1)"); rc->next = prev; rc->nclients = 0; rc->client = (ino_t *) malloc(sizeof (ino_t) * maxshadowclients); if (rc->client == NULL) errexit("newshadowclient: cannot malloc (2)"); return (rc); } void registershadowclient(ino_t shadow, ino_t client, struct shadowclientinfo **info) { struct shadowclientinfo *sci; struct shadowclients *scc; for (sci = *info; sci; sci = sci->next) if (sci->shadow == shadow) break; if (sci == NULL) { sci = (struct shadowclientinfo *)malloc(sizeof (*sci)); if (sci == NULL) errexit("registershadowclient: cannot malloc"); sci->next = *info; *info = sci; sci->shadow = shadow; sci->totalClients = 0; sci->clients = newshadowclient(NULL); } sci->totalClients++; scc = sci->clients; if (scc->nclients >= maxshadowclients) { scc = newshadowclient(sci->clients); sci->clients = scc; } scc->client[scc->nclients++] = client; } /* * Allocate more buffer as need arises but allocate one at a time. * This is done to make sure that fsck does not exit with error if it * needs more buffer to complete it's task. */ static struct bufarea * alloc_bufarea() { struct bufarea *bp; char *bufp; bp = (struct bufarea *)malloc(sizeof (struct bufarea)); bufp = malloc((unsigned int)sblock.fs_bsize); if (bp == NULL || bufp == NULL) { if (bp) free((char *)bp); if (bufp) free(bufp); return (NULL); } bp->b_un.b_buf = bufp; bp->b_prev = &bufhead; bp->b_next = bufhead.b_next; bufhead.b_next->b_prev = bp; bufhead.b_next = bp; initbarea(bp); bufhead.b_size++; return (bp); }