DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦c80c6be42⟧ TextFile

    Length: 15385 (0x3c19)
    Types: TextFile
    Notes: UNIX file
    Names: »icheck.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cmd/icheck.c« 

TextFile

/*
 * Icheck - check i-list consistency of
 * filesystems and (optionally) repair
 * filesystems.
 */
#include <stdio.h>
extern char *calloc();
#include <filsys.h>
#include <fblk.h>
#include <dir.h>
#include <ino.h>
#include <check.h>
#include <canon.h>

#define	ROOTINO	2		/* I-number of root */
#define	NBLOCK	20		/* Maximum number of blocks to check */
#define	NBPC	8		/* Bits per character */
#define	BOOTB	0		/* Boot block # */
#define	SUPERB	1		/* Super block */
#define	INOORG	2		/* Inodes begin here */
#define	IBLK	12		/* I-node read blocking factor */
#define	BSIZE	512
#define	ND	10		/* Number of direct block */
#undef	NI
#define	NI	1
#define	NII	1
#define	NIII	1
#undef	NADDR
#define	NADDR	(ND+NI+NII+NIII)

/*
 * Flags for crawldown.
 */
#define	PLAIN	0
#define	DIR	1
#define	BAD	2

/*
 * A chain of these structures
 * holds all of the defective blocks found
 * in the bad block file. The list is sorted for
 * easy access by those parts of the program
 * that scan blocks.
 */
struct	defect
{
	struct defect	*d_next;	/* Link to next */
	daddr_t		d_start;	/* First bad block in cluster */
	int		d_length;	/* Size of cluster */
};

#define	test(bn) (bitmap[((unsigned)bn)/NBPC] & 1<<(((unsigned)bn)%NBPC))
#define	mark(bn) (bitmap[((unsigned)bn)/NBPC] |= 1<<(((unsigned)bn)%NBPC))

char	tmb[] = "Too many block numbers specified\n";

int	nblock;
daddr_t	blocks[NBLOCK];
ino_t	freei[NICFREE];		/* Free i-nodes to put into superblock */
ino_t	*freeip;
struct	defect	*deflist;
char	superb[BSIZE];
char	ibuf[IBLK*BSIZE];
char	fbuf[BSIZE];
char	idbuf[3][BSIZE];		/* One for each indirect level */

/*
 * Offsets of levels of indirection
 * into the i-node addresses.
 */
char	offsets[] = {
	0, ND, ND+NI, ND+NI+NII, ND+NI+NII+NIII,
};

/*
 * Types of indirect and direct blocks
 * by name.
 */
char	*btypes[] = {
	"direct",
	"indirect",
	"double indirect",
	"triple indirect",
};

int	sflag;			/* Repair filesystem */
int	vflag;			/* More verbose information */
int	exstat;			/* Final exit status -- bits from <check.h> */
FILE	*fs;			/* File system i/o stream pointer */
char	*bitmap;		/* Bit map for blocks */
daddr_t	fsize	= SUPERB+1;	/* Allow read of super-block */
unsigned	isize;

/* Various counters */
unsigned nfiles;
unsigned nreg;
unsigned ndir;
unsigned nbad;			/* # of bad blocks */
unsigned nibad;			/* # of bad blocks that were in ilist */
unsigned nbsp;
unsigned ncsp;
unsigned nmpx;
unsigned npipe;
long	nblk;
long	nfblk[4];	/* # of direct, single, double, and triple indirects */
long	ndirb;
long	nfreeb;
long	nmissing;
long	nfdup;
ino_t	nifree;

long	atol();

main(argc, argv)
char *argv[];
{

	while (argc>1 && *argv[1]=='-') {
		switch (argv[1][1]) {
		case 'b':
			nblock = 0;
			while (blocks[nblock] = atol(argv[2])) {
				if (nblock++ >= NBLOCK) {
					fprintf(stderr, tmb);
					exstat |= IC_MISC;
					break;
				}
				argv++;
				argc--;
			}
			break;

		case 's':
			sflag = 1;
			break;

		case 'v':
			vflag = 1;
			break;


		default:
			usage();
		}
		argc--;
		argv++;
	}
	if (argc > 1)
		allcheck(argv+1);
	else
		usage();
	exit(exstat);
}

/*
 * Check the given list of filesystems
 */
allcheck(fsl)
register char **fsl;
{
	while (*fsl != NULL)
		if (icheck(*fsl++))
			return;
}

/*
 * Check one filesystem
 */
icheck(fsname)
char *fsname;
{
	struct filsys *sbp;
	register struct dinode *ip;
	register int i;
	register ino_t inum;
	register char *mode;
	int thischunk;
	daddr_t seek, limit;
	struct defect *cdsp;

	nfiles = 0;
	nreg = 0;
	ndir = 0;
	ndirb = 0;
	npipe = 0;
	nbsp = 0;
	ncsp = 0;
	nmpx = 0;
	nblk = 0;
	nbad = 0;
	nibad = 0;
	for (i=0; i<4; i++)
		nfblk[i] = 0;
	nmissing = 0;
	nfdup = 0;
	nfreeb = 0;
	nifree = 0;
	freeip = freei;
	mode = sflag ? "r+w" : "r";
	if ((fs = fopen(fsname, mode)) == NULL) {
		fprintf(stderr, "%s: cannot open\n", fsname);
		exstat |= IC_MISC;
		return;
	}
	printf("%s:\n", fsname);
	if (!sflag)
		sync();
	bread((daddr_t)SUPERB, superb);
	sbp = superb;
	canint(sbp->s_isize);
	candaddr(sbp->s_fsize);
	canshort(sbp->s_nfree);
	for (i=0; i<NICFREE; ++i)
		candaddr(sbp->s_free[i]);
	canshort(sbp->s_ninode);
	for (i=0; i<NICINOD; ++i)
		canino(sbp->s_inode[i]);
	cantime(sbp->s_time);
	candaddr(sbp->s_tfree);
	canino(sbp->s_tinode);
	canshort(sbp->s_m);
	canshort(sbp->s_n);
	canlong(sbp->s_unique);
	fsize = sbp->s_fsize;
	isize = sbp->s_isize;
	if (isize<INOORG+1 || isize>=fsize || fsize<INOORG+1)
		cerr("Ridiculous fsize/isize");
	if ((bitmap=calloc((int)((fsize+NBPC-1)/NBPC), sizeof(char))) == NULL)
		cerr("No space for bitmap");
	bmark((daddr_t)BOOTB, "bootstrap", 0);
	bmark((daddr_t)SUPERB, "super block", 0);
	finddefective();
	nblk = isize;
	inum = 1;
	seek = INOORG;
	cdsp = deflist;
	while (seek < isize) {
		if (cdsp!=NULL && cdsp->d_start==seek) {
			nibad += cdsp->d_length;
			seek  += cdsp->d_length;
			inum  += cdsp->d_length*INOPB;
			cdsp   = cdsp->d_next;
			continue;
		}
		limit = seek+IBLK;
		if (cdsp!=NULL && limit>cdsp->d_start)
			limit = cdsp->d_start;
		if (limit > isize)
			limit = isize;
		thischunk = limit-seek;
		lseek(fileno(fs), seek*BSIZE, 0);
		for (i=0; i<thischunk; ++i)
			bmark((daddr_t)seek++, "inodes", 0);
		thischunk *= BSIZE;
		if (read(fileno(fs), ibuf, thischunk) != thischunk) {
			fprintf(stderr, "I-node read error\n");
			exstat |= IC_HARD;
			break;
		}
		ip = (struct dinode *) &ibuf[0];
		while (ip < (struct dinode *) &ibuf[thischunk]) {
			if (inum != BADFIN) {
				canshort(ip->di_mode);
				canshort(ip->di_nlink);
				canshort(ip->di_uid);
				canshort(ip->di_gid);
				cansize(ip->di_size);
				cantime(ip->di_atime);
				cantime(ip->di_mtime);
				cantime(ip->di_ctime);
				ilook(ip, inum);
			}
			++inum;
			++ip;
		}
	}
	freecount();
	free(bitmap);
	freedefective();
	if (nmissing != 0)
		exstat |= IC_MISS;
	if (nfdup != 0)
		exstat |= IC_DUPF;
	if (!sflag && vflag) {
		printf("f=%u,r=%u,d=%u,b=%u,c=%u,m=%u,p=%u\n", nfiles, nreg,
		    ndir, nbsp, ncsp, nmpx, npipe);
		printf("blks=%D, dirb=%D, d=%D, i=%D, ii=%D, iii=%D\n",
		    nblk, ndirb, nfblk[0], nfblk[1], nfblk[2], nfblk[3]);
		printf("free = %D\n", nfreeb);
		printf("bad=%u (%u in I-list)\n", nbad, nibad);
	}
	if (!sflag) {
		if (nmissing != 0)
			printf("missing = %D\n", nmissing);
		if (nfdup != 0)
			printf("%D dups in free\n", nfdup);
		if (sbp->s_tinode != nifree) {
			printf("Bad ifree list\n");
			exstat |= IC_BFB;
		}
	}
	if (sflag)
		makesuper();
	fclose(fs);
	return (0);
}

/*
 * Look at each inode marking used blocks
 * and checking consistency.
 */
ilook(ip, inum)
register struct dinode *ip;
ino_t inum;
{
	daddr_t addrs[NADDR];
	register i, l;
	int flag;

	if (ip->di_mode == 0) {
		if (freeip < &freei[NICFREE])
			*freeip++ = inum;
		nifree++;
		return;
	}
	nfiles++;
	flag = PLAIN;
	switch (ip->di_mode & IFMT) {
	case IFREG:
		nreg++;
		break;

	case IFDIR:
		ndir++;
		flag = DIR;
		break;

	case IFBLK:
		nbsp++;
		return;

	case IFCHR:
		ncsp++;
		return;

	case IFPIPE:
		npipe++;
		return;

	case IFMPB:
	case IFMPC:
		nmpx++;
		return;

	default:
		printf("%u: Bad filetype %o\n", inum, ip->di_mode&IFMT);
		return;
	}
	l3tol(addrs, ip->di_addr, NADDR);
	for (i = NADDR-1; i >= 0; i--)
		for (l=1; l<=4; l++)
			if (i < offsets[l]) {
				crawldown(addrs[i], l-1, flag, inum);
				break;
			}
}

/*
 * Crawl down through `lev' levels
 * of indirect blocks, starting at block
 * `bn'. The `ino' argument is the inumber that
 * started this all off; is just gets passed
 * to `bmark'. The `flag' tells you what kind
 * of blocks you have at level 0 (in can be BAD,
 * DIR or PLAIN).
 */
crawldown(bn, lev, flag, ino)
daddr_t bn;
int lev;
ino_t ino;
{
	register char *bp;
	register char *type;
	register int i;

	if (bn == 0)
		return;
	nblk++;
	if (lev==0 && flag==DIR) {
		type = "dir";
		ndirb++;
	} else if (lev==0 && flag==BAD) {
		type = "bad";
		nbad++;
	} else {
		type = btypes[lev];
		nfblk[lev]++;
	}
	if (bmark(bn, type, ino))
		return;
	if (lev==0 && flag==BAD)
		savedefective(bn);
	if (lev-- > 0) {
		bread(bn, bp = idbuf[lev]);
		for (i=0; i<NBN; i++) {
			bn = ((long *)bp)[i];
			candaddr(bn);
			crawldown(bn, lev, flag, ino);
		}
	}
}

/*
 * This routine finds all of the
 * defective space on the filsystem by reading
 * the bad block file and marking all the blocks.
 * The defective space list, used by the I-list
 * scanner and other guys, is constructued.
 */
finddefective()
{
	register struct dinode *ip;
	register i, level;
	daddr_t	 addrs[NADDR];

	++nfiles;
	lseek(fileno(fs), (long)iblockn(BADFIN)*BSIZE, 0);
	if (read(fileno(fs), ibuf, BSIZE) != BSIZE) {
		printf("I/O error reading bad block inode\n");
		exstat |= IC_HARD;
		return;
	}
	ip = (struct dinode *) &ibuf[0] + iblocko(BADFIN);
	canshort(ip->di_mode);
	if (ip->di_mode == 0)
		return;
	if ((ip->di_mode&IFMT) != IFREG) {
		printf("Bad block file has bad mode\n");
		exstat |= IC_HARD;
		return;
	}
	l3tol(addrs, ip->di_addr, NADDR);
	for (i=NADDR-1; i>=0; --i) {
		for (level=1; level<=4; ++level) {
			if (i < offsets[level]) {
				crawldown(addrs[i], level-1, BAD, BADFIN);
				break;
			}
		}
	}
}

/*
 * Free all of the nodes
 * in the defective space list.
 */
freedefective()
{
	register struct defect *cdsp1, *cdsp2;

	cdsp1 = deflist;
	deflist = NULL;
	while (cdsp1 != NULL) {
		cdsp2 = cdsp1->d_next;
		free((char *) cdsp1);
		cdsp1 = cdsp2;
	}
}

/*
 * Add a new, defective block
 * into the sorted defective block chain.
 * Merge this block with the ends of
 * any existing entries. No check is made
 * for entries fusing; bad blocks get scooped
 * (in general) up in order, and the bad blocks
 * are generally sparsely placed on the disc.
 */
savedefective(bn)
daddr_t bn;
{
	register struct defect *cdsp1, *cdsp2, *cdsp3;

	cdsp1 = NULL;
	cdsp2 = deflist;
	while (cdsp2!=NULL && bn>cdsp2->d_start) {
		cdsp1 = cdsp2;
		cdsp2 = cdsp2->d_next;
	}
	if (cdsp1!=NULL && bn==cdsp1->d_start+cdsp1->d_length) {
		++cdsp1->d_length;
		return;
	}
	if (cdsp2!=NULL && bn==cdsp2->d_start-1) {
		--cdsp2->d_start;
		++cdsp2->d_length;
		return;
	}
	if ((cdsp3=(struct defect *)malloc(sizeof(struct defect))) == NULL)
		cerr("Out of space for bad blocks");
	if (cdsp1 == NULL)
		deflist = cdsp3; else
		cdsp1->d_next = cdsp3;
	cdsp3->d_next = cdsp2;
	cdsp3->d_start = bn;
	cdsp3->d_length = 1;
}

/*
 * Look at the free count for a filesystem
 * by chasing down the free-list.
 */
freecount()
{
	register char *bmp;
	register struct fblk *fbp;
	struct filsys *sbp;
	register unsigned i;
	daddr_t bn;
	long ntfree;

	sbp = superb;
	fbp = &sbp->s_nfree;
	ntfree = sbp->s_tfree;
	while ((i = fbp->df_nfree) != 0) {
		if ((unsigned)(fbp->df_nfree) > NICFREE) {
			badfreelist();
			return;
		}
		for (i=0; i<fbp->df_nfree; i++) {
			bn = fbp->df_free[i];
			bmark(bn, "free", 0);
			nfreeb++;
		}
		bread(fbp->df_free[0], fbuf);
		fbp = fbuf;
		canint(fbp->df_nfree);
		for (i=0; i<NICFREE; ++i)
			candaddr(fbp->df_free[i]);
	}
	/*
	 * Count number of blocks not in bitmap
	 */
	i = 1;
	for (bn=0, bmp=bitmap; bn < fsize; bn++) {
		if (i == 1<<NBPC) {
			i = 1;
			bmp++;
		}
		if ((*bmp & i) == 0)
			nmissing++;
		i <<= 1;
	}
	if (sflag)
		nmissing -= isize + nfreeb;
	if (nfreeb != ntfree) {
		if (!sflag)
			printf("Free list/tfree counts differ\n");
		exstat |= IC_MISS;
	}
}

/*
 * Remake the superblock - reconstructing
 * the free-list if sflag is set.
 */
makesuper()
{
	register struct filsys *sbp;
	register ino_t	*fip;
	register	i;
	daddr_t		bn;

	sbp = superb;
	/*
	 * Remake list of free i-numbers.
	 */
	fip = sbp->s_inode;
	sbp->s_ninode = freeip-freei;
	while (freeip > freei)
		*fip++ = *--freeip;
	while (fip < &sbp->s_inode[NICFREE])
		*fip++ = 0;
	sbp->s_tinode = nifree;
	/*
	 * Free all remaining blocks
	 * and write last one as tail of free-list
	 */
	sbp->s_nfree = 0;
	sbp->s_tfree = 0;
	bn = fsize;
	for (bn=fsize-1; bn>=isize; --bn)
		if (!test(bn))
			bfree(bn);
	canint(sbp->s_isize);
	candaddr(sbp->s_fsize);
	canshort(sbp->s_nfree);
	for (i=0; i<NICFREE; ++i)
		candaddr(sbp->s_free[i]);
	canshort(sbp->s_ninode);
	for (i=0; i<NICINOD; ++i)
		canino(sbp->s_inode[i]);
	cantime(sbp->s_time);
	candaddr(sbp->s_tfree);
	canino(sbp->s_tinode);
	canshort(sbp->s_m);
	canshort(sbp->s_n);
	canlong(sbp->s_unique);
	bwrite((daddr_t)SUPERB, sbp);
}

/*
 * Free a block and, in so doing, construct
 * the free list chain.
 */
bfree(bn)
daddr_t bn;
{
	register struct filsys	*sbp;
	register struct fblk	*fbp;
	register		i;

	sbp = superb;
	if (sbp->s_tfree == 0) {
		bclear(fbuf, BSIZE);
		bwrite(bn, fbuf);
	}
	if (sbp->s_nfree == NICFREE) {
		bclear(fbp = fbuf, BSIZE);
		fbp->df_nfree = sbp->s_nfree;
		canint(fbp->df_nfree);
		for (i=0; i<sbp->s_nfree; ++i) {
			fbp->df_free[i] = sbp->s_free[i];
			candaddr(fbp->df_free[i]);
		}
		bwrite(bn, fbuf);
		sbp->s_nfree = 0;
	}
	sbp->s_free[sbp->s_nfree++] = bn;
	sbp->s_tfree++;
}

/*
 * Read the specified block number
 * into `buf'.
 */
bread(bn, buf)
daddr_t bn;
char *buf;
{
	if (bn<0 || bn>=fsize) {
		badblock(bn, "any", 0);
		bclear(buf, BSIZE);
		return;
	}
	lseek(fileno(fs), (size_t)BSIZE * bn, 0);
	if (read(fileno(fs), buf, BSIZE) != BSIZE) {
		fprintf(stderr, "Read error %D\n", (long)bn);
		exstat |= IC_HARD;
		bclear(buf, BSIZE);
	}
}

/*
 * Write block `bn' from `buf'.
 */
bwrite(bn, buf)
daddr_t bn;
char *buf;
{
	if (bn<0 || bn>=fsize) {
		badblock(bn, "any", 0);
		return;
	}
	lseek(fileno(fs), (size_t)BSIZE * bn, 0);
	if (write(fileno(fs), buf, BSIZE) != BSIZE) {
		fprintf(stderr, "Write error %D\n", (long)bn);
		exstat |= IC_HARD;
	}
}

/*
 * Mark block # `bn' as
 * seen before an check for
 * duplicates.
 * Bmark only marks file blocks if `sflag' is
 * set so that the free list can be constructed
 * again.
 * Return 1 when something is wrong.
 */
bmark(bn, type, inum)
daddr_t bn;
char *type;
ino_t inum;
{
	register nb;

	if (bn<0 || bn>=fsize) {
		badblock(bn, type, inum);
		return (1);
	}
	if (nb = nblock) {
		register i;

		for (i=0; i<nb; i++)
			if (blocks[i] == bn)
				printf("%D arg, class=%s, inode=%u\n",
				    (long)bn, type, inum);
	}
	{
		register char *bp;
		register int mask;

		mask = 1 << ((unsigned)bn)%NBPC;
		bp = bitmap + ((unsigned)bn)/NBPC;
		if (*bp & mask)			/* if (test(bn)) */
			dupblock(bn, type, inum);
		else if (!sflag  || inum!=0)
			*bp |= mask;		/* mark(bn) */
	}
	return (0);
}

/*
 * Clear a block of memory
 * pointed to by `bp' for size
 * `nb' bytes.
 */
bclear(bp, nb)
register char *bp;
register unsigned nb;
{
	if (nb)
		do {
			*bp++ = 0;
		} while (--nb);
}

/*
 * Error routines
 */
badblock(bn, type, inum)
daddr_t bn;
char *type;
ino_t inum;
{
	register int perr = 0;

	if (strcmp(type, "free") != 0) {
		perr++;
		exstat |= IC_HARD;
	} else {
		exstat |= IC_BADF;
		if (!sflag)
			perr++;
	}
	if (perr)
		printf("%D bad, class=%s, inode=%u\n", (long)bn, type, inum);
}

dupblock(bn, type, inum)
daddr_t bn;
char *type;
ino_t inum;
{
	if (inum != 0)
		exstat |= IC_HARD;
	else
		nfdup++;
	if (vflag || inum!=0)
		printf("%D dup, class=%s, inode=%u\n", (long)bn, type, inum);
}

badfreelist()
{
	if (!sflag) {
		printf("Bad freelist\n");
		exstat |= IC_BFB;
	}
}

/*
 * Unrecoverable errors
 */
cerr(x)
{
	printf("%r", &x);
	putchar('\n');
	exit(IC_MISC);
}

usage()
{
	cerr("Usage: icheck [-sv] [-b bn ...] filesystem ...");
}

/*
 * Block copy routine
 */
bcopy(in, out, nb)
register char *in, *out;
register unsigned nb;
{
	if (nb)
		do {
			*out++ = *in++;
		} while (--nb);
}