incorporate fs.h -r1.10, and param.h -r1.8 reorganization
[unix-history] / usr / src / sbin / fsck / main.c
static char *sccsid = "@(#)main.c 1.16 (Berkeley) %G%";
#include <stdio.h>
#include <ctype.h>
#include "../h/param.h"
#include "../h/fs.h"
#include "../h/dir.h"
#include "../h/inode.h"
#include "../h/stat.h"
#include "../h/ostat.h"
#include <fstab.h>
typedef int (*SIG_TYP)();
#define NDIRECT(fs) ((fs)->fs_bsize / sizeof(struct direct))
#define MAXNDIRECT (MAXBSIZE / sizeof(struct direct))
#define MAXNINDIR (MAXBSIZE / sizeof (daddr_t))
#define MAXINOPB (MAXBSIZE / sizeof (struct dinode))
#define SPERB (MAXBSIZE / sizeof(short))
#define MAXDUP 10 /* limit on dup blks (per inode) */
#define MAXBAD 10 /* limit on bad blks (per inode) */
#define USTATE 0 /* inode not allocated */
#define FSTATE 01 /* inode is file */
#define DSTATE 02 /* inode is directory */
#define CLEAR 03 /* inode is to be cleared */
typedef struct dinode DINODE;
typedef struct direct DIRECT;
#define ALLOC ((dp->di_mode & IFMT) != 0)
#define DIR ((dp->di_mode & IFMT) == IFDIR)
#define REG ((dp->di_mode & IFMT) == IFREG)
#define BLK ((dp->di_mode & IFMT) == IFBLK)
#define CHR ((dp->di_mode & IFMT) == IFCHR)
#define MPC ((dp->di_mode & IFMT) == IFMPC)
#define MPB ((dp->di_mode & IFMT) == IFMPB)
#define SPECIAL (BLK || CHR || MPC || MPB)
ino_t startinum; /* blk num of first in raw area */
struct bufarea {
struct bufarea *b_next; /* must be first */
daddr_t b_bno;
int b_size;
union {
char b_buf[MAXBSIZE]; /* buffer space */
short b_lnks[SPERB]; /* link counts */
daddr_t b_indir[MAXNINDIR]; /* indirect block */
struct fs b_fs; /* super block */
struct cg b_cg; /* cylinder group */
struct dinode b_dinode[MAXINOPB]; /* inode block */
DIRECT b_dir[MAXNDIRECT]; /* directory */
} b_un;
char b_dirty;
};
typedef struct bufarea BUFAREA;
BUFAREA inoblk; /* inode blocks */
BUFAREA fileblk; /* other blks in filesys */
BUFAREA sblk; /* file system superblock */
BUFAREA cgblk;
#define initbarea(x) (x)->b_dirty = 0;(x)->b_bno = (daddr_t)-1
#define dirty(x) (x)->b_dirty = 1
#define inodirty() inoblk.b_dirty = 1
#define sbdirty() sblk.b_dirty = 1
#define cgdirty() cgblk.b_dirty = 1
#define dirblk fileblk.b_un
#define sblock sblk.b_un.b_fs
#define cgrp cgblk.b_un.b_cg
struct filecntl {
int rfdes;
int wfdes;
int mod;
} dfile; /* file descriptors for filesys */
#define DUPTBLSIZE 100 /* num of dup blocks to remember */
daddr_t duplist[DUPTBLSIZE]; /* dup block table */
daddr_t *enddup; /* next entry in dup table */
daddr_t *muldup; /* multiple dups part of table */
#define MAXLNCNT 20 /* num zero link cnts to remember */
ino_t badlncnt[MAXLNCNT]; /* table of inos with zero link cnts */
ino_t *badlnp; /* next entry in table */
char rawflg;
char nflag; /* assume a no response */
char yflag; /* assume a yes response */
int bflag; /* location of alternate super block */
char preen; /* just fix normal inconsistencies */
char rplyflag; /* any questions asked? */
char hotroot; /* checking root device */
char fixcg; /* corrupted free list bit maps */
char *blkmap; /* ptr to primary blk allocation map */
char *freemap; /* ptr to secondary blk allocation map */
char *statemap; /* ptr to inode state table */
short *lncntp; /* ptr to link count table */
char *pathp; /* pointer to pathname position */
char *thisname; /* ptr to current pathname component */
char *srchname; /* name being searched for in dir */
char pathname[200];
char *lfname = "lost+found";
ino_t inum; /* inode we are currently working on */
ino_t imax; /* number of inodes */
ino_t parentdir; /* i number of parent directory */
ino_t lastino; /* hiwater mark of inodes */
ino_t lfdir; /* lost & found directory */
ino_t orphan; /* orphaned inode */
off_t filsize; /* num blks seen in file */
off_t maxblk; /* largest logical blk in file */
off_t bmapsz; /* num chars in blkmap */
daddr_t n_ffree; /* number of small free blocks */
daddr_t n_bfree; /* number of large free blocks */
daddr_t n_blks; /* number of blocks used */
daddr_t n_files; /* number of files seen */
daddr_t n_index;
daddr_t n_bad;
daddr_t fmax; /* number of blocks in the volume */
daddr_t badblk;
daddr_t dupblk;
int inosumbad;
int offsumbad;
int frsumbad;
#define zapino(x) (*(x) = zino)
struct dinode zino;
#define setlncnt(x) (lncntp[inum] = x)
#define getlncnt() (lncntp[inum])
#define declncnt() (--lncntp[inum])
#define setbmap(x) setbit(blkmap, x)
#define getbmap(x) isset(blkmap, x)
#define clrbmap(x) clrbit(blkmap, x)
#define setfmap(x) setbit(freemap, x)
#define getfmap(x) isset(freemap, x)
#define clrfmap(x) clrbit(freemap, x)
#define setstate(x) (statemap[inum] = x)
#define getstate() statemap[inum]
#define DATA 1
#define ADDR 0
#define ALTERD 010
#define KEEPON 04
#define SKIP 02
#define STOP 01
int (*signal())();
long lseek();
time_t time();
DINODE *ginode();
BUFAREA *getblk();
int dirscan();
int findino();
int catch();
int mkentry();
int chgdd();
int pass1(), pass1b(), pass2(), pass4(), pass5();
int (*pfunc)();
char *rawname(), *rindex(), *unrawname();
extern int inside[], around[];
extern unsigned char *fragtbl[];
char *devname;
main(argc, argv)
int argc;
char *argv[];
{
struct fstab *fsp;
int pid, passno, anygtr, sumstatus;
sync();
while (--argc > 0 && **++argv == '-') {
switch (*++*argv) {
case 'p':
preen++;
break;
case 'b':
bflag = atoi(argv[0]+1);
printf("Alternate super block location: %d\n", bflag);
break;
case 'n': /* default no answer flag */
case 'N':
nflag++;
yflag = 0;
break;
case 'y': /* default yes answer flag */
case 'Y':
yflag++;
nflag = 0;
break;
default:
errexit("%c option?\n", **argv);
}
}
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, catch);
if (argc) {
while (argc-- > 0) {
hotroot = 0;
check(*argv++);
}
exit(0);
}
sumstatus = 0;
passno = 1;
do {
anygtr = 0;
if (setfsent() == 0)
errexit("Can't open checklist file: %s\n", FSTAB);
while ((fsp = getfsent()) != 0) {
if (strcmp(fsp->fs_type, FSTAB_RW) &&
strcmp(fsp->fs_type, FSTAB_RO))
continue;
if (preen == 0 ||
passno == 1 && fsp->fs_passno == passno) {
if (blockcheck(fsp->fs_spec) == 0 && preen)
exit(8);
} else if (fsp->fs_passno > passno)
anygtr = 1;
else if (fsp->fs_passno == passno) {
pid = fork();
if (pid < 0) {
perror("fork");
exit(8);
}
if (pid == 0)
if (blockcheck(fsp->fs_spec)==0)
exit(8);
else
exit(0);
}
}
if (preen) {
int status;
while (wait(&status) != -1)
sumstatus |= status;
}
passno++;
} while (anygtr);
if (sumstatus)
exit(8);
endfsent();
exit(0);
}
blockcheck(name)
char *name;
{
struct ostat stslash, stblock, stchar;
char *raw;
int looped = 0;
hotroot = 0;
if (stat("/", &stslash) < 0){
error("Can't stat root\n");
return (0);
}
retry:
if (stat(name, &stblock) < 0){
error("Can't stat %s\n", name);
return (0);
}
if (stblock.st_mode & S_IFBLK) {
raw = rawname(name);
if (stat(raw, &stchar) < 0){
error("Can't stat %s\n", raw);
return (0);
}
if (stchar.st_mode & S_IFCHR) {
if (stslash.st_dev == stblock.st_rdev) {
hotroot++;
raw = unrawname(name);
}
check(raw);
return (1);
} else {
error("%s is not a character device\n", raw);
return (0);
}
} else if (stblock.st_mode & S_IFCHR) {
if (looped) {
error("Can't make sense out of name %s\n", name);
return (0);
}
name = unrawname(name);
looped++;
goto retry;
}
error("Can't make sense out of name %s\n", name);
return (0);
}
char *
unrawname(cp)
char *cp;
{
char *dp = rindex(cp, '/');
struct ostat stb;
if (dp == 0)
return (cp);
if (stat(cp, &stb) < 0)
return (cp);
if ((stb.st_mode&S_IFMT) != S_IFCHR)
return (cp);
if (*(dp+1) != 'r')
return (cp);
strcpy(dp+1, dp+2);
return (cp);
}
char *
rawname(cp)
char *cp;
{
static char rawbuf[32];
char *dp = rindex(cp, '/');
if (dp == 0)
return (0);
*dp = 0;
strcpy(rawbuf, cp);
*dp = '/';
strcat(rawbuf, "/r");
strcat(rawbuf, dp+1);
return (rawbuf);
}
check(dev)
char *dev;
{
register DINODE *dp;
register ino_t *blp;
register int i, n;
ino_t savino;
int b, c;
daddr_t d, s;
devname = dev;
if (setup(dev) == 0) {
if (preen)
pfatal("CAN'T CHECK DEVICE.");
return;
}
/* 1 */
if (preen==0) {
if (hotroot)
printf("** Root file system\n");
printf("** Phase 1 - Check Blocks and Sizes\n");
}
pfunc = pass1;
inum = 0;
n_blks += howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag;
for (c = 0; c < sblock.fs_ncg; c++) {
if (getblk(&cgblk, cgtod(c, &sblock), sblock.fs_cgsize) == 0)
continue;
n = 0;
for (i = 0; i < sblock.fs_ipg; i++, inum++) {
dp = ginode();
if (dp == NULL)
continue;
if (ALLOC) {
if (!isset(cgrp.cg_iused, i)) {
/*
printf("%d bad, not used\n", inum);
*/
inosumbad++;
n++;
}
lastino = inum;
if (ftypeok(dp) == 0) {
pfatal("UNKNOWN FILE TYPE I=%u", inum);
if (reply("CLEAR") == 1) {
zapino(dp);
inodirty();
inosumbad++;
}
continue;
}
n_files++;
if (setlncnt(dp->di_nlink) <= 0) {
if (badlnp < &badlncnt[MAXLNCNT])
*badlnp++ = inum;
else {
pfatal("LINK COUNT TABLE OVERFLOW");
if (reply("CONTINUE") == 0)
errexit("");
}
}
setstate(DIR ? DSTATE : FSTATE);
badblk = dupblk = 0; filsize = 0; maxblk = 0;
ckinode(dp, ADDR);
if (DIR && dp->di_size % sizeof(DIRECT)) {
pwarn("DIRECTORY MISALIGNED I=%u\n",
inum);
if (preen == 0)
printf("\n");
}
} else {
n++;
if (isset(cgrp.cg_iused, i)) {
/*
printf("%d bad, marked used\n", inum);
*/
inosumbad++;
n--;
}
if (dp->di_mode != 0) {
pfatal("PARTIALLY ALLOCATED INODE I=%u", inum);
if (reply("CLEAR") == 1) {
zapino(dp);
inodirty();
inosumbad++;
}
}
}
}
if (n != cgrp.cg_cs.cs_nifree) {
printf("cg[%d].cg_cs.cs_nifree is %d not %d\n",
c, cgrp.cg_cs.cs_nifree, n);
inosumbad++;
}
}
/* 1b */
if (enddup != &duplist[0]) {
if (preen)
pfatal("INTERNAL ERROR: dups with -p");
printf("** Phase 1b - Rescan For More DUPS\n");
pfunc = pass1b;
inum = 0;
for (c = 0; c < sblock.fs_ncg; c++) {
for (i = 0; i < sblock.fs_ipg; i++, inum++) {
dp = ginode();
if (dp == NULL)
continue;
if (getstate() != USTATE &&
(ckinode(dp, ADDR) & STOP))
goto out1b;
}
}
}
out1b:
flush(&dfile, &inoblk);
/* 2 */
if (preen == 0)
printf("** Phase 2 - Check Pathnames\n");
inum = ROOTINO;
thisname = pathp = pathname;
pfunc = pass2;
switch (getstate()) {
case USTATE:
errexit("ROOT INODE UNALLOCATED. TERMINATING.\n");
case FSTATE:
pfatal("ROOT INODE NOT DIRECTORY");
if (reply("FIX") == 0 || (dp = ginode()) == NULL)
errexit("");
dp->di_mode &= ~IFMT;
dp->di_mode |= IFDIR;
inodirty();
inosumbad++;
setstate(DSTATE);
/* fall into ... */
case DSTATE:
descend();
break;
case CLEAR:
pfatal("DUPS/BAD IN ROOT INODE");
printf("\n");
if (reply("CONTINUE") == 0)
errexit("");
setstate(DSTATE);
descend();
}
/* 3 */
if (preen == 0)
printf("** Phase 3 - Check Connectivity\n");
for (inum = ROOTINO; inum <= lastino; inum++) {
if (getstate() == DSTATE) {
pfunc = findino;
srchname = "..";
savino = inum;
do {
orphan = inum;
if ((dp = ginode()) == NULL)
break;
filsize = dp->di_size;
parentdir = 0;
ckinode(dp, DATA);
if ((inum = parentdir) == 0)
break;
} while (getstate() == DSTATE);
inum = orphan;
if (linkup() == 1) {
thisname = pathp = pathname;
*pathp++ = '?';
pfunc = pass2;
descend();
}
inum = savino;
}
}
/* 4 */
if (preen == 0)
printf("** Phase 4 - Check Reference Counts\n");
pfunc = pass4;
for (inum = ROOTINO; inum <= lastino; inum++) {
switch (getstate()) {
case FSTATE:
if (n = getlncnt())
adjust((short)n);
else {
for (blp = badlncnt;blp < badlnp; blp++)
if (*blp == inum) {
clri("UNREF", 1);
break;
}
}
break;
case DSTATE:
clri("UNREF", 1);
break;
case CLEAR:
clri("BAD/DUP", 1);
break;
}
}
if (imax - ROOTINO - n_files != sblock.fs_cstotal.cs_nifree) {
pwarn("FREE INODE COUNT WRONG IN SUPERBLK");
if (preen)
printf(" (FIXED)\n");
if (preen || reply("FIX") == 1) {
sblock.fs_cstotal.cs_nifree = imax - n_files;
sbdirty();
}
}
flush(&dfile, &fileblk);
/* 5 */
if (preen == 0)
printf("** Phase 5 - Check Cyl groups\n");
copy(blkmap, freemap, (unsigned)bmapsz);
dupblk = 0;
n_index = sblock.fs_ncg * (cgdmin(0, &sblock) - cgtod(0, &sblock));
for (c = 0; c < sblock.fs_ncg; c++) {
daddr_t cbase = cgbase(c,&sblock);
short bo[MAXCPG][NRPOS];
long frsum[MAXFRAG];
int blk;
for (n = 0; n < sblock.fs_cpg; n++)
for (i = 0; i < NRPOS; i++)
bo[n][i] = 0;
for (i = 0; i < sblock.fs_frag; i++) {
frsum[i] = 0;
}
/*
* need to account for the spare boot and super blocks
* which appear (inaccurately) bad
*/
n_bad += cgtod(c, &sblock) - cbase;
if (getblk(&cgblk, cgtod(c, &sblock), sblock.fs_cgsize) == 0)
continue;
for (b = 0; b < sblock.fs_fpg; b += sblock.fs_frag) {
if (isblock(&sblock, cgrp.cg_free, b/sblock.fs_frag)) {
if (pass5(cbase+b, sblock.fs_frag) == STOP)
goto out5;
/* this is clumsy ... */
n_ffree -= sblock.fs_frag;
n_bfree++;
s = b * NSPF(&sblock);
bo[s/sblock.fs_spc]
[s%sblock.fs_nsect*NRPOS/sblock.fs_nsect]++;
} else {
for (d = 0; d < sblock.fs_frag; d++)
if (isset(cgrp.cg_free, b+d))
if (pass5(cbase+b+d,1) == STOP)
goto out5;
blk = ((cgrp.cg_free[b / NBBY] >> (b % NBBY)) &
(0xff >> (NBBY - sblock.fs_frag)));
if (blk != 0)
fragacct(&sblock, blk, frsum, 1);
}
}
for (i = 0; i < sblock.fs_frag; i++) {
if (cgrp.cg_frsum[i] != frsum[i]) {
printf("cg[%d].cg_frsum[%d] have %d calc %d\n",
c, i, cgrp.cg_frsum[i], frsum[i]);
frsumbad++;
}
}
for (n = 0; n < sblock.fs_cpg; n++)
for (i = 0; i < NRPOS; i++)
if (bo[n][i] != cgrp.cg_b[n][i]) {
printf("cg[%d].cg_b[%d][%d] have %d calc %d\n",
c, n, i, cgrp.cg_b[n][i], bo[n][i]);
offsumbad++;
}
}
out5:
if (dupblk)
pwarn("%d DUP BLKS IN BIT MAPS\n", dupblk);
if (fixcg == 0) {
if ((b = n_blks+n_ffree+sblock.fs_frag*n_bfree+n_index+n_bad) != fmax) {
pwarn("%ld BLK(S) MISSING\n", fmax - b);
fixcg = 1;
} else if (inosumbad + offsumbad + frsumbad) {
pwarn("SUMMARY INFORMATION %s%s%sBAD\n",
inosumbad ? "(INODE FREE) " : "",
offsumbad ? "(BLOCK OFFSETS) " : "",
frsumbad ? "(FRAG SUMMARIES) " : "");
fixcg = 1;
} else if (n_ffree != sblock.fs_cstotal.cs_nffree ||
n_bfree != sblock.fs_cstotal.cs_nbfree) {
pwarn("FREE BLK COUNT(S) WRONG IN SUPERBLK");
if (preen)
printf(" (FIXED)\n");
if (preen || reply("FIX") == 1) {
sblock.fs_cstotal.cs_nffree = n_ffree;
sblock.fs_cstotal.cs_nbfree = n_bfree;
sbdirty();
}
}
}
if (fixcg) {
pwarn("BAD CYLINDER GROUPS");
if (preen)
printf(" (SALVAGED)\n");
else if (reply("SALVAGE") == 0)
fixcg = 0;
}
if (fixcg) {
if (preen == 0)
printf("** Phase 6 - Salvage Cylinder Groups\n");
makecg();
n_ffree = sblock.fs_cstotal.cs_nffree;
n_bfree = sblock.fs_cstotal.cs_nbfree;
}
pwarn("%d files, %d used, %d free (%d frags, %d blocks)\n",
n_files, n_blks - howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag,
n_ffree + sblock.fs_frag * n_bfree, n_ffree, n_bfree);
if (dfile.mod) {
time(&sblock.fs_time);
sbdirty();
}
ckfini();
sync();
if (dfile.mod && hotroot) {
printf("\n***** BOOT UNIX (NO SYNC!) *****\n");
exit(4);
}
if (dfile.mod && preen == 0)
printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
free(blkmap);
free(freemap);
free(statemap);
free(lncntp);
}
/* VARARGS1 */
error(s1, s2, s3, s4)
char *s1;
{
printf(s1, s2, s3, s4);
}
/* VARARGS1 */
errexit(s1, s2, s3, s4)
char *s1;
{
error(s1, s2, s3, s4);
exit(8);
}
/*
* An inconsistency occured which shouldn't during normal operations.
* Die if preening, otw just printf.
*/
/* VARARGS1 */
pfatal(s, a1, a2, a3)
char *s;
{
if (preen) {
printf("%s: ", devname);
printf(s, a1, a2, a3);
printf("\n");
preendie();
}
printf(s, a1, a2, a3);
}
preendie()
{
printf("%s: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.\n", devname);
exit(8);
}
/*
* Pwarn is like printf when not preening,
* or a warning (preceded by filename) when preening.
*/
/* VARARGS1 */
pwarn(s, a1, a2, a3, a4, a5)
char *s;
{
if (preen)
printf("%s: ", devname);
printf(s, a1, a2, a3, a4, a5);
}
ckinode(dp, flg)
DINODE *dp;
register flg;
{
register daddr_t *ap;
register ret;
int (*func)(), n, ndb, size;
if (SPECIAL)
return (KEEPON);
func = (flg == ADDR) ? pfunc : dirscan;
ndb = howmany(dp->di_size, sblock.fs_bsize);
for (ap = &dp->di_db[0]; ap < &dp->di_db[NDADDR]; ap++) {
if (--ndb == 0 && (dp->di_size % sblock.fs_bsize))
size = howmany(dp->di_size % sblock.fs_bsize, sblock.fs_fsize);
else
size = sblock.fs_frag;
if (*ap && (ret = (*func)(*ap, size)) & STOP)
return (ret);
}
for (ap = &dp->di_ib[0], n = 1; n <= 2; ap++, n++) {
if (*ap && (ret = iblock(*ap, n, flg, dp->di_size - sblock.fs_bsize * NDADDR)) & STOP)
return (ret);
}
return (KEEPON);
}
iblock(blk, ilevel, flg, isize)
daddr_t blk;
register ilevel;
int isize;
{
register daddr_t *ap;
register daddr_t *aplim;
register int i, n;
int (*func)(), nif;
BUFAREA ib;
if (flg == ADDR) {
func = pfunc;
if (((n = (*func)(blk, sblock.fs_frag)) & KEEPON) == 0)
return (n);
} else
func = dirscan;
if (outrange(blk)) /* protect thyself */
return (SKIP);
initbarea(&ib);
if (getblk(&ib, blk, sblock.fs_bsize) == NULL)
return (SKIP);
ilevel--;
if (ilevel == 0) {
nif = isize / sblock.fs_bsize + 1;
} else /* ilevel == 1 */ {
nif = isize / (sblock.fs_bsize * NINDIR(&sblock)) + 1;
}
if (nif > NINDIR(&sblock))
nif = NINDIR(&sblock);
aplim = & ib.b_un.b_indir[nif];
for (ap = ib.b_un.b_indir, i = 1; ap < aplim; ap++, i++)
if (*ap) {
if (ilevel > 0)
n = iblock(*ap, ilevel, flg, isize - i * NINDIR(&sblock) * sblock.fs_bsize);
else
n = (*func)(*ap, sblock.fs_frag);
if (n & STOP)
return (n);
}
return (KEEPON);
}
pass1(blk, size)
daddr_t blk;
int size;
{
register daddr_t *dlp;
int res = KEEPON;
for (; size > 0; blk++, size--) {
if (outrange(blk)) {
blkerr("BAD", blk);
if (++badblk >= MAXBAD) {
printf("EXCESSIVE BAD BLKS I=%u", inum);
if (reply("CONTINUE") == 0)
errexit("");
return (STOP);
}
res = SKIP;
} else if (getbmap(blk)) {
blkerr("DUP", blk);
if (++dupblk >= MAXDUP) {
printf("EXCESSIVE DUP BLKS I=%u", inum);
if (reply("CONTINUE") == 0)
errexit("");
return (STOP);
}
if (enddup >= &duplist[DUPTBLSIZE]) {
printf("DUP TABLE OVERFLOW.");
if (reply("CONTINUE") == 0)
errexit("");
return (STOP);
}
for (dlp = duplist; dlp < muldup; dlp++)
if (*dlp == blk) {
*enddup++ = blk;
break;
}
if (dlp >= muldup) {
*enddup++ = *muldup;
*muldup++ = blk;
}
} else {
n_blks++;
setbmap(blk);
}
filsize++;
}
return (res);
}
pass1b(blk, size)
daddr_t blk;
int size;
{
register daddr_t *dlp;
int res = KEEPON;
for (; size > 0; blk++, size--) {
if (outrange(blk))
res = SKIP;
for (dlp = duplist; dlp < muldup; dlp++)
if (*dlp == blk) {
blkerr("DUP", blk);
*dlp = *--muldup;
*muldup = blk;
if (muldup == duplist)
return (STOP);
}
}
return (res);
}
pass2(dirp)
register DIRECT *dirp;
{
register char *p;
register n;
DINODE *dp;
if ((inum = dirp->d_ino) == 0)
return (KEEPON);
thisname = pathp;
for (p = dirp->d_name; p < &dirp->d_name[DIRSIZ]; )
if ((*pathp++ = *p++) == 0) {
--pathp;
break;
}
*pathp = 0;
n = 0;
if (inum > imax || inum <= 0)
n = direrr("I OUT OF RANGE");
else {
again:
switch (getstate()) {
case USTATE:
n = direrr("UNALLOCATED");
break;
case CLEAR:
if ((n = direrr("DUP/BAD")) == 1)
break;
if ((dp = ginode()) == NULL)
break;
setstate(DIR ? DSTATE : FSTATE);
goto again;
case FSTATE:
declncnt();
break;
case DSTATE:
declncnt();
descend();
break;
}
}
pathp = thisname;
if (n == 0)
return (KEEPON);
dirp->d_ino = 0;
return (KEEPON|ALTERD);
}
pass4(blk, size)
daddr_t blk;
{
register daddr_t *dlp;
int res = KEEPON;
for (; size > 0; blk++, size--) {
if (outrange(blk))
res = SKIP;
else if (getbmap(blk)) {
for (dlp = duplist; dlp < enddup; dlp++)
if (*dlp == blk) {
*dlp = *--enddup;
return (KEEPON);
}
clrbmap(blk);
n_blks--;
}
}
return (res);
}
pass5(blk, size)
daddr_t blk;
int size;
{
int res = KEEPON;
for (; size > 0; blk++, size--) {
if (outrange(blk)) {
fixcg = 1;
if (preen)
pfatal("BAD BLOCKS IN BIT MAPS.");
if (++badblk >= MAXBAD) {
printf("EXCESSIVE BAD BLKS IN BIT MAPS.");
if (reply("CONTINUE") == 0)
errexit("");
return (STOP);
}
} else if (getfmap(blk)) {
fixcg = 1;
if (++dupblk >= DUPTBLSIZE) {
printf("EXCESSIVE DUP BLKS IN BIT MAPS.");
if (reply("CONTINUE") == 0)
errexit("");
return (STOP);
}
} else {
n_ffree++;
setfmap(blk);
}
}
return (res);
}
outrange(blk)
daddr_t blk;
{
register int c;
c = dtog(blk, &sblock);
if (blk >= fmax || blk < cgdmin(c, &sblock)) {
return (1);
}
return (0);
}
blkerr(s, blk)
daddr_t blk;
char *s;
{
pfatal("%ld %s I=%u", blk, s, inum);
printf("\n");
setstate(CLEAR); /* mark for possible clearing */
}
descend()
{
register DINODE *dp;
register char *savname;
off_t savsize;
setstate(FSTATE);
if ((dp = ginode()) == NULL)
return;
savname = thisname;
*pathp++ = '/';
savsize = filsize;
filsize = dp->di_size;
ckinode(dp, DATA);
thisname = savname;
*--pathp = 0;
filsize = savsize;
}
dirscan(blk, nf)
daddr_t blk;
int nf;
{
register DIRECT *dirp;
register DIRECT *edirp;
register char *p1, *p2;
register n;
DIRECT direntry;
if (outrange(blk)) {
filsize -= sblock.fs_bsize;
return (SKIP);
}
edirp = &dirblk.b_dir[NDIRECT(&sblock)*nf/sblock.fs_frag];
for (dirp = dirblk.b_dir; dirp < edirp &&
filsize > 0; dirp++, filsize -= sizeof(DIRECT)) {
if (getblk(&fileblk, blk, nf * sblock.fs_fsize) == NULL) {
filsize -= (&dirblk.b_dir[NDIRECT(&sblock)]-dirp)*sizeof(DIRECT);
return (SKIP);
}
p1 = &dirp->d_name[DIRSIZ];
p2 = &direntry.d_name[DIRSIZ];
while (p1 > (char *)dirp)
*--p2 = *--p1;
if ((n = (*pfunc)(&direntry)) & ALTERD) {
if (getblk(&fileblk, blk, nf * sblock.fs_fsize) != NULL) {
p1 = &dirp->d_name[DIRSIZ];
p2 = &direntry.d_name[DIRSIZ];
while (p1 > (char *)dirp)
*--p1 = *--p2;
dirty(&fileblk);
sbdirty();
} else
n &= ~ALTERD;
}
if (n & STOP)
return (n);
}
return (filsize > 0 ? KEEPON : STOP);
}
direrr(s)
char *s;
{
register DINODE *dp;
pwarn("%s ", s);
pinode();
printf("\n");
if ((dp = ginode()) != NULL && ftypeok(dp))
pfatal("%s=%s", DIR?"DIR":"FILE", pathname);
else
pfatal("NAME=%s", pathname);
return (reply("REMOVE"));
}
adjust(lcnt)
register short lcnt;
{
register DINODE *dp;
if ((dp = ginode()) == NULL)
return;
if (dp->di_nlink == lcnt) {
if (linkup() == 0)
clri("UNREF", 0);
}
else {
pwarn("LINK COUNT %s",
(lfdir==inum)?lfname:(DIR?"DIR":"FILE"));
pinode();
printf(" COUNT %d SHOULD BE %d",
dp->di_nlink, dp->di_nlink-lcnt);
if (preen) {
if (lcnt < 0) {
printf("\n");
preendie();
}
printf(" (ADJUSTED)\n");
}
if (preen || reply("ADJUST") == 1) {
dp->di_nlink -= lcnt;
inodirty();
}
}
}
clri(s, flg)
char *s;
{
register DINODE *dp;
if ((dp = ginode()) == NULL)
return;
if (flg == 1) {
pwarn("%s %s", s, DIR?"DIR":"FILE");
pinode();
}
if (preen || reply("CLEAR") == 1) {
if (preen)
printf(" (CLEARED)\n");
n_files--;
pfunc = pass4;
ckinode(dp, ADDR);
zapino(dp);
setstate(USTATE);
inodirty();
inosumbad++;
}
}
setup(dev)
char *dev;
{
dev_t rootdev;
struct ostat statb;
int super = bflag ? bflag : SBLOCK;
bflag = 0;
if (stat("/", &statb) < 0)
errexit("Can't stat root\n");
rootdev = statb.st_dev;
if (stat(dev, &statb) < 0) {
error("Can't stat %s\n", dev);
return (0);
}
rawflg = 0;
if ((statb.st_mode & S_IFMT) == S_IFBLK)
;
else if ((statb.st_mode & S_IFMT) == S_IFCHR)
rawflg++;
else {
if (reply("file is not a block or character device; OK") == 0)
return (0);
}
if (rootdev == statb.st_rdev)
hotroot++;
if ((dfile.rfdes = open(dev, 0)) < 0) {
error("Can't open %s\n", dev);
return (0);
}
if (preen == 0)
printf("** %s", dev);
if (nflag || (dfile.wfdes = open(dev, 1)) < 0) {
dfile.wfdes = -1;
if (preen)
pfatal("NO WRITE ACCESS");
printf(" (NO WRITE)");
}
if (preen == 0)
printf("\n");
fixcg = 0; inosumbad = 0; offsumbad = 0; frsumbad = 0;
dfile.mod = 0;
n_files = n_blks = n_ffree = n_bfree = 0;
muldup = enddup = &duplist[0];
badlnp = &badlncnt[0];
lfdir = 0;
rplyflag = 0;
initbarea(&sblk);
initbarea(&fileblk);
initbarea(&inoblk);
initbarea(&cgblk);
if (bread(&dfile, &sblock, super, SBSIZE) == 0)
return (0);
sblk.b_bno = super;
sblk.b_size = SBSIZE;
if (sblock.fs_magic != FS_MAGIC)
{ badsb("MAGIC NUMBER WRONG"); return (0); }
if (sblock.fs_ncg < 1)
{ badsb("NCG OUT OF RANGE"); return (0); }
if (sblock.fs_cpg < 1 || sblock.fs_cpg > MAXCPG)
{ badsb("CPG OUT OF RANGE"); return (0); }
if (sblock.fs_nsect < 1)
{ badsb("NSECT < 1"); return (0); }
if (sblock.fs_ntrak < 1)
{ badsb("NTRAK < 1"); return (0); }
if (sblock.fs_ipg*sblock.fs_ncg > 65535 || sblock.fs_ipg%INOPB(&sblock))
{ badsb("TOO MANY INODES IMPLIED"); return (0); }
if (sblock.fs_ipg/INOPF(&sblock)+IBLOCK(&sblock) >=
sblock.fs_cpg*sblock.fs_nsect*sblock.fs_ntrak/NSPF(&sblock))
{ badsb("IMPLIES MORE INODE THAN DATA BLOCKS"); return (0); }
/* THE FOLLOWING COULD BE CHECKED MORE CLOSELY... */
if ((sblock.fs_ncg + 1) * sblock.fs_cpg < sblock.fs_ncyl ||
(sblock.fs_ncg - 1) * sblock.fs_cpg > sblock.fs_ncyl)
{ badsb("NCYL DOES NOT JIVE WITH NCG*CPG"); return (0); }
if (sblock.fs_fpg != sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock))
{ badsb("FPG DOES NOT JIVE WITH CPG & SPC"); return (0); }
if (sblock.fs_size <=
(sblock.fs_ncg-1)*sblock.fs_fpg+IBLOCK(&sblock)+sblock.fs_ipg/INOPF(&sblock))
{ badsb("SIZE PREPOSTEROUSLY SMALL"); return (0); }
if (sblock.fs_size*NSPF(&sblock) >
(sblock.fs_ncg+2)*sblock.fs_cpg*sblock.fs_spc)
{ badsb("SIZE PREPOSTEROUSLY LARGE"); return (0); }
/* rest we COULD repair... */
if (sblock.fs_sblkno != SBLOCK)
{ badsb("BLKNO CORRUPTED"); return (0); }
if (sblock.fs_spc != sblock.fs_nsect * sblock.fs_ntrak)
{ badsb("SPC DOES NOT JIVE w/NTRAK*NSECT"); return (0); }
if (sblock.fs_cgsize !=
roundup(sizeof(struct cg) + howmany(sblock.fs_fpg, NBBY),
sblock.fs_fsize))
{ badsb("CGSIZE INCORRECT"); return (0); }
if (sblock.fs_cssize != sblock.fs_ncg * sizeof(struct csum))
{ badsb("CSSIZE INCORRECT"); return (0); }
fmax = sblock.fs_size;
imax = sblock.fs_ncg * sblock.fs_ipg;
bmapsz = roundup(howmany(fmax, NBBY), sizeof(short));
blkmap = (char *)calloc(bmapsz, sizeof (char));
freemap = (char *)calloc(bmapsz, sizeof (char));
statemap = (char *)calloc(imax+1, sizeof(char));
lncntp = (short *)calloc(imax+1, sizeof(short));
startinum = imax + 1;
return (1);
badsb:
ckfini();
return (0);
}
badsb(s)
char *s;
{
if (preen)
printf("%s: ", devname);
printf("BAD SUPER BLOCK: %s\n", s);
pwarn("USE -b OPTION TO FSCK TO SPECIFY LOCATION OF AN ALTERNATE\n");
pfatal("SUPER-BLOCK TO SUPPLY NEEDED INFORMATION; SEE fsck(8).\n");
}
DINODE *
ginode()
{
daddr_t iblk;
if (inum < ROOTINO || inum > imax)
return (NULL);
if (inum < startinum || inum >= startinum + INOPB(&sblock)) {
iblk = itod(inum, &sblock);
if (getblk(&inoblk, iblk, sblock.fs_bsize) == NULL) {
return (NULL);
}
startinum = (inum / INOPB(&sblock)) * INOPB(&sblock);
}
return (&inoblk.b_un.b_dinode[inum % INOPB(&sblock)]);
}
ftypeok(dp)
DINODE *dp;
{
switch (dp->di_mode & IFMT) {
case IFDIR:
case IFREG:
case IFBLK:
case IFCHR:
case IFMPC:
case IFMPB:
return (1);
default:
return (0);
}
}
reply(s)
char *s;
{
char line[80];
if (preen)
pfatal("INTERNAL ERROR: GOT TO reply()");
rplyflag = 1;
printf("\n%s? ", s);
if (nflag || dfile.wfdes < 0) {
printf(" no\n\n");
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
return (0);
}
getline(fp, loc, maxlen)
FILE *fp;
char *loc;
{
register n;
register 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);
}
BUFAREA *
getblk(bp, blk, size)
daddr_t blk;
register BUFAREA *bp;
int size;
{
register struct filecntl *fcp;
daddr_t dblk;
fcp = &dfile;
dblk = fsbtodb(&sblock, blk);
if (bp->b_bno == dblk)
return (bp);
flush(fcp, bp);
if (bread(fcp, bp->b_un.b_buf, dblk, size) != 0) {
bp->b_bno = dblk;
bp->b_size = size;
return (bp);
}
bp->b_bno = (daddr_t)-1;
return (NULL);
}
flush(fcp, bp)
struct filecntl *fcp;
register BUFAREA *bp;
{
if (bp->b_dirty)
bwrite(fcp, bp->b_un.b_buf, bp->b_bno, bp->b_size);
bp->b_dirty = 0;
}
rwerr(s, blk)
char *s;
daddr_t blk;
{
if (preen == 0)
printf("\n");
pfatal("CANNOT %s: BLK %ld", s, blk);
if (reply("CONTINUE") == 0)
errexit("Program terminated\n");
}
ckfini()
{
flush(&dfile, &fileblk);
flush(&dfile, &sblk);
if (sblk.b_bno != SBLOCK) {
sblk.b_bno = SBLOCK;
sbdirty();
flush(&dfile, &sblk);
}
flush(&dfile, &inoblk);
close(dfile.rfdes);
close(dfile.wfdes);
}
pinode()
{
register DINODE *dp;
register char *p;
char uidbuf[200];
char *ctime();
printf(" I=%u ", inum);
if ((dp = ginode()) == NULL)
return;
printf(" OWNER=");
if (getpw((int)dp->di_uid, uidbuf) == 0) {
for (p = uidbuf; *p != ':'; p++);
*p = 0;
printf("%s ", uidbuf);
}
else {
printf("%d ", dp->di_uid);
}
printf("MODE=%o\n", dp->di_mode);
if (preen)
printf("%s: ", devname);
printf("SIZE=%ld ", dp->di_size);
p = ctime(&dp->di_mtime);
printf("MTIME=%12.12s %4.4s ", p+4, p+20);
}
copy(fp, tp, size)
register char *tp, *fp;
unsigned size;
{
while (size--)
*tp++ = *fp++;
}
makecg()
{
int c, blk;
daddr_t dbase, d, dmin, dmax;
long i, j, s;
register struct csum *cs;
register DINODE *dp;
sblock.fs_cstotal.cs_nbfree = 0;
sblock.fs_cstotal.cs_nffree = 0;
sblock.fs_cstotal.cs_nifree = 0;
sblock.fs_cstotal.cs_ndir = 0;
for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
sblock.fs_csp[i] = (struct csum *)calloc(1, sblock.fs_bsize);
getblk((char *)sblock.fs_csp[i],
sblock.fs_csaddr + (i * sblock.fs_frag), sblock.fs_bsize);
}
for (c = 0; c < sblock.fs_ncg; c++) {
dbase = cgbase(c, &sblock);
dmax = dbase + sblock.fs_fpg;
if (dmax > sblock.fs_size)
dmax = sblock.fs_size;
dmin = cgdmin(c, &sblock) - dbase;
cs = &sblock.fs_cs(&sblock, c);
cgrp.cg_time = time(0);
cgrp.cg_magic = CG_MAGIC;
cgrp.cg_cgx = c;
cgrp.cg_ncyl = sblock.fs_cpg;
cgrp.cg_niblk = sblock.fs_ipg;
cgrp.cg_ndblk = dmax - dbase;
cgrp.cg_cs.cs_ndir = 0;
cgrp.cg_cs.cs_nffree = 0;
cgrp.cg_cs.cs_nbfree = 0;
cgrp.cg_cs.cs_nifree = 0;
cgrp.cg_rotor = dmin;
cgrp.cg_frotor = dmin;
cgrp.cg_irotor = 0;
for (i = 0; i < sblock.fs_frag; i++)
cgrp.cg_frsum[i] = 0;
inum = sblock.fs_ipg * c;
for (i = 0; i < sblock.fs_ipg; inum++, i++) {
dp = ginode();
if (dp == NULL)
continue;
if (ALLOC) {
if (DIR)
cgrp.cg_cs.cs_ndir++;
setbit(cgrp.cg_iused, i);
continue;
}
cgrp.cg_cs.cs_nifree++;
clrbit(cgrp.cg_iused, i);
}
while (i < MAXIPG) {
clrbit(cgrp.cg_iused, i);
i++;
}
for (s = 0; s < MAXCPG; s++)
for (i = 0; i < NRPOS; i++)
cgrp.cg_b[s][i] = 0;
if (c == 0) {
dmin += howmany(sblock.fs_cssize, sblock.fs_bsize) * sblock.fs_frag;
}
for (d = 0; d < dmin; d++)
clrbit(cgrp.cg_free, d);
for (; (d + sblock.fs_frag) <= dmax - dbase; d += sblock.fs_frag) {
j = 0;
for (i = 0; i < sblock.fs_frag; i++) {
if (!getbmap(dbase+d+i)) {
setbit(cgrp.cg_free, d+i);
j++;
} else
clrbit(cgrp.cg_free, d+i);
}
if (j == sblock.fs_frag) {
cgrp.cg_cs.cs_nbfree++;
s = d * NSPF(&sblock);
cgrp.cg_b[s/sblock.fs_spc]
[s%sblock.fs_nsect*NRPOS/sblock.fs_nsect]++;
} else if (j > 0) {
cgrp.cg_cs.cs_nffree += j;
blk = ((cgrp.cg_free[d / NBBY] >> (d % NBBY)) &
(0xff >> (NBBY - sblock.fs_frag)));
fragacct(&sblock, blk, cgrp.cg_frsum, 1);
}
}
for (j = d; d < dmax - dbase; d++) {
if (!getbmap(dbase+d)) {
setbit(cgrp.cg_free, d);
cgrp.cg_cs.cs_nffree++;
} else
clrbit(cgrp.cg_free, d);
}
if (j != d) {
blk = ((cgrp.cg_free[j / NBBY] >> (j % NBBY)) &
(0xff >> (NBBY - sblock.fs_frag)));
fragacct(&sblock, blk, cgrp.cg_frsum, 1);
}
for (; d < MAXBPG(&sblock); d++)
clrbit(cgrp.cg_free, d);
sblock.fs_cstotal.cs_nffree += cgrp.cg_cs.cs_nffree;
sblock.fs_cstotal.cs_nbfree += cgrp.cg_cs.cs_nbfree;
sblock.fs_cstotal.cs_nifree += cgrp.cg_cs.cs_nifree;
sblock.fs_cstotal.cs_ndir += cgrp.cg_cs.cs_ndir;
*cs = cgrp.cg_cs;
bwrite(&dfile, &cgrp, fsbtodb(&sblock, cgtod(c, &sblock)),
roundup(sblock.fs_cgsize, DEV_BSIZE));
}
for (i = 0; i < howmany(sblock.fs_cssize, sblock.fs_bsize); i++) {
bwrite(&dfile, (char *)sblock.fs_csp[i],
fsbtodb(&sblock, sblock.fs_csaddr + (i * sblock.fs_frag)),
sblock.fs_bsize);
}
sblock.fs_ronly = 0;
sblock.fs_fmod = 0;
sbdirty();
}
/*
* update the frsum fields to reflect addition or deletion
* of some frags
*/
fragacct(fs, fragmap, fraglist, cnt)
struct fs *fs;
int fragmap;
long fraglist[];
int cnt;
{
int inblk;
register int field, subfield;
register int siz, pos;
inblk = (int)(fragtbl[fs->fs_frag][fragmap] << 1);
fragmap <<= 1;
for (siz = 1; siz < fs->fs_frag; siz++) {
if (((1 << siz) & inblk) == 0)
continue;
field = around[siz];
subfield = inside[siz];
for (pos = siz; pos <= fs->fs_frag; pos++) {
if ((fragmap & field) == subfield) {
fraglist[siz] += cnt;
pos += siz;
field <<= siz;
subfield <<= siz;
}
field <<= 1;
subfield <<= 1;
}
}
}
findino(dirp)
register DIRECT *dirp;
{
register char *p1, *p2;
if (dirp->d_ino == 0)
return (KEEPON);
for (p1 = dirp->d_name, p2 = srchname;*p2++ == *p1; p1++) {
if (*p1 == 0 || p1 == &dirp->d_name[DIRSIZ-1]) {
if (dirp->d_ino >= ROOTINO && dirp->d_ino <= imax)
parentdir = dirp->d_ino;
return (STOP);
}
}
return (KEEPON);
}
mkentry(dirp)
register DIRECT *dirp;
{
register ino_t in;
register char *p;
if (dirp->d_ino)
return (KEEPON);
dirp->d_ino = orphan;
in = orphan;
p = &dirp->d_name[8];
*--p = 0;
while (p > dirp->d_name) {
*--p = (in % 10) + '0';
in /= 10;
}
*p = '#';
return (ALTERD|STOP);
}
chgdd(dirp)
register DIRECT *dirp;
{
if (dirp->d_name[0] == '.' && dirp->d_name[1] == '.' &&
dirp->d_name[2] == 0) {
dirp->d_ino = lfdir;
return (ALTERD|STOP);
}
return (KEEPON);
}
linkup()
{
register DINODE *dp;
register lostdir;
register ino_t pdir;
if ((dp = ginode()) == NULL)
return (0);
lostdir = DIR;
pdir = parentdir;
pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
pinode();
if (preen && dp->di_size == 0)
return (0);
if (preen)
printf(" (RECONNECTED)\n");
else
if (reply("RECONNECT") == 0)
return (0);
orphan = inum;
if (lfdir == 0) {
inum = ROOTINO;
if ((dp = ginode()) == NULL) {
inum = orphan;
return (0);
}
pfunc = findino;
srchname = lfname;
filsize = dp->di_size;
parentdir = 0;
ckinode(dp, DATA);
inum = orphan;
if ((lfdir = parentdir) == 0) {
pfatal("SORRY. NO lost+found DIRECTORY");
printf("\n\n");
return (0);
}
}
inum = lfdir;
if ((dp = ginode()) == NULL || !DIR || getstate() != FSTATE) {
inum = orphan;
pfatal("SORRY. NO lost+found DIRECTORY");
printf("\n\n");
return (0);
}
if (dp->di_size % sblock.fs_bsize) {
dp->di_size = roundup(dp->di_size, sblock.fs_bsize);
inodirty();
}
filsize = dp->di_size;
inum = orphan;
pfunc = mkentry;
if ((ckinode(dp, DATA) & ALTERD) == 0) {
pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
printf("\n\n");
return (0);
}
declncnt();
if (lostdir) {
pfunc = chgdd;
dp = ginode();
filsize = dp->di_size;
ckinode(dp, DATA);
inum = lfdir;
if ((dp = ginode()) != NULL) {
dp->di_nlink++;
inodirty();
setlncnt(getlncnt()+1);
}
inum = orphan;
pwarn("DIR I=%u CONNECTED. ", orphan);
printf("PARENT WAS I=%u\n", pdir);
if (preen == 0)
printf("\n");
}
return (1);
}
bread(fcp, buf, blk, size)
daddr_t blk;
register struct filecntl *fcp;
register size;
char *buf;
{
if (lseek(fcp->rfdes, blk * DEV_BSIZE, 0) < 0)
rwerr("SEEK", blk);
else if (read(fcp->rfdes, buf, size) == size)
return (1);
rwerr("READ", blk);
return (0);
}
bwrite(fcp, buf, blk, size)
daddr_t blk;
register struct filecntl *fcp;
register size;
char *buf;
{
if (fcp->wfdes < 0)
return (0);
if (lseek(fcp->wfdes, blk * DEV_BSIZE, 0) < 0)
rwerr("SEEK", blk);
else if (write(fcp->wfdes, buf, size) == size) {
fcp->mod = 1;
return (1);
}
rwerr("WRITE", blk);
return (0);
}
catch()
{
ckfini();
exit(12);
}
/*
* block operations
*/
isblock(fs, cp, h)
struct fs *fs;
unsigned char *cp;
int h;
{
unsigned char mask;
switch (fs->fs_frag) {
case 8:
return (cp[h] == 0xff);
case 4:
mask = 0x0f << ((h & 0x1) << 2);
return ((cp[h >> 1] & mask) == mask);
case 2:
mask = 0x03 << ((h & 0x3) << 1);
return ((cp[h >> 2] & mask) == mask);
case 1:
mask = 0x01 << (h & 0x7);
return ((cp[h >> 3] & mask) == mask);
default:
fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag);
return;
}
}