utilities.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/mntent.h>
#include <sys/filio.h>
#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 <sys/fs/ufs_fs.h>
#include <sys/vnode.h>
#include <sys/fs/ufs_inode.h>
#include <sys/fs/ufs_acl.h>
#define _KERNEL
#include <sys/fs/ufs_fsdir.h>
#undef _KERNEL
#include <sys/mnttab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/signal.h>
#include <string.h>
#include <ctype.h>
#include "fsck.h"
#include <sys/vfstab.h>
#include <sys/lockfs.h>
#include <errno.h>
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: "<null>", 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);
}