/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * is hereby transferred.
 */
#ifndef lint
static char rcsid[] = "$Header: mkproto.c 2.4 90/01/23 $";
#endif
/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

/*
 * Make a file system prototype.
 * usage: mkproto filsys proto
 */
#include <stdio.h>
#include <sys/param.h>
#include <ufs/inode.h>
#include <ufs/fs.h>
#include <sys/dir.h>

void setup_sb(), unsetup_sb();

/* inode.h no longer gives this, so we have to make it up here */
struct inode {
	dev_t	i_dev;		/* device where inode resides */
	ino_t	i_number;	/* i number, 1-to-1 with device address */
	struct 	icommon i_ic;	/* disk portion of inode */
};

extern char *valloc();

/* Arrange for aligned versions of data structures */
struct fs *xxfs;
#define sblock (*xxfs)
struct cg *xxcg;
#define acg (*xxcg)

/*
 * Macro to create an aligned version of the data structure in the stack
 * frame.  What we could really use is a valloc-like version of alloca(),
 * but I don't feel like dealing with that right now.
 */
#define ALIGNED(var, tvar, type, size) \
	char tvar[size + NBPG]; \
	type *var = (type *)(((unsigned long)tvar + (NBPG-1)) & ~(NBPG-1))

struct	fs *fs;
struct	csum *fscs;
int	fso, fsi;
FILE	*proto;
char	token[BUFSIZ];
int	errs;
long	dev_bsize = 1;
int	ino = 10;
long	getnum();
char	*strcpy();

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

	if (argc != 3) {
		fprintf(stderr, "usage: mkproto filsys proto\n");
		exit(1);
	}

	xxfs = (struct fs *)valloc(SBSIZE);
	if (!xxfs)
		fprintf(stderr, "Can't valloc sblock.\n"), exit(1);
	xxcg = (struct cg *)valloc(MAXBSIZE);
	if (!xxcg)
		fprintf(stderr, "Can't valloc acg.\n"), exit(1);

	fso = open(argv[1], 1);
	fsi = open(argv[1], 0);
	if (fso < 0 || fsi < 0) {
		perror(argv[1]);
		exit(1);
	}
	fs = &sblock;
	rdfs(SBOFF, SBSIZE, (char *)fs);
	setup_sb(fs);
	dev_bsize = fs->fs_fsize / fsbtodb(fs, 1);
	fscs = (struct csum *)valloc((u_int)fs->fs_cssize);
	if (!fscs)
		fprintf(stderr, "Can't valloc fscs.\n"), exit(1);
	for (i = 0; i < fs->fs_cssize; i += fs->fs_bsize)
		rdfs(fsbtodb(fs, fs->fs_csaddr + numfrags(fs, i)),
			(int)(fs->fs_cssize - i < fs->fs_bsize ?
			    fs->fs_cssize - i : fs->fs_bsize),
			((char *)fscs) + i);
	proto = fopen(argv[2], "r");
	if (!proto)
		perror(argv[2]), exit(1);
	descend((struct inode *)0);
	unsetup_sb(fs);
	wtfs(SBOFF / dev_bsize, SBSIZE, (char *)fs);
	for (i = 0; i < fs->fs_cssize; i += fs->fs_bsize)
		wtfs(fsbtodb(&sblock, fs->fs_csaddr + numfrags(&sblock, i)),
			(int)(fs->fs_cssize - i < fs->fs_bsize ?
			    fs->fs_cssize - i : fs->fs_bsize),
			((char *)fscs) + i);
	exit(errs);
}

descend(par)
	struct inode *par;
{
	struct inode in;
	int ibc = 0;
	int i, f, c;
	struct dinode *dip;
	ALIGNED(inos, pagealigninos, struct dinode, MAXBSIZE);
	ALIGNED(ib, pagealignib, daddr_t, MAXBSIZE);
	ALIGNED(buf, pagealignbuf, char, MAXBSIZE);

	getstr();
	in.i_mode = gmode(token[0], "-bcd", IFREG, IFBLK, IFCHR, IFDIR);
	in.i_mode |= gmode(token[1], "-u", 0, ISUID, 0, 0);
	in.i_mode |= gmode(token[2], "-g", 0, ISGID, 0, 0);
	for (i = 3; i < 6; i++) {
		c = token[i];
		if (c < '0' || c > '7') {
			printf("%c/%s: bad octal mode digit\n", c, token);
			errs++;
			c = 0;
		}
		in.i_mode |= (c-'0')<<(15-3*i);
	}
	in.i_uid = getnum(); in.i_gid = getnum();
	for (i = 0; i < fs->fs_bsize; i++)
		buf[i] = 0;
	for (i = 0; i < NINDIR(fs); i++)
		ib[i] = (daddr_t)0;
	in.i_nlink = 1;
	in.i_size = 0;
	for (i = 0; i < NDADDR; i++)
		in.i_db[i] = (daddr_t)0;
	for (i = 0; i < NIADDR; i++)
		in.i_ib[i] = (daddr_t)0;
	if (par != (struct inode *)0) {
		ialloc(&in);
		in.i_ic.ic_blocks = 0;
	} else {
		par = &in;
		i = itod(fs, ROOTINO);
		rdfs(fsbtodb(fs, i), fs->fs_bsize, (char *)inos);
		dip = &inos[ROOTINO % INOPB(fs)];
		in.i_number = ROOTINO;
		in.i_nlink = dip->di_nlink;
		in.i_size = dip->di_size;
		in.i_db[0] = dip->di_db[0];
		rdfs(fsbtodb(fs, in.i_db[0]), fs->fs_bsize, buf);
	}

	switch (in.i_mode&IFMT) {

	case IFREG:
		getstr();
		f = open(token, 0);
		if (f < 0) {
			printf("%s: cannot open\n", token);
			errs++;
			break;
		}
		while ((i = read(f, buf, (int)fs->fs_bsize)) > 0) {
			in.i_size += i;
			in.i_ic.ic_blocks += blksize(fs, &in, ibc)/DEV_BSIZE;
			newblk(buf, &ibc, ib, (int)blksize(fs, &in, ibc));
		}
		/* only handle single-indirect block's worth */
		if (in.i_size > NDADDR*fs->fs_bsize)
			in.i_ic.ic_blocks += fs->fs_bsize/DEV_BSIZE;
		close(f);
		break;

	case IFBLK:
	case IFCHR:
		/*
		 * special file
		 * content is maj/min types
		 */

		i = getnum() & 0377;
		f = getnum() & 0377;
		in.i_rdev = (i << 8) | f;
		break;

	case IFDIR:
		/*
		 * directory
		 * put in extra links
		 * call recursively until
		 * name of "$" found
		 */

		in.i_ic.ic_blocks = fsbtodb(fs, 1);
		if (in.i_number != ROOTINO) {
			par->i_nlink++;
			in.i_nlink++;
			entry(&in, in.i_number, ".", buf);
			entry(&in, par->i_number, "..", buf);
		}
		for (;;) {
			getstr();
			if (token[0]=='$' && token[1]=='\0')
				break;
			entry(&in, (ino_t)(ino+1), token, buf);
			descend(&in);
		}
		if (in.i_number != ROOTINO)
			newblk(buf, &ibc, ib, (int)blksize(fs, &in, 0));
		else
			wtfs(fsbtodb(fs, in.i_db[0]), (int)fs->fs_bsize, buf);
		break;
	}
	iput(&in, &ibc, ib);
}

/*ARGSUSED*/
gmode(c, s, m0, m1, m2, m3)
	char c, *s;
{
	int i;

	for (i = 0; s[i]; i++)
		if (c == s[i])
			return((&m0)[i]);
	printf("%c/%s: bad mode\n", c, token);
	errs++;
	return(0);
}

long
getnum()
{
	int i, c;
	long n;

	getstr();
	n = 0;
	i = 0;
	for (i = 0; c=token[i]; i++) {
		if (c<'0' || c>'9') {
			printf("%s: bad number\n", token);
			errs++;
			return((long)0);
		}
		n = n*10 + (c-'0');
	}
	return(n);
}

getstr()
{
	int i, c;

loop:
	switch (c = getc(proto)) {

	case ' ':
	case '\t':
	case '\n':
		goto loop;

	case EOF:
		printf("Unexpected EOF\n");
		exit(1);

	case ':':
		while (getc(proto) != '\n')
			;
		goto loop;

	}
	i = 0;
	do {
		token[i++] = c;
		c = getc(proto);
	} while (c != ' ' && c != '\t' && c != '\n' && c != '\0');
	token[i] = 0;
}

entry(ip, inum, str, buf)
	struct inode *ip;
	ino_t inum;
	char *str;
	char *buf;
{
	register struct direct *dp, *odp;
	int oldsize, newsize, spacefree;

	odp = dp = (struct direct *)buf;
	while ((int)dp - (int)buf < ip->i_size) {
		odp = dp;
		dp = (struct direct *)((int)dp + dp->d_reclen);
	}
	if (odp != dp)
		oldsize = DIRSIZ(odp);
	else
		oldsize = 0;
	spacefree = odp->d_reclen - oldsize;
	dp = (struct direct *)((int)odp + oldsize);
	dp->d_ino = inum;
	dp->d_namlen = strlen(str);
	newsize = DIRSIZ(dp);
	if (spacefree >= newsize) {
		odp->d_reclen = oldsize;
		dp->d_reclen = spacefree;
	} else {
		dp = (struct direct *)((int)odp + odp->d_reclen);
		if ((int)dp - (int)buf >= fs->fs_bsize) {
			printf("directory too large\n");
			exit(1);
		}
		dp->d_ino = inum;
		dp->d_namlen = strlen(str);
		dp->d_reclen = DIRBLKSIZ;
	}
	strcpy(dp->d_name, str);
	ip->i_size = roundup((int)dp - (int)buf + newsize, DIRBLKSIZ);
}

newblk(buf, aibc, ib, size)
	int *aibc;
	char *buf;
	daddr_t *ib;
	int size;
{
	int i;
	daddr_t bno, alloc();

	bno = alloc(size);
	wtfs(fsbtodb(fs, bno), (int)fs->fs_bsize, buf);
	for (i = 0; i < fs->fs_bsize; i++)
		buf[i] = 0;
	ib[(*aibc)++] = bno;
	if (*aibc >= NINDIR(fs)) {
		printf("indirect block full\n");
		errs++;
		*aibc = 0;
	}
}

iput(ip, aibc, ib)
	struct inode *ip;
	int *aibc;
	daddr_t *ib;
{
	daddr_t d, alloc();
	int i;
	ALIGNED(buf, pagealignbuf, struct dinode, MAXBSIZE);
	time_t time();

	ip->i_atime = ip->i_mtime = ip->i_ctime = time((time_t *)NULL);
	switch (ip->i_mode&IFMT) {

	case IFDIR:
	case IFREG:
		for (i = 0; i < *aibc; i++) {
			if (i >= NDADDR)
				break;
			ip->i_db[i] = ib[i];
		}
		if (*aibc > NDADDR) {
			ip->i_ib[0] = alloc((int)fs->fs_bsize);
			for (i = 0; i < NINDIR(fs) - NDADDR; i++) {
				ib[i] = ib[i+NDADDR];
				ib[i+NDADDR] = (daddr_t)0;
			}
			wtfs(fsbtodb(fs, ip->i_ib[0]),
			    (int)fs->fs_bsize, (char *)ib);
		}
		break;

	case IFBLK:
	case IFCHR:
		break;

	default:
		printf("bad mode %o\n", ip->i_mode);
		exit(1);
	}
	d = fsbtodb(fs, itod(fs, ip->i_number));
	rdfs(d, (int)fs->fs_bsize, (char *)buf);
	buf[itoo(fs, ip->i_number)].di_ic = ip->i_ic;
	wtfs(d, (int)fs->fs_bsize, (char *)buf);
}

daddr_t
alloc(size)
	int size;
{
	int i, frag;
	daddr_t d;
	static int cg = 0;

again:
	rdfs(fsbtodb(&sblock, cgtod(&sblock, cg)), (int)sblock.fs_cgsize,
	    (char *)&acg);
	if (!cg_chkmagic(&acg)) {
		printf("cg %d: bad magic number\n", cg);
		return (0);
	}
	if (acg.cg_cs.cs_nbfree == 0) {
		cg++;
		if (cg >= fs->fs_ncg) {
			printf("ran out of space\n");
			return (0);
		}
		goto again;
	}
	for (d = 0; d < acg.cg_ndblk; d += sblock.fs_frag)
		if (isblock(&sblock, (u_char *)cg_blksfree(&acg),
		    d / sblock.fs_frag))
			goto goth;
	printf("internal error: can't find block in cyl %d\n", cg);
	return (0);
goth:
	clrblock(&sblock, (u_char *)cg_blksfree(&acg), d / sblock.fs_frag);
	acg.cg_cs.cs_nbfree--;
	sblock.fs_cstotal.cs_nbfree--;
	fscs[cg].cs_nbfree--;
	cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
	cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
	if (size != sblock.fs_bsize) {
		frag = howmany(size, sblock.fs_fsize);
		fscs[cg].cs_nffree += sblock.fs_frag - frag;
		sblock.fs_cstotal.cs_nffree += sblock.fs_frag - frag;
		acg.cg_cs.cs_nffree += sblock.fs_frag - frag;
		acg.cg_frsum[sblock.fs_frag - frag]++;
		for (i = frag; i < sblock.fs_frag; i++)
			setbit(cg_blksfree(&acg), d + i);
	}
	wtfs(fsbtodb(&sblock, cgtod(&sblock, cg)), (int)sblock.fs_cgsize,
	    (char *)&acg);
	return (acg.cg_cgx * fs->fs_fpg + d);
}

/*
 * Allocate an inode on the disk
 */
ialloc(ip)
	register struct inode *ip;
{
	int c;

	ip->i_number = ++ino;
	c = itog(&sblock, ip->i_number);
	rdfs(fsbtodb(&sblock, cgtod(&sblock, c)), (int)sblock.fs_cgsize,
	    (char *)&acg);
	if (!cg_chkmagic(&acg)) {
		printf("cg %d: bad magic number\n", c);
		exit(1);
	}
	if ((ip->i_mode & IFMT) == IFDIR) {
		acg.cg_cs.cs_ndir++;
		sblock.fs_cstotal.cs_ndir++;
		fscs[c].cs_ndir++;
	}
	acg.cg_cs.cs_nifree--;
	setbit(cg_inosused(&acg), ip->i_number);
	wtfs(fsbtodb(&sblock, cgtod(&sblock, c)), (int)sblock.fs_cgsize,
	    (char *)&acg);
	sblock.fs_cstotal.cs_nifree--;
	fscs[c].cs_nifree--;
	if(ip->i_number >= sblock.fs_ipg * sblock.fs_ncg) {
		printf("fsinit: inode value out of range (%lu).\n",
		    ip->i_number);
		exit(1);
	}
	/* return (ip->i_number); */
}

/*
 * read a block from the file system
 */
rdfs(bno, size, bf)
	int bno, size;
	char *bf;
{
	int n;
	off_t lseek();

	if (lseek(fsi, bno * dev_bsize, 0) < 0) {
		printf("seek error: %d\n", bno);
		perror("rdfs");
		exit(1);
	}
	n = read(fsi, bf, size);
	if(n != size) {
		printf("read error: %d\n", bno);
		perror("rdfs");
		exit(1);
	}
}

/*
 * write a block to the file system
 */
wtfs(bno, size, bf)
	int bno, size;
	char *bf;
{
	int n;
	off_t lseek();

	if (lseek(fso, bno * dev_bsize, 0) < 0) {
		printf("seek error: %d\n", bno);
		perror("wtfs");
		exit(1);
	}
	n = write(fso, bf, size);
	if(n != size) {
		printf("write error: %d\n", bno);
		perror("wtfs");
		exit(1);
	}
}
/*
 * check if a block is available
 */
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 %ld\n", fs->fs_frag);
		return (0);
	}
	/*NOTREACHED*/
}

/*
 * take a block out of the map
 */
clrblock(fs, cp, h)
	struct fs *fs;
	unsigned char *cp;
	int h;
{
	switch ((fs)->fs_frag) {
	case 8:
		cp[h] = 0;
		return;
	case 4:
		cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
		return;
	case 2:
		cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
		return;
	case 1:
		cp[h >> 3] &= ~(0x01 << (h & 0x7));
		return;
	default:
		fprintf(stderr, "clrblock bad fs_frag %ld\n", fs->fs_frag);
		return;
	}
}

/*
 * 4.3-tahoe has defined some new fields which are used for super-block
 * information.  In the kernel, ufs_mount fakes these up.  As we're not
 * in the kernel, we fake them up here instead.
 */
void
setup_sb(fs)
	register struct fs *fs;
{
	register struct fs_dsp *ofs;

	if (PTXFS(fs))
		return;

	/*
	 * Move fs_id[] stuff from its current place to the PTX-
	 * defined place.  We do this because the "spares" we were
	 * keeping fs_id[] have been used by PTX for the Tahoe
	 * parameters.
	 */
	ofs = &fs->fs_un.fs_dsp;
	fs->fs_id[0] = ofs->dfs_id[0];
	fs->fs_id[1] = ofs->dfs_id[1];

	/*
	 * construct new tahoe-style "spares".
	 */
	fs->fs_nrpos = 8;
	fs->fs_optim = FS_OPTSPACE;
	fs->fs_npsect = fs->fs_nsect;
	fs->fs_interleave = 1;
	fs->fs_trackskew = 0;
	fs->fs_headswitch = 0;
	fs->fs_trkseek = 0;
}

/*
 * Undo the faking-up of the superblock before writing it back
 */
void
unsetup_sb(fs)
	register struct fs *fs;
{
	register struct fs_dsp *ofs;

	if (PTXFS(fs))
		return;

	/* Restore old 4.2 "spares" */
	ofs = &fs->fs_un.fs_dsp;
	ofs->dfs_id[0] = fs->fs_id[0];
	ofs->dfs_id[1] = fs->fs_id[1];
	bzero((char *)ofs->dfs_sparecon, sizeof(ofs->dfs_sparecon));

	/* Restore old 4.2 fs_postbl[] */
	fs->fs_nrpos = -1;
	fs->fs_id[0] = -1;
	fs->fs_id[1] = -1;
	fs->fs_state = -1;
}
