!<arch>
all.h           716098360   0     0     664     2241      `
#include	"u.h"
#include	"lib.h"
#include	"dat.h"
#include	"fns.h"
#include	"auth.h"

#define	CHAT(cp)	((cons.flags&chatflag)||(cp&&(((Chan*)cp)->flags&chatflag)))
#define	DEV(a,b,c,d)	(Device){a,b,c,d}
#define	QID(a,b)	(Qid){a,b}
#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))

#define	QPDIR		0x80000000L
#define	QPNONE		0
#define	QPROOT		1
#define	QPSUPER		2

/*
 * perm argument in p9 create
 */
#define	PDIR	(1L<<31)	/* is a directory */
#define	PAPND	(1L<<30)	/* is append only */
#define	PLOCK	(1L<<29)	/* is locked on open */

#define	FID1		1
#define	FID2		2

#define	NOF	(-1)

#define SECOND(n) 	(n)
#define MINUTE(n)	(n*SECOND(60))
#define HOUR(n)		(n*MINUTE(60))
#define DAY(n)		(n*HOUR(24))
#define	MAXBIAS		SECOND(20)
#define	TLOCK		MINUTE(5)
#define	TWORM		MINUTE(2)

#define	NQUEUE		20

Nvrsafe	nvr;
Uid*	uid;
short*	gidspace;
Lock	printing;
Time	tim;
File*	files;
Wpath*	wpaths;
Lock	wpathlock;
char*	errstr[MAXERR];
void	(*p9call[MAXSYSCALL])(Chan*, Fcall*, Fcall*);
Chan*	chans;
RWlock	mainlock;
long	mktime;
long	boottime;
Queue*	serveq;
Queue*	raheadq;
Rabuf*	rabuffree;
QLock	reflock;
Lock	rabuflock;
Tlock	tlocks[NTLOCK];
Device	devnone;
long	startsb;
int	predawn;		/* set in early boot, causes polling ttyout */
int	mballocs[MAXCAT];
Filsys	filsys[10];		/* named file systems -- from config block */
char	service[50];		/* my name -- from config block */
uchar	sysip[4];		/* my ip -- from config block */
uchar	defmask[4];		/* ip mask -- from config block */
uchar	defgwip[4];		/* gateway ip -- from config block */
uchar 	authip[4];		/* ip address of server -- from config block */
char	srvkey[DESKEYLEN];
ulong	dkflag;
ulong	roflag;
ulong	chatflag;
ulong	attachflag;
Ifc*	enets;			/* List of configured interfaces */
int	echo;
int	wstatallow;		/* set to circumvent wstat permissions */
int	writeallow;		/* set to circumvent owrite permissions */
Device	cwdevs[100];		/* structures for pseudo devices */

int	noauth;			/* Debug */
Queue*	authreply;		/* Auth replys */
Chan*	authchan;

File*	flist[5003];		/* base of file structures */
Lock	flock;			/* manipulate flist */

struct
{
	RWlock	uidlock;
	Iobuf*	uidbuf;
	int	flen;
	int	find;
} uidgc;

extern	char	statecall[];
extern	char*	wormscode[];
extern	char*	tagnames[];

arp.c           701152944   0     0     664     8065      `
#include "all.h"

#define	DEBUG	if(cons.flags&arpcache.flag)print

typedef struct	Arpentry	Arpentry;
typedef struct	Arpstats	Arpstats;
typedef	struct	Arpe		Arpe;

struct	Arpe
{
	uchar	tpa[Pasize];
	uchar	tha[Easize];
};

static	int	ipahash(uchar*);
static	void	cmd_arp(int, char*[]);

static
struct
{
	Lock;
	uchar	null[Pasize];
	int	start;
	int	idgen;
	ulong	flag;
	Msgbuf*	unresol;
	struct
	{
		int	laste;
		Arpe	arpe[Ne];
	} abkt[Nb];
} arpcache;

int
nhgets(uchar *p)
{
	return (p[0]<<8) | p[1];
}

long
nhgetl(uchar *p)
{
	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
}

void
hnputs(uchar *p, int x)
{
	p[0] = x>>8;
	p[1] = x;
}

void
hnputl(uchar *p, long x)
{
	p[0] = x>>24;
	p[1] = x>>16;
	p[2] = x>>8;
	p[3] = x;
}

void
arpstart(void)
{
	if(arpcache.start == 0) {
		lock(&arpcache);
		if(arpcache.start == 0) {
			cmd_install("arp", "subcommand -- arp protocol", cmd_arp);
			arpcache.flag = flag_install("arp", "-- verbose");
			arpcache.start = 1;
		}
		unlock(&arpcache);
	}
}

void
arpreceive(Enpkt *ep, int l, Ifc *ifc)
{
	Arppkt *p, *q;
	Msgbuf *mb, **mbp;
	Arpe *a;
	uchar *tpa;
	int type, i, h;
	ulong t;

	if(l < Ensize+Arpsize)
		return;

	p = (Arppkt*)ep;

	if(nhgets(p->pro) != Iptype ||
	   nhgets(p->hrd) != 1 ||
	   p->pln != Pasize ||
	   p->hln != Easize)
		return;

	type = nhgets(p->op);
	switch(type) {
	case Arprequest:
		if(memcmp(p->tpa, ifc->ipa, Pasize) != 0)
			break;

		DEBUG("rcv arp req for %I from %I\n", p->tpa, p->spa);

		mb = mballoc(Ensize+Arpsize, 0, Mbarp1);
		q = (Arppkt*)mb->data;

		memmove(q, p, Ensize+Arpsize);

		hnputs(q->op, Arpreply);
		memmove(q->tha, p->sha, Easize);
		memmove(q->tpa, p->spa, Pasize);
		memmove(q->sha, ifc->ea, Easize);
		memmove(q->spa, ifc->ipa, Pasize);
		memmove(q->d, q->s, Easize);

		send(ifc->reply, mb);
		break;

	case Arpreply:
		DEBUG("rcv arp rpl for %I is %E\n", p->spa, p->sha);

		h = ipahash(p->spa);
		a = arpcache.abkt[h].arpe;
		lock(&arpcache);
		for(i=0; i<Ne; i++,a++) {
			if(memcmp(a->tpa, p->spa, Pasize) == 0) {
				memmove(a->tha, p->sha, Easize);
				goto out;
			}
		}

		i = arpcache.abkt[h].laste + 1;
		if(i < 0 || i >= Ne)
			i = 0;
		arpcache.abkt[h].laste = i;

		a = &arpcache.abkt[h].arpe[i];
		memmove(a->tpa, p->spa, Pasize);
		memmove(a->tha, p->sha, Easize);

	out:
		/*
		 * go thru unresolved queue
		 */
		t = toytime();
		mbp = &arpcache.unresol;
		for(mb = *mbp; mb; mb = *mbp) {
			if(t >= mb->param) {
				*mbp = mb->next;
				mbfree(mb);
				continue;
			}
			tpa = ((Ippkt*)mb->data)->dst;
			if(mb->chan->ilp.usegate)
				tpa = mb->chan->ilp.ipgate;
			if(memcmp(a->tpa, tpa, Pasize) == 0) {
				*mbp = mb->next;
				unlock(&arpcache);
				ipsend(mb);
				continue;
			}
			mbp = &mb->next;
		}
		unlock(&arpcache);
		break;
	}
}

static
int
ipahash(uchar *p)
{
	int h;

	h = p[0];
	h = h*7 + p[1];
	h = h*7 + p[2];
	h = h*7 + p[3];
	if(h < 0)
		h = ~h;
	return h%Nb;
}

void
ipsend(Msgbuf *mb)
{
	Chan *cp;
	Msgbuf **mbp, *m;
	uchar *tpa;
	Ippkt *p;
	Arppkt *q;
	Arpe *a;
	int i, id, len, dlen, off;
	ulong t;

	p = (Ippkt*)mb->data;
	cp = mb->chan;

	tpa = p->dst;
	if(cp->ilp.usegate)
		tpa = cp->ilp.ipgate;

	a = arpcache.abkt[ipahash(tpa)].arpe;
	lock(&arpcache);
	for(i=0; i<Ne; i++,a++)
		if(memcmp(a->tpa, tpa, Pasize) == 0)
			goto found;

	/*
	 * queue ip pkt to be resolved later
	 */
	t = toytime();
	mbp = &arpcache.unresol;
	for(m = *mbp; m; m = *mbp) {
		if(t >= m->param) {
			*mbp = m->next;
			mbfree(m);
			continue;
		}
		mbp = &m->next;
	}
	mb->param = t + SECOND(10);
	mb->next = 0;
	*mbp = mb;

	/*
	 * send an arp request
	 */
	unlock(&arpcache);

	m = mballoc(Ensize+Arpsize, 0, Mbarp2);
	q = (Arppkt*)m->data;

	DEBUG("snd arp req target %I ip dest %I\n", tpa, p->dst);

	memset(q->d, 0xff, Easize);		/* broadcast */
	hnputs(q->type, Arptype);
	hnputs(q->hrd, 1);
	hnputs(q->pro, Iptype);
	q->hln = Easize;
	q->pln = Pasize;
	hnputs(q->op, Arprequest);
	memmove(q->sha, cp->ilp.ea, Easize);
	memmove(q->spa, cp->ilp.ipmy, Pasize);
	memset(q->tha, 0, Easize);
	memmove(q->tpa, tpa, Pasize);

	send(cp->ilp.reply, m);

	return;

found:
	len = mb->count;		/* includes Ensize+Ipsize+Ilsize */
	memmove(p->d, a->tha, Easize);
	p->vihl = IP_VER|IP_HLEN;
	p->tos = 0;
	p->ttl = 255;
	id = arpcache.idgen;
	if(id == 0)
		id = toytime() * 80021;
	arpcache.idgen = id+1;
	unlock(&arpcache);
	hnputs(p->id, id);
	hnputs(p->type, Iptype);

	/*
	 * If we dont need to fragment just send it
	 */
	if(len <= ETHERMAXTU) {
		hnputs(p->length, len-Ensize);
		p->frag[0] = 0;
		p->frag[1] = 0;
		p->cksum[0] = 0;
		p->cksum[1] = 0;
		hnputs(p->cksum, ipcsum(&p->vihl));

		send(cp->ilp.reply, mb);
		return;
	}

	off = 0;
	len -= Ensize+Ipsize;		/* just ip data */

#define	ORDER	1

	while(len > 0) {
		dlen = (ETHERMAXTU-(Ensize+Ipsize)) & ~7;
		if(dlen > len)
			dlen = len;
		len -= dlen;

		/*
		 * use first frag in place,
		 * make copies of subsequent frags
		 * this saves a copy of a MTU-size buffer
		 */
		if(ORDER && off == 0) {
			m = 0;
			mb->count = (Ensize+Ipsize)+dlen;
			p = (Ippkt*)mb->data;
		} else {
			m = mballoc((Ensize+Ipsize)+dlen, cp, Mbip1);
			p = (Ippkt*)m->data;

			memmove(m->data, mb->data, Ensize+Ipsize);
			memmove(m->data+(Ensize+Ipsize),
				mb->data+(Ensize+Ipsize)+off, dlen);
		}

		hnputs(p->length, dlen+Ipsize);
		if(len == 0)
			hnputs(p->frag, off>>3);
		else
			hnputs(p->frag, (off>>3)|IP_MF);
		p->cksum[0] = 0;
		p->cksum[1] = 0;
		hnputs(p->cksum, ipcsum(&p->vihl));

		if(m)
			send(cp->ilp.reply, m);

		off += dlen;
	}
	if(ORDER)
		send(cp->ilp.reply, mb);
	else
		mbfree(mb);
}

int
ipforme(uchar addr[Pasize], Ifc *ifc)
{
	ulong haddr;

	if(memcmp(addr, ifc->ipa, Pasize) == 0)
		return 1;

	haddr = nhgetl(addr);

	/* My subnet broadcast */
	if((haddr&ifc->mask) == (ifc->ipaddr&ifc->mask))
		return 1;

	/* Real ip broadcast */
	if(haddr == 0)
		return 1;

	/* Old style 255.255.255.255 address */
	if(haddr == ~0)
		return 1;

	return 0;
}

/*
 * ipcsum - Compute internet header checksums
 */
int
ipcsum(uchar *addr)
{
	int len;
	ulong sum = 0;

	len = (addr[0]&0xf) << 2;

	while(len > 0) {
		sum += (addr[0]<<8) | addr[1] ;
		len -= 2;
		addr += 2;
	}

	sum = (sum & 0xffff) + (sum >> 16);
	sum = (sum & 0xffff) + (sum >> 16);
	return sum^0xffff;
}

/* 
 * protcol checksum routine
 */

static	short	endian	= 1;
static	char*	aendian	= (char*)&endian;
#define	LITTLE	*aendian

int
ptclcsum(uchar *addr, int len)
{
	ulong losum, hisum, mdsum, x;
	ulong t1, t2;

	losum = 0;
	hisum = 0;
	mdsum = 0;

	x = 0;
	if((ulong)addr & 1) {
		if(len) {
			hisum += addr[0];
			len--;
			addr++;
		}
		x = 1;
	}
	while(len >= 16) {
		t1 = *(ushort*)(addr+0);
		t2 = *(ushort*)(addr+2);	mdsum += t1;
		t1 = *(ushort*)(addr+4);	mdsum += t2;
		t2 = *(ushort*)(addr+6);	mdsum += t1;
		t1 = *(ushort*)(addr+8);	mdsum += t2;
		t2 = *(ushort*)(addr+10);	mdsum += t1;
		t1 = *(ushort*)(addr+12);	mdsum += t2;
		t2 = *(ushort*)(addr+14);	mdsum += t1;
		mdsum += t2;
		len -= 16;
		addr += 16;
	}
	while(len >= 2) {
		mdsum += *(ushort*)addr;
		len -= 2;
		addr += 2;
	}
	if(x) {
		if(len)
			losum += addr[0];
		if(LITTLE)
			losum += mdsum;
		else
			hisum += mdsum;
	} else {
		if(len)
			hisum += addr[0];
		if(LITTLE)
			hisum += mdsum;
		else
			losum += mdsum;
	}

	losum += hisum >> 8;
	losum += (hisum & 0xff) << 8;
	while(hisum = losum>>16)
		losum = hisum + (losum & 0xffff);

	return ~losum & 0xffff;
}

void
cmd_arp(int argc, char *argv[])
{
	int h, i, j;
	Arpe *a;

	if(argc <= 1) {
		print("arp flush -- clear cache\n");
		print("arp print -- print cache\n");
		return;
	}
	for(i=1; i<argc; i++) {
		if(strcmp(argv[i], "flush") == 0) {
			lock(&arpcache);
			for(h=0; h<Nb; h++)
				memset(&arpcache.abkt[h], 0, sizeof(arpcache.abkt[0]));
			unlock(&arpcache);
			continue;
		}
		if(strcmp(argv[i], "print") == 0) {
			for(h=0; h<Nb; h++) {
				a = arpcache.abkt[h].arpe;
				for(j=0; j<Ne; j++,a++) {
					if(memcmp(arpcache.null, a->tpa, Pasize) == 0)
						continue;
					print("%-15I %E\n", a->tpa, a->tha);
					prflush();
				}
			}
			continue;
		}
	}
}

bit.c           699855604   0     0     664     8723      `
#include	"all.h"
#include	"mem.h"
#include	"io.h"

enum
{
	MaxBit		= 2,
	NOUT		= 100,
	INTDELAY	= 1000,
	DMADELAY	= 1000,
	Ureset		= 0,
	Uread,
	Uwrite,
};

static
struct
{
	Queue*	reply;
	struct	Bp
	{
		Rendez	r;
		QLock	l;
		Chan*	chan;
		int	cmdwait;
		int	dma;
		Vmedevice* vme;

		struct	Rout
		{
			Msgbuf*	msg;
			ulong	u;
			int	t1;
		} rout[NOUT];

		Filter	rate;
		Filter	count;
	} b[MaxBit];
} b;

struct	Bit
{
	struct	localnode
	{
		uchar	junk1;
		uchar	lncommand;
		uchar	junk2;
		uchar	lnstatus;
		uchar	lnaddrmod;
		uchar	junk3;
		uchar	junk4;
		uchar	lnvec;
	};
	union	remotenode
	{
		struct
		{
			uchar	rncommand2;
			uchar	rncommand1;	/* also status */
			ushort	rnpage;
			uchar	rnaddrmod;
			uchar	junk5;
			uchar	rniackhi;
			uchar	rniacklo;
		};
		struct
		{
			uchar	junka1;
			uchar	rnstatus;
			uchar	junka2;
			uchar	junka3;
			uchar	junka4;
			uchar	junka5;
			uchar	junka6;
			uchar	junka7;
		};
	};
	struct	localdma
	{
		uchar	ldcommand;
		uchar	junk6;
		ushort	ldaddrhi;
		ushort	ldaddrlo;
		ushort	ldcount;
	};
	struct	remotedma
	{
		uchar	junk7;
		uchar	junk8;
		ushort	rdaddrhi;
		ushort	rdaddrlo;
		uchar	junk9;
		uchar	junka;
	};
};

int
bitinit(Vmedevice *vp)
{
	Bp *bp;
	Rout *ro;
	Bit *bit;
	int c;

	print("bitinit %s\n", vp->name);
	if(vp->ctlr >= MaxBit)
		return -1;
	bp = &b.b[vp->ctlr];
	if(bp->vme)
		return -1;
	bit = vp->address;
	/*
	 * does the board exist?
	if(probe(&bit->lnstatus, sizeof(bit->lnstatus)))
		return -1;
	 */

	bp->dma = 0;

	c = bit->lnstatus;
	if(c & 1) {
		print("	%s: remote power off\n", vp->name);
		return -1;
	}
	c = bit->rnstatus;
	USED(c);
	bit->lncommand = 0xc0;		/* clear errors, clear PR ff */
	bit->rncommand1 = 0x48;		/* clear PT ff, page mode */
	c = bit->lnstatus;
	if(c & 0xe3) {
		print("	%s: bad local status = %.2x\n", vp->name, c);
		return -1;
	}
	bit->lnvec = vp->vector;

	bit->rnaddrmod = 0x09;		/* A32 non-priv */
	bit->rncommand2 = 0x40;		/* use addr mod reg */
	bit->lnaddrmod = 0x09;		/* A32 non-priv */
	bit->ldcommand = 0x0;		/* dma done, no interrupt */

	ro = bp->rout;
	for(c=0; c<NOUT; c++, ro++) {
		ro->u = 0;
		ro->msg = 0;
		ro->t1 = 0;
	}

	bp->l.name = "bit3";
	qlock(&bp->l);
	qunlock(&bp->l);
	lock(&bp->r);
	unlock(&bp->r);

	dofilter(&bp->count);
	dofilter(&bp->rate);

	bp->vme = vp;
	vp->private = bp;
	return 0;
}

void
bitintr(Vmedevice *vp)
{
	Bp *bp;
	Bit *bit;
	int rst;

	bp = vp->private;
	bit = vp->address;

	if(bp->dma) {
		print("%s: dma set\n", vp->name);
		return;
	}
	rst = bit->rnstatus;
	if(rst == 0xff) {
		print("%s: status r=%.2x\n", vp->name, rst);
		return;
	}
	if(!(rst & 0x02)) {
		print("%s: PT not set r=%.2x\n", vp->name, rst);
		return;
	}

	bit->rncommand1 = 0x48;		/* clear PT ff, page mode */

	if(bp->cmdwait)
		print("%s: second interrupt before reply\n", vp->name);
	bp->cmdwait = 1;
	wakeup(&bp->r);
}

void
bitstore(Bp *bp, ulong va, ulong l)
{
	Bit *bit;
	Vmedevice *vp;
	ulong a;

	vp = bp->vme;
	bit = vp->address;
	a = (va & 0x0ffffffc) | (SLAVE << 28);
	bit->rnpage = a >> 16;
	*(ulong*)(vp->address1 | (a & 0xfffc)) = l;
}

ulong
bitfetch(Bp *bp, ulong va)
{
	Bit *bit;
	Vmedevice *vp;
	ulong a;

	vp = bp->vme;
	bit = vp->address;
	a = (va & 0x0ffffffc) | (SLAVE << 28);
	bit->rnpage = a >> 16;
	return *(ulong*)(vp->address1 | (a & 0xfffc));
}

void
bitcopy(Bp *bp, void *la, ulong ra, int n, int dir)
{
	Bit *bit;
	Vmedevice *vp;
	ulong *l1, *l2, a;
	int i;

	vp = bp->vme;
	bit = vp->address;

	bp->rate.count += n/4;

	a = (ra & 0x0ffffffc) | (SLAVE << 28);
	if(n <= 80 && (((ra+n-1) ^ ra) & 0xffff0000) == 0) {
		bit->rnpage = a >> 16;
		l1 = (ulong*)(vp->address1 | (a & 0xfffc));
		l2 = (ulong*)la;
		if(dir == Uread) {
			while(n > 0) {
				*l2++ = *l1++;
				n -= 4;
			}
		} else {
			while(n > 0) {
				*l1++ = *l2++;
				n -= 4;
			}
		}
		return;
	}

	bit->rdaddrhi = a >> 16;
	bit->rdaddrlo = a >> 0;

	a = ((ulong)la & 0x0ffffffc) | (SLAVE << 28);
	bit->ldaddrhi = a >> 16;
	bit->ldaddrlo = a >> 0;

	bit->ldcount = (n+255) >> 8;

	bit->rnaddrmod = 0x09;	/* A32 non-priv */
	bit->lnaddrmod = 0x09;	/* A32 non-priv */
	bit->lncommand = 0xc4;	/* disable local interrupts */
	bit->rncommand2 = 0x50;	/* no pause, use modreg, disable intr, block mode */

	for(i=0; i<DMADELAY; i++)
		;
	bp->dma = 1;
	bit->rncommand1 = 0x00;	/* no page mode */
	if(dir == Uread)	/* start, to/from, longword, nopause, block mode */
		bit->ldcommand = 0x90;
	else
		bit->ldcommand = 0xb0;	/* why b0 and 91?? */
	wbflush();

	while(!(bit->ldcommand & 0x2))
		;

	bit->ldcommand = 0;	/* clear done */
	bp->dma = 0;
	bit->lncommand = 0xc0;	/* reenable interrupts? */
	bit->rncommand2 = 0x40;	/* reenable interrupts? */
	bit->rncommand1 = 0x08;	/* page mode */
	bit->rnaddrmod = 0x09;	/* A32 */
	bit->lnaddrmod = 0x09;	/* A32 */
}

void
bitsync(Bp *bp, ulong u, int type, Msgbuf *mb)
{
	Rout *ro;
	ulong u2, u3;
	int i, j;

	ro = bp->rout;
	for(i=0; i<NOUT; i++, ro++)
		if(ro->u == u)
			goto found;
	if(type != 0) {
		print("new bitsync and not allocated\n");
		return;
	}
loop:
	ro = bp->rout;
	for(i=0; i<NOUT; i++, ro++)
		if(ro->u == 0)
			goto found;
	print("nout full, flushing all outstanding\n");

	ro = bp->rout;
	for(i=0; i<NOUT; i++, ro++)
		ro->u = 0;
	goto loop;

found:
	switch(type) {
	case 0:			/* write */
		if(cons.flags&Fbit)
			print("s0 i=%d\n", i);
		ro->u = u;
		ro->t1 = 0;
		if(ro->msg)
			mbfree(ro->msg);
		ro->msg = 0;
		return;
	case 1:			/* read */
		if(cons.flags&Fbit)
			print("s1 i=%d m=%lux\n", i, ro->msg);
		ro->t1 = 1;
		break;
	case 2:			/* reply */
		if(cons.flags&Fbit)
			print("s2 i=%d t=%d\n", i, ro->t1);
		ro->msg = mb;
		break;
	}
	if(!ro->t1 || !ro->msg)
		return;
	bitstore(bp, 0x78, 1);			/* disable interrupts */
	for(i=0; i<INTDELAY; i++)
		;

	u2 = bitfetch(bp, u+1*sizeof(ulong));	/* buf */
	u3 = bitfetch(bp, u+2*sizeof(ulong));	/* count */

	mb = ro->msg;
	if(mb->count < u3)
		u3 = mb->count;

	if(cons.flags&Fbit)
		print("se i=%d r=%d b=%lux c=%d\n", i,
			mb->data[0], u2, u3);
	if(cons.flags&Fbitx)
	if(mb->data[0] == 37 && mb->data[6] > 2)
		for(j=9; j<u3; j++)
			if(mb->data[8] != mb->data[j]) {
				print("r %d %x %x\n", j-1, mb->data[8],
					mb->data[j]);
				break;
			}

	bitcopy(bp, mb->data, u2, u3, Uwrite);
	mbfree(mb);
	ro->u = 0;
	ro->t1 = 0;
	ro->msg = 0;
	bitstore(bp, u+3*sizeof(ulong), u3);	/* set reply count */

	bitstore(bp, 0x78, 0);			/* enable interrupts */
}

void
bito(void)
{
	Msgbuf *mb;
	Chan *cp;
	Bp *bp;
	ulong u;

	print("b3o\n");

loop:
	mb = recv(b.reply, 0);
	if(!mb) {
		print("zero message\n");
		goto loop;
	}

	cp = mb->chan;
	bp = cp->bitp.bitp;

	qlock(&bp->l);
	bitsync(bp, mb->param, 2, mb);
	qunlock(&bp->l);

	goto loop;
}

int
bitcmd(void *v)
{
	return ((Bp*)v)->cmdwait;
}

void
biti(void)
{
	Chan *cp;
	Bp *bp;
	Vmedevice *vp;
	Msgbuf *mb;
	ulong u, u1, u2, u3;
	int i, j;

	bp = getarg();
	vp = bp->vme;

	print("b3i: %s\n", vp->name);
	cp = chaninit(Devbit, 1);
	bp->chan = cp;
	cp->send = serveq;
	cp->reply = b.reply;
	cp->bitp.bitp = bp;

loop:
	while(!bitcmd(bp))
		sleep(&bp->r, bitcmd, bp);
	qlock(&bp->l);

	bp->count.count++;
	u = bitfetch(bp, 0x7c);
	u1 = bitfetch(bp, u+0*sizeof(ulong));	/* opcode */
	switch(u1) {
	default:
		print("%s: unknown bit command\n", vp->name);
		break;

	case Ureset:
		print("%s: reset\n", vp->name);
		fileinit(cp);
		cp->whotime = 0;
		strcpy(cp->whoname, "<none>");
		for(i=0; i<NOUT; i++)
			bp->rout[i].u = 0;
		break;

	case Uread:
		bitsync(bp, u, 1, 0);
		break;

	case Uwrite:
		u2 = bitfetch(bp, u+1*sizeof(ulong));	/* bufferp */
		u3 = bitfetch(bp, u+2*sizeof(ulong));	/* count */
		mb = mballoc(u3+256, cp, Mbbit);
		bitcopy(bp, mb->data, u2, u3, Uread);
		mb->count = u3;
		mb->param = u;
		if(cons.flags&Fbitx)
		if(mb->data[0] == 38 && mb->data[12] > 2)
			for(j=15; j<u3; j++)
				if(mb->data[14] != mb->data[j]) {
					print("w %d %x %x\n", j-1,
						mb->data[14],
						mb->data[j]);
					break;
				}
		send(cp->send, mb);
		bitsync(bp, u, 0, 0);
		break;
	}
	bp->cmdwait = 0;
	bitstore(bp, 0x7c, 0);
	qunlock(&bp->l);
	goto loop;
}

static
void
cmd_stats(int argc, char *argv[])
{
	Bp *bp;
	Vmedevice *vp;

	USED(argc);
	USED(argv);

	print("bit stats\n");
	for(bp = &b.b[0]; bp < &b.b[MaxBit]; bp++) {
		vp = bp->vme;
		if(!vp)
			continue;
		print("	%s\n", vp->name);
		print("		work = %F mps\n", (Filta){&bp->count, 1});
		print("		rate = %F tBps\n", (Filta){&bp->rate, 250});
		bp++;
	}
}

void
bitstart(void)
{
	Bp *bp;
	int any;

	any = 0;
	for(bp = &b.b[0]; bp < &b.b[MaxBit]; bp++) {
		if(bp->vme == 0)
			continue;
		if(any == 0) {
			any = 1;
			b.reply = newqueue(NQUEUE);
			userinit(bito, 0, "b3o");
			cmd_install("statb", "-- bit stats", cmd_stats);
		}
		userinit(biti, bp, "b3i");
	}
}

chk.c           715886577   0     0     664     13555     `
#include	"all.h"

#define	DSIZE		546000

static	char*	abits;
static	long	sizabits;
static	char*	qbits;
static	long	sizqbits;
static	char*	name;
static	long	sizname;
static	long	fstart;
static	long	fsize;
static	long	nfiles;
static	long	maxq;
static	char*	calloc;
static	Device	dev;
static	long	ndup;
static	long	nused;
static	long	nfdup;
static	long	nqbad;
static	long	nfree;
static	long	nbad;
static	int	mod;
static	int	flags;
static	int	ronly;
static	int	cwflag;
static	long	sbaddr;
static	long	oldblock;
static	int	depth;
static	int	maxdepth;

/* local prototypes */
static	int	fsck(Dentry*);
static	void	ckfreelist(Superb*);
static	void	mkfreelist(Superb*);
static	void	trfreelist(Superb*);
static	void	xaddfree(Device, long, Superb*, Iobuf*);
static	void	xflush(Device, Superb*, Iobuf*);
static	Dentry*	maked(long, int, long);
static	void	modd(long, int, Dentry*);
static	void	xread(long, long);
static	int	amark(long);
static	int	fmark(long);
static	int	ftest(long);
static	void	missing(void);
static	void	qmark(long);
static	void*	malloc(ulong);
static	Iobuf*	xtag(long, int, long);

static
void*
malloc(ulong n)
{
	char *p, *q;

	p = calloc;
	while((ulong)p & 3)
		p++;
	q = p+n;
	if(((ulong)q&0x0fffffffL) >= conf.mem)
		panic("check: mem size");
	calloc = q;
	memset(p, 0, n);
	return p;
}

/*
 * check flags
 */
enum
{
	Crdall	= (1<<0),	/* read all files */
	Ctag	= (1<<1),	/* rebuild tags */
	Cpfile	= (1<<2),	/* print files */
	Cpdir	= (1<<3),	/* print directories */
	Cfree	= (1<<4),	/* rebuild free list */
	Csetqid	= (1<<5),	/* resequence qids */
	Cream	= (1<<6),	/* clear all bad tags */
	Cbad	= (1<<7),	/* clear all bad blocks */
	Ctouch	= (1<<8),	/* touch old dir and indir */
};

static
struct
{
	char*	option;
	long	flag;
} ckoption[] =
{
	"rdall",	Crdall,
	"tag",		Ctag,
	"pfile",	Cpfile,
	"pdir",		Cpdir,
	"free",		Cfree,
	"setqid",	Csetqid,
	"ream",		Cream,
	"bad",		Cbad,
	"touch",	Ctouch,
	0,
};

void
cmd_check(int argc, char *argv[])
{
	long f, i;
	Filsys *fs;
	Iobuf *p;
	Superb *sb;
	Dentry *d;
	long raddr, flag;

	flag = 0;
	for(i=1; i<argc; i++) {
		for(f=0; ckoption[f].option; f++)
			if(strcmp(argv[i], ckoption[f].option) == 0)
				goto found;
		print("unnown check option %s\n", argv[i]);
		for(f=0; ckoption[f].option; f++)
			print("	%s\n", ckoption[f].option);
		return;
	found:
		flag |= ckoption[f].flag;
	}
	fs = cons.curfs;

	dev = fs->dev;
	ronly = (dev.type == Devro);
	cwflag = (dev.type == Devcw) | (dev.type == Devro);
	if(!ronly)
		wlock(&mainlock);		/* check */
	calloc = (char*)ialloc(0, 0) + 100000;
	flags = flag;

	sizqbits = ((1<<22) + 7) / 8;		/* botch */
	qbits = malloc(sizqbits);

	sbaddr = superaddr(dev);
	raddr = getraddr(dev);
	p = xtag(sbaddr, Tsuper, QPSUPER);
	if(!p)
		goto out;
	sb = (Superb*)p->iobuf;
	fstart = 2;
	cons.noage = 1;

	fsize = sb->fsize;
	sizabits = (fsize-fstart + 7)/8;
	abits = malloc(sizabits);

	sizname = 4000;
	name = malloc(sizname);
	sizname -= NAMELEN+10;	/* for safety */

	mod = 0;
	nfree = 0;
	nfdup = 0;
	nused = 0;
	nbad = 0;
	ndup = 0;
	nqbad = 0;
	depth = 0;
	maxdepth = 0;

	if(flags & Ctouch) {
		oldblock = fsize/DSIZE;
		oldblock *= DSIZE;
		if(oldblock < 0)
			oldblock = 0;
		print("oldblock = %ld\n", oldblock);
	}
	if(amark(sbaddr))
		;
	if(cwflag) {
		if(amark(sb->roraddr))
			;
		if(amark(sb->next))
			;
	}

	print("checking filsys: %s\n", fs->name);
	nfiles = 0;
	maxq = 0;

	d = maked(raddr, 0, QPROOT);
	if(d) {
		if(amark(raddr))
			;
		if(fsck(d))
			modd(raddr, 0, d);
		depth--;
		calloc -= sizeof(Dentry);
		if(depth)
			print("depth not zero on return\n");
	}

	if(flags & Cfree) {
		if(cwflag)
			trfreelist(sb);
		else
			mkfreelist(sb);
	}

	if(sb->qidgen < maxq)
		print("qid generator low path=%ld maxq=%ld\n",
			sb->qidgen, maxq);
	if(!(flags & Cfree))
		ckfreelist(sb);
	if(mod) {
		sb->qidgen = maxq;
		print("file system was modified\n");
		settag(p, Tsuper, QPNONE);
	}

	print("nfiles = %ld\n", nfiles);
	print("fsize  = %ld\n", fsize);
	print("nused  = %ld\n", nused);
	print("ndup   = %ld\n", ndup);
	print("nfree  = %ld\n", nfree);
	print("tfree  = %ld\n", sb->tfree);
	print("nfdup  = %ld\n", nfdup);
	print("nmiss  = %ld\n", fsize-fstart-nused-nfree);
	print("nbad   = %ld\n", nbad);
	print("nqbad  = %ld\n", nqbad);
	print("maxq   = %ld\n", maxq);
	if(!cwflag)
		missing();

out:
	cons.noage = 0;
	putbuf(p);
	if(!ronly)
		wunlock(&mainlock);
}

static
int
fsck(Dentry *d)
{
	Dentry *nd;
	Iobuf *p1, *p2, *pd;
	int i, j, k, ns, dmod;
	long a, qpath;

	depth++;
	if(depth >= maxdepth) {
		maxdepth = depth;
		if(maxdepth >= 100) {
			print("max depth exceeded: %s\n", name);
			return 0;
		}
	}
	dmod = 0;
	if(!(d->mode & DALLOC))
		goto out;
	nfiles++;

	ns = strlen(name);
	i = strlen(d->name);
	if(i >= NAMELEN) {
		d->name[NAMELEN-1] = 0;
		print("%s->name (%s) not terminated\n", name, d->name);
		return 0;
	}
	ns += i;
	if(ns >= sizname) {
		print("%s->name (%s) name too large\n", name, d->name);
		return 0;
	}
	strcat(name, d->name);

	if(d->mode & DDIR) {
		if(ns > 1) {
			strcat(name, "/");
			ns++;
		}
		if(flags & Cpdir) {
			print("%s\n", name);
			prflush();
		}
	} else
	if(flags & Cpfile) {
		print("%s\n", name);
		prflush();
	}

	qpath = d->qid.path & ~QPDIR;
	qmark(qpath);
	if(qpath > maxq)
		maxq = qpath;
	for(i=0; i<NDBLOCK; i++) {
		a = d->dblock[i];
		if(amark(a)) {
			if(flags & Cbad) {
				d->dblock[i] = 0;
				dmod++;
			}
			a = 0;
		}
		if(!a)
			continue;
		if(d->mode & DDIR) {
			if((flags&Ctouch) && a < oldblock) {
				pd = getbuf(dev, a, Bread|Bmod);
				if(pd)
					putbuf(pd);
				dmod++;
			}
			for(k=0; k<DIRPERBUF; k++) {
				nd = maked(a, k, qpath);
				if(!nd)
					break;
				if(fsck(nd)) {
					modd(a, k, nd);
					dmod++;
				}
				depth--;
				calloc -= sizeof(Dentry);
				name[ns] = 0;
			}
			continue;
		}
		if(flags & Crdall)
			xread(a, qpath);
	}
	a = d->iblock;
	if(amark(a)) {
		if(flags & Cbad) {
			d->iblock = 0;
			dmod++;
		}
		a = 0;
	}
	if(a) {
		if((flags&Ctouch) && a < oldblock) {
			pd = getbuf(dev, a, Bread|Bmod);
			if(pd)
				putbuf(pd);
			dmod++;
		}
		if(p1 = xtag(a, Tind1, qpath))
		for(i=0; i<INDPERBUF; i++) {
			a = ((long*)p1->iobuf)[i];
			if(amark(a)) {
				if(flags & Cbad) {
					((long*)p1->iobuf)[i] = 0;
					p1->flags |= Bmod;
				}
				a = 0;
			}
			if(!a)
				continue;
			if(d->mode & DDIR) {
				if((flags&Ctouch) && a < oldblock) {
					pd = getbuf(dev, a, Bread|Bmod);
					if(pd)
						putbuf(pd);
					dmod++;
				}
				for(k=0; k<DIRPERBUF; k++) {
					nd = maked(a, k, qpath);
					if(!nd)
						break;
					if(fsck(nd)) {
						modd(a, k, nd);
						dmod++;
					}
					depth--;
					calloc -= sizeof(Dentry);
					name[ns] = 0;
				}
				continue;
			}
			if(flags & Crdall)
				xread(a, qpath);
		}
		if(p1)
			putbuf(p1);
	}
	a = d->diblock;
	if(amark(a)) {
		if(flags & Cbad) {
			d->diblock = 0;
			dmod++;
		}
		a = 0;
	}
	if((flags&Ctouch) && a && a < oldblock) {
		pd = getbuf(dev, a, Bread|Bmod);
		if(pd)
			putbuf(pd);
		dmod++;
	}
	if(p2 = xtag(a, Tind2, qpath))
	for(i=0; i<INDPERBUF; i++) {
		a = ((long*)p2->iobuf)[i];
		if(amark(a)) {
			if(flags & Cbad) {
				((long*)p2->iobuf)[i] = 0;
				p2->flags |= Bmod;
			}
			continue;
		}
		if((flags&Ctouch) && a && a < oldblock) {
			pd = getbuf(dev, a, Bread|Bmod);
			if(pd)
				putbuf(pd);
			dmod++;
		}
		if(p1 = xtag(a, Tind1, qpath))
		for(j=0; j<INDPERBUF; j++) {
			a = ((long*)p1->iobuf)[j];
			if(amark(a)) {
				if(flags & Cbad) {
					((long*)p1->iobuf)[j] = 0;
					p1->flags |= Bmod;
				}
				continue;
			}
			if(!a)
				continue;
			if(d->mode & DDIR) {
				if((flags&Ctouch) && a < oldblock) {
					pd = getbuf(dev, a, Bread|Bmod);
					if(pd)
						putbuf(pd);
					dmod++;
				}
				for(k=0; k<DIRPERBUF; k++) {
					nd = maked(a, k, qpath);
					if(!nd)
						break;
					if(fsck(nd)) {
						modd(a, k, nd);
						dmod++;
					}
					depth--;
					calloc -= sizeof(Dentry);
					name[ns] = 0;
				}
				continue;
			}
			if(flags & Crdall)
				xread(a, qpath);
		}
		if(p1)
			putbuf(p1);
	}
	if(p2)
		putbuf(p2);
out:
	return dmod;
}

#define	XFEN	(FEPERBUF+6)
typedef
struct
{
	int	flag;
	int	count;
	int	next;
	long	addr[XFEN];
} Xfree;

void
xaddfree(Device dev, long a, Superb *sb, Iobuf *p)
{
	Xfree *x;

	x = (Xfree*)p->iobuf;
	if(x->count < XFEN) {
		x->addr[x->count] = a;
		x->count++;
		return;
	}
	if(!x->flag) {
		memset(&sb->fbuf, 0, sizeof(sb->fbuf));
		sb->fbuf.free[0] = 0L;
		sb->fbuf.nfree = 1;
		sb->tfree = 0;
		x->flag = 1;
	}
	addfree(dev, a, sb);
}

void
xflush(Device dev, Superb *sb, Iobuf *p)
{
	int i;
	Xfree *x;

	x = (Xfree*)p->iobuf;
	if(!x->flag) {
		memset(&sb->fbuf, 0, sizeof(sb->fbuf));
		sb->fbuf.free[0] = 0L;
		sb->fbuf.nfree = 1;
		sb->tfree = 0;
	}
	for(i=0; i<x->count; i++)
		addfree(dev, x->addr[i], sb);
}

/*
 * make freelist
 * from existing freelist
 * (cw devices)
 */
static
void
trfreelist(Superb *sb)
{
	long a;
	int n, i;
	Iobuf *p, *xp;
	Fbuf *fb;


	xp = getbuf(devnone, Cckbuf, 0);
	memset(xp->iobuf, 0, BUFSIZE);
	fb = &sb->fbuf;
	p = 0;
	for(;;) {
		n = fb->nfree;
		if(n < 0 || n > FEPERBUF)
			break;
		for(i=1; i<n; i++) {
			a = fb->free[i];
			if(a && !ftest(a))
				xaddfree(dev, a, sb, xp);
		}
		a = fb->free[0];
		if(!a)
			break;
		if(ftest(a))
			break;
		xaddfree(dev, a, sb, xp);
		if(p)
			putbuf(p);
		p = xtag(a, Tfree, QPNONE);
		if(!p)
			break;
		fb = (Fbuf*)p->iobuf;
	}
	if(p)
		putbuf(p);
	xflush(dev, sb, xp);
	putbuf(xp);
	mod++;
	print("%ld blocks free\n", sb->tfree);
}

static
void
ckfreelist(Superb *sb)
{
	long a, lo, hi;
	int n, i;
	Iobuf *p;
	Fbuf *fb;


	strcpy(name, "free list");
	print("check %s\n", name);
	fb = &sb->fbuf;
	a = sbaddr;
	p = 0;
	lo = 0;
	hi = 0;
	for(;;) {
		n = fb->nfree;
		if(n < 0 || n > FEPERBUF) {
			print("check: nfree bad %ld\n", a);
			break;
		}
		for(i=1; i<n; i++) {
			a = fb->free[i];
			if(a && !fmark(a)) {
				if(!lo || lo > a)
					lo = a;
				if(!hi || hi < a)
					hi = a;
			}
		}
		a = fb->free[0];
		if(!a)
			break;
		if(fmark(a))
			break;
		if(!lo || lo > a)
			lo = a;
		if(!hi || hi < a)
			hi = a;
		if(p)
			putbuf(p);
		p = xtag(a, Tfree, QPNONE);
		if(!p)
			break;
		fb = (Fbuf*)p->iobuf;
	}
	if(p)
		putbuf(p);
	print("lo = %ld; hi = %ld\n", lo, hi);
}

/*
 * make freelist from scratch
 */
static
void
mkfreelist(Superb *sb)
{
	long a;
	int i, b;

	if(ronly) {
		print("cant make freelist on ronly device\n");
		return;
	}
	strcpy(name, "free list");
	memset(&sb->fbuf, 0, sizeof(sb->fbuf));
	sb->fbuf.free[0] = 0L;
	sb->fbuf.nfree = 1;
	sb->tfree = 0;
	for(a=fsize-fstart-1; a >= 0; a--) {
		i = a/8;
		if(i < 0 || i >= sizabits)
			continue;
		b = 1 << (a&7);
		if(abits[i] & b)
			continue;
		addfree(dev, fstart+a, sb);
	}
	print("%ld blocks free\n", sb->tfree);
	mod++;
}

static
Dentry*
maked(long a, int s, long qpath)
{
	Iobuf *p;
	Dentry *d, *d1;

	p = xtag(a, Tdir, qpath);
	if(!p)
		return 0;
	d = getdir(p, s);
	d1 = malloc(sizeof(Dentry));
	memmove(d1, d, sizeof(Dentry));
	putbuf(p);
	return d1;
}

static
void
modd(long a, int s, Dentry *d1)
{
	Iobuf *p;
	Dentry *d;

	if(!(flags & Cbad))
		return;
	p = getbuf(dev, a, Bread);
	d = getdir(p, s);
	if(!d) {
		if(p)
			putbuf(p);
		return;
	}
	memmove(d, d1, sizeof(Dentry));
	p->flags |= Bmod;
	putbuf(p);
}

static
void
xread(long a, long qpath)
{
	Iobuf *p;

	p = xtag(a, Tfile, qpath);
	if(p)
		putbuf(p);
}

static
Iobuf*
xtag(long a, int tag, long qpath)
{
	Iobuf *p;

	if(a == 0)
		return 0;
	p = getbuf(dev, a, Bread);
	if(!p) {
		print("check: \"%s\": xtag: p null\n", name);
		if(flags & (Cream|Ctag)) {
			p = getbuf(dev, a, Bmod);
			if(p) {
				memset(p->iobuf, 0, RBUFSIZE);
				settag(p, tag, qpath);
				mod++;
				return p;
			}
		}
		return 0;
	}
	if(checktag(p, tag, qpath)) {
		print("check: \"%s\": xtag: checktag\n", name);
		if(flags & (Cream|Ctag)) {
			if(flags & Cream)
				memset(p->iobuf, 0, RBUFSIZE);
			settag(p, tag, qpath);
			mod++;
			return p;
		}
		return p;
	}
	return p;
}

static
int
amark(long a)
{
	long i;
	int b;

	if(a < fstart || a >= fsize) {
		if(a == 0)
			return 0;
		print("check: \"%s\": range %ld\n",
			name, a);
		nbad++;
		return 1;
	}
	a -= fstart;
	i = a/8;
	b = 1 << (a&7);
	if(abits[i] & b) {
		if(!ronly) {
			if(ndup < 10)
				print("check: \"%s\": address dup %ld\n",
					name, fstart+a);
			else
			if(ndup == 10)
				print("...");
		}
		ndup++;
		return 1;
	}
	abits[i] |= b;
	nused++;
	return 0;
}

static
int
fmark(long a)
{
	long i;
	int b;

	if(a < fstart || a >= fsize) {
		print("check: \"%s\": range %ld\n",
			name, a);
		nbad++;
		return 1;
	}
	a -= fstart;
	i = a/8;
	b = 1 << (a&7);
	if(abits[i] & b) {
		print("check: \"%s\": address dup %ld\n",
			name, fstart+a);
		nfdup++;
		return 1;
	}
	abits[i] |= b;
	nfree++;
	return 0;
}

static
int
ftest(long a)
{
	long i;
	int b;

	if(a < fstart || a >= fsize)
		return 1;
	a -= fstart;
	i = a/8;
	b = 1 << (a&7);
	if(abits[i] & b)
		return 1;
	abits[i] |= b;
	return 0;
}

static
void
missing(void)
{
	long a, i;
	int b, n;

	n = 0;
	for(a=fsize-fstart-1; a>=0; a--) {
		i = a/8;
		b = 1 << (a&7);
		if(!(abits[i] & b)) {
			print("missing: %ld\n", fstart+a);
			n++;
		}
		if(n > 10) {
			print(" ...\n");
			break;
		}
	}
}

static
void
qmark(long qpath)
{
	int i, b;

	i = qpath/8;
	b = 1 << (qpath&7);
	if(i < 0 || i >= sizqbits) {
		nqbad++;
		if(nqbad < 20)
			print("check: \"%s\": qid out of range %lux\n",
				name, qpath);
		return;
	}
	if((qbits[i] & b) && !ronly) {
		nqbad++;
		if(nqbad < 20)
			print("check: \"%s\": qid dup %lux\n",
				name, qpath);
	}
	qbits[i] |= b;
}

clock.c         710793598   0     0     664     3302      `
#include	"all.h"
#include	"mem.h"
#include	"io.h"
#include	"ureg.h"

Alarm	*alarmtab;

/*
 * Insert new into list after where
 */
static void
insert(List **head, List *where, List *new)
{
	if(where == 0){
		new->next = *head;
		*head = new;
	}else{
		new->next = where->next;
		where->next = new;
	}
		
}

/*
 * Delete old from list.  where->next is known to be old.
 */
static void
delete(List **head, List *where, List *old)
{
	if(where == 0){
		*head = old->next;
		return;
	}
	where->next = old->next;
}

Alarm*
alarm(int ms, void (*f)(Alarm*, void*), void *arg)
{
	Alarm *a, *w, *pw;
	ulong s;
	if(ms < 0)
		ms = 0;
	a = newalarm();
	a->dt = ms/MS2HZ;
	a->f = f;
	a->arg = arg;
	s = splhi();
	lock(&m->alarmlock);
	pw = 0;
	for(w=m->alarm; w; pw=w, w=w->next){
		if(w->dt <= a->dt){
			a->dt -= w->dt;
			continue;
		}
		w->dt -= a->dt;
		break;
	}
	insert(&m->alarm, pw, a);
	unlock(&m->alarmlock);
	splx(s);
	return a;
}

void
cancel(Alarm *a)
{
	a->f = 0;
}

Alarm*
newalarm(void)
{
	int i;
	Alarm *a;

	for(i=0,a=alarmtab; i<conf.nalarm; i++,a++)
		if(a->busy==0 && a->f==0 && canlock(a)){
			if(a->busy){
				unlock(a);
				continue;
			}
			a->f = 0;
			a->arg = 0;
			a->busy = 1;
			unlock(a);
			return a;
		}
	panic("newalarm");
	return 0;
}

void
alarminit(void)
{
	extern char etext[];

	cons.minpc = KTZERO;
	cons.maxpc = (ulong)etext;
	cons.nprofbuf = (cons.maxpc-cons.minpc) >> LRES;
	cons.profbuf = ialloc(cons.nprofbuf*sizeof(cons.profbuf[0]), 0);

	alarmtab = ialloc(conf.nalarm*sizeof(Alarm), 0);
}

static
struct
{
	int	nfilter;
	Filter*	filters[100];
	int	time;
	int	duart;
} f;

void
dofilter(Filter *ft)
{
	int i;

	i = f.nfilter;
	if(i >= sizeof(f.filters)/sizeof(f.filters[0])) {
		print("dofilter: too many filters\n");
		return;
	}
	f.filters[i] = ft;
	f.nfilter = i+1;
}

void
clock(ulong n, ulong pc)
{
	int i;
	Alarm *a;
	void (*fn)(Alarm*, void*);
	User *p;
	Filter *ft;
	ulong c0, c1;

	clockreload(n);
	m->ticks++;

	if(cons.profile) {
		cons.profbuf[0] += MS2HZ;
		if(cons.minpc<=pc && pc<cons.maxpc){
			pc -= cons.minpc;
			pc >>= LRES;
			cons.profbuf[pc] += MS2HZ;
		} else
			cons.profbuf[1] += MS2HZ;
	}

	lights(Lreal, (m->ticks>>6)&1);
	if(m->machno == 0){
		if(f.duart >= 0) {
			duartxmit(f.duart);
			f.duart = -1;
		}
		p = m->proc;
		if(p == 0)
			p = m->intrp;
		if(p)
			p->time.count += MS2HZ;
		for(i=1; i<conf.nmach; i++){
			if(active.machs & (1<<i)){
				p = MACHP(i)->proc;
				if(p && p != m->intrp)
					p->time.count += MS2HZ;
			}
		}
		m->intrp = 0;

		f.time += MS2HZ;
		while(f.time >= 1000) {
			f.time -= 1000;
			for(i=0; i<f.nfilter; i++) {
				ft = f.filters[i];
				c0 = ft->count;
				c1 = c0 - ft->oldcount;
				ft->oldcount = c0;
				ft->filter[0] = famd(ft->filter[0], c1, 59, 60);
				ft->filter[1] = famd(ft->filter[1], c1, 599, 600);
				ft->filter[2] = famd(ft->filter[2], c1, 5999, 6000);
			}
		}
	}
	if(active.exiting && active.machs&(1<<m->machno)){
		print("someone's exiting\n");
		exit();
	}
	if(canlock(&m->alarmlock)){
		if(m->alarm){
			a = m->alarm;
			a->dt--;
			while(a && a->dt<=0){
				fn = a->f;	/* avoid race with cancel */
				if(fn)
					(*fn)(a, a->arg);
				delete(&m->alarm, 0, a);
				a->busy = 0;
				a = m->alarm;
			}
		}
		unlock(&m->alarmlock);
	}
}

void
duartstart(int c)
{
	f.duart = c;
}
con.c           716114338   0     0     664     11871     `
#include	"all.h"
#include	"mem.h"

static	char	conline[100];
static	Command	command[100];
static	Flag	flag[35];
static	void	installcmds(void);
static	void	consserve1(void);
static	char	statsdef[20];	/* default stats list */

void
consserve(void)
{
	strncpy(cons.chan->whochan, "console", sizeof(cons.chan->whochan));
	installcmds();
	con_session();
	cmd_exec("cfs");
	cmd_exec("users");
	cmd_exec("version");

	/* Should be checksumed */
/*
	if(nvr.authcmd[0] == 'a')
		cmd_exec(nvr.authcmd);
*/


	cmd_exec("cwcmd touchsb");
	userinit(consserve1, 0, "con");
}

static
void
consserve1(void)
{
	int i, ch;

	conslock();

loop:

	print("%s: ", service);
	for(i=0;;) {
		ch = getc();
		switch(ch) {
		default:
			if(i < nelem(conline)-2)
				conline[i++] = ch;
			break;
		case '\b':
			if(i > 0)
				i--;
			break;
		case 'U' - '@':
			i = 0;
			break;
		case '\n':
			conline[i] = 0;
			cmd_exec(conline);
		case -1:
			goto loop;
		case 'D' - '@':
			print("\n");
			conslock();
			goto loop;
		}
	}
	goto loop;
}

static
int
cmdcmp(Command *a, Command *b)
{
	return strcmp(a->arg0, b->arg0);
}

void
cmd_install(char *arg0, char *help, void (*func)(int, char*[]))
{
	int i;

	qlock(&cons);
	for(i=0; command[i].arg0; i++)
		;
	if(i >= nelem(command)-2) {
		qunlock(&cons);
		print("cmd_install: too many commands\n");
		return;
	}
	command[i+1].arg0 = 0;
	command[i].help = help;
	command[i].func = func;
	command[i].arg0 = arg0;
	qsort(command, i+1, sizeof(Command), cmdcmp);
	qunlock(&cons);
}

void
cmd_exec(char *arg)
{
	char line[100], *s;
	char *argv[10];
	int argc, i, c;

	if(strlen(arg) >= nelem(line)-2) {
		print("cmd_exec: line too long\n");
		return;
	}
	strcpy(line, arg);

	argc = 0;
	s = line;
	c = *s++;
	for(;;) {
		while(c == ' ' || c == '\t')
			c = *s++;
		if(c == 0)
			break;
		if(argc >= nelem(argv)-2) {
			print("cmd_exec: too many args\n");
			return;
		}
		argv[argc++] = s-1;
		while(c != ' ' && c != '\t' && c != 0)
			c = *s++;
		s[-1] = 0;
	}
	if(argc <= 0)
		return;
	for(i=0; s=command[i].arg0; i++)
		if(strcmp(argv[0], s) == 0) {
			(*command[i].func)(argc, argv);
			prflush();
			return;
		}
	print("cmd_exec: unknown command: %s\n", argv[0]);
}

static
void
cmd_halt(int argc, char *argv[])
{

	USED(argc);
	USED(argv);

	wlock(&mainlock);	/* halt */
	sync("halt");
	exit();
}

static
void
cmd_stats(int argc, char *argv[])
{
	int i, c;
	char buf[30], *s, *p, *q;

	if(argc <= 1) {
		if(statsdef[0] == 0)
			strcpy(statsdef, "a");
		sprint(buf, "stats s%s", statsdef);
		cmd_exec(buf);
		return;
	}

	strcpy(buf, "stat");
	p = strchr(buf, 0);
	p[1] = 0;

	q = 0;
	for(i=1; i<argc; i++) {
		for(s=argv[i]; c=*s; s++) {
			if(c == 's')
				continue;
			if(c == '-') {
				q = statsdef;
				continue;
			}
			if(q) {
				*q++ = c;
				*q = 0;
			}
			*p = c;
			cmd_exec(buf);
		}
	}
}

static
void
cmd_stata(int argc, char *argv[])
{
	int i;

	USED(argc);
	USED(argv);

	print("cons stats\n");
	print("	work = %F rps\n", (Filta){&cons.work, 1});
	print("	rate = %F tBps\n", (Filta){&cons.rate, 1000});
	print("	hits = %F iops\n", (Filta){&cons.bhit, 1});
	print("	read = %F iops\n", (Filta){&cons.bread, 1});
	print("	rah  = %F iops\n", (Filta){&cons.brahead, 1});
	print("	init = %F iops\n", (Filta){&cons.binit, 1});
	print("	bufs =    %3ld sm %3ld lg %d res\n",
		cons.nsmall, cons.nlarge, cons.nreseq);

	for(i=0; i<nelem(mballocs); i++)
		if(mballocs[i])
			print("	[%d]=%d\n", i, mballocs[i]);

	print("	ioerr=    %3ld wr %3ld ww %3ld dr %3ld dw\n",
		cons.nwormre, cons.nwormwe, cons.nwrenre, cons.nwrenwe);
}

static
int
flagcmp(Flag *a, Flag *b)
{
	return strcmp(a->arg0, b->arg0);
}

ulong
flag_install(char *arg, char *help)
{
	int i;

	qlock(&cons);
	for(i=0; flag[i].arg0; i++)
		;
	if(i >= 32) {
		qunlock(&cons);
		print("flag_install: too many flags\n");
		return 0;
	}
	flag[i+1].arg0 = 0;
	flag[i].arg0 = arg;
	flag[i].help = help;
	flag[i].flag = 1<<i;
	qsort(flag, i+1, sizeof(Flag), flagcmp);
	qunlock(&cons);
	return 1<<i;
}

void
cmd_flag(int argc, char *argv[])
{
	int f, n, i, j;
	char *s;
	Chan *cp;

	if(argc <= 1) {
		for(i=0; flag[i].arg0; i++)
			print("%.4lux %s %s\n",
				flag[i].flag, flag[i].arg0, flag[i].help);
		if(cons.flags)
			print("flag[*]   = %.4ux\n", cons.flags);
		for(cp = chans; cp; cp = cp->next)
			if(cp->flags)
				print("flag[%3d] = %.4ux\n", cp->chan, cp->flags);
		return;
	}

	f = 0;
	n = -1;
	for(i=1; i<argc; i++) {
		for(j=0; s=flag[j].arg0; j++)
			if(strcmp(s, argv[i]) == 0)
				goto found;
		j = number(argv[i], -1, 10);
		if(j < 0) {
			print("bad flag argument: %s\n", argv[i]);
			continue;
		}
		n = j;
		continue;
	found:
		f |= flag[j].flag;
	}

	if(n < 0) {
		cons.flags ^= f;
		if(f == 0)
			cons.flags = 0;
		print("flag      = %.8ux\n", cons.flags);
		return;
	}
	for(cp = chans; cp; cp = cp->next) {
		if(cp->chan == n) {
			cp->flags ^= f;
			if(f == 0)
				cp->flags = 0;
			print("flag[%3d] = %.8ux\n", cp->chan, cp->flags);
			return;
		}
	}
	print("no such channel\n");
}

static
void
cmd_who(int argc, char *argv[])
{
	Chan *cp;
	int i;

	for(cp = chans; cp; cp = cp->next)
		if(cp->whotime != 0) {
			if(argc > 1) {
				for(i=1; i<argc; i++)
					if(strcmp(argv[i], cp->whoname) == 0)
						goto brk;
				continue;
			}
		brk:
			print("%3d: %10s %24s %T %.4ux\n",
				cp->chan,
				cp->whoname,
				cp->whochan,
				cp->whotime,
				cp->flags);
			prflush();
		}
}

static
void
cmd_sync(int argc, char *argv[])
{
	USED(argc);
	USED(argv);

	wlock(&mainlock);	/* sync */
	sync("command");
	wunlock(&mainlock);
	print("\n");
}

static
void
cmd_help(int argc, char *argv[])
{
	char *arg;
	int i, j;

	for(i=0; arg=command[i].arg0; i++) {
		if(argc > 1) {
			for(j=1; j<argc; j++)
				if(strcmp(argv[j], arg) == 0)
					goto found;
			continue;
		}
	found:
		print("	%s %s\n", arg, command[i].help);
		prflush();
	}
}

static
void
cmd_date(int argc, char *argv[])
{
	long t, ct;
	char *arg;

	USED(argc);
	USED(argv);

	if(argc <= 1)
		goto out;

	ct = time();
	arg = argv[1];
	switch(*argv) {
	default:
		t = number(arg, -1, 10);
		if(t <= 0)
			goto out;
		ct = t;
		break;
	case '+':
		t = number(arg+1, 0, 10);
		ct += t;
		break;
	case '-':
		t = number(arg+1, 0, 10);
		ct -= t;
	}
	settime(ct);
out:
	prdate();
}

void
cmd_create(int argc, char *argv[])
{
	int uid, gid;
	long perm;
	char elem[NAMELEN], *p;

	if(argc < 5) {
		print("usage: create path uid gid mode [lad]\n");
		return;
	}

	p = utfrrune(argv[1], '/');
	if(p) {
		*p++ = 0;
		if(walkto(argv[1])) {
			print("create failed in walkto: %s\n", p);
			return;
		}
	} else {
		if(walkto("/"))
			return;
		p = argv[1];
	}
	if(strlen(p) >= NAMELEN) {
		print("name too long %s\n", p);
		return;
	}

	memset(elem, 0, sizeof(elem));
	strcpy(elem, p);

	uid = strtouid(argv[2], 1);
	if(uid == 0)
		uid = number(argv[2], 0, 10);
	if(uid == 0) {
		print("bad uid %s\n", argv[2]);
		return;
	}

	gid = strtouid(argv[3], 1);
	if(gid == 0)
		gid = number(argv[3], 0, 10);
	if(gid == 0) {
		print("bad gid %s\n", argv[3]);
		return;
	}

	perm = number(argv[4], 0777, 8) & 0777;

	if(argc > 5) {
		if(strchr(argv[5], 'l'))
			perm |= PLOCK;
		if(strchr(argv[5], 'a'))
			perm |= PAPND;
		if(strchr(argv[5], 'd'))
			perm |= PDIR;
	}

	if(con_create(FID2, elem, uid, gid, perm, 0))
		print("create failed: %s/%s\n", argv[1], p);
}

static
void
cmd_clri(int argc, char *argv[])
{
	int i;

	for(i=1; i<argc; i++) {
		if(walkto(argv[i])) {
			print("cant remove %s\n", argv[i]);
			continue;
		}
		con_clri(FID2);
	}
}

static
void
cmd_remove(int argc, char *argv[])
{
	int i;

	for(i=1; i<argc; i++) {
		if(walkto(argv[i])) {
			print("cant remove %s\n", argv[i]);
			continue;
		}
		con_remove(FID2);
	}
}

static
void
cmd_version(int argc, char *argv[])
{

	USED(argc);
	USED(argv);

	print("%s as of %T\n", service, mktime);
	print("	last boot %T\n", boottime);
}

static
void
cmd_cfs(int argc, char *argv[])
{
	Filsys *fs;
	char *name;

	name = "main";
	if(argc > 1)
		name = argv[1];
	fs = fsstr(name);
	if(fs == 0) {
		print("%s: unknown file system\n", name);
		if(cons.curfs)
			return;
		fs = &filsys[0];
	}
	if(con_attach(FID1, "adm", fs->name))
		panic("FID1 attach to root");
	cons.curfs = fs;
	print("current fs is \"%s\"\n", cons.curfs->name);
}

static
void
cmd_prof(int argc, char *argv[])
{
	int n;
	long m, o;

	if(cons.profbuf == 0) {
		print("no buffer\n");
		return;
	}
	n = !cons.profile;
	if(argc > 1)
		n = number(argv[1], n, 10);
	if(n && !cons.profile) {
		print("clr and start\n");
		memset(cons.profbuf, 0, cons.nprofbuf*sizeof(cons.profbuf[0]));
		cons.profile = 1;
		return;
	}
	if(!n && cons.profile) {
		cons.profile = 0;
		print("stop and write\n");
		if(walkto("/adm/kprofdata"))
			goto bad;
		if(con_open(FID2, MWRITE|MTRUNC)) {
		bad:
			print("cant open /adm/kprofdata\n");
			return;
		}
		m = cons.nprofbuf*sizeof(cons.profbuf[0]);
		o = 0;
		while(m > 0) {
			n = 8192;
			if(n > m)
				n = m;
			con_write(FID2, (char*)cons.profbuf+o, o, n);
			m -= n;
			o += n;
		}
		return;
	}
}

static
void
cmd_time(int argc, char *argv[])
{
	long t1, t2;
	int i;

	t1 = MACHP(0)->ticks;
	conline[0] = 0;
	for(i=1; i<argc; i++) {
		strcat(conline, " ");
		strcat(conline, argv[i]);
	}
	cmd_exec(conline);
	t2 = MACHP(0)->ticks;
	print("time = %d ms\n", (t2-t1)*MS2HZ);
}

void
cmd_noauth(int argc, char *argv[])
{
	USED(argc, argv);

	noauth = !noauth;
	if(noauth)
		print("authorisation is DISABLED\n");
}

static
void
installcmds(void)
{
	cmd_install("cfs", "[file] -- set current filesystem", cmd_cfs);
	cmd_install("check", "[options]", cmd_check);
	cmd_install("clri", "[file ...] -- purge files/dirs", cmd_clri);
	cmd_install("create", "path uid gid perm [lad] -- make a file/dir", cmd_create);
	cmd_install("date", "[[+-]seconds] -- print/set date", cmd_date);
	cmd_install("flag", "-- print set flags", cmd_flag);
	cmd_install("halt", "-- return to boot rom", cmd_halt);
	cmd_install("help", "", cmd_help);
	cmd_install("newuser", "username -- add user to /adm/users", cmd_newuser);
	cmd_install("profile", "[01] -- kernel profile", cmd_prof);
	cmd_install("remove", "[file ...] -- remove files/dirs", cmd_remove);
	cmd_install("stata", "-- overall stats", cmd_stata);
	cmd_install("stats", "[[-]flags ...] -- various stats", cmd_stats);
	cmd_install("sync", "", cmd_sync);
	cmd_install("time", "command -- time another command", cmd_time);
	cmd_install("users", "[file] -- read /adm/users", cmd_users);
	cmd_install("version", "-- print time of mk and boot", cmd_version);
	cmd_install("who", "[user ...] -- print attaches", cmd_who);
	cmd_install("passwd", "passwd -- set passkey", cmd_passwd);
	cmd_install("auth", "[file] -- control authentication", cmd_auth);
	cmd_install("noauth", "toggle authorisation", cmd_noauth);

	attachflag = flag_install("attach", "-- attach calls");
	chatflag = flag_install("chat", "-- verbose");
}

int
walkto(char *name)
{
	char elem[NAMELEN], *p;
	int n;

	if(con_clone(FID1, FID2))
		return 1;

	for(;;) {
		p = utfrune(name, '/');
		if(p == 0)
			p = strchr(name, 0);
		if(p == name) {
			if(*name == 0)
				return 0;
			name = p+1;
			continue;
		}
		n = p-name;
		if(n > NAMELEN)
			return 1;
		memset(elem, 0, sizeof(elem));
		memmove(elem, name, n);
		if(con_walk(FID2, elem))
			return 1;
		name = p;
	}
	return 0;
}

long
number(char *arg, int def, int base)
{
	int c, sign, any;
	long n;

	if(arg == 0)
		return def;

	sign = 0;
	any = 0;
	n = 0;

	c = *arg;
	while(c == ' ') {
		arg++;
		c = *arg;
	}
	if(c == '-') {
		sign = 1;
		arg++;
		c = *arg;
	}
	while((c >= '0' && c <= '9') ||
	      (base == 16 && c >= 'a' && c <= 'f') ||
	      (base == 16 && c >= 'A' && c <= 'F')) {
		n *= base;
		if(c >= 'a' && c <= 'f')
			n += c - 'a' + 10;
		else
		if(c >= 'A' && c <= 'F')
			n += c - 'A' + 10;
		else
			n += c - '0';
		arg++;
		c = *arg;
		any = 1;
	}
	if(!any)
		return def;
	if(sign)
		n = -n;
	return n;
}

console.c       710793600   0     0     664     3537      `
#include	"all.h"

void
p9fcall(Chan *cp, Fcall *in, Fcall *ou)
{
	int t;

	rlock(&mainlock);
	t = in->type;
	if(t < 0 || t >= MAXSYSCALL || (t&1) || !p9call[t]) {
		print("bad message type %d\n", t);
		panic("");
	}
	ou->type = t+1;
	ou->err = 0;

	rlock(&cp->reflock);
	(*p9call[t])(cp, in, ou);
	runlock(&cp->reflock);

	if(ou->err)
		if(CHAT(cp))
			print("	error: %s\n", errstr[ou->err]);
	cons.work.count++;
	runlock(&mainlock);
}

int
con_session(void)
{
	Fcall in, ou;

	in.type = Tsession;
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_attach(int fid, char *uid, char *arg)
{
	Fcall in, ou;

	in.type = Tattach;
	in.fid = fid;
	strncpy(in.uname, uid, NAMELEN);
	strncpy(in.aname, arg, NAMELEN);
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_clone(int fid1, int fid2)
{
	Fcall in, ou;

	in.type = Tclone;
	in.fid = fid1;
	in.newfid = fid2;
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_walk(int fid, char *name)
{
	Fcall in, ou;

	in.type = Twalk;
	in.fid = fid;
	strncpy(in.name, name, NAMELEN);
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_open(int fid, int mode)
{
	Fcall in, ou;

	in.type = Topen;
	in.fid = fid;
	in.mode = mode;
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_read(int fid, char *data, long offset, int count)
{
	Fcall in, ou;

	in.type = Tread;
	in.fid = fid;
	in.offset = offset;
	in.count = count;
	ou.data = data;
	p9fcall(cons.chan, &in, &ou);
	if(ou.err)
		return 0;
	return ou.count;
}

int
con_write(int fid, char *data, long offset, int count)
{
	Fcall in, ou;

	in.type = Twrite;
	in.fid = fid;
	in.data = data;
	in.offset = offset;
	in.count = count;
	p9fcall(cons.chan, &in, &ou);
	if(ou.err)
		return 0;
	return ou.count;
}

int
con_remove(int fid)
{
	Fcall in, ou;

	in.type = Tremove;
	in.fid = fid;
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
con_create(int fid, char *name, int uid, int gid, long perm, int mode)
{
	Fcall in, ou;

	in.type = Tcreate;
	in.fid = fid;
	strncpy(in.name, name, NAMELEN);
	in.perm = perm;
	in.mode = mode;
	cons.uid = uid;			/* beyond ugly */
	cons.gid = gid;
	p9fcall(cons.chan, &in, &ou);
	return ou.err;
}

int
doclri(File *f)
{
	Iobuf *p, *p1;
	Dentry *d, *d1;
	int err;

	err = 0;
	p = 0;
	p1 = 0;
	if(f->fs->dev.type == Devro) {
		err = Eronly;
		goto out;
	}
	/*
	 * check on parent directory of file to be deleted
	 */
	if(f->wpath == 0 || f->wpath->addr == f->addr) {
		err = Ephase;
		goto out;
	}
	p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
	d1 = getdir(p1, f->wpath->slot);
	if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
		err = Ephase;
		goto out;
	}

	accessdir(p1, d1, FWRITE);
	putbuf(p1);
	p1 = 0;

	/*
	 * check on file to be deleted
	 */
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);


	/*
	 * do it
	 */
	memset(d, 0, sizeof(Dentry));
	settag(p, Tdir, QPNONE);
	freewp(f->wpath);
	freefp(f);

out:
	if(p1)
		putbuf(p1);
	if(p)
		putbuf(p);
	return err;
}

void
f_clri(Chan *cp, Fcall *in, Fcall *ou)
{
	File *f;

	if(CHAT(cp)) {
		print("c_clri %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	ou->err = doclri(f);

out:
	ou->fid = in->fid;
	if(f)
		qunlock(f);
}

int
con_clri(int fid)
{
	Fcall in, ou;
	Chan *cp;

	in.type = Tremove;
	in.fid = fid;
	cp = cons.chan;

	rlock(&mainlock);
	ou.type = Tremove+1;
	ou.err = 0;

	rlock(&cp->reflock);
	f_clri(cp, &in, &ou);
	runlock(&cp->reflock);

	cons.work.count++;
	runlock(&mainlock);
	return ou.err;
}

cw.c            711081413   0     0     664     38096     `
#include "all.h"

#define	DEBUG		0
#define	RECOVCW		0		/* address of root to be recovered */
#define	RECOVRO		0		/* address of root to be recovered */

#define	FIRST		SUPER_ADDR
#define	ADDFREE		(100)
#define	CACHE_ADDR	SUPER_ADDR
#define	DSIZE		546000
#define	MAXAGE		10000
#define	BKPERBLK	10
#define CEPERBK		((BUFSIZE-BKPERBLK*sizeof(long))/\
				(sizeof(Centry)*BKPERBLK))

/* cache state */
enum
{
	/* states -- beware these are recorded on the cache */
				/*    cache    worm	*/
	Cnone = 0,		/*	0	?	*/
	Cdirty,			/*	1	0	*/
	Cdump,			/*	1	0->1	*/
	Cread,			/*	1	1	*/
	Cwrite,			/*	2	1	*/
	Cdump1,			/* inactive form of dump */
	Cerror,

	/* opcodes -- these are not recorded */
	Onone,
	Oread,
	Owrite,
	Ogrow,
	Odump,
	Orele,
	Ofree,
};

typedef	struct	Cache	Cache;
typedef	struct	Centry	Centry;
typedef	struct	Bucket	Bucket;

struct	Cache
{
	long	maddr;		/* cache map addr */
	long	msize;		/* cache map size in buckets */
	long	caddr;		/* cache addr */
	long	csize;		/* cache size */
	long	fsize;		/* current size of worm */
	long	wsize;		/* max size of the worm */
	long	wmax;		/* highwater write */

	long	sbaddr;		/* super block addr */
	long	cwraddr;	/* cw root addr */
	long	roraddr;	/* dump root addr */

	long	toytime;	/* somewhere convienent */
	long	time;
};

struct	Centry
{
	ushort	age;
	short	state;
	long	waddr;		/* worm addr */
};

struct	Bucket
{
	long	agegen;		/* generator for ages in this bkt */
	Centry	entry[CEPERBK];
};

static
struct
{
	Filter	ncwio;
	int	inited;
	int	dbucket;	/* last bucket dumped */
	long	daddr;		/* last block dumped */
	long	ncopy;
	int	nodump;
/*
 * following are wormcp variables
 */
	int	wcpflg;
	long	wcpmaddr;
	int	wcpcaddr;
	int	wcpplatter;
	long	wcpcount;
	Device	wcpcdev;
	Device	wcpwdev;
/*
 * following are cached variables for dumps
 */
	Device	dev;
	Device	cdev;
	Device	wdev;
	Device	rodev;
	long	fsize;
	long	ndump;
	int	depth;
	int	all;		/* local flag to recur on modified directories */
	int	allflag;	/* global flag to recur on modified directories */
	long	falsehits;	/* times recur found modified blocks */
	struct
	{
		char	name[500];
		char	namepad[NAMELEN+10];
	};
} cw;

char*	cwnames[] =
{
	[Cnone]		"none",
	[Cdirty]	"dirty",
	[Cdump]		"dump",
	[Cread]		"read",
	[Cwrite]	"write",
	[Cdump1]	"dump1",
	[Cerror]	"error",

	[Onone]		"none",
	[Oread]		"read",
	[Owrite]	"write",
	[Ogrow]		"grow",
	[Odump]		"dump",
	[Orele]		"rele",
};

Centry*	getcentry(Bucket*, long);
int	cwio(Device, long, void*, int);
void	cmd_cwcmd(int, char*[]);

/*
 * console command
 * initiate a dump
 */
void
cmd_dump(int argc, char *argv[])
{
	Filsys *fs;

	fs = cons.curfs;
	if(argc > 1)
		fs = fsstr(argv[1]);
	if(fs == 0) {
		print("%s: unknown file system\n", argv[1]);
		return;
	}
	cfsdump(fs);
}

/*
 * console command
 * worm stats
 */
static
void
cmd_statw(int argc, char *argv[])
{
	Filsys *fs;
	Iobuf *p, *cb;
	Superb *sb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m, nw, bw, state[Onone];
	Device dev, cdev;
	int s;

	USED(argc);
	USED(argv);

	print("cwstats\n");
	print("	nio   = %F\n", (Filta){&cw.ncwio, 1});

	for(fs=filsys; fs->name; fs++) {
		dev = fs->dev;
		if(dev.type != Devcw)
			continue;

		print("	cache %s\n", fs->name);

		cdev = CDEV(dev);
		p = getbuf(dev, cwsaddr(dev), Bread);
		if(!p || checktag(p, Tsuper, QPSUPER)) {
			print("cwstats: checktag super\n");
			if(p)
				putbuf(p);
			continue;
		}
		sb = (Superb*)p->iobuf;
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
		if(!cb || checktag(cb, Tcache, QPSUPER)) {
			print("cwstats: checktag c bucket\n");
			putbuf(p);
			if(cb)
				putbuf(cb);
			continue;
		}
		h = (Cache*)cb->iobuf;

		print("		maddr  = %8ld\n", h->maddr);
		print("		msize  = %8ld\n", h->msize);
		print("		caddr  = %8ld\n", h->caddr);
		print("		csize  = %8ld\n", h->csize);
		print("		sbaddr = %8ld\n", h->sbaddr);
		print("		craddr = %8ld %8ld\n", h->cwraddr, sb->cwraddr);
		print("		roaddr = %8ld %8ld\n", h->roraddr, sb->roraddr);
		print("		fsize  = %8ld %8ld %2ld+%2ld%%\n", h->fsize, sb->fsize,
					h->fsize/DSIZE,
					(h->fsize%DSIZE)/(DSIZE/100));
		print("		slast  =          %8ld\n", sb->last);
		print("		snext  =          %8ld\n", sb->next);
		print("		wmax   = %8ld          %2ld+%2ld%%\n", h->wmax,
					h->wmax/DSIZE,
					(h->wmax%DSIZE)/(DSIZE/100));
		print("		wsize  = %8ld          %2ld+%2ld%%\n", h->wsize,
					h->wsize/DSIZE,
					(h->wsize%DSIZE)/(DSIZE/100));

		putbuf(p);

		bw = 0;	/* max filled bucket */
		memset(state, 0, sizeof(state));
		for(m=0; m<h->msize; m++) {
			p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bres);
			if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
				panic("cwstats: checktag c bucket");
			b = (Bucket*)p->iobuf + m%BKPERBLK;
			ce = b->entry + CEPERBK;
			nw = 0;
			for(c=b->entry; c<ce; c++) {
				s = c->state;
				state[s]++;
				if(s != Cnone && s != Cread)
					nw++;
			}
			putbuf(p);
			if(nw > bw)
				bw = nw;
		}
		putbuf(cb);
		for(s=Cnone; s<Cerror; s++)
			print("		%6ld %s\n", state[s], cwnames[s]);
		print("		cache %2ld%% full\n", (bw*100)/CEPERBK);
	}
}

int
dumpblock(Device dev)
{
	Device cdev, wdev;
	Iobuf *p, *cb, *p1, *p2;
	Cache *h;
	Centry *c, *ce, *bc;
	Bucket *b;
	long m, a, msize, maddr, wmax, caddr;
	int s1, s2;

	if(cw.nodump)
		return 0;

	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	h = (Cache*)cb->iobuf;
	msize = h->msize;
	maddr = h->maddr;
	wmax = h->wmax;
	caddr = h->caddr;
	putbuf(cb);
	for(m=msize; m>=0; m--) {
		a = cw.dbucket + 1;
		if(a < 0 || a >= msize)
			a = 0;
		cw.dbucket = a;
		p = getbuf(cdev, maddr + a/BKPERBLK, Bread|Bres);
		b = (Bucket*)p->iobuf + a%BKPERBLK;
		ce = b->entry + CEPERBK;
		bc = 0;
		for(c=b->entry; c<ce; c++)
			if(c->state == Cdump) {
				if(bc == 0) {
					bc = c;
					continue;
				}
				if(c->waddr < cw.daddr) {
					if(bc->waddr < cw.daddr && bc->waddr > c->waddr)
						bc = c;
					continue;
				}
				if(bc->waddr < cw.daddr || bc->waddr > c->waddr)
					bc = c;
			}
		if(bc) {
			c = bc;
			goto found;
		}
		putbuf(p);
	}
	if(cw.ncopy) {
		print("%ld blocks copied to worm\n", cw.ncopy);
		cw.ncopy = 0;
	}
	cw.nodump = 1;
	return 0;

found:
	a = a*CEPERBK + (c - b->entry) + caddr;
	p1 = getbuf(devnone, Cwdump1, 0);

retry:
	if(devread(cdev, a, p1->iobuf))
		goto stop;
	m = c->waddr;
	cw.daddr = m;
	wdev = WDEV(dev);
	s1 = devwrite(wdev, m, p1->iobuf);
	if(s1) {
		p2 = getbuf(devnone, Cwdump2, 0);
		s2 = devread(wdev, m, p2->iobuf);
		if(s2) {
			if(s1 == 0x61 && s2 == 0x60) {
				putbuf(p2);
				goto retry;
			}
			goto stop1;
		}
		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
			goto stop1;
		putbuf(p2);
	}
	/*
	 * reread and compare
	 */
	p2 = getbuf(devnone, Cwdump2, 0);
	s1 = devread(wdev, m, p2->iobuf);
	if(s1)
		goto stop1;
	if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
		print("reread C%ld W%ld didnt compare\n", a, m);
		goto stop1;
	}
	putbuf(p2);

	putbuf(p1);
	c->state = Cread;
	p->flags |= Bmod;
	putbuf(p);

	if(m > wmax) {
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
		h = (Cache*)cb->iobuf;
		if(m > h->wmax)
			h->wmax = m;
		putbuf(cb);
	}
	cw.ncopy++;
	return 1;

stop1:
	putbuf(p2);
	putbuf(p1);
	c->state = Cdump1;
	p->flags |= Bmod;
	putbuf(p);
	return 1;

stop:
	putbuf(p1);
	putbuf(p);
	print("stopping dump!!\n");
	cw.nodump = 1;
	return 0;
}

void
cwinit(Device dev)
{
	Cache *h;
	Iobuf *cb, *p;
	long l, m;
	Device cdev;

	if(!cw.inited) {
		cw.inited = 1;
		cw.allflag = 0;
		dofilter(&cw.ncwio);
		cmd_install("dump", "-- make dump backup to worm", cmd_dump);
		cmd_install("statw", "-- cache/worm stats", cmd_statw);
		cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd);

		roflag = flag_install("ro", "-- ro reads and writes");
	}
	cdev = CDEV(dev);
	devinit(cdev);
	devinit(WDEV(dev));
	l = devsize(WDEV(dev));

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	h->toytime = toytime() + SECOND(30);
	h->time = time();
	m = h->wsize;
	if(l != m) {
		print("wdev changed size %ld to %ld\n", m, l);
		h->wsize = l;
		cb->flags |= Bmod;
	}

	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwinit: checktag c bucket");
		putbuf(p);
	}

	putbuf(cb);
}

long
cwsaddr(Device dev)
{
	Iobuf *cb;
	long sa;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	sa = ((Cache*)cb->iobuf)->sbaddr;
	putbuf(cb);
	return sa;
}

long
cwraddr(Device dev, int flag)
{
	Iobuf *cb;
	long ra;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	if(flag)
		ra = ((Cache*)cb->iobuf)->roraddr;
	else
		ra = ((Cache*)cb->iobuf)->cwraddr;
	putbuf(cb);
	return ra;
}

long
cwsize(Device dev)
{
	Iobuf *cb;
	long fs;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	fs = ((Cache*)cb->iobuf)->fsize;
	putbuf(cb);
	return fs;
}

int
cwread(Device a, long b, void *c)
{
	return cwio(a, b, c, Oread) == Cerror;
}

int
cwwrite(Device a, long b, void *c)
{
	return cwio(a, b, c, Owrite) == Cerror;
}

int
roread(Device a, long b, void *c)
{
	Device d;
	int s;

	d = a;
	d.type = Devcw;

	/*
	 * maybe better is to try buffer pool first
	 */
	s = cwio(d, b, 0, Onone);
	if(s == Cdump || s == Cdump1 || s == Cread) {
		s = cwio(d, b, c, Oread);
		if(s == Cdump || s == Cdump1 || s == Cread) {
			if(cons.flags & roflag)
				print("roread: %D %ld -> %D(hit)\n", a, b, d);
			return 0;
		}
	}
	if(cons.flags & roflag)
		print("roread: %D %ld -> %D(miss)\n", a, b, WDEV(d));
	return devread(WDEV(d), b, c);
}

int
cwio(Device dev, long addr, void *buf, int opcode)
{
	Iobuf *p, *p1, *p2, *cb;
	Cache *h;
	Bucket *b;
	Centry *c;
	Device cdev, wdev;
	long bn, a1, a2, max, newmax;
	int state, s1, s2;

	cw.ncwio.count++;
	cdev = CDEV(dev);
	wdev = WDEV(dev);

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	h = (Cache*)cb->iobuf;
	if(toytime() >= h->toytime) {
		cb->flags |= Bmod;
		h->toytime = toytime() + SECOND(30);
		h->time = time();
	}

	if(addr < 0) {
		putbuf(cb);
		panic("cwio neg address %ld", addr);
	}
/*
	if(addr >= h->fsize)
		print("cwio large address %ld\n", addr);
*/
	bn = addr % h->msize;
	a1 = h->maddr + bn/BKPERBLK;
	a2 = bn*CEPERBK + h->caddr;
	max = h->wmax;

	putbuf(cb);
	newmax = 0;

	p = getbuf(cdev, a1, Bread|Bmod|Bres);
	if(!p || checktag(p, Tbuck, a1))
		panic("cwio: checktag c bucket");
	b = (Bucket*)p->iobuf + bn%BKPERBLK;

	c = getcentry(b, addr);
	if(c == 0) {
		putbuf(p);
		print("disk cache bucket %ld is full\n", a1);
		return Cerror;
	}
	a2 += c - b->entry;

	state = c->state;
	switch(opcode)
	{
	default:
		goto bad;

	case Onone:
		break;

	case Oread:
		switch(state) {
		default:
			goto bad;

		case Cread:
			if(!devread(cdev, a2, buf))
				break;

		case Cnone:
			if(devread(wdev, addr, buf)) {
				state = Cerror;
				break;
			}
			if(addr > max)
				newmax = addr;
			if(!devwrite(cdev, a2, buf))
				c->state = Cread;
			break;


		case Cdirty:
		case Cdump:
		case Cdump1:
		case Cwrite:
			if(devread(cdev, a2, buf))
				state = Cerror;
			break;
		}
		break;

	case Owrite:
		switch(state) {
		default:
			goto bad;

		case Cdump:
			p1 = getbuf(devnone, Cwio1, 0);

		retry:
			p2 = 0;
			if(devread(cdev, a2, p1->iobuf)) {
			err:
				putbuf(p1);
				if(p2)
					putbuf(p2);
				print("cwio: write induced dump error\n");
				state = Cerror;
				break;
			}
			s1 = devwrite(wdev, addr, p1->iobuf);
			if(s1) {
				p2 = getbuf(devnone, Cwio2, 0);
				s2 = devread(wdev, addr, p2->iobuf);
				if(s2) {
					if(s1 == 0x61 && s2 == 0x60) {
						putbuf(p2);
						goto retry;
					}
					goto err;
				}
				if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
					goto err;
				putbuf(p2);
			}
			putbuf(p1);
			c->state = Cread;
			if(addr > max)
				newmax = addr;
			cw.ncopy++;

		case Cnone:
		case Cread:
			if(devwrite(cdev, a2, buf)) {
				state = Cerror;
				break;
			}
			c->state = Cwrite;
			break;

		case Cdump1:
		case Cdirty:
		case Cwrite:
			if(devwrite(cdev, a2, buf))
				state = Cerror;
			break;
		}
		break;

	case Ogrow:
		if(state != Cnone) {
			print("cwgrow with state = %s\n",
				cwnames[state]);
			break;
		}
		c->state = Cdirty;
		break;

	case Odump:
		if(state != Cdirty) {	/* BOTCH */
			print("cwdump with state = %s\n",
				cwnames[state]);
			break;
		}
		c->state = Cdump;
		cw.ndump++;	/* only called from dump command */
		break;

	case Orele:
		if(state != Cwrite) {
			if(state != Cdump1)
				print("cwrele with state = %s\n",
					cwnames[state]);
			break;
		}
		c->state = Cnone;
		break;

	case Ofree:
		if(state == Cwrite || state == Cread)
			c->state = Cnone;
		break;
	}
	if(DEBUG)
		print("cwio: %ld s=%s o=%s ns=%s\n",
			addr, cwnames[state],
			cwnames[opcode],
			cwnames[c->state]);
	putbuf(p);
	if(newmax) {
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
		h = (Cache*)cb->iobuf;
		if(newmax > h->wmax)
			h->wmax = newmax;
		putbuf(cb);
	}
	return state;

bad:
	print("cw state = %s; cw opcode = %s",
		cwnames[state], cwnames[opcode]);
	return Cerror;
}

int
cwgrow(Device dev, Superb *sb)
{
	Iobuf *cb;
	Cache *h;
	long fs, nfs, ws;
	int state;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	ws = h->wsize;
	fs = h->fsize;
	if(fs >= ws)
		return 0;
	nfs = fs + ADDFREE;
	if(nfs >= ws)
		nfs = ws;
	h->fsize = nfs;
	putbuf(cb);

	sb->fsize = nfs;
	print("%D grow from %ld to %ld limit %ld\n", dev, fs, nfs, ws);
	for(nfs--; nfs>=fs; nfs--) {
		state = cwio(dev, nfs, 0, Ogrow);
		if(state == Cnone)
			addfree(dev, nfs, sb);
	}
	return 1;
}

int
cwfree(Device dev, long addr)
{
	int state;

	if(dev.type == Devcw) {
		state = cwio(dev, addr, 0, Ofree);
		if(state != Cdirty)
			return 1;	/* do not put in freelist */
	}
	return 0;			/* put in freelist */
}

int
bktcheck(Bucket *b)
{
	Centry *c, *c1, *c2, *ce;
	int err;

	err = 0;
	if(b->agegen < CEPERBK || b->agegen > MAXAGE) {
		print("agegen %ld\n", b->agegen);
		err = 1;
	}

	ce = b->entry + CEPERBK;
	c1 = 0;		/* lowest age last pass */
	for(;;) {
		c2 = 0;		/* lowest age this pass */
		for(c = b->entry; c < ce; c++) {
			if(c1 != 0 && c != c1) {
				if(c->age == c1->age) {
					print("same age %d\n", c->age);
					err = 1;
				}
				if(c1->waddr == c->waddr)
				if(c1->state != Cnone)
				if(c->state != Cnone) {
					print("same waddr %ld\n", c->waddr);
					err = 1;
				}
			}
			if(c1 != 0 && c->age <= c1->age)
				continue;
			if(c2 == 0 || c->age < c2->age)
				c2 = c;
		}
		if(c2 == 0)
			break;
		c1 = c2;
		if(c1->age >= b->agegen) {
			print("age >= generator %d %d\n", c1->age, b->agegen);
			err = 1;
		}
	}
	return err;
}

void
resequence(Bucket *b)
{
	Centry *c, *ce, *cr;
	int age, i;

	ce = b->entry + CEPERBK;
	for(c = b->entry; c < ce; c++) {
		c->age += CEPERBK;
		if(c->age < CEPERBK)
			c->age = MAXAGE;
	}
	b->agegen += CEPERBK;

	age = 0;
	for(i=0;; i++) {
		cr = 0;
		for(c = b->entry; c < ce; c++) {
			if(c->age < i)
				continue;
			if(cr == 0 || c->age < age) {
				cr = c;
				age = c->age;
			}
		}
		if(cr == 0)
			break;
		cr->age = i;
	}
	b->agegen = i;
	cons.nreseq++;
}

Centry*
getcentry(Bucket *b, long addr)
{
	Centry *c, *ce, *cr;
	int s, age;

	/*
	 * search for cache hit
	 * find oldest block as byproduct
	 */
	ce = b->entry + CEPERBK;
	age = 0;
	cr = 0;
	for(c = b->entry; c < ce; c++) {
		s = c->state;
		if(s == Cnone) {
			cr = c;
			age = 0;
			continue;
		}
		if(c->waddr == addr)
			goto found;
		if(s == Cread) {
			if(cr == 0 || c->age < age) {
				cr = c;
				age = c->age;
			}
		}
	}

	/*
	 * remap entry
	 */
	c = cr;
	if(c == 0)
		return 0;	/* bucket is full */

	c->state = Cnone;
	c->waddr = addr;

found:
	/*
	 * update the age to get filo cache.
	 * small number in age means old
	 */
	if(!cons.noage || c->state == Cnone) {
		age = b->agegen;
		c->age = age;
		age++;
		b->agegen = age;
		if(age < 0 || age >= MAXAGE)
			resequence(b);
	}
	return c;
}

/*
 * ream the cache
 * calculate new buckets
 */
Iobuf*
cacheinit(Device dev)
{
	Iobuf *cb, *p;
	Cache *h;
	Device cdev;
	long m;

	print("cache init %D\n", dev);
	cdev = CDEV(dev);
	devinit(cdev);

	cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
	memset(cb->iobuf, 0, RBUFSIZE);
	settag(cb, Tcache, QPSUPER);
	h = (Cache*)cb->iobuf;

	/*
	 * calculate csize such that
	 * tsize = msize/BKPERBLK + csize and
	 * msize = csize/CEPERBK
	 */
	h->maddr = CACHE_ADDR + 1;
	m = devsize(cdev) - h->maddr;
	h->csize = ((m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
	h->msize = h->csize/CEPERBK;
	while(!prime(h->msize))
		h->msize--;
	h->csize = h->msize*CEPERBK;
	h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
	h->wsize = devsize(WDEV(dev));

	if(h->msize <= 0)
		panic("cache too small");
	if(h->caddr + h->csize > m)
		panic("cache size error");

	/*
	 * setup cache map
	 */
	for(m=h->maddr; m<h->caddr; m++) {
		p = getbuf(cdev, m, Bmod|Bres);
		memset(p->iobuf, 0, RBUFSIZE);
		settag(p, Tbuck, m);
		putbuf(p);
	}
	print("done cacheinit\n");
	return cb;
}

/*
 * ream the cache
 * calculate new buckets
 * get superblock from
 * last worm dump block.
 */
void
cwrecover(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Superb *s;
	long m, baddr;
	Device wdev;

	wdev = WDEV(dev);

	devinit(wdev);

	p = getbuf(devnone, Cwxx1, 0);
	s = (Superb*)p->iobuf;
	baddr = 0;
	m = FIRST;
	if(startsb)		/* to force a starting superblock */
		m = startsb;
	for(;;) {
		memset(p->iobuf, 0, RBUFSIZE);
		if(devread(wdev, m, p->iobuf) ||
		   checktag(p, Tsuper, QPSUPER))
			break;
		baddr = m;
		m = s->next;
		print("dump %ld is good; %ld next\n", baddr, m);
	}
	putbuf(p);
	if(!baddr)
		panic("recover: no superblock\n");

	p = getbuf(wdev, baddr, Bread);
	s = (Superb*)p->iobuf;

	cb = cacheinit(dev);
	h = (Cache*)cb->iobuf;
	h->sbaddr = baddr;
	h->cwraddr = s->cwraddr;
	h->roraddr = s->roraddr;
	h->fsize = s->fsize;		/* this must be accurate */
	if(RECOVCW)
		h->cwraddr = RECOVCW;
	if(RECOVRO)
		h->roraddr = RECOVRO;

	putbuf(cb);
	putbuf(p);

	p = getbuf(dev, baddr, Bread|Bmod);
	s = (Superb*)p->iobuf;

	memset(&s->fbuf, 0, sizeof(s->fbuf));
	s->fbuf.free[0] = 0;
	s->fbuf.nfree = 1;
	s->tfree = 0;
	if(RECOVCW)
		s->cwraddr = RECOVCW;
	if(RECOVRO)
		s->roraddr = RECOVRO;

	putbuf(p);
	print("done recover\n");
}
/*
 * ream the cache
 * calculate new buckets
 * initialize superblock.
 */
void
cwream(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Superb *s;
	long m, baddr;
	Device cdev;


	print("cwream %D\n", dev);
	cdev = CDEV(dev);
	devinit(cdev);

	baddr = FIRST;	/*	baddr   = super addr
				baddr+1 = cw root
				baddr+2 = ro root
				baddr+3 = reserved next superblock */

	cb = cacheinit(dev);
	h = (Cache*)cb->iobuf;

	h->sbaddr = baddr;
	h->cwraddr = baddr+1;
	h->roraddr = baddr+2;
	h->fsize = 0;	/* prevents superream from freeing */

	putbuf(cb);

	for(m=0; m<3; m++)
		cwio(dev, baddr+m, 0, Ogrow);
	superream(dev, baddr);
	rootream(dev, baddr+1);			/* cw root */
	rootream(dev, baddr+2);			/* ro root */

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	h->fsize = baddr+4;
	putbuf(cb);

	p = getbuf(dev, baddr, Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->last = baddr;
	s->cwraddr = baddr+1;
	s->roraddr = baddr+2;
	s->next = baddr+3;
	s->fsize = baddr+4;
	putbuf(p);

	for(m=0; m<3; m++)
		cwio(dev, baddr+m, 0, Odump);
}

long
rewalk1(long addr, int slot, Wpath *up)
{
	Iobuf *p, *p1;
	Dentry *d;

	if(up == 0)
		return cwraddr(cw.dev, 0);
	up->addr = rewalk1(up->addr, up->slot, up->up);
	p = getbuf(cw.dev, up->addr, Bread|Bmod);
	d = getdir(p, up->slot);
	if(!d || !(d->mode & DALLOC)) {
		print("rewalk1 1\n");
		if(p)
			putbuf(p);
		return addr;
	}
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0);
	if(!p1) {
		print("rewalk1 2\n");
		if(p)
			putbuf(p);
		return addr;
	}
	if(DEBUG)
		print("rewalk1 %ld to %ld \"%s\"\n",
			addr, p1->addr, d->name);
	addr = p1->addr;
	p1->flags |= Bmod;
	putbuf(p1);
	putbuf(p);
	return addr;
}

long
rewalk2(long addr, int slot, Wpath *up)
{
	Iobuf *p, *p1;
	Dentry *d;

	if(up == 0)
		return cwraddr(cw.dev, 1);
	up->addr = rewalk2(up->addr, up->slot, up->up);
	p = getbuf(cw.rodev, up->addr, Bread);
	d = getdir(p, up->slot);
	if(!d || !(d->mode & DALLOC)) {
		print("rewalk2 1\n");
		if(p)
			putbuf(p);
		return addr;
	}
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0);
	if(!p1) {
		print("rewalk2 2\n");
		if(p)
			putbuf(p);
		return addr;
	}
	if(DEBUG)
		print("rewalk2 %ld to %ld \"%s\"\n",
			addr, p1->addr, d->name);
	addr = p1->addr;
	putbuf(p1);
	putbuf(p);
	return addr;
}

void
rewalk(void)
{
	int h;
	File *f;

	for(h=0; h<nelem(flist); h++) {
		for(f=flist[h]; f; f=f->next) {
			if(!f->fs)
				continue;
			if(!devcmp(cw.dev, f->fs->dev))
				f->addr = rewalk1(f->addr, f->slot, f->wpath);
			else
			if(!devcmp(cw.rodev, f->fs->dev))
				f->addr = rewalk2(f->addr, f->slot, f->wpath);
		}
	}
}

long
split(Iobuf *p, long addr)
{
	long na;
	int state;

	na = 0;
	if(p && (p->flags & Bmod)) {
		p->flags |= Bimm;
		putbuf(p);
		p = 0;
	}
	state = cwio(cw.dev, addr, 0, Onone);	/* read the state (twice?) */
	switch(state)
	{
	default:
		panic("split: unknown state %s", cwnames[state]);

	case Cnone:
	case Cdump:
	case Cread:
		break;

	case Cdump1:
	case Cwrite:
		/*
		 * botch.. could be done by relabeling
		 */
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("split: null getbuf\n");
				break;
			}
		}
		na = cw.fsize;
		cw.fsize = na+1;
		cwio(cw.dev, na, 0, Ogrow);
		cwio(cw.dev, na, p->iobuf, Owrite);
		cwio(cw.dev, na, 0, Odump);
		cwio(cw.dev, addr, 0, Orele);
		break;

	case Cdirty:
		cwio(cw.dev, addr, 0, Odump);
		break;
	}
	if(p)
		putbuf(p);
	return na;
}

int
isdirty(Iobuf *p, long addr)
{
	int s;

	if(p && (p->flags & Bmod))
		return 1;
	s = cwio(cw.dev, addr, 0, Onone);
	if(s == Cdirty || s == Cwrite)
		return 1;
	return 0;
}

long
cwrecur(long addr, int tag, int tag1, long qp)
{
	Iobuf *p;
	Dentry *d;
	int i, j, shouldstop;
	long na;
	char *np;


	shouldstop = 0;
	p = getbuf(cw.dev, addr, Bprobe);
	if(tag != Tind1 && tag != Tind2)	/* botch, get these modified */
	if(!isdirty(p, addr)) {
		if(!cw.all) {
			if(DEBUG)
				print("cwrecur: %s %ld t=%s not dirty\n",
					cw.name, addr, tagnames[tag]);
			if(p)
				putbuf(p);
			return 0;
		}
		shouldstop = 1;
	}
	if(DEBUG)
		print("cwrecur: %s %ld t=%s\n",
			cw.name, addr, tagnames[tag]);
	if(cw.depth >= 100) {
		print("dump depth too great\n");
		if(p)
			putbuf(p);
		return 0;
	}
	cw.depth++;

	switch(tag)
	{
	default:
		print("cwrecur: unknown tag %d\n", tag);

	case Tfile:
		break;

	case Tsuper:
	case Tdir:
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("cwrecur: Tdir p null\n");
				break;
			}
		}
		if(tag == Tdir) {
			cw.namepad[0] = 0;	/* force room */
			np = strchr(cw.name, 0);
			*np++ = '/';
		} else {
			np = 0;	/* set */
			cw.name[0] = 0;
		}

		for(i=0; i<DIRPERBUF; i++) {
			d = getdir(p, i);
			if(!(d->mode & DALLOC))
				continue;
			qp = d->qid.path & ~QPDIR;
			if(tag == Tdir)
				strncpy(np, d->name, NAMELEN);
			else
			if(i > 0)
				print("cwrecur: root with >1 directory\n");
			tag1 = Tfile;
			if(d->mode & DDIR)
				tag1 = Tdir;
			for(j=0; j<NDBLOCK; j++) {
				if(na = d->dblock[j]) {
					na = cwrecur(na, tag1, 0, qp);
					if(na) {
						d->dblock[j] = na;
						p->flags |= Bmod;
					}
				}
			}
			if(na = d->iblock) {
				na = cwrecur(na, Tind1, tag1, qp);
				if(na) {
					d->iblock = na;
					p->flags |= Bmod;
				}
			}
			if(na = d->diblock) {
				na = cwrecur(na, Tind2, tag1, qp);
				if(na) {
					d->diblock = na;
					p->flags |= Bmod;
				}
			}
		}
		break;

	case Tind1:
		j = tag1;
		tag1 = 0;
		goto tind;

	case Tind2:
		j = Tind1;

	tind:
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("cwrecur: Tind p null\n");
				break;
			}
		}
		for(i=0; i<INDPERBUF; i++) {
			if(na = ((long*)p->iobuf)[i]) {
				na = cwrecur(na, j, tag1, qp);
				if(na) {
					((long*)p->iobuf)[i] = na;
					p->flags |= Bmod;
				}
			}
		}
		break;
	}
	na = split(p, addr);
	cw.depth--;
	if(na && shouldstop) {
		if(cw.falsehits < 10)
			print("shouldstop %s %ld %ld t=%s\n",
				cw.name, addr, na, tagnames[tag]);
		cw.falsehits++;
	}
	return na;
}

void
cfsdump(Filsys *fs)
{
	Iobuf *pr, *p1, *p;
	Dentry *dr, *d1, *d;
	Cache *h;
	Superb *s;
	long orba, rba, oroa, roa, sba, a, m, n, i;
	char tstr[20];

	if(fs->dev.type != Devcw) {
		print("cant dump this: %D\n", fs->dev);
		return;
	}

	wlock(&mainlock);		/* dump */

	/*
	 * set up static structure
	 * with frequent variables
	 */
	cw.dev = fs->dev;
	cw.rodev = cw.dev;
	cw.rodev.type = Devro;
	cw.wdev = WDEV(cw.dev);
	cw.cdev = CDEV(cw.dev);


	cw.ndump = 0;
	cw.name[0] = 0;
	cw.depth = 0;

	/*
	 * cw root
	 */
	sync("before dump");
	cw.fsize = cwsize(cw.dev);
	orba = cwraddr(cw.dev, 0);
	print("cwroot %ld", orba);
	cons.noage = 1;
	cw.all = cw.allflag;
	rba = cwrecur(orba, Tsuper, 0, QPROOT);
	if(rba == 0)
		rba = orba;
	print("->%ld\n", rba);
	sync("after cw");

	/*
	 * partial super block
	 */
	p = getbuf(cw.dev, cwsaddr(cw.dev), Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->fsize = cw.fsize;
	s->cwraddr = rba;
	putbuf(p);

	/*
	 * partial cache block
	 */
	p = getbuf(cw.cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
	h = (Cache*)p->iobuf;
	h->fsize = cw.fsize;
	h->cwraddr = rba;
	putbuf(p);

	/*
	 * ro root
	 */
	oroa = cwraddr(cw.dev, 1);
	pr = getbuf(cw.dev, oroa, Bread|Bmod);
	dr = getdir(pr, 0);

	datestr(tstr, time());	/* tstr = "yyyymmdd" */
	n = 0;
	for(a=0;; a++) {
		p1 = dnodebuf(pr, dr, a, Tdir);
		if(!p1)
			goto bad;
		n++;
		for(i=0; i<DIRPERBUF; i++) {
			d1 = getdir(p1, i);
			if(!d1)
				goto bad;
			if(!(d1->mode & DALLOC))
				goto found1;
			if(!memcmp(d1->name, tstr, 4))
				goto found2;	/* found entry */
		}
		putbuf(p1);
	}

	/*
	 * no year directory, create one
	 */
found1:
	p = getbuf(cw.dev, rba, Bread);
	d = getdir(p, 0);
	d1->qid = d->qid;
	d1->qid.version += n;
	memmove(d1->name, tstr, 4);
	d1->mode = d->mode;
	d1->uid = d->uid;
	d1->gid = d->gid;
	putbuf(p);
	accessdir(p1, d1, FWRITE);

	/*
	 * put mmdd[count] in year directory
	 */
found2:
	accessdir(p1, d1, FREAD);
	putbuf(pr);
	pr = p1;
	dr = d1;

	n = 0;
	m = 0;
	for(a=0;; a++) {
		p1 = dnodebuf(pr, dr, a, Tdir);
		if(!p1)
			goto bad;
		n++;
		for(i=0; i<DIRPERBUF; i++) {
			d1 = getdir(p1, i);
			if(!d1)
				goto bad;
			if(!(d1->mode & DALLOC))
				goto found;
			if(!memcmp(d1->name, tstr+4, 4))
				m++;
		}
		putbuf(p1);
	}

	/*
	 * empty slot put in root
	 */
found:
	if(m)	/* how many dumps this date */
		sprint(tstr+8, "%d", m);

	p = getbuf(cw.dev, rba, Bread);
	d = getdir(p, 0);
	*d1 = *d;				/* qid is QPROOT */
	putbuf(p);
	strcpy(d1->name, tstr+4);
	d1->qid.version += n;
	accessdir(p1, d1, FWRITE);
	putbuf(p1);
	putbuf(pr);

	cw.fsize = cwsize(cw.dev);
	oroa = cwraddr(cw.dev, 1);		/* probably redundant */
	print("roroot %ld", oroa);

	cons.noage = 0;
	cw.all = 0;
	roa = cwrecur(oroa, Tsuper, 0, QPROOT);
	if(roa == 0) {
		print("[same]");
		roa = oroa;
	}
	print("->%ld /%.4s/%s\n", roa, tstr, tstr+4);
	sync("after ro");

	/*
	 * final super block
	 */
	a = cwsaddr(cw.dev);
	print("sblock %ld", a);
	p = getbuf(cw.dev, a, Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->last = a;
	sba = s->next;
	s->next = cw.fsize;
	cw.fsize++;
	s->fsize = cw.fsize;
	s->roraddr = roa;

	cwio(cw.dev, sba, 0, Ogrow);
	cwio(cw.dev, sba, p->iobuf, Owrite);
	cwio(cw.dev, sba, 0, Odump);
	print("->%ld (->%ld)\n", sba, s->next);

	putbuf(p);

	/*
	 * final cache block
	 */
	p = getbuf(cw.cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
	h = (Cache*)p->iobuf;
	h->fsize = cw.fsize;
	h->roraddr = roa;
	h->sbaddr = sba;
	putbuf(p);

	rewalk();
	sync("all done");

	print("%ld blocks queued for worm\n", cw.ndump);
	print("%ld falsehits\n", cw.falsehits);
	cw.nodump = 0;

	wunlock(&mainlock);
	return;

bad:
	panic("dump: bad");
}

void
mvstates(Device dev, int s1, int s2)
{
	Iobuf *p, *cb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m;
	Device cdev;

	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++)
			if(c->state == s1)
				c->state = s2;
		putbuf(p);
	}
	putbuf(cb);
}

void
prchain(Device dev, long m, int flg)
{
	Iobuf *p;
	Superb *s;

	if(m == 0) {
		if(flg)
			m = cwsaddr(dev);
		else
		if(startsb)
			m = startsb;
		else
			m = FIRST;
	}
	p = getbuf(devnone, Cwxx2, 0);
	s = (Superb*)p->iobuf;
	for(;;) {
		memset(p->iobuf, 0, RBUFSIZE);
		if(devread(WDEV(dev), m, p->iobuf) ||
		   checktag(p, Tsuper, QPSUPER))
			break;
		if(flg) {
			print("dump %ld is good; %ld prev\n", m, s->last);
			print("\t%ld cwroot; %ld roroot\n", s->cwraddr, s->roraddr);
			if(m <= s->last)
				break;
			m = s->last;
		} else {
			print("dump %ld is good; %ld next\n", m, s->next);
			print("\t%ld cwroot; %ld roroot\n", s->cwraddr, s->roraddr);
			if(m >= s->next)
				break;
			m = s->next;
		}
	}
	putbuf(p);
}

void
touchsb(Device dev)
{
	Iobuf *p;
	long m;

	m = cwsaddr(dev);
	p = getbuf(devnone, Cwxx2, 0);

	memset(p->iobuf, 0, RBUFSIZE);
	if(devread(WDEV(dev), m, p->iobuf) ||
	   checktag(p, Tsuper, QPSUPER))
		goto out;
	print("touch superblock %ld\n", m);
out:
	putbuf(p);
}

void
cwreseq(Device dev, int which)
{
	Iobuf *p, *cb;
	Cache *h;
	Bucket *b;
	long m;
	Device cdev;
	int e;

	e = 0;
	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwreseq: checktag c bucket");
	h = (Cache*)cb->iobuf;
	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwreseq: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		if(which) {
			if(bktcheck(b))
				e++;
		} else
			resequence(b);
		putbuf(p);
		if(e > 10)
			break;
	}
	putbuf(cb);
}

void
savecache(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m, maddr, msize, left, *longp, nbyte;
	int age;
	Device cdev;

	if(walkto("/adm/cache"))
		goto bad;
	if(con_open(FID2, MWRITE|MTRUNC))
		goto bad;


	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	msize = h->msize;
	maddr = h->maddr;
	putbuf(cb);

	cb = getbuf(devnone, Cwxx4, 0);
	longp = (long*)cb->iobuf;
	left = BUFSIZE/sizeof(long);
	cons.offset = 0;

	for(m=0; m<msize; m++) {
		if(left < BKPERBLK) {
			nbyte = (BUFSIZE/sizeof(long) - left) * sizeof(long);
			con_write(FID2, cb->iobuf, cons.offset, nbyte);
			cons.offset += nbyte;
			longp = (long*)cb->iobuf;
			left = BUFSIZE/sizeof(long);
		}
		p = getbuf(cdev, maddr + m/BKPERBLK, Bread|Bres);
		if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		resequence(b);
		age = b->agegen - CEPERBK/3;
		if(age < 0)
			age = 0;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++)
			if(c->state == Cread && c->age >= age) {
				*longp++ = c->waddr;
				left--;
			}
		putbuf(p);
	}
	nbyte = (BUFSIZE/sizeof(long) - left) * sizeof(long);
	con_write(FID2, cb->iobuf, cons.offset, nbyte);
	putbuf(cb);
	return;

bad:
	print("cant open /adm/cache\n");
}

void
loadcache(Device dev, int dskno)
{
	Iobuf *p, *cb;
	long m, nbyte, *longp, count;

	if(walkto("/adm/cache"))
		goto bad;
	if(con_open(FID2, MREAD))
		goto bad;

	cb = getbuf(devnone, Cwxx4, 0);
	cons.offset = 0;
	count = 0;

	for(;;) {
		memset(cb->iobuf, 0, BUFSIZE);
		nbyte = con_read(FID2, cb->iobuf, cons.offset, BUFSIZE) / sizeof(long);
		if(nbyte <= 0)
			break;
		cons.offset += nbyte * sizeof(long);
		longp = (long*)cb->iobuf;
		while(nbyte > 0) {
			m = *longp++;
			nbyte--;
			if(m == 0)
				continue;
			if(dskno < 0 ||
			   m >= dskno*DSIZE && m < (dskno+1)*DSIZE) {
				p = getbuf(dev, m, Bread);
				if(p)
					putbuf(p);
				count++;
			}
		}
	}
	putbuf(cb);
	print("%ld blocks loaded from worm %d\n", count, dskno);
	return;

bad:
	print("cant open /adm/cache\n");
}

int
dowcmp(void)
{
	Iobuf *p, *p1, *p2;
	Cache *h;
	Centry *c;
	Bucket *b;
	long maddr, msize, caddr, a;

	if(cw.wcpflg == 0)
		return 0;
	
	p = getbuf(cw.wcpcdev, CACHE_ADDR, Bread|Bres);
	if(!p || checktag(p, Tcache, QPSUPER)) {
		print("dowcmp: checktag c bucket\n");
		if(p)
			putbuf(p);
		cw.wcpflg = 0;
		return 0;
	}
	h = (Cache*)p->iobuf;
	maddr = h->maddr;
	msize = h->msize;
	caddr = h->caddr;
	putbuf(p);

	cw.wcpcaddr++;
	if(cw.wcpcaddr < 0 || cw.wcpcaddr >= CEPERBK) {
		cw.wcpcaddr = 0;
		cw.wcpmaddr++;
	}
	if(cw.wcpmaddr < 0 || cw.wcpmaddr >= msize)
		cw.wcpmaddr = 0;

	p = getbuf(cw.wcpcdev, maddr + cw.wcpmaddr/BKPERBLK, Bread|Bres);
	if(!p || checktag(p, Tbuck, maddr + cw.wcpmaddr/BKPERBLK)) {
		print("dowcmp: checktag c bucket\n");
		if(p)
			putbuf(p);
		cw.wcpflg = 0;
		return 0;
	}

	b = (Bucket*)p->iobuf + cw.wcpmaddr%BKPERBLK;
	c = b->entry + cw.wcpcaddr;
	while(c->state == Cread) {
		a = c->waddr;
		if(a < cw.wcpplatter*DSIZE || a >= (cw.wcpplatter+1)*DSIZE)
			break;

		cw.wcpcount++;

		p1 = getbuf(cw.wcpwdev, a, Bread);
		if(!p1) {
			print("dowcmp: wdev error\n");
			break;
		}

		a = cw.wcpmaddr*CEPERBK + cw.wcpcaddr + caddr;
		p2 = getbuf(cw.wcpcdev, a, Bread);
		if(!p2) {
			print("dowcmp: cdev error\n");
			putbuf(p1);
			break;
		}

		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
			print("dowcmp: dont compare w=%ld c=%ld\n", c->waddr, a);
			c->state = Cdump1;
			p->flags |= Bmod;
		}
		putbuf(p1);
		putbuf(p2);
		break;
	}

	putbuf(p);
	if(cw.wcpmaddr == 0 && cw.wcpcaddr == 0) {
		print("dowcmp: wrap %ld\n", cw.wcpcount);
		cw.wcpflg = 0;
		cw.wcpcount = 0;
	}
	return 1;
}

void
blockcmp(Device dev, long wa, long ca)
{
	Iobuf *p1, *p2;
	int i, c;

	p1 = getbuf(WDEV(dev), wa, Bread);
	if(!p1) {
		print("dowcmp: wdev error\n");
		return;
	}

	p2 = getbuf(CDEV(dev), ca, Bread);
	if(!p2) {
		print("dowcmp: cdev error\n");
		putbuf(p1);
		return;
	}

	c = 0;
	for(i=0; i<RBUFSIZE; i++)
		if(p1->iobuf[i] != p2->iobuf[i]) {
			print("%4d: %.2x %.2x\n",
				i,
				p1->iobuf[i]&0xff,
				p2->iobuf[i]&0xff);
			c++;
			if(c >= 10)
				break;
		}

	if(c == 0)
		print("no error\n");
	putbuf(p1);
	putbuf(p2);
}

#ifdef	XXX
/* garbage to change sb size
 * probably will need it someday
 */
	fsz = number(0, 0, 10);
	count = 0;
	if(fsz == number(0, -1, 10))
		count = -1;		/* really do it */
	print("fsize = %ld\n", fsz);
	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++) {
			if(c->waddr < fsz)
				continue;
			if(count < 0) {
				c->state = Cnone;
				continue;
			}
			if(c->state != Cdirty)
				count++;
		}
		putbuf(p);
	}
	if(count < 0) {
		print("old cache hsize = %ld\n", h->fsize);
		h->fsize = fsz;
		cb->flags |= Bmod;
		p = getbuf(dev, h->sbaddr, Bread|Bmod);
		s = (Superb*)p->iobuf;
		print("old super hsize = %ld\n", s->fsize);
		s->fsize = fsz;
		putbuf(p);
	}
	putbuf(cb);
	print("count = %ld\n", count);
#endif

int
convstate(char *name)
{
	int i;

	for(i=0; i<nelem(cwnames); i++)
		if(cwnames[i])
			if(strcmp(cwnames[i], name) == 0)
				return i;
	return -1;
}

void
cmd_cwcmd(int argc, char *argv[])
{
	Filsys *fs;
	Device dev;
	char *arg;
	long s1, s2, a;

	if(argc <= 1) {
		print("	cwcmd mvstate state1 state2\n");
		print("	cwcmd prchain [start] [bakflg]\n");
		print("	cwcmd touchsb\n");
		print("	cwcmd savecache\n");
		print("	cwcmd loadcache [dskno]\n");
		print("	cwcmd wormcmp [dskno]\n");
		print("	cwcmd blockcmp wbno cbno\n");
		print("	cwcmd startdump [01]\n");
		print("	cwcmd resequence\n");
		print("	cwcmd bktcheck\n");
		print("	cwcmd allflag\n");
		return;
	}
	arg = argv[1];
	for(fs=filsys; fs->name; fs++) {
		dev = fs->dev;
		if(dev.type != Devcw)
			continue;

		if(strcmp(arg, "mvstate") == 0) {
			if(argc <= 3)
				goto bad;
			s1 = convstate(argv[2]);
			s2 = convstate(argv[3]);
			if(s1 < 0 || s2 < 0)
				goto bad;
			mvstates(dev, s1, s2);
			continue;
		bad:
			print("cwcmd mvstate: bad args\n");
			continue;
		}
		if(strcmp(arg, "prchain") == 0) {
			a = 0;
			if(argc > 2)
				a = number(argv[2], 0, 10);
			s1 = 0;
			if(argc > 3)
				s1 = 1;
			prchain(dev, a, s1);
			continue;
		}
		if(strcmp(arg, "touchsb") == 0) {
			touchsb(dev);
			continue;
		}
		if(strcmp(arg, "savecache") == 0) {
			savecache(dev);
			continue;
		}
		if(strcmp(arg, "loadcache") == 0) {
			s1 = -1;
			if(argc > 2)
				s1 = number(argv[2], 0, 10);
			loadcache(dev, s1);
			continue;
		}
		if(strcmp(arg, "wormcmp") == 0) {
			s1 = 0;
			if(argc > 2)
				s1 = number(argv[2], 0, 10);
			if(cw.wcpflg == 0) {
				cw.wcpcdev = CDEV(dev);
				cw.wcpwdev = WDEV(dev);
				cw.wcpplatter = s1;
				cw.wcpflg = 1;
				print("wormcmp dskno %d\n", cw.wcpplatter);
			} else {
				cw.wcpflg = 0;
				print("wormcmp off\n");
			}
			continue;
		}
		if(strcmp(arg, "blockcmp") == 0) {
			if(argc < 4) {
				print("cannot arg count\n");
				continue;
			}
			s1 = number(argv[2], 0, 10);
			s2 = number(argv[3], 0, 10);
			blockcmp(dev, s1, s2);
			continue;
		}
		if(strcmp(arg, "startdump") == 0) {
			if(argc > 2)
				cw.nodump = number(argv[2], 0, 10);
			cw.nodump = !cw.nodump;
			if(cw.nodump)
				print("dump stopped\n");
			else
				print("dump allowed\n");
			continue;
		}
		if(strcmp(arg, "resequence") == 0) {
			cwreseq(dev, 0);
			continue;
		}
		if(strcmp(arg, "bktcheck") == 0) {
			cwreseq(dev, 1);
			continue;
		}
		if(strcmp(arg, "allflag") == 0) {
			if(argc > 2)
				cw.allflag = number(argv[2], 0, 10);
			else
				cw.allflag = !cw.allflag;
			print("allflag = %d; falsehits = %d\n",
				cw.allflag, cw.falsehits);
			continue;
		}
		print("unknown cwcmd %s\n", arg);
	}
}
data.c          701403142   0     0     664     3934      `
#include	"all.h"

Device	devnone	= {Devnone,0,0,0};

void
(*p9call[MAXSYSCALL])(Chan*, Fcall*, Fcall*) =
{
	[Tnop]		f_nop,
	[Tsession]	f_session,
	[Tflush]	f_flush,
	[Tattach]	f_attach,
	[Tclone]	f_clone,
	[Twalk]		f_walk,
	[Topen]		f_open,
	[Tcreate]	f_create,
	[Tread]		f_read,
	[Twrite]	f_write,
	[Tclunk]	f_clunk,
	[Tremove]	f_remove,
	[Tstat]		f_stat,
	[Twstat]	f_wstat,
	[Tclwalk]	f_clwalk,
	[Tauth]		f_auth,
};

char	*errstr[MAXERR] =
{
	[Ebadspc]	"attach -- bad specifier",
	[Efid]		"unknown fid",
	[Echar]		"bad character in directory name",
	[Eopen]		"read/write -- on non open fid",
	[Ecount]	"read/write -- count too big",
	[Ealloc]	"phase error -- directory entry not allocated",
	[Eqid]		"phase error -- qid does not match",
	[Eaccess]	"access permission denied",
	[Eentry]	"directory entry not found",
	[Emode]		"open/create -- unknown mode",
	[Edir1]		"walk -- in a non-directory",
	[Edir2]		"create -- in a non-directory",
	[Ephase]	"phase error -- cannot happen",
	[Eexist]	"create -- file exists",
	[Edot]		"create -- . and .. illegal names",
	[Eempty]	"remove -- directory not empty",
	[Ebadu]		"attach -- privileged user",
	[Enotu]		"wstat -- not owner",
	[Enotg]		"wstat -- not in group",
	[Ename]		"create/wstat -- bad character in file name",
	[Ewalk]		"walk -- too many (system wide)",
	[Eronly]	"file system read only",
	[Efull]		"file system full",
	[Eoffset]	"read/write -- offset negative",
	[Elocked]	"open/create -- file is locked",
	[Ebroken]	"close/read/write -- lock is broken",
	[Eauth]		"attach -- authentication failed",
};

char*	wormscode[0x80] =
{
	[0x00]	"no sense",
	[0x01]	"invalid command",
	[0x02]	"recovered error",
	[0x03]	"illegal request",
	[0x06]	"unit attention",
	[0x07]	"parity error",
	[0x08]	"message reject error",
	[0x0a]	"copy aborted",
	[0x0b]	"inniator idetected error",
	[0x0c]	"select re-select failed",
	[0x0e]	"miscompare",

	[0x10]	"ecc trouble occurred",
	[0x11]	"time out error",
	[0x12]	"controller error",
	[0x13]	"sony i/f II hardware/firmware error",
	[0x14]	"scsi hardware/firmware error",
	[0x15]	"rom version unmatched error",
	[0x16]	"logical block address out of range",

	[0x20]	"command not terminated",
	[0x21]	"drive interface parity error",
	[0x22]	"loading trouble",
	[0x23]	"focus trouble",
	[0x24]	"tracking trouble",
	[0x25]	"spindle trouble",
	[0x26]	"slide trouble",
	[0x27]	"skew trouble",
	[0x28]	"head lead out",
	[0x29]	"write modulation trouble",
	[0x2a]	"under laser power",
	[0x2b]	"over laser power",
	[0x2f]	"drive error",

	[0x30]	"drive power off",
	[0x31]	"no disk in drive",
	[0x32]	"drive not ready",
	[0x38]	"disk already exists in drive",
	[0x39]	"no disk in shelf",
	[0x3a]	"disk already exists in shelf",

	[0x40]	"write warning",
	[0x41]	"write error",
	[0x42]	"disk error",
	[0x43]	"cannot read disk ID",
	[0x44]	"write protect error 1",
	[0x45]	"write protect error 2",
	[0x46]	"disk warning",
	[0x47]	"alternation trouble",

	[0x50]	"specified address not found",
	[0x51]	"address block not found",
	[0x52]	"all address could not be read",
	[0x53]	"data could not be read",
	[0x54]	"uncorrectable read error",
	[0x55]	"tracking error",
	[0x56]	"write servo error",
	[0x57]	"write monitor error",
	[0x58]	"write verify error",

	[0x60]	"no data in specified address",
	[0x61]	"blank check failed",
	[0x62]	"controller diagnostics failed",
	[0x63]	"drive diagnostice failed",
	[0x64]	"diagnostice aborted",
	[0x67]	"juke diagnostice failed",
	[0x68]	"z-axis servo failed",
	[0x69]	"roter servo error",
	[0x6a]	"hook servo error",
	[0x6b]	"I/O self error",
	[0x6c]	"drive 0 error",
	[0x6d]	"drive 1 error",
	[0x6e]	"shelf error",
	[0x6f]	"carrier error",

	[0x70]	"rob made me do it",
	[0x71]	"out of range",
};

char*	tagnames[] =
{
	[Tbuck]		"Tbuck",
	[Tdir]		"Tdir",
	[Tfile]		"Tfile",
	[Tfree]		"Tfree",
	[Tind1]		"Tind1",
	[Tind2]		"Tind2",
	[Tnone]		"Tnone",
	[Tsuper]	"Tsuper",
	[Tvirgo]	"Tvirgo",
	[Tcache]	"Tcache",
};
dentry.c        701734010   0     0     664     3273      `
#include	"all.h"

Dentry*
getdir(Iobuf *p, int slot)
{
	Dentry *d;

	if(!p)
		return 0;
	d = (Dentry*)p->iobuf + slot%DIRPERBUF;
	return d;
}

void
accessdir(Iobuf *p, Dentry *d, int f)
{
	long t;

	if(p && p->dev.type != Devro) {
		p->flags |= Bmod;
		t = time();
		if(f & (FREAD|FWRITE))
			d->atime = t;
		if(f & FWRITE) {
			d->mtime = t;
			d->qid.version++;
		}
	}
}

void
preread(Device dev, long addr)
{
	Rabuf *rb;

	if(addr == 0)
		return;
	lock(&rabuflock);
	rb = rabuffree;
	if(rb == 0) {
		unlock(&rabuflock);
		return;
	}
	rabuffree = rb->link;
	unlock(&rabuflock);
	rb->dev = dev;
	rb->addr = addr;
	send(raheadq, rb);
	cons.brahead.count++;
}

void
dbufread(Iobuf *p, Dentry *d, long a)
{
	long addr;

	if(a < 0)
		return;
	if(a < NDBLOCK) {
		addr = d->dblock[a];
		if(addr)
			preread(p->dev, addr);
		return;
	}
	a -= NDBLOCK;
	if(a < INDPERBUF) {
		addr = d->iblock;
		if(addr) {
			addr = indfetch(p, d, addr, a, Tind1, 0);
			if(addr)
				preread(p->dev, addr);
		}
		return;
	}
	a -= INDPERBUF;
	if(a < INDPERBUF2) {
		addr = d->diblock;
		if(addr) {
			addr = indfetch(p, d, addr, a/INDPERBUF, Tind2, Tind1);
			if(addr) {
				addr = indfetch(p, d, addr, a%INDPERBUF, Tind1, 0);
				if(addr)
					preread(p->dev, addr);
			}
		}
		return;
	}
}

Iobuf*
dnodebuf(Iobuf *p, Dentry *d, long a, int tag)
{
	Iobuf *bp;
	long addr;

	if(a < 0) {
		print("dnodebuf: neg\n");
		return 0;
	}
	bp = 0;
	if(a < NDBLOCK) {
		addr = d->dblock[a];
		if(addr)
			return getbuf(p->dev, addr, Bread);
		if(tag) {
			addr = bufalloc(p->dev, tag, d->qid.path);
			if(addr) {
				d->dblock[a] = addr;
				p->flags |= Bmod|Bimm;
				bp = getbuf(p->dev, addr, Bmod);
			}
		}
		return bp;
	}
	a -= NDBLOCK;
	if(a < INDPERBUF) {
		addr = d->iblock;
		if(!addr && tag) {
			addr = bufalloc(p->dev, Tind1, d->qid.path);
			d->iblock = addr;
			p->flags |= Bmod|Bimm;
		}
		addr = indfetch(p, d, addr, a, Tind1, tag);
		if(addr)
			bp = getbuf(p->dev, addr, Bread);
		return bp;
	}
	a -= INDPERBUF;
	if(a < INDPERBUF2) {
		addr = d->diblock;
		if(!addr && tag) {
			addr = bufalloc(p->dev, Tind2, d->qid.path);
			d->diblock = addr;
			p->flags |= Bmod|Bimm;
		}
		addr = indfetch(p, d, addr, a/INDPERBUF, Tind2, Tind1);
		addr = indfetch(p, d, addr, a%INDPERBUF, Tind1, tag);
		if(addr)
			bp = getbuf(p->dev, addr, Bread);
		return bp;
	}
	print("dnodebuf: trip indirect\n");
	return 0;
}

long
indfetch(Iobuf *p, Dentry *d, long addr, long a, int itag, int tag)
{
	Iobuf *bp;

	if(!addr)
		return 0;
	bp = getbuf(p->dev, addr, Bread);
	if(!bp || checktag(bp, itag, d->qid.path)) {
		if(!bp) {
			print("ind fetch bp = 0\n");
			return 0;
		}
		print("ind fetch tag\n");
		putbuf(bp);
		return 0;
	}
	addr = ((long*)bp->iobuf)[a];
	if(!addr && tag) {
		addr = bufalloc(p->dev, tag, d->qid.path);
		if(addr) {
			((long*)bp->iobuf)[a] = addr;
			p->flags |= Bmod;
			if(tag == Tdir)
				bp->flags |= Bimm;
			settag(bp, itag, d->qid.path);
		}
	}
	putbuf(bp);
	return addr;
}

void
dtrunc(Iobuf *p, Dentry *d)
{
	int i;

	buffree(p->dev, d->diblock, 2);
	d->diblock = 0;
	buffree(p->dev, d->iblock, 1);
	d->iblock = 0;
	for(i=NDBLOCK-1; i>=0; i--) {
		buffree(p->dev, d->dblock[i], 0);
		d->dblock[i] = 0;
	}
	d->size = 0;
	p->flags |= Bmod|Bimm;
	accessdir(p, d, FWRITE);
}

dk.h            699862060   0     0     664     6460      `
#define	DEBUG		(cons.flags & dkflag)
#define	CEBUG		((cons.flags|up->flags) & dkflag)
#define DPRINT		if(DEBUG)print
#define CPRINT		if(CEBUG)print

enum
{
	CCCHAN		= 4,		/* the common control channel */
	SRCHAN,				/* channel for service requests */
	CKCHAN,				/* channel for call to clock */
	NDK		= 256,		/* number of channels per hsvme */
	MaxDk		= 4,		/* max number of hsvmes */
};
#define	DKTIME(n)	(n/2)
#define WSIZE		WS_512

typedef	struct	Hsdev	Hsdev;
typedef	struct	Dialin	Dialin;
typedef	struct	Dk	Dk;

/*
 * Unixp message structure
 */
struct	Dialin
{
	uchar	type;
	uchar	srv;
	uchar	param0l, param0h;
	uchar	param1l, param1h;
	uchar	param2l, param2h;
	uchar	param3l, param3h;
	uchar	param4l, param4h;
};

/*
 * Message codes to UNIX driver
 */
#define	T_SRV	1		/* request for service */
#define	T_SRV2	'k'		/* alternative request for service */
#define		D_SERV	1	/* announce a server */
#define		D_DIAL	2	/* dialout from host */

#define	T_REPLY	2		/* reply to request for service */
#define		D_OK	1	/* ? */
#define		D_OPEN	2	/* successful open */
#define		D_FAIL	3	/* NAK return */

#define	T_CHG	3		/* change in channel state */
#define		D_CLOSE	1	/* one side wishes to close a channel */
#define		D_ISCLOSED 2	/* response to UNIX close */
#define		D_CLOSEALL 3	/* sent to close all UNIX channels */

#define	T_ALIVE	4		/* keep-alive message */
#define		D_STILL	0	/* still alive */
#define		D_RESTART 1	/* just rebooted */
#define		D_XINIT	7	/* ? */

#define	T_RESTART 8		/* host rebooted, re-init channel 1 */



/*  URP window-size negotiation.
 *
 *  The 16 bits of the window-size argument are defined as:
 *
 *  -----------------------------------------------------------------
 *  |valid|   spare   |   originator   |   destination  |   traffic |
 *  |     |           |   window size  |   window size  |           |
 *  -----------------------------------------------------------------
 *     1        3              4                4              4
 *
 *  The "valid" bit equals 1 to indicate that the window size values
 *  are specified.  Zero means they should be ignored and that default
 *  values must be used instead.  Old controllers that do not have
 *  window size negotiation happen to set this bit to zero, fortunately.
 *
 *  The actual window size is computed from the 4-bit window size 
 *  parameter (n) as 2**(n+4).  Thus, window sizes range from 16 to
 *  512K bytes.
 *
 *  The originator of a call sets his "originator window size" to be
 *  less than or equal to the size of his receive buffer, and he
 *  requests through the "destination window size" the size of the
 *  buffer that he would like to use for transmission.  As the call
 *  is being set up, trunk processes and the destination line process
 *  may lower either or both window sizes.  The resulting window sizes
 *  are returned to the originator in the call ANSWER message.
 */

/* Format originator, destination, and traffic values */

#define W_WINDOW(o,d,t)	((o<<8) | (d<<4) | t | 0100000)
#define W_VALID(x)	((x) & 0100000)
#define W_ORIG(x)	(((x)>>8) & 017)
#define W_DEST(x)	(((x)>>4) & 017)
#define W_TRAF(x)	((x) & 017)
#define W_DESTMAX(x,y)	W_WINDOW(W_ORIG(x), MIN(W_DEST(x),y), W_TRAF(x))
#define W_LIMIT(x,y)	W_WINDOW(MIN(W_ORIG(x),y), MIN(W_DEST(x),y), W_TRAF(x))
#define	W_VALUE(x)	(1<<((x)+4))

enum
{
	/*
	 * window sizes
	 */
	WS_16	= 0,
	WS_32,
	WS_64,
	WS_128,
	WS_256,
	WS_512,
	WS_1K,
	WS_2K,
	WS_4K,
	WS_8K,
	WS_16K,
	WS_32K,
	WS_64K,
	WS_128K,
	WS_256K,
	WS_512K,

/*
 * control characters
 */
	CSEQ0	= 8,
	CECHO0	= 16,
	CREJ0	= 24,
	CACK0	= 32,
	CBOT	= 40,
	CBOTM	= 41,
	CBOTS	= 42,
	CENQ	= 45,
	CINITREQ= 47,		/* request initialization */
	CINIT0	= 48,
	CINIT1	= 49,
	CAINIT	= 50,

/*
 * xx
 */
	SUDAT	= 0,
	SIGNOR,
	SMARK,
	SECHO,
	SREJ,
	SACK,
	SBOT,
	SBOTM,
	SBOTS,
	SENQ,
	SINIT1,
	SSEQ,
	SUCTRL,
	SAINIT,
	SINITREQ,
/*
 * urpstate's
 */
	INITING 	= 0,	/* sent INIT1, awaiting AINIT */
	INITED,			/* received AINIT */

/*
 * common dkstate's
 */
	CLOSED		= 0,
	DIALING,
	LCLOSE,
	OPENED,

/*
 * indexes in timeout array
 */
	DKATO	= 0,
	DKBTO,
	TIMETO,
	NTIMEOUT,

/*
 * length of timeouts
 */
	DKATIME	= SECOND(2),	/* enq */
	DKBTIME	= SECOND(15),	/* alive */
	TIMENTIME= HOUR(4),	/* call for time */
	TIMEQTIME= MINUTE(1),	/* call for time until established */
};

/*
 *  hsvme datakit board
 */
struct	Hsdev
{
	ushort	version;
	ushort	pad1;
	ushort	vector;
	ushort	pad2;
	ushort	csr;
		#define ALIVE		0x0001
		#define IENABLE		0x0004
		#define EXOFLOW		0x0008
		#define IRQ		0x0010
		#define EMUTE		0x0020
		#define EPARITY		0x0040
		#define EFRAME		0x0080
		#define EROFLOW		0x0100
		#define REF		0x0800
		#define XFF		0x4000
		#define XHF		0x8000
		/* after reset */
		#define FORCEW		0x0008
		#define IPL(x)		((x)<<5)
		#define NORESET		0xFF00
		#define RESET		0x0000
	ushort	pad3;
	ushort	data;
		#define CTL		0x0100
		#define CHNO		0x0200
		#define TXEOD		0x0400
		#define NND		0x8000
	ushort	pad4;
};

struct	Dk
{
	Filter	irate;		/* aggrigate bytes */
	Filter	orate;		/* aggrigate bytes */
	Rendez	rren;		/* receiver non-empty */
	Rendez	xren;		/* xmitter half-full */
	Rendez	dkto;		/* timer */
	Queue*	reply;		/* return from file system server, gets Msgbuf* */
	Queue*	local;		/* server of dk special channels */
	Queue*	poke;		/* output to dk device, gets Chan* */
	Chan*	dkchan;		/* points to NDK channels */
	Vmedevice* vme;		/* hardware info */

	long	timeout[NTIMEOUT];
	char	boot;		/* horrible flag in xpannounce */
	char	anytime;	/* horrible flag that we know the time */
};

Dk	dkctlr[MaxDk];

void	cccinit(Dk*);
void	xpinit(Chan*);
void	urpinit(Chan*);
void	urpxinit(Chan*, int wins);
void	urprinit(Chan*);
void	dk_rintr(int u);
void	dkreply(Chan *up, int c1, int c2);
int	dk_init(int j);
void	dk_start(int c, uchar *p, int n);
void	dk_timer(Dk*);
void	dkecho(Chan *up, int cc);
void	prwindow(Chan *up, int cc);
void	dkchaninit(Chan*);

void	xpmesg(Chan*, int type, int srv, int p0, int p1);
void	xplisten(Dk*, char *cp, int n);
void	xpannounce(Chan*, char *addr);
void	xpcall(Chan*, char *dialstr, int);
void	xphup(Chan*);
void	srlisten(Dk*, char *cp, int n);
void	dkservice(Chan *up);
void	dk_timeout(void);
long	readclock(char *buf, int n);
long	readclock(char*, int);

void	dklock(Dk*, Chan*);
void	dkunlock(Dk*, Chan*);
void	dkinput(void);
void	dkoutput(void);
void	dkpoke(void);
void	dkqmesg(Msgbuf*, Chan*);
void	dklocal(void);
void	dktimer(void);
void	dkinit(Dk*);
void	dkreset(Dk*, int);
dk1.c           699862033   0     0     664     3550      `
#include	"all.h"
#include	"dk.h"

/*
 * called once at startup time
 */
void
dkinit(Dk *dk)
{
	Chan *up;
	int i;

/*	cons.flags |= dkflag;	/**/

	dk->dkchan = chaninit(Devdk, NDK);
	dk->poke = newqueue(NQUEUE);
	dk->reply = newqueue(NQUEUE);
	dk->local = newqueue(NQUEUE);
	dk->anytime = 0;

	for(i=0; i<NDK; i++) {
		up = &dk->dkchan[i];
		up->dkp.name = "dk";
		dklock(dk, up);
		up->dkp.cno = i;

		xpinit(up);
		switch(i) {
		case SRCHAN:
		case CCCHAN:
		case CKCHAN:
			up->send = dk->local;
			up->reply = 0;
			break;

		default:
			up->send = serveq;
			up->reply = dk->reply;
			break;
		}

		dkunlock(dk, up);
	}
	dofilter(&dk->irate);
	dofilter(&dk->orate);
	wakeup(&dk->rren);
	wakeup(&dk->xren);
	wakeup(&dk->dkto);
}

enum
{
	Hunt,		/* looking for newline */
	Skipb,		/* skipping blanks */
	Digits		/* reading digits */
};

long
readclock(char *buf, int n)
{
	static int state, ndigits;	/* BOTCH */
	static long number;		/* BOTCH */
	int c, i;
	long v;

	v = 0;
	for(i=0; i<n; i++) {
		c = buf[i];
		switch(state) {
		case Hunt:
			if(c == '\n')
				state = Skipb;
			break;

		case Skipb:
			if(c == ' ')
				break;
			state = Digits;
			ndigits = 0;
			number = 0;

		case Digits:
			if(c >= '0' && c <= '9') {
				ndigits++;
				number = number*10 + c - '0';
				break;
			}
			state = Hunt;
			if(c =='\r' || c == '\n')
			if(ndigits >= 9)
				v = number;
			break;
		}
	}
	return v;
}

static
void
stats(Dk *dk)
{
	Chan *up;
	char flag[10], *fp;
	int i, n, l, w;
	Msgbuf *mb;

	print("dkstats %d\n", dk-dkctlr);
	n = 0;
	for(i=0; i<NDK; i++) {
		fp = flag;
		up = &dk->dkchan[i];

		l = 1;
		if(!canqlock(&up->dkp)) {
			l = 0;
			*fp++ = 'l';
		}

		if(up->dkp.inmsg)
			*fp++ = 'r';
		if(mb = up->dkp.outmsg) {
			*fp++ = 'w';
			if(l) {
				w = 0;
				while(mb) {
					w++;
					mb = mb->next;
				}
				*fp++ = 'A'+w;
			}
		}
		if(up->dkp.dkstate == OPENED)
			n++;
		if(fp != flag) {
			*fp++ = up->dkp.dkstate+'0';
			*fp = 0;
			print("	cno=%3d %s\n",
				up->dkp.cno,
				flag);
		}
		if(l)
			qunlock(&up->dkp);
	}
	print("	%d opened dk channels\n", n);
	print("	in  t=%F cbps\n", (Filta){&dk->irate, 100});
	print("	out t=%F cbps\n", (Filta){&dk->orate, 100});
}

static
void
cmd_statk(int argc, char *argv[])
{
	Dk *dk;

	USED(argc);
	USED(argv);

	for(dk = &dkctlr[0]; dk < &dkctlr[MaxDk]; dk++)
		if(dk->vme)
			stats(dk);
}

void
dklock(Dk *dk, Chan *up)
{

	USED(dk);
	qlock(&up->dkp);
	if(up->dkp.pokeflg)
		print("dk:%d pokeflg before start\n", up->dkp.cno);
	if(up->dkp.ccmsg)
		print("dk:%d ccmsg before start\n", up->dkp.cno);
}

void
dkunlock(Dk *dk, Chan *up)
{
	int f;
	Msgbuf *mb, *mb1;

loop:
	f = up->dkp.pokeflg;
	up->dkp.pokeflg = 0;

	mb = up->dkp.ccmsg;
	up->dkp.ccmsg = 0;

	qunlock(&up->dkp);
	if(f)
		send(dk->poke, up);

	if(mb) {
		up = &dk->dkchan[CCCHAN];
		dklock(dk, up);
		while(mb) {
			mb1 = mb->next;
			dkqmesg(mb, up);
			mb = mb1;
		}
		goto loop;
	}
		
}

void
dkreset(Dk *dk, int cno)
{
	int i;
	Chan *up;

	for(i=0; i<NDK; i++) {
		up = &dk->dkchan[i];
		if(up->chan != cno)
			continue;
		qlock(&up->dkp);
		xpinit(up);
		qunlock(&up->dkp);
	}
}

void
dkstart(void)
{
	Dk *dk;
	int any;

	any = 0;
	for(dk = &dkctlr[0]; dk < &dkctlr[MaxDk]; dk++)
		if(dk->vme) {
			dkinit(dk);
			userinit(dkinput, dk, "dki");
			userinit(dkoutput, dk, "dko");
			userinit(dkpoke, dk, "dkp");
			userinit(dktimer, dk, "dkt");
			userinit(dklocal, dk, "dkl");

			cmd_install("statk", "-- dk stats", cmd_statk);
			dkflag = flag_install("dkit", "-- datakit noise");
			any = 1;
		}
	USED(any);
}
dk2.c           710793753   0     0     664     6571      `
#include	"all.h"
#include	"dk.h"

/*
 *  disconnect a dk channel, a CLOSED channel can do no I/O
 *	called with up locked
 */
void
xpinit(Chan *up)
{
	Msgbuf *mb;

	up->dkp.dkstate = CLOSED;
	while(up->dkp.outmsg) {
		mb = up->dkp.outmsg->next;
		mbfree(up->dkp.outmsg);
		up->dkp.outmsg = mb;
	}
	if(up->dkp.inmsg) {
		mbfree(up->dkp.inmsg);
		up->dkp.inmsg = 0;
	}
	up->dkp.repr = 0;
	up->dkp.repw = 0;
	up->dkp.mout = 0;
	up->dkp.nout = 0;
	up->dkp.mcount = 0;
	fileinit(up);
}

/*
 *  start up the common controller channel (done once)
 *	CCCHAN is NOT locked
 */
void
cccinit(Dk *dk)
{
	Chan *up;

	up = &dk->dkchan[CCCHAN];
	dklock(dk, up);
	up->dkp.dkstate = OPENED;
	urpinit(up);
	xpmesg(up, T_ALIVE, D_RESTART, 0, 0);
	dkunlock(dk, up);
}

/*
 *  send a message to CMC
 *	the message is queued on up and placed
 *	on CCCHAN when this chan is unlocked.
 *	up is locked
 */
void
xpmesg(Chan *up, int type, int srv, int p0, int p1)
{
	Dialin *dp;
	Msgbuf *mb;

	mb = mballoc(sizeof(Dialin), 0, Mbdkxpmesg);
	dp = (Dialin*)mb->data;

	dp->type = type;
	dp->srv = srv;
	dp->param0l = p0;
	dp->param0h = p0>>8;
	dp->param1l = p1;
	dp->param1h = p1>>8;
	dp->param2l = 0;
	dp->param2h = 0;
	dp->param3l = 0;
	dp->param3h = 0;
	dp->param4l = 0;
	dp->param4h = 0;

	mb->next = up->dkp.ccmsg;
	up->dkp.ccmsg = mb;
}

static
char	*msgs[] =
{
	"DK controller system error",
	"destination busy",
	"remote node not answering",
	"destination not answering",
	"unassigned destination",
	"DK system overload",
	"server already exists",
	"call rejected by destination",
};


/*
 * parse message from CMC
 *	nothing locked
 */
void
xplisten(Dk *dk, char *cp, int n)
{
	Dialin *dialp;
	Chan *up;
	char *error;
	int i, j, p1;

	if(n < sizeof(Dialin)) {
		print("xplisten: n=%d\n", n);
		return;
	}
	error = 0;
	dialp = (Dialin*)cp;
	i = (dialp->param0h << 8) + dialp->param0l;
	p1 = (dialp->param1h << 8) + dialp->param1l;

	up = 0;
	if(i > CCCHAN && i < NDK) {
		up = &dk->dkchan[i];
		dklock(dk, up);
	}

	DPRINT("xplisten type=%d, srv=%d, chan=%d, p1=%d\n",
		dialp->type, dialp->srv, i, p1);

	switch(dialp->type) {
	case T_REPLY:	/* a solicited reply */
		if(!up || up->dkp.dkstate != DIALING) {
			error = "T_REPLY for non-existant or idle channel";
			goto out;
		}
		switch (dialp->srv) {
		case D_OPEN:
			up->dkp.dkstate = OPENED;
			urpxinit(up, p1);
			break;

		default:
			if(p1 > 7)
				p1 = 0;
			print("channel %d call failed, %s\n", up->dkp.cno, msgs[p1]);
			xpinit(up);
			xpmesg(up, T_CHG, D_CLOSE, i, 0);
			break;
		}
		break;

	case T_CHG:	/* something has changed */
		if(!up && dialp->srv != D_CLOSEALL) {
			error = "T_CHG for non-existant or idle channel";
			goto out;
		}
		switch(dialp->srv) {
		case D_ISCLOSED:	/* acknowledging a local shutdown */
			CPRINT("D_ISCLOSED %d\n", i);
			xpinit(up);
			break;
		case D_CLOSE:		/* remote shutdown */
			CPRINT("T_CHG, D_CLOSE, %d\n", i);
			xpinit(up);
			xpmesg(up, T_CHG, D_CLOSE, i, 0);
			break;
		case D_CLOSEALL:	/* close every channel, except CCCHAN */
			print("T_CHG D_CLOSEALL: (type=%d, srv=%d), chan=%d, p1=%d\n",
				dialp->type, dialp->srv, i, p1);
			if(up)
				dkunlock(dk, up);
			for(j=SRCHAN; j<NDK; j++) {
				up = &dk->dkchan[j];
				dklock(dk, up);
				xpinit(up);
				dkunlock(dk, up);
			}
			up = 0;
			break;
		default:
			error="unrecognized T_CHG message";
			break;
		}
		break;

	case T_RESTART:
		DPRINT("T_RESTART: (type=%d), srv=%d, chan=%d, p1=%d\n",
			dialp->type, dialp->srv, i, p1);
		break;
	default:
		error = "unrecognized csc message";
	}
out:
	if(error)
		print("xplisten: error %s\n\ttype=%d, srv=%d, chan=%d, p1=%d\n",
			error, dialp->type, dialp->srv, i, p1);
	if(up)
		dkunlock(dk, up);
}

/*
 *  make a call or an announcement
 *	called with up locked
 */
void
xpcall(Chan *up, char *dialstr, int announce)
{
	Msgbuf *mb;

	/*
	 *  tell datakit we're calling
	 */
	print("dialing: %s\n", dialstr);
	xpmesg(up, T_SRV, announce? D_SERV: D_DIAL,
		up->dkp.cno, W_WINDOW(WSIZE,WSIZE,2));

	/*
	 *  call out on channel
	 */
	urpinit(up);
	up->dkp.dkstate = DIALING;
	mb = mballoc(strlen(dialstr), up, Mbdkxpcall);
	memmove(mb->data, dialstr, mb->count);
	dkqmesg(mb, up);
	up->dkp.calltimeout = DKTIME(120);
}

/*
 *  hang up a call
 *	called with up locked
 */
void
xphup(Chan *up)
{
	up->dkp.dkstate = LCLOSE;
	xpmesg(up, T_CHG, D_CLOSE, up->dkp.cno, 0);
	up->dkp.timeout = DKTIME(20);
}

/*
 *  listen to service (connection) requests
 *	nothing locked
 */
void
srlisten(Dk *dk, char *cp, int n)
{
	Chan *up;
	Msgbuf *mb;
	char *p, *error, *addr, *user, source[100];
	int i, c, clock, traffic, reason, wins;

	if(DEBUG) {
		i = n;
		if(i >= sizeof(source)-1)
			i = sizeof(source)-1;
		memmove(source, cp, i);
		source[i] = 0;
		p = source;
		while(p = strchr(p, '\n'))
			*p = '\\';
		print("srlisten: %s\n", source);
	}

	p = cp;
	cp += n;	/* end pointer */
	error = "bad format";
	clock = 0;	/* set */
	reason = 7;

	c = number(p, 0, 10);
	if(!(p = strchr(p, '.')) || p >= cp)
		goto out;

	p++;
	clock = number(p, 0, 10);
	if(!(p = strchr(p, '.')) || p >= cp)
		goto out;

	p++;
	traffic = number(p, 0, 10);
	if(!(p = strchr(p, '\n')) || p >= cp)
		goto out;

	p++;
	addr = p;
	if(!(p = strchr(p, '\n')) || p >= cp)
		goto out;

	*p++ = 0;
	user = p;
	if(!(p = strchr(p, '\n')) || p >= cp)
		goto out;

	*p++ = 0;
	i = cp - p;
	if(i > sizeof(source)-1)
		i = sizeof(source)-1;
	memmove(source, p, i);
	source[i] = 0;
	if(c < 0 || c >= NDK) {
		print("srlisten ctobig: addr=%s c=%d user=%s src=%s\n",
			addr, c, user, source);
		error = "SR for non-existant channel";
		goto out;
	}

	up = &dk->dkchan[c];
	DPRINT("C%d: %s/%d, %s, %s\n", up->dkp.cno, addr, c, user, source);
	if(c <= CKCHAN){
		error = "SR for dedicated channel";
		goto out;
	}

	dklock(dk, up);
	if(up->dkp.dkstate != CLOSED) {
		error = "SR for active channel";
		dkunlock(dk, up);
		goto out;
	}
	reason = 0;
	error = 0;
	strncpy(up->whoname, user, sizeof(up->whoname));
	strncpy(up->whochan, source, sizeof(up->whochan));
	wins = 0;
	if(W_VALID(traffic))
		wins = W_VALUE(W_ORIG(traffic));
	urprinit(up);
	urpxinit(up, wins);
	up->dkp.dkstate = OPENED;

	/*
	 *  The evil OK
	 */
	p = "OK";
	mb = mballoc(strlen(p), up, Mbdksrlisten1);
	memmove(mb->data, p, mb->count);
	dkqmesg(mb, up);
	dkunlock(dk, up);

out:
	if(error)
		print("call rejected, %s\n", error);
	sprint(source, "%d.%d.%d", c, clock, reason);

	up = &dk->dkchan[SRCHAN];
	dklock(dk, up);
	mb = mballoc(strlen(source), up, Mbdksrlisten2);
	memmove(mb->data, source, mb->count);
	dkqmesg(mb, up);
	dkunlock(dk, up);
}

dk3.c           713586928   0     0     664     3418      `
#include	"all.h"
#include	"dk.h"

/*
 * poll process
 */
void
calldk(Alarm *a, Dk *dk)
{
	cancel(a);
	wakeup(&dk->dkto);
}

void
dktimer(void)
{

	long t, toy;
	Chan *up;
	Dk *dk;

	dk = getarg();
	print("dk local %d\n", dk-dkctlr);

	cccinit(dk);
	toy = 0;
	memset(dk->timeout, 0, sizeof(dk->timeout));
	t = toytime();
	dk->timeout[DKATO] = t + DKATIME;
	dk->timeout[DKBTO] = t + DKBTIME;
	dk->timeout[TIMETO] = t + SECOND(20);

loop:
	alarm(300, calldk, dk);	/* 3 times/sec */
	sleep(&dk->dkto, no, 0);

	/*
	 * general timeouts
	 */
	t = toytime();
	if(t != toy) {
		if(t < toy)		/* decreasing */
			memset(dk->timeout, 0, sizeof(dk->timeout));
		toy = t;
		if(t >= dk->timeout[DKATO]) {
			dk_timer(dk);
			dk->timeout[DKATO] = t + DKATIME;
		}
		if(t >= dk->timeout[DKBTO]) {
			/*
			 *  if the service channel is closed, reannounce
			 */
			up = &dk->dkchan[SRCHAN];
			dklock(dk, up);
			if(up->dkp.dkstate == CLOSED)
				xpcall(up, service, 1);

			/*
			 *  send a keep alive on the signalling channel
			 */
			xpmesg(up, T_ALIVE, D_STILL, 0, 0);
			dkunlock(dk, up);

			dk->timeout[DKBTO] = t + DKBTIME;
		}
		if(t >= dk->timeout[TIMETO]) {
			/*
			 *  if clock is closed, redial
			 */
			up = &dk->dkchan[CKCHAN];
			dklock(dk, up);
			if(up->dkp.dkstate == CLOSED) {
				if(0)
					xpcall(up, "nj/mhe/mhpbs.clock\nrfs\n", 0);
				else
					xpcall(up, "nj/astro/clock\nrfs\n", 0);
			}
			dkunlock(dk, up);

			dk->timeout[TIMETO] = t + TIMEQTIME;
			if(dk->anytime)
				dk->timeout[TIMETO] = t + TIMENTIME;
		}
	}
	goto loop;
}

void
dk_timer(Dk *dk)
{
	int i;
	Chan *up;

	/*
	 * called every 2 second
	 * thus the DKTIME macro is n/2
	 */
	for(i=0; i<NDK; i++) {
		up = &dk->dkchan[i];
		dklock(dk, up);
		/*
		 *  dialup timeout
		 */
		if(up->dkp.dkstate == DIALING){
			up->dkp.calltimeout--;
			if(up->dkp.calltimeout <= 0)
				xphup(up);
		}
		/*
		 *  all other timeouts
		 */
		up->dkp.timeout--;
		if(up->dkp.timeout > 0) {
			dkunlock(dk, up);
			continue;
		}
		switch(up->dkp.dkstate) {
		case LCLOSE:
			/*
			 *  No ISCLOSED received.
			 *  remind dk of channels that we've closed
			 */
			up->dkp.timeout = DKTIME(20);
			xpmesg(up, T_CHG, D_CLOSE, up->dkp.cno, 0);
			break;
		case DIALING:
		case OPENED:
			/*
			 *  NO AINIT received
			 *  resend INIT1
			 */
			if(up->dkp.urpstate == INITING) {
				CPRINT("dk %d: xINIT1 dk_timer\n", up->dkp.cno);
				up->dkp.timeout = DKTIME(20);
				dkreply(up, CINIT1, 0);
				break;
			}
			/*
			 *  No acknowledges
			 *  send ENQ to get state of receiver
			 */
			if(up->dkp.nout != 0) {
				CPRINT("dk %d: xENQ, window = (%d,%d]\n",
					up->dkp.cno,
					(up->dkp.seq+up->dkp.mout-up->dkp.nout)&7,
					(up->dkp.seq+up->dkp.mout)&7);
				up->dkp.timeout = DKTIME(4);
				dkreply(up, CENQ, 0);
				break;
			}
			break;
		}
		dkunlock(dk, up);
	}
}

/*
 * local (non-filesystem) messages 
 */
void
dklocal(void)
{
	Chan *up;
	Msgbuf *mb;
	long t;
	Dk *dk;

	dk = getarg();
	print("dk local %d\n", dk-dkctlr);

loop:
	mb = recv(dk->local, 0);
	if(mb == 0) {
		print("dklocal: null\n");
		goto loop;
	}
	up = mb->chan;

	switch(up->dkp.cno) {
	case CCCHAN:
		xplisten(dk, mb->data, mb->count);
		break;

	case SRCHAN:
		srlisten(dk, mb->data, mb->count);
		break;

	case CKCHAN:
		t = readclock(mb->data, mb->count);
		if(t) {
			settime(t);
			setrtc(t);
			prdate();
			dk->anytime = 1;
		}
		break;
	}

	mbfree(mb);
	goto loop;

}
eagle.c         709571760   0     0     664     12620     `
#include "all.h"
#include "io.h"
#include "mem.h"

typedef	struct	Mcsb	Mcsb;
typedef	struct	Mce	Mce;
typedef	struct	Cqe	Cqe;
typedef	struct	Iopb	Iopb;
typedef	struct	Cib	Cib;
typedef	struct	Crb	Crb;
typedef	struct	Csb	Csb;
typedef	struct	Macsi	Macsi;
typedef	struct	Eagle	Eagle;

enum
{
	MaxEagle	= 1,			/* Max. configurable controllers */
	Nriopb		= 20,			/* Number of Receive IOPB's */
	Ntiopb		= 16,			/* Number of Transmit IOPB's */
	Ncqe		= Nriopb+Ntiopb,	/* Number of Command Queue Entries */
	PktBufAlign	= BY2PG,		/* Alignment of I/O packet buffers */
};

#define NEXT(x, l)	(((x)+1)%(l))
#define OFFSETOF(t, m)	((unsigned)&(((t*)0)->m))
#define	HOWMANY(x, y)	(((x)+((y)-1))/(y))
#define ROUNDUP(x, y)	(HOWMANY((x), (y))*(y))

/*
 * Master Control/Status Block
 */
struct	Mcsb
{
	ushort	msr;			/* Status Register */
	ushort	pad0x0002;
	ushort	mcr;			/* Control Register */
	ushort	pad0x0006;
	ushort	qhp[2];			/* Queue Head Pointer */
	ushort	pad0x000C[2];
};

#define CNA		0x0001		/* msr: Controller Not Available */
#define BOK		0x0002		/* msr: Board OK */
#define STARTQMODE	0x0001		/* mcr: Start Queue Mode */

/*
 * Master Command Entry
 */
struct	Mce
{
	ushort	mcecr;			/* Control Register */
	ushort	iopba;			/* IOPB Address */
	ulong	tag;			/* Command Tag */
	ushort	pad0x0018[2];
};

/*
 * Command Queue Entry
 */
struct	Cqe
{
	ushort	qecr;			/* Control Register */
	ushort	iopba;			/* IOPB Address */
	ulong	tag;			/* Command Tag */
	ushort	wqn;			/* Work Queue Number */
	ushort	pad0x0026;
};

/*
 * IOPB
 */
struct	Iopb
{
	ushort	command;		/* Command Code */
	ushort	options;		/* Command Options */
	ushort	status;			/* Return Status */
	ushort	irqv;			/* Command Complete IRQ and Vector */
	ushort	eirqv;			/* Error IRQ and Vector */
	ushort	vmeoptions;		/* VMEbus Transfer Options */
	union
	{
		struct			/* Initialise Controller */
		{
			ulong	ciboff;	/* Cib Offset */
		};
		struct			/* Receive/Transmit */
		{
			ulong	addr;	/* Transfer Address */
			ulong	mtu;	/* Transfer Size */
			ushort	padrxtx0;
			ushort	ptlf;	/* Packet Type/Length Field */
			uchar	sna[6];	/* Source Node Address */
			ushort	padrxtx1;
			ushort	law1;	/* Lance Descriptor Word 1 */
			ushort	law3;	/* Lance Descriptor Word 3 */
		};
		uchar	padunion[24];
	};
};

/*
 * Controller Initialisation Block
 */
struct	Cib
{
	uchar	pad0x0000;
	uchar	ncqe;			/* Number of Command Queue Entries */
	ushort	options;		/* Special Network Options */
	ushort	pad0x0004;
	uchar	enaddr[6];		/* Physical Node Address */
	uchar	laf[8];			/* Logical Address Filter */
	uchar	pad0x0014[10];
	ushort	irqv;			/* Command Complete IRQ and Vector */
	ushort	eirqv;			/* Error IRQ and Vector */
	ushort	dmabc;			/* DMA Burst Count */
	uchar	pad0x022[8];
};

/*
 * Command Response Block + RIOPB
 */
struct	Crb
{
	ushort	crsw;			/* Command Response Status Word */
	ushort	pad0x0732;
	ulong	tag;			/* Command Tag */
	ushort	wqn;			/* Work Queue Number */
	ushort	pad0x073A[3];
	Iopb;				/* Returned IOPB */
};

/*
 * Configuration Status Block
 */
struct	Csb
{
	uchar	pad0x0764[48];		/* Miscellaneous Junk */
	uchar	enaddr[6];		/* Physical Node Address */
	uchar	pad0x079A[18];		/* More Junk */
};

/*
 * Multiple Active Command Software Interface.
 * This is 2048 bytes of host/controller communications
 * area.
 */
struct	Macsi
{
	Mcsb;				/* Master Control/Status Block */
	Mce	mce;			/* Master Command Entry */
	Cqe	cqe[Ncqe];		/* Command Queue */
	Iopb	riopb[Nriopb];		/* Receive IOPB's */
	Iopb	tiopb[Ntiopb];		/* Transmit IOPB's */
	Cib	cib;			/* Controller Initialisation Block */
	uchar	hus[1812		/* Host Usable Space */
		- (Ncqe)*sizeof(Cqe)
		- (Nriopb)*sizeof(Iopb)
		- (Ntiopb)*sizeof(Iopb)
		- sizeof(Cib)];
	Crb	crb;			/* Command Response Block */
	Csb	csb;			/* Configuration Status Block */
	uchar	pad0x07AC[84];		/* Controller Statistics Block + pad */
};

/*
 * Software Controller
 */
struct	Eagle
{
	Lock	cqel;
	uchar	nextcqe;
	uchar	re;			/* first rcv IOPB belonging to Eagle */
	uchar	rc;			/* first rcv IOPB belonging to CPU */
	Rendez	rr;
	uchar	te;			/* first tx IOPB belonging to Eagle */
	uchar	tc;			/* first tx IOPB belonging to CPU */
	Rendez	tr;
	Vmedevice* vme;			/* hardware info */
	ulong	vmeaddr;		/* vme2sysmap of this Eagle */
	Enpkt*	rpkt[Nriopb];
	Enpkt*	tpkt[Ntiopb];
	Ifc	ifc;
};

static	Eagle	eagle[MaxEagle];

static
void
qrxiopb(Eagle *e)
{
	Macsi *m;
	Cqe *cqe;

	m = e->vme->address;
	invalcache(e->rpkt[e->rc], sizeof(Enpkt));
	lock(&e->cqel);
	cqe = &m->cqe[e->nextcqe];
	cqe->wqn = 0x02;
	cqe->iopba = OFFSETOF(Macsi, riopb[e->rc]);
	cqe->tag = e->rc;
	e->rc = NEXT(e->rc, Nriopb);
	e->nextcqe = NEXT(e->nextcqe, Ncqe);
	cqe->qecr = 0x01;
	unlock(&e->cqel);
}

static
int
anyrxiopb(Eagle *e)
{
	return e->rc != e->re;
}

static
void
eagleinput(void)
{
	Eagle *e;
	Macsi *m;
	Iopb *iopb;
	int l, type;
	Enpkt *p;
	Ifc *ifc;

	e = getarg();
	ifc = &e->ifc;
	m = e->vme->address;

	print("eagle input %d: %E %I\n", e->vme->ctlr, e->ifc.ea, e->ifc.ipa);	
	for(l = 0; l < Nriopb; l++)
		qrxiopb(e);

loop:
	while(anyrxiopb(e) == 0)
		sleep(&e->rr, anyrxiopb, e);
	iopb = &m->riopb[e->rc];
	if(iopb->status == 0){
		l = iopb->ptlf - 4;
		vmeflush(e->vme->bus, iopb->addr, l);
		p = e->rpkt[e->rc];
		invalcache(p, l);
		type = nhgets(p->type);
		switch(type){
		case Arptype:
			arpreceive(p, l, ifc);
			break;
		case Iptype:
			ipreceive(p, l, ifc);
			break;
		}
		ifc->rxpkt++;
		ifc->work.count++;
		ifc->rate.count += l;
	}
	else
		ifc->rcverr++;
out:
	qrxiopb(e);
	goto loop;
}

static
void
qtxiopb(Eagle *e, int mtu)
{
	Macsi *m;
	Cqe *cqe;

	m = e->vme->address;
	m->tiopb[e->te].mtu = mtu;
	wbackcache(e->tpkt[e->te], mtu);
	invalcache(e->tpkt[e->te], mtu);
	lock(&e->cqel);
	cqe = &m->cqe[e->nextcqe];
	cqe->wqn = 0x03;
	cqe->iopba = OFFSETOF(Macsi, tiopb[e->te]);
	cqe->tag = e->te;
	e->te = NEXT(e->te, Ntiopb);
	e->nextcqe = NEXT(e->nextcqe, Ncqe);
	cqe->qecr = 0x01;
	unlock(&e->cqel);
}

static
int
anytxiopb(Eagle *e)
{
	return NEXT(e->te, Ntiopb) != e->tc;
}

static
void
eagleoutput(void)
{
	Eagle *e;
	Enpkt *p;
	Msgbuf *mb;
	int n;

	e = getarg();
	print("eagle output %d\n", e->vme->ctlr);

loop:
	mb = recv(e->ifc.reply, 0);
	if(mb == 0)
		goto loop;
	memmove(((Enpkt*)(mb->data))->s, e->ifc.ea, Easize);
	n = mb->count;
	if(n > ETHERMAXTU){
		print("eagle: pkt too big\n");
		mbfree(mb);
		goto loop;
	}
	while(anytxiopb(e) == 0)
		sleep(&e->tr, anytxiopb, e);
	p = e->tpkt[e->te];
	memmove(p, mb->data, n);
	if(n < ETHERMINTU){
		memset((char*)p+n, 0, ETHERMINTU-n);
		n = ETHERMINTU;
	}
	qtxiopb(e, n);
	e->ifc.work.count++;
	e->ifc.rate.count += n;
	e->ifc.txpkt++;
	mbfree(mb);
	goto loop;
}

int
eagleinit(Vmedevice *vp)
{
	Eagle *e;
	Macsi *m;
	ushort crsw;
	int l, x;
	uchar *p;

	print("eagle init %d\n", vp->ctlr);
	if(vp->ctlr >= MaxEagle)
		return -1;
	e = &eagle[vp->ctlr];
	if(e->vme)
		return -1;
	m = vp->address;

	/*
	 * does the board exist?
	if(probe(&m->mcr, sizeof(m->mcr)))
		return -1;
	 */

	/*
	 * Set the control register to a known state and
	 * check that the status says the board is available
	 * and passed power-on diagnostics.
	 * If it's not OK, then we could try resetting it
	 * here.
	 */
	m->mcr = 0;
	if((m->msr & (BOK|CNA)) != BOK)
		return -1;
	/*
	 * Set the Controller Initialisation Block.
	 */
	memset(&m->cib, 0, sizeof(Cib));
	m->cib.ncqe = Ncqe;
	m->cib.options = 0x0001;			/* IEEE 802.3 */
	memmove(m->cib.enaddr, m->csb.enaddr, 6);
	m->cib.irqv = (vp->irq<<8)|vp->vector;
	m->cib.eirqv = (vp->irq<<8)|vp->vector;

	/*
	 * Set the IOPB for the MCE. We can use
	 * one of the command queue IOPB's for the
	 * moment, until we turn on queueing.
	 */
	memset(&m->riopb[0], 0, sizeof(Iopb));
	m->riopb[0].command = 0x41;			/* Initialise Controller */
	m->riopb[0].ciboff = OFFSETOF(Macsi, cib);

	/*
	 * Set up the MCE, and start the init.
	 */
	memset(&m->mce, 0, sizeof(Mce));
	m->mce.iopba = OFFSETOF(Macsi, riopb[0]);
	m->mce.mcecr = 0x01;				/* GO */

	/*
	 * Wait for Command Response Block to
	 * become valid.
	 * Free the controller, check for error
	 * or exception.
	 */
	while(((crsw = m->crb.crsw) & 0x01) == 0)	/* Crb valid */
		;
	m->crb.crsw &= ~0x01;				/* free controller */
	if(crsw & 0x0C)					/* error or exception */
		return -1;

	/*
	 * Do we need to initialise the work queues?
	 * The manual says so, in many places, EXCEPT
	 * in the description of the 'INITIALIZE WORK
	 * QUEUE' command, where it says they are automatically
	 * initialised by the 'INITIALIZE CONTROLLER'
	 * command.
	 */

	/*
	 * Allocate space for I/O packets. These need to be
	 * suitably aligned on cache boundaries. PktBufAlign
	 * should be at least LINESIZE, but aligning on page
	 * boundaries seems to give better performance.
	 * Map the space to the VME bus.
	 */
	l = ROUNDUP(sizeof(Enpkt), PktBufAlign);
	p = ialloc(l*(Nriopb+Ntiopb), PktBufAlign);
	e->vmeaddr = vme2sysmap(vp->bus, (ulong)p, l*(Nriopb+Ntiopb));

	/*
	 * Initialise the unchanging parts of the IOPB's,
	 * and the I/O packet pointers.
	 */
	memset(m->cqe, 0, sizeof(m->cqe));
	memset(m->riopb, 0, sizeof(m->riopb));
	for(x = 0; x < Nriopb; x++){
		m->riopb[x].command = 0x60;		/* Receive */
		m->riopb[x].options = 0x05;		/* DMA|IENABLE */
		m->riopb[x].irqv = (vp->irq<<8)|vp->vector;
		m->riopb[x].eirqv = (vp->irq<<8)|vp->vector;
		m->riopb[x].vmeoptions = 0x160B;	/* Block mode, 32b */
		m->riopb[x].addr = e->vmeaddr + l*x;
		m->riopb[x].mtu = sizeof(Enpkt);
		e->rpkt[x] = (Enpkt*)p;
		p += l;
	}
	memset(m->tiopb, 0, sizeof(m->tiopb));
	for(x = 0; x < Ntiopb; x++){
		m->tiopb[x].command = 0x50;		/* Transmit */
		m->tiopb[x].options = 0x05;		/* DMA|IENABLE */
		m->tiopb[x].irqv = (vp->irq<<8)|vp->vector;
		m->tiopb[x].eirqv = (vp->irq<<8)|vp->vector;
		m->tiopb[x].vmeoptions = 0x060B;	/* Block mode, 32b */
		m->tiopb[x].addr = e->vmeaddr + l*(Nriopb+x);
		e->tpkt[x] = (Enpkt*)p;
		p += l;
	}

	/*
	 * Hardware data structures all set up, start the
	 * controller scanning the command queue.
	 * This command will generate an interrupt, after which
	 * we shall post some receive buffers.
	 */
	m->mcr = STARTQMODE;

	/*
	 * Cross-link the Eagle and Vmedevice
	 * structures to show we're happy the 
	 * controller is usable.
	 */
	e->vme = vp;
	vp->private = e;

	memmove(e->ifc.ea, m->csb.enaddr, 6);

	return 0;
}

void
eagleintr(Vmedevice *vme)
{
	Eagle *e;
	Macsi *m;
	Iopb *iopb;

	if(vme->ctlr >= MaxEagle || vme->private == 0)
		panic("eagle%d: spurious intr\n", vme->ctlr);
	e = vme->private;
	m = vme->address;
	switch(m->crb.wqn){

	case 0x02:					/* Receive WQ */
		/*
		 * Save any useful info from the RIOPB in
		 * the real IOPB.
		 * The manual doesn't say where the received
		 * packet length is stored, it's in the mtu
		 * field. We save it in the ptlf field of the
		 * real IOPB for the input process to find.
		 */
		iopb = &m->riopb[m->crb.tag];
		if(iopb->status = m->crb.status){
			iopb->law1 = m->crb.law1;
			iopb->law3 = m->crb.law3;
		}
		iopb->ptlf = m->crb.mtu;
		e->re = NEXT(e->re, Nriopb);
		wakeup(&e->rr);
		break;

	case 0x03:					/* Transmit WQ */
		/*
		 * Nothing much to do,
		 * just bump the queue.
		 */
		e->tc = NEXT(e->tc, Ntiopb);
		wakeup(&e->tr);
		break;

	case 0x00:					/* Miscellaneous WQ */
		if(m->crb.crsw & 0x20)			/* Queue Mode Started */
			break;
		/*FALLTHROUGH*/

	default:
		print("eagle%d: spurious wq: tag #%lux\n", vme->ctlr, m->crb.tag);
		break;
	}
	if((m->crb.crsw & 0x0C) || m->crb.status){
		print("eagle%d: status: wq %d, crsw #%ux, status #%ux\n",
			vme->ctlr, m->crb.wqn,  m->crb.crsw, m->crb.status);
		print("eagle%d: lance: law1 #%ux, law3 #%ux\n",
			vme->ctlr, m->crb.law1, m->crb.law3);
	}
	m->crb.crsw &= ~0x01;				/* free controller */
}

static
void
stats(Eagle *e)
{
	Ifc *ifc;

	print("eagle stats %d\n", e-eagle);
	ifc = &e->ifc;
	print("	work = %F pkts\n", (Filta){&ifc->work, 1});
	print("	rate = %F tBps\n", (Filta){&ifc->rate, 1000});
	print("	err  =    %3ld rc %3ld sum\n", ifc->rcverr, ifc->sumerr);
}

static
void
cmd_state(int argc, char *argv[])
{
	Eagle *e;

	USED(argc);
	USED(argv);

	for(e = &eagle[0]; e < &eagle[MaxEagle]; e++){
		if(e->vme)
			stats(e);
	}
}

void
eaglestart(void)
{
	Eagle *e;
	Ifc *ifc;

	for(e = &eagle[0]; e < &eagle[MaxEagle]; e++){
		if(e->vme){
			ifc = &e->ifc;
			lock(ifc);
			if(ifc->reply == 0){
				dofilter(&ifc->work);
				dofilter(&ifc->rate);
				ifc->reply = newqueue(Nqueue);
			}
			getipa(ifc);
			unlock(ifc);
			userinit(eagleinput, e, "egi");
			userinit(eagleoutput, e, "ego");
			cmd_install("state", "-- eagle stats", cmd_state);

			ifc->next = enets;
			enets = ifc;
		}
	}
	arpstart();
}
errno.h         684451409   0     0     664     1285      `
enum{
	Enevermind,	/* never mind */
	Enofd,		/* no free file descriptors */
	Ebadfd,		/* fd out of range or not open */
	Ebadusefd,	/* inappropriate use of fd */
	Ebadarg,	/* bad arg in system call */
	Enonexist,	/* file does not exist */
	Efilename,	/* file name syntax */
	Ebadchar,	/* bad character in file name */
	Ebadsharp,	/* unknown device in # filename */
	Ebadexec,	/* a.out header invalid */
	Eioload,	/* i/o error in demand load */
	Eperm,		/* permission denied */
	Enotdir,	/* not a directory */
	Enochild,	/* no living children */
	Enoseg,		/* no free segments */
	Ebuf,		/* buffer wrong size */
	Ebadmount,	/* inconsistent mount */
	Enomount,	/* mount table full */
	Enomntdev,	/* no free mount devices */
	Eshutdown,	/* mounted device shut down */
	Einuse,		/* device or object already in use */
	Eio,		/* i/o error (ramfs) */
	Eisdir,		/* file is a directory */
	Ebaddirread,	/* directory read not quantized */
	Esegaddr,	/* illegal segment addresses or size */
	Enoenv,		/* no free environment resources */
	Eprocdied,	/* process exited */
	Enocreate,	/* mounted directory forbids creation */
	Enotunion,	/* attempt to union with non-mounted directory */
	Emount,		/* inconsistent mount */
	Enosrv,		/* no free server slots */
	Egreg,		/* it's all greg's fault */
};

fcall.c         713561224   0     0     664     5843      `
#include	"all.h"

#define	CHAR(x)		*p++ = f->x
#define	SHORT(x)	p[0] = f->x; p[1] = f->x>>8; p += 2
#define	VLONG(q)	p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4
#define	LONG(x)		VLONG(f->x)
#define	STRING(x,n)	memmove(p, f->x, n); p += n

int
convS2M(Fcall *f, char *ap)
{
	uchar *p;
	int t;

	p = (uchar*)ap;
	CHAR(type);
	t = f->type;
	SHORT(tag);
	switch(t)
	{
	default:
		print("bad type: %d\n", t);
		return 0;

	case Tnop:
	case Tsession:
		break;

	case Tflush:
		SHORT(oldtag);
		break;

	case Tattach:
		SHORT(fid);
		STRING(uname, sizeof(f->uname));
		STRING(aname, sizeof(f->aname));
		STRING(auth, sizeof(f->auth));
		break;

	case Tauth:
		SHORT(fid);
		STRING(uname, sizeof(f->uname));
		STRING(chal, AUTHSTR+NAMELEN);
		break;

	case Tclone:
		SHORT(fid);
		SHORT(newfid);
		break;

	case Twalk:
		SHORT(fid);
		STRING(name, sizeof(f->name));
		break;

	case Tclwalk:
		SHORT(fid);
		SHORT(newfid);
		STRING(name, sizeof(f->name));
		break;

	case Topen:
		SHORT(fid);
		CHAR(mode);
		break;

	case Tcreate:
		SHORT(fid);
		STRING(name, sizeof(f->name));
		LONG(perm);
		CHAR(mode);
		break;

	case Tread:
		SHORT(fid);
		LONG(offset); VLONG(0);
		SHORT(count);
		break;

	case Twrite:
		SHORT(fid);
		LONG(offset); VLONG(0);
		SHORT(count);
		p++;
		if((uchar*)p == (uchar*)f->data) {
			p += f->count;
			break;
		}
		STRING(data, f->count);
		break;

	case Tclunk:
	case Tremove:
	case Tstat:
		SHORT(fid);
		break;

	case Twstat:
		SHORT(fid);
		STRING(stat, sizeof(f->stat));
		break;
/*
 */
	case Rnop:
	case Rsession:
	case Rflush:
		break;

	case Rerror:
		STRING(ename, sizeof(f->ename));
		break;

	case Rclone:
	case Rclunk:
	case Rremove:
	case Rwstat:
		SHORT(fid);
		break;

	case Rwalk:
	case Rattach:
	case Ropen:
	case Rcreate:
	case Rclwalk:
		SHORT(fid);
		LONG(qid.path);
		LONG(qid.version);
		break;

	case Rauth:
		SHORT(fid);
		STRING(chal, 2*AUTHSTR+2*DESKEYLEN);
		break;

	case Rread:
		SHORT(fid);
		SHORT(count);
		p++;
		if((uchar*)p == (uchar*)f->data) {
			p += f->count;
			break;
		}
		STRING(data, f->count);
		break;

	case Rwrite:
		SHORT(fid);
		SHORT(count);
		break;

	case Rstat:
		SHORT(fid);
		STRING(stat, sizeof(f->stat));
		break;
	}
	return p - (uchar*)ap;
}

/*
 * buggery to give false qid for
 * the top 2 levels of the dump fs
 */
ulong
fakeqid(Dentry *f)
{
	ulong q;
	int c;

	q = f->qid.path;
	if(q == (QPROOT|QPDIR)) {
		c = f->name[0];
		if(c >= '0' && c <= '9') {
			q = 3|QPDIR;
			c = (c-'0')*10 + (f->name[1]-'0');
			if(c >= 1 && c <= 12)
				q = 4|QPDIR;
		}
	}
	return q;
}

int
convD2M(Dentry *f, char *ap)
{
	uchar *p;
	ulong q;

	p = (uchar*)ap;
	STRING(name, sizeof(f->name));

	uidtostr((char*)p, f->uid, 1);
	p += NAMELEN;

	uidtostr((char*)p, f->gid, 1);
	p += NAMELEN;

	q = fakeqid(f);
	VLONG(q);
	LONG(qid.version);
	{
		q = f->mode & 0x0fff;
		if(f->mode & DDIR)
			q |= PDIR;
		if(f->mode & DAPND)
			q |= PAPND;
		if(f->mode & DLOCK)
			q |= PLOCK;
		VLONG(q);
	}
	LONG(atime);
	LONG(mtime);
	LONG(size); VLONG(0);
	VLONG(0);
	return p - (uchar*)ap;
}

#undef	CHAR
#undef	SHORT
#undef	LONG
#undef	VLONG
#undef	STRING

#define	CHAR(x)		f->x = *p++
#define	SHORT(x)	f->x = (p[0] | (p[1]<<8)); p += 2
#define	VLONG(q)	q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4
#define	LONG(x)		VLONG(f->x)
#define	STRING(x,n)	memmove(f->x, p, n); p += n

int
convM2S(char *ap, Fcall *f, int n)
{
	uchar *p;
	int t;

	p = (uchar*)ap;
	CHAR(type);
	t = f->type;
	SHORT(tag);
	switch(t)
	{
	default:
		print("bad type: %d\n", f->type);
		return 0;

	case Tnop:
	case Tsession:
		break;

	case Tflush:
		SHORT(oldtag);
		break;

	case Tattach:
		SHORT(fid);
		STRING(uname, sizeof(f->uname));
		STRING(aname, sizeof(f->aname));
		STRING(auth, sizeof(f->auth));
		break;

	case Tauth:
		SHORT(fid);
		STRING(uname, sizeof(f->uname));
		STRING(chal, 8+NAMELEN);
		break;

	case Tclone:
		SHORT(fid);
		SHORT(newfid);
		break;

	case Twalk:
		SHORT(fid);
		STRING(name, sizeof(f->name));
		break;

	case Tclwalk:
		SHORT(fid);
		SHORT(newfid);
		STRING(name, sizeof(f->name));
		break;

	case Tremove:
		SHORT(fid);
		break;

	case Topen:
		SHORT(fid);
		CHAR(mode);
		break;

	case Tcreate:
		SHORT(fid);
		STRING(name, sizeof(f->name));
		LONG(perm);
		CHAR(mode);
		break;

	case Tread:
		SHORT(fid);
		LONG(offset); p += 4;
		SHORT(count);
		break;

	case Twrite:
		SHORT(fid);
		LONG(offset); p += 4;
		SHORT(count);
		p++;
		f->data = (char*)p; p += f->count;
		break;

	case Tclunk:
	case Tstat:
		SHORT(fid);
		break;

	case Twstat:
		SHORT(fid);
		STRING(stat, sizeof(f->stat));
		break;

/*
 */
	case Rnop:
	case Rsession:
		break;

	case Rerror:
		STRING(ename, sizeof(f->ename));
		break;

	case Rflush:
		break;

	case Rclone:
	case Rclunk:
	case Rremove:
	case Rwstat:
		SHORT(fid);
		break;

	case Rwalk:
	case Rclwalk:
	case Rattach:
	case Ropen:
	case Rcreate:
		SHORT(fid);
		LONG(qid.path);
		LONG(qid.version);
		break;

	case Rauth:
		SHORT(fid);
		STRING(chal, 8+8+7+7);
		break;

	case Rread:
		SHORT(fid);
		SHORT(count);
		p++;
		f->data = (char*)p; p += f->count;
		break;

	case Rwrite:
		SHORT(fid);
		SHORT(count);
		break;

	case Rstat:
		SHORT(fid);
		STRING(stat, sizeof(f->stat));
		break;
	}
	if((uchar*)ap+n == p)
		return n;
	return 0;
}

int
convM2D(char *ap, Dentry *f)
{
	uchar *p;
	char str[28];

	p = (uchar*)ap;
	STRING(name, sizeof(f->name));

	memmove(str, p, NAMELEN);
	p += NAMELEN;
	f->uid = strtouid(str, 1);

	memmove(str, p, NAMELEN);
	p += NAMELEN;
	f->gid = strtouid(str, 1);

	LONG(qid.path);
	LONG(qid.version);
	{
		LONG(atime);
		f->mode = (f->atime & 0x0fff) | DALLOC;
		if(f->atime & PDIR)
			f->mode |= DDIR;
		if(f->atime & PAPND)
			f->mode |= DAPND;
		if(f->atime & PLOCK)
			f->mode |= DLOCK;
	}
	LONG(atime);
	LONG(mtime);
	LONG(size); p += 4;
	p += 4;
	return p - (uchar*)ap;
}

fs.c            715727358   0     0     664     23167     `
#include	"all.h"

void
f_nop(Chan *cp, Fcall *in, Fcall *ou)
{

	USED(in);
	USED(ou);
	if(CHAT(cp))
		print("c_nop %d\n", cp->chan);
}

void
f_flush(Chan *cp, Fcall *in, Fcall *ou)
{

	USED(in);
	USED(ou);
	if(CHAT(cp))
		print("c_flush %d\n", cp->chan);
	runlock(&cp->reflock);
	wlock(&cp->reflock);
	wunlock(&cp->reflock);
	rlock(&cp->reflock);
}

void
f_session(Chan *cp, Fcall *in, Fcall *ou)
{

	USED(in);
	USED(ou);
	if(CHAT(cp))
		print("c_session %d\n", cp->chan);
	fileinit(cp);
}

void
f_attach(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p;
	Dentry *d;
	File *f;
	int u;
	Filsys *fs;
	long raddr;

	if(CHAT(cp)) {
		print("c_attach %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	uid = %s\n", in->uname);
		print("	arg = %s\n", in->aname);
	}

	ou->qid = QID(0,0);
	ou->fid = in->fid;
	if(!in->aname[0])	/* default */
		strncpy(in->aname, filsys[0].name, sizeof(in->aname));
	p = 0;
	f = filep(cp, in->fid, 1);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	u = strtouid(in->uname, 1);
	if(u < 0 && cp != cons.chan) {
		ou->err = Ebadu;
		goto out;
	}
	if(cp == cons.chan)
		u = -1;
	else
	if(authorise(f, in) == 0 || strcmp(in->uname, "adm") == 0) {
		ou->err = Eauth;
		goto out;
	}
	fs = fsstr(in->aname);
	if(fs == 0) {
		ou->err = Ebadspc;
		goto out;
	}
	raddr = getraddr(fs->dev);
	p = getbuf(fs->dev, raddr, Bread);
	d = getdir(p, 0);
	if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(iaccess(f, d, DREAD)) {
		ou->err = Eaccess;
		goto out;
	}
	if(u == 0 && fs->dev.type == Devro) {
		/*
		 * 'none' not allowed on dump
		 */
		ou->err = Eaccess;
		goto out;
	}
	f->uid = u;
	accessdir(p, d, FREAD);
	f->qid.path = fakeqid(d);
	f->qid.version = d->qid.version;
	f->fs = fs;
	f->addr = raddr;
	f->slot = 0;
	f->open = 0;
	freewp(f->wpath);
	f->wpath = 0;

	ou->qid = f->qid;

	strncpy(cp->whoname, in->uname, sizeof(cp->whoname));
	cp->whotime = time();

	if(cons.flags & attachflag)
		print("attach %s %T to \"%s\" C%d\n",
			cp->whoname, cp->whotime, fs->name, cp->chan);

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
}

void
f_clwalk(Chan *cp, Fcall *in, Fcall *ou)
{
	int er, fid;

	if(CHAT(cp))
		print("c_clwalk macro\n");

	f_clone(cp, in, ou);		/* sets tag, fid */
	if(ou->err)
		return;
	fid = in->fid;
	in->fid = in->newfid;
	f_walk(cp, in, ou);		/* sets tag, fid, qid */
	er = ou->err;
	if(er == Eentry) {
		/*
		 * if error is "no entry"
		 * return non error and fid
		 */
		ou->err = 0;
		f_clunk(cp, in, ou);	/* sets tag, fid */
		ou->err = 0;
		ou->fid = fid;
		if(CHAT(cp)) 
			print("	error: %s\n", errstr[er]);
		return;
	}
	if(er) {
		/*
		 * if any other error
		 * return an error
		 */
		ou->err = 0;
		f_clunk(cp, in, ou);	/* sets tag, fid */
		ou->err = er;
		return;
	}
	/*
	 * non error
	 * return newfid
	 */
}

void
f_clone(Chan *cp, Fcall *in, Fcall *ou)
{
	File *f1, *f2;
	Wpath *p;
	int fid, fid1;

	if(CHAT(cp)) {
		print("c_clone %d\n", cp->chan);
		print("	old fid = %d\n", in->fid);
		print("	new fid = %d\n", in->newfid);
	}

	fid = in->fid;
	fid1 = in->newfid;

	f1 = 0;
	f2 = 0;
	if(fid < fid1) {
		f1 = filep(cp, fid, 0);
		f2 = filep(cp, fid1, 1);
	} else
	if(fid1 < fid) {
		f2 = filep(cp, fid1, 1);
		f1 = filep(cp, fid, 0);
	}
	if(!f1 || !f2) {
		ou->err = Efid;
		goto out;
	}


	f2->fs = f1->fs;
	f2->addr = f1->addr;
	f2->open = f1->open & ~FREMOV;
	f2->uid = f1->uid;
	f2->slot = f1->slot;
	f2->qid = f1->qid;

	freewp(f2->wpath);
	lock(&wpathlock);
	f2->wpath = f1->wpath;
	for(p = f2->wpath; p; p = p->up)
		p->refs++;
	unlock(&wpathlock);

out:
	ou->fid = fid;
	if(f1)
		qunlock(f1);
	if(f2)
		qunlock(f2);
}

void
f_walk(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p, *p1;
	Dentry *d, *d1;
	File *f;
	Wpath *w;
	int slot;
	long addr;

	if(CHAT(cp)) {
		print("c_walk %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	name = %s\n", in->name);
	}

	ou->fid = in->fid;
	ou->qid = QID(0,0);
	p = 0;
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(!(d->mode & DDIR)) {
		ou->err = Edir1;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	if(cp != cons.chan && iaccess(f, d, DEXEC)) {
		ou->err = Eaccess;
		goto out;
	}
	accessdir(p, d, FREAD);
	if(!strcmp(in->name, "."))
		goto setdot;
	if(!strcmp(in->name, "..")) {
		if(f->wpath == 0)
			goto setdot;
		putbuf(p);
		p = 0;
		addr = f->wpath->addr;
		slot = f->wpath->slot;
		p1 = getbuf(f->fs->dev, addr, Bread);
		d1 = getdir(p1, slot);
		if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
			if(p1)
				putbuf(p1);
			ou->err = Ephase;
			goto out;
		}
		lock(&wpathlock);
		f->wpath->refs--;
		f->wpath = f->wpath->up;
		unlock(&wpathlock);
		goto found;
	}
	for(addr=0;; addr++) {
		p1 = dnodebuf(p, d, addr, 0);
		if(!p1 || checktag(p1, Tdir, d->qid.path) ) {
			if(p1)
				putbuf(p1);
			ou->err = Eentry;
			goto out;
		}
		for(slot=0; slot<DIRPERBUF; slot++) {
			d1 = getdir(p1, slot);
			if(!(d1->mode & DALLOC))
				continue;
			if(strncmp(in->name, d1->name, sizeof(in->name)))
				continue;
			/*
			 * update walk path
			 */
			w = newwp();
			if(!w) {
				ou->err = Ewalk;
				putbuf(p1);
				goto out;
			}
			w->addr = f->addr;
			w->slot = f->slot;
			w->up = f->wpath;
			f->wpath = w;
			slot += DIRPERBUF*addr;
			goto found;
		}
		putbuf(p1);
	}

found:
	f->addr = p1->addr;
	f->qid.path = fakeqid(d1);
	f->qid.version = d1->qid.version;
	putbuf(p1);
	f->slot = slot;

setdot:
	ou->qid = f->qid;
	f->open = 0;

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
}

void
f_open(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p;
	Dentry *d;
	File *f;
	Tlock *t;
	Qid qid;
	int ro, fmod;

	if(CHAT(cp)) {
		print("c_open %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	mode = %o\n", in->mode);
	}

	p = 0;
	qid = QID(0,0);
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}

	/*
	 * if remove on close, check access here
	 */
	ro = f->fs->dev.type == Devro;
	if(in->mode & MRCLOSE) {
		if(ro) {
			ou->err = Eronly;
			goto out;
		}
		/*
		 * check on parent directory of file to be deleted
		 */
		if(f->wpath == 0 || f->wpath->addr == f->addr) {
			ou->err = Ephase;
			goto out;
		}
		p = getbuf(f->fs->dev, f->wpath->addr, Bread);
		d = getdir(p, f->wpath->slot);
		if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
			ou->err = Ephase;
			goto out;
		}
		if(iaccess(f, d, DWRITE)) {
			ou->err = Eaccess;
			goto out;
		}
		putbuf(p);
	}
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	qid.path = fakeqid(d);
	qid.version = d->qid.version;
	switch(in->mode & 7) {

	case MREAD:
		if(iaccess(f, d, DREAD) && !writeallow)
			goto badaccess;
		fmod = FREAD;
		break;

	case MWRITE:
		if((d->mode & DDIR) ||
		   (iaccess(f, d, DWRITE) && !writeallow))
			goto badaccess;
		if(ro) {
			ou->err = Eronly;
			goto out;
		}
		fmod = FWRITE;
		break;

	case MBOTH:
		if((d->mode & DDIR) ||
		   (iaccess(f, d, DREAD) && !writeallow) ||
		   (iaccess(f, d, DWRITE) && !writeallow))
			goto badaccess;
		if(ro) {
			ou->err = Eronly;
			goto out;
		}
		fmod = FREAD+FWRITE;
		break;

	case MEXEC:
		if((d->mode & DDIR) ||
		   iaccess(f, d, DEXEC))
			goto badaccess;
		fmod = FREAD;
		break;

	default:
		ou->err = Emode;
		goto out;
	}
	if(in->mode & MTRUNC) {
		if((d->mode & DDIR) ||
		   (iaccess(f, d, DWRITE) && !writeallow))
			goto badaccess;
		if(ro) {
			ou->err = Eronly;
			goto out;
		}
	}
	t = 0;
	if(d->mode & DLOCK) {
		t = tlocked(p, d);
		if(t == 0) {
			ou->err = Elocked;
			goto out;
		}
		t->file = f;
	}
	if(in->mode & MRCLOSE)
		fmod |= FREMOV;
	f->open = fmod;
	if(in->mode & MTRUNC)
		if(!(d->mode & DAPND))
			dtrunc(p, d);
	f->tlock = t;
	f->lastra = 0;
	goto out;

badaccess:
	ou->err = Eaccess;
	f->open = 0;

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
	ou->qid = qid;
}

void
f_create(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p, *p1;
	Dentry *d, *d1;
	File *f;
	int slot, slot1, fmod;
	long addr, addr1;
	Qid qid;
	Tlock *t;
	Wpath *w;

	if(CHAT(cp)) {
		print("c_create %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	name = %s\n", in->name);
		print("	perm = %x+%o\n", (in->perm>>28)&0xf,
				in->perm&0777);
		print("	mode = %d\n", in->mode);
	}

	p = 0;
	qid = QID(0,0);
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	if(f->fs->dev.type == Devro) {
		ou->err = Eronly;
		goto out;
	}

	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	if(!(d->mode & DDIR)) {
		ou->err = Edir2;
		goto out;
	}
	if(cp != cons.chan && iaccess(f, d, DWRITE) && !writeallow) {
		ou->err = Eaccess;
		goto out;
	}
	accessdir(p, d, FREAD);
	if(!strncmp(in->name, ".", sizeof(in->name)) ||
	   !strncmp(in->name, "..", sizeof(in->name))) {
		ou->err = Edot;
		goto out;
	}
	if(checkname(in->name)) {
		ou->err = Ename;
		goto out;
	}
	addr1 = 0;
	slot1 = 0;	/* set */
	for(addr=0;; addr++) {
		p1 = dnodebuf(p, d, addr, 0);
		if(!p1) {
			if(addr1)
				break;
			p1 = dnodebuf(p, d, addr, Tdir);
		}
		if(p1 == 0) {
			ou->err = Efull;
			goto out;
		}
		if(checktag(p1, Tdir, d->qid.path)) {
			putbuf(p1);
			goto phase;
		}
		for(slot=0; slot<DIRPERBUF; slot++) {
			d1 = getdir(p1, slot);
			if(!(d1->mode & DALLOC)) {
				if(!addr1) {
					addr1 = p1->addr;
					slot1 = slot + addr*DIRPERBUF;
				}
				continue;
			}
			if(!strncmp(in->name, d1->name, sizeof(in->name))) {
				putbuf(p1);
				ou->err = Eexist;
				goto out;
			}
		}
		putbuf(p1);
	}
	switch(in->mode & 7) {
	case MEXEC:
	case MREAD:		/* seems only useful to make directories */
		fmod = FREAD;
		break;

	case MWRITE:
		fmod = FWRITE;
		break;

	case MBOTH:
		fmod = FREAD+FWRITE;
		break;

	default:
		ou->err = Emode;
		goto out;
	}
	if(in->perm & PDIR)
		if((in->mode & MTRUNC) || (in->perm & PAPND) || (fmod & FWRITE))
			goto badaccess;
	/*
	 * do it
	 */
	qid = newqid(f->fs->dev);
	p1 = getbuf(f->fs->dev, addr1, Bread|Bimm|Bmod);
	d1 = getdir(p1, slot1);
	if(!d1 || checktag(p1, Tdir, d->qid.path)) {
		if(p1)
			putbuf(p1);
		goto phase;
	}
	if(d1->mode & DALLOC) {
		putbuf(p1);
		goto phase;
	}

	strncpy(d1->name, in->name, sizeof(in->name));
	if(cp == cons.chan) {
		d1->uid = cons.uid;
		d1->gid = cons.gid;
	} else {
		d1->uid = f->uid;
		d1->gid = d->gid;
		in->perm &= d->mode | ~0666;
		if(in->perm & PDIR)
			in->perm &= d->mode | ~0777;
	}
	d1->mode = DALLOC | (in->perm & 0777);
	if(in->perm & PDIR) {
		d1->mode |= DDIR;
		qid.path |= QPDIR;
	}
	d1->qid = qid;
	if(in->perm & PAPND)
		d1->mode |= DAPND;
	t = 0;
	if(in->perm & PLOCK) {
		d1->mode |= DLOCK;
		t = tlocked(p1, d1);
	}
	accessdir(p1, d1, FWRITE);
	putbuf(p1);
	accessdir(p, d, FWRITE);

	/*
	 * do a walk to new directory entry
	 */
	w = newwp();
	if(!w) {
		ou->err = Ewalk;
		goto out;
	}
	w->addr = f->addr;
	w->slot = f->slot;
	w->up = f->wpath;
	f->wpath = w;
	f->qid = qid;
	f->tlock = t;
	f->lastra = 0;
	if(in->mode & MRCLOSE)
		fmod |= FREMOV;
	f->open = fmod;
	f->addr = addr1;
	f->slot = slot1;
	if(t)
		t->file = f;
	goto out;

badaccess:
	ou->err = Eaccess;
	goto out;

phase:
	ou->err = Ephase;

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
	ou->qid = qid;
}

void
f_read(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p, *p1;
	File *f;
	Dentry *d, *d1;
	Tlock *t;
	long addr, offset, tim;
	int nread, count, n, o, slot;

	if(CHAT(cp)) {
		print("c_read %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	offset = %ld\n", in->offset);
		print("	count = %d\n", in->count);
	}

	p = 0;
	count = in->count;
	offset = in->offset;
	nread = 0;
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	if(!(f->open & FREAD)) {
		ou->err = Eopen;
		goto out;
	}
	if(count < 0 || count > MAXDAT) {
		ou->err = Ecount;
		goto out;
	}
	if(offset < 0) {
		ou->err = Eoffset;
		goto out;
	}
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	if(t = f->tlock) {
		tim = toytime();
		if(t->time < tim || t->file != f) {
			ou->err = Ebroken;
			goto out;
		}
		/* renew the lock */
		t->time = tim + TLOCK;
	}
	accessdir(p, d, FREAD);
	if(d->mode & DDIR) {
		addr = 0;
		goto dread;
	}
	if(offset+count > d->size)
		count = d->size - offset;
	while(count > 0) {
		addr = offset / BUFSIZE;
		if(addr == f->lastra+1)
			dbufread(p, d, addr+1);
		f->lastra = addr;
		o = offset % BUFSIZE;
		n = BUFSIZE - o;
		if(n > count)
			n = count;
		p1 = dnodebuf(p, d, addr, 0);
		if(p1) {
			if(checktag(p1, Tfile, QPNONE)) {
				ou->err = Ephase;
				putbuf(p1);
				goto out;
			}
			memmove(ou->data+nread, p1->iobuf+o, n);
			putbuf(p1);
		} else
			memset(ou->data+nread, 0, n);
		count -= n;
		nread += n;
		offset += n;
	}
	goto out;

dread:
	p1 = dnodebuf(p, d, addr, 0);
	if(!p1)
		goto out;
	if(checktag(p1, Tdir, QPNONE)) {
		ou->err = Ephase;
		putbuf(p1);
		goto out;
	}
	n = DIRREC;
	for(slot=0; slot<DIRPERBUF; slot++) {
		d1 = getdir(p1, slot);
		if(!(d1->mode & DALLOC))
			continue;
		if(offset >= n) {
			offset -= n;
			continue;
		}
		if(count < n) {
			putbuf(p1);
			goto out;
		}
		if(convD2M(d1, ou->data+nread) != n)
			print("dirread convD2M\n");
		nread += n;
		count -= n;
	}
	putbuf(p1);
	addr++;
	goto dread;

out:
	count = in->count - nread;
	if(count > 0)
		memset(ou->data+nread, 0, count);
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
	ou->count = nread;
	if(CHAT(cp))
		print("	nread = %d\n", nread);
}

void
f_write(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p, *p1;
	Dentry *d;
	File *f;
	Tlock *t;
	long offset, addr, tim;
	int count, nwrite, o, n;

	if(CHAT(cp)) {
		print("c_write %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
		print("	offset = %ld\n", in->offset);
		print("	count = %d\n", in->count);
	}

	offset = in->offset;
	count = in->count;
	nwrite = 0;
	p = 0;
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	if(!(f->open & FWRITE)) {
		ou->err = Eopen;
		goto out;
	}
	if(f->fs->dev.type == Devro) {
		ou->err = Eronly;
		goto out;
	}
	if(count < 0 || count > MAXDAT) {
		ou->err = Ecount;
		goto out;
	}
	if(offset < 0) {
		ou->err = Eoffset;
		goto out;
	}
	p = getbuf(f->fs->dev, f->addr, Bread|Bmod);
	d = getdir(p, f->slot);
	if(!d || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	if(t = f->tlock) {
		tim = toytime();
		if(t->time < tim || t->file != f) {
			ou->err = Ebroken;
			goto out;
		}
		/* renew the lock */
		t->time = tim + TLOCK;
	}
	accessdir(p, d, FWRITE);
	if(d->mode & DAPND)
		offset = d->size;
	if(offset+count > d->size)
		d->size = offset+count;
	while(count > 0) {
		addr = offset / BUFSIZE;
		o = offset % BUFSIZE;
		n = BUFSIZE - o;
		if(n > count)
			n = count;
		p1 = dnodebuf(p, d, addr, Tfile);
		if(p1 == 0) {
			ou->err = Efull;
			goto out;
		}
		if(checktag(p1, Tfile, d->qid.path)) {
			putbuf(p1);
			ou->err = Ephase;
			goto out;
		}
		memmove(p1->iobuf+o, in->data+nwrite, n);
		p1->flags |= Bmod;
		putbuf(p1);
		count -= n;
		nwrite += n;
		offset += n;
	}
	if(CHAT(cp))
		print("	nwrite = %d\n", nwrite);

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
	ou->count = nwrite;
}

int
doremove(File *f, int iscon)
{
	Iobuf *p, *p1;
	Dentry *d, *d1;
	long addr;
	int slot, err;

	err = 0;
	p = 0;
	p1 = 0;
	if(f->fs->dev.type == Devro) {
		err = Eronly;
		goto out;
	}
	/*
	 * check on parent directory of file to be deleted
	 */
	if(f->wpath == 0 || f->wpath->addr == f->addr) {
		err = Ephase;
		goto out;
	}
	p1 = getbuf(f->fs->dev, f->wpath->addr, Bread);
	d1 = getdir(p1, f->wpath->slot);
	if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) {
		err = Ephase;
		goto out;
	}
	if(!iscon && iaccess(f, d1, DWRITE)) {
		err = Eaccess;
		goto out;
	}
	accessdir(p1, d1, FWRITE);
	putbuf(p1);
	p1 = 0;

	/*
	 * check on file to be deleted
	 */
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		err = Eqid;
		goto out;
	}

	/*
	 * if deleting a directory, make sure it is empty
	 */
	if((d->mode & DDIR))
	for(addr=0;; addr++) {
		p1 = dnodebuf(p, d, addr, 0);
		if(!p1)
			break;
		if(checktag(p1, Tdir, d->qid.path)) {
			err = Ephase;
			goto out;
		}
		for(slot=0; slot<DIRPERBUF; slot++) {
			d1 = getdir(p1, slot);
			if(!(d1->mode & DALLOC))
				continue;
			err = Eempty;
			goto out;
		}
		putbuf(p1);
	}

	/*
	 * do it
	 */
	dtrunc(p, d);
	memset(d, 0, sizeof(Dentry));
	settag(p, Tdir, QPNONE);

out:
	if(p1)
		putbuf(p1);
	if(p)
		putbuf(p);
	return err;
}

void
f_clunk(Chan *cp, Fcall *in, Fcall *ou)
{
	File *f;
	Tlock *t;
	long tim;

	if(CHAT(cp)) {
		print("c_clunk %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	if(t = f->tlock) {
		tim = toytime();
		if(t->time < tim || t->file != f)
			ou->err = Ebroken;
		t->time = 0;	/* free the lock */
		f->tlock = 0;
	}
	if(f->open & FREMOV)
		ou->err = doremove(f, 0);
	f->open = 0;
	freewp(f->wpath);
	freefp(f);

out:
	if(f)
		qunlock(f);
	ou->fid = in->fid;
}

void
f_remove(Chan *cp, Fcall *in, Fcall *ou)
{
	File *f;

	if(CHAT(cp)) {
		print("c_remove %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	ou->err = doremove(f, cp==cons.chan);

out:
	ou->fid = in->fid;
	if(f)
		qunlock(f);
}

void
f_stat(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p;
	Dentry *d;
	File *f;

	if(CHAT(cp)) {
		print("c_stat %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	p = 0;
	memset(ou->stat, 0, sizeof(ou->stat));
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}
	if(d->qid.path == QPROOT)	/* stat of root gives time */
		d->atime = time();
	if(convD2M(d, ou->stat) != DIRREC)
		print("stat convD2M\n");

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
}

void
f_wstat(Chan *cp, Fcall *in, Fcall *ou)
{
	Iobuf *p;
	Dentry *d, xd;
	File *f;

	if(CHAT(cp)) {
		print("c_wstat %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	p = 0;
	f = filep(cp, in->fid, 0);
	if(!f) {
		ou->err = Efid;
		goto out;
	}
	if(f->fs->dev.type == Devro) {
		ou->err = Eronly;
		goto out;
	}
	p = getbuf(f->fs->dev, f->addr, Bread);
	d = getdir(p, f->slot);
	if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) {
		ou->err = Ealloc;
		goto out;
	}
	if(fakeqid(d) != f->qid.path) {
		ou->err = Eqid;
		goto out;
	}

	convM2D(in->stat, &xd);
	if(CHAT(cp)) {
		print("	d.name = %s\n", xd.name);
		print("	d.uid  = %d\n", xd.uid);
		print("	d.gid  = %d\n", xd.gid);
		print("	d.mode = %.4x\n", xd.mode);
	}

	/*
	 * if chown,
	 * must be god
	 */
	while(xd.uid != d->uid) {
		if(wstatallow)			/* set to allow chown during boot */
			break;
		ou->err = Enotu;
		goto out;
	}

	/*
	 * if chgroup,
	 * must be either
	 *	a) owner and in new group
	 *	b) leader of both groups
	 */
	while(xd.gid != d->gid) {
		if(wstatallow || writeallow)		/* set to allow chgrp during boot */
			break;
		if(d->uid == f->uid && ingroup(f->uid, xd.gid))
			break;
		if(leadgroup(f->uid, xd.gid))
			if(leadgroup(f->uid, d->gid))
				break;
		ou->err = Enotg;
		goto out;
	}

	/*
	 * if rename,
	 * must have write permission in dir
	 */
	while(strncmp(d->name, xd.name, sizeof(d->name)) != 0) {
		if(checkname(xd.name)) {
			ou->err = Ename;
			goto out;
		}
		if(wstatallow || writeallow)			/* set to allow rename during boot */
			break;
/*
 * BUG -- owner of file instead
 */
		if(d->uid == f->uid)
			break;
		ou->err = Enotu;
		goto out;
	}

	/*
	 * if mode, either
	 *	a) owner
	 *	b) leader of either group
	 */
	while((d->mode^xd.mode) & (DAPND|DLOCK|0777)) {
		if(wstatallow)			/* set to allow chmod during boot */
			break;
		if(d->uid == f->uid)
			break;
		if(leadgroup(f->uid, xd.gid))
			break;
		if(leadgroup(f->uid, d->gid))
			break;
		ou->err = Enotu;
		goto out;
	}
	d->uid = xd.uid;
	d->gid = xd.gid;
	d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR));
/*
 * BUG -- look for name
 */
	strncpy(d->name, xd.name, sizeof(d->name));
	accessdir(p, d, FREAD); /*BUG*/

out:
	if(p)
		putbuf(p);
	if(f)
		qunlock(f);
	ou->fid = in->fid;
}

/* f_auth is called from ilauth, dkauth etc. */
void
f_auth(Chan *cp, Fcall *in, Fcall *ou)
{
	char buf[256], key[DESKEYLEN];
	int i, r, k1, k2;
	File *f;

	if(CHAT(cp)) {
		print("c_auth %d\n", cp->chan);
		print("	fid = %d\n", in->fid);
	}

	f = filep(cp, in->fid, 1);
	if(!f) {
		ou->err = Efid;
		return;
	}
	ou->fid = in->fid;

	/* File system is the authtication service */
	if(authchan == 0) {	
		if(nametokey(in->uname, key)) {
			ou->err = Eauth;
			goto out;
		}

		decrypt(key, in->chal, AUTHSTR+NAMELEN);
		if(strncmp(service, in->chal+AUTHSTR, NAMELEN) != 0 &&
		   strcmp("any", in->chal+AUTHSTR) != 0) {
			ou->err = Eauth;
			goto out;
		}

		if(in->chal[0] != FScchal) {
			ou->err = Eauth;
			goto out;
		}

		in->chal[0] = FSctick;
		memmove(ou->chal, in->chal, AUTHSTR);
		f->ticket[0] = FSstick;
		for(i = 1; i < AUTHSTR; i++)
			f->ticket[i] = nrand(256);

		k1 = AUTHSTR;
		k2 = 2*AUTHSTR+DESKEYLEN;
		for(i = 0; i < DESKEYLEN; i++) {
			r = nrand(256);
			ou->chal[k1++] = r;
			ou->chal[k2++] = r;
		}

		memmove(ou->chal+AUTHSTR+DESKEYLEN, f->ticket, AUTHSTR);
		encrypt(nvr.authkey, ou->chal+AUTHSTR+DESKEYLEN, AUTHSTR+DESKEYLEN);
		encrypt(key, ou->chal, 2*AUTHSTR+2*DESKEYLEN);
		goto out;
	}

	/* Use external authentication server */

	buf[0] = FSschal;
	for(i = 1; i < AUTHSTR; i++)
		buf[i] = nrand(256);

	memmove(f->ticket, buf, AUTHSTR);
	strncpy(&buf[i], in->uname, NAMELEN);
	i += NAMELEN;
	memmove(&buf[i], in->chal, AUTHSTR+NAMELEN);
	i += AUTHSTR+NAMELEN;

	encrypt(nvr.authkey, buf, i);
	strncpy(&buf[i], service, NAMELEN);
	i += NAMELEN;

	i = authsrv(authchan, buf, i);
	if(i < 0) {
		ou->err = Eauth;
		goto out;
	}	
	
	decrypt(nvr.authkey, buf, i);

	if(buf[0] == FSerr){
		buf[AUTHSTR+ERRREC] = '\0';
		print("auth failed: %d '%s'\n", i, buf+AUTHSTR);
		ou->err = Eauth;
		goto out;;
	}

	f->ticket[0] = FSok;
	if(memcmp(f->ticket, buf, AUTHSTR) || i != AUTHSTR+2*AUTHSTR+2*DESKEYLEN){
		print("auth reply: len %d ticket mismatch\n", i);
		ou->err = Eauth;
		goto out;
	}
	memmove(ou->chal, &buf[AUTHSTR], i-AUTHSTR);
	f->ticket[0] = FSstick;
out:
	qunlock(f);
}

fworm.c         688774624   0     0     664     1741      `
#include	"all.h"

#define	DEBUG		0

long
fwormsize(Device dev)
{
	Device wdev;
	long l;

	wdev = CDEV(dev);
	l = devsize(wdev);
	l -= l/(BUFSIZE*8) + 1;
	return l;
}

void
fwormream(Device dev)
{
	Iobuf *p;
	Device wdev;
	long a, b;

	print("fworm ream\n");
	devinit(dev);
	wdev = CDEV(dev);
	a = fwormsize(dev);
	b = devsize(wdev);
	print("	fwsize = %ld\n", a);
	print("	bwsize = %ld\n", b-a);
	for(; a < b; a++) {
		p = getbuf(wdev, a, Bmod|Bres);
		if(!p)
			panic("fworm: init");
		memset(p->iobuf, 0, RBUFSIZE);
		settag(p, Tvirgo, a);
		putbuf(p);
	}
}

void
fworminit(Device dev)
{

	print("fworm init\n");
	devinit(CDEV(dev));
}

int
fwormread(Device dev, long b, void *c)
{
	Iobuf *p;
	Device wdev;
	long l;

	if(DEBUG)
		print("fworm read  %ld\n", b);
	wdev = CDEV(dev);
	l = devsize(wdev);
	l -= l/(BUFSIZE*8) + 1;
	if(b >= l)
		panic("fworm: rbounds %ld\n", b);
	l += b/(BUFSIZE*8);
	p = getbuf(wdev, l, Bread|Bres);
	if(!p || checktag(p, Tvirgo, l))
		panic("fworm: checktag %ld\n", l);
	l = b % (BUFSIZE*8);
	if(!(p->iobuf[l/8] & (1<<(l%8)))) {
		putbuf(p);
		print("fworm: read %ld\n", b);
		return 1;
	}
	putbuf(p);
	return devread(wdev, b, c);
}

int
fwormwrite(Device dev, long b, void *c)
{
	Iobuf *p;
	Device wdev;
	long l;

	if(DEBUG)
		print("fworm write %ld\n", b);
	wdev = CDEV(dev);
	l = devsize(wdev);
	l -= l/(BUFSIZE*8) + 1;
	if(b >= l)
		panic("fworm: wbounds %ld\n", b);
	l += b/(BUFSIZE*8);
	wdev = CDEV(dev);
	p = getbuf(wdev, l, Bread|Bmod|Bres);
	if(!p || checktag(p, Tvirgo, l))
		panic("fworm: checktag %ld", l);
	l = b % (BUFSIZE*8);
	if((p->iobuf[l/8] & (1<<(l%8)))) {
		putbuf(p);
		print("fworm: write %ld\n", b);
		return 1;
	}
	p->iobuf[l/8] |= 1<<(l%8);
	putbuf(p);
	return devwrite(wdev, b, c);
}

hotrod.c        697348589   0     0     664     14300     `
#include	"all.h"
#include	"hotrod.h"
#include	"io.h"

#define FID3	3

Hot*	HOT;						/* GAK */
static Vmedevice *vme;					/* hack for multiple buses */

typedef	struct	Hcmd	Hcmd;
typedef	struct	Hhdr	Hhdr;
typedef	struct	Exec	Exec;
typedef	struct	Out	Out;

/*
 * from include/a.out.h
 */
struct	Exec
{
	long	magic;		/* magic number */
	long	text;	 	/* size of text segment */
	long	data;	 	/* size of initialized data */
	long	bss;	  	/* size of uninitialized data */
	long	syms;	 	/* size of symbol table */
	long	entry;	 	/* entry point */
	long	unused;		/* not used */
	long	flag;	 	/* relocation info stripped */
};
#define	Z_MAGIC		407

enum
{
	SUM	= 1,
	NBUF	= 100,
	NLBUF	= 50,
	NPARAM	= 6,
	NOUT	= 10,
	NHASH	= 290,
};

struct	Hhdr
{
	Hcmd*	hcmd;
	int	mbsize;
	Msgbuf*	mb;
	ulong	hcmap;		/* vme map for this Hcmd structure */
	ulong	mbmap;		/* vme map for this mb->data */
};

struct	Hcmd
{
	ulong	cmd;
	ulong	param[NPARAM];
};

struct Out
{
	Hhdr;
	Msgbuf*	mb;
	Hcmd*	hc;
	ulong*	ip;
};

static
struct
{
	char*	prbufp;
	char*	prbufpe;
	char*	prbufpc;
	int	hpactive;
	int	cmdwait;
	int	noise;
	Rendez	hpr;
	Rendez	hrir;			/* hotrod vme interrupt */
	Queue*	reply;
	QLock	issueq;
	ulong	issuep;
	ulong	replyp;
	int	bhash[NHASH];
	int	vhash;
	Hhdr	buf[NBUF];		/* outstanding buffers */
	Chan*	chan;
	Out	out[NOUT];
} h;

ulong	lswab(ulong);
void	hrodstart(void);
void	hotprint(void);
void	callhp(Alarm*, void*);
int	hotissue(Hhdr*, Hcmd*, int);
static	void	mem(Hot*, ulong*, ulong);

void
hrodintr(Vmedevice *vp)
{
	ulong junk;

	USED(vp);
	HOT->lcsr3 &= ~INT_VME;
	junk = HOT->lcsr3;
	USED(junk);
	h.cmdwait = 1;
	wakeup(&h.hrir);
}

int
hrodinit(Vmedevice *vp)
{
	int i, n, bv, bc, c;
	Msgbuf *m;
	Chan *cp;
	Hhdr *hc;

	print("hotinit\n");

	HOT = vp->address;
	/*
	 * does the board exist?
	if(probe(&HOT->rom[0], sizeof(HOT->rom[0]))) {
		HOT = 0;
		return -1;
	}
	 */

	vme = vp;
	memset(&h, 0, sizeof(h));
	h.reply = newqueue(10);
	cp = chaninit(Devhot, 1);
	h.chan = cp;
	cp->send = serveq;
	cp->reply = h.reply;

	for(i=0; i<NBUF; i++) {
		n = LARGEBUF;
		if(i >= NLBUF)
			n = SMALLBUF;
		m = mballoc(n, h.chan, Mbhotrod1);
		hc = &h.buf[i];
		hc->hcmd = ialloc(sizeof(Hcmd), LINESIZE);
		hc->mb = m;
		hc->mbsize = n;
		hc->mbmap = vme2sysmap(vme->bus, (ulong)m->data, n);
		hc->hcmap = vme2sysmap(vme->bus, (ulong)hc->hcmd, sizeof(Hcmd));
	}

	/*
	 * find best hash divisor
	 */
	bc = NHASH;
	bv = 0;
	for(h.vhash=10; h.vhash<=NHASH; h.vhash++) {
		memset(h.bhash, 0, sizeof(h.bhash));
		c = 0;
		for(i=0; i<NBUF; i++) {
			hc = &h.buf[i];
			n = hc->hcmap%h.vhash;
			if(h.bhash[n])
				c++;
			h.bhash[n] = 1;
		}
		if(c < bc) {
			bc = c;
			bv = h.vhash;
		}
	}
	print("best v = %d %d collisions\n", bv, bc);
	h.vhash = bv;
	memset(h.bhash, 0, sizeof(h.bhash));
	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		h.bhash[hc->hcmap%h.vhash] = i;
	}
	return 0;
}

ulong
addsum(void *d, int count)
{
	ulong *p, s;
	int i, n;

	n = (count+3)/4;
	p = d;
	s = 0;
	for(i=0; i<n; i++)
		s += *p++;
	return s;
}

void
hotrodou(void)
{
	Out *op, *uop;
	Msgbuf *mb;
	Hcmd *hc;
	int i, count;

	for(i=0; i<NOUT; i++) {
		op = &h.out[i];
		op->mb = 0;
		op->hc = ialloc(sizeof(Hcmd), LINESIZE);
		op->hcmap = vme2sysmap(vme->bus, (ulong)op->hc, sizeof(Hcmd));
	}

	print("hro: start\n");

loop:
	mb = recv(h.reply, 0);
	if(!mb) {
		print("zero message\n");
		goto loop;
	}

	/*
	 * look for done guys
	 * also find spare slot->uop
	 */
busy:
	op = h.out;
	uop = 0;
	for(i=0; i<NOUT; i++, op++) {
		if(op->mb == 0) {
			if(uop == 0)
				uop = op;
			continue;
		}
		if(*op->ip)
			continue;

		vmeflush(vme->bus, op->hcmap, sizeof(Hcmd));
		vme2sysfree(vme->bus, op->mbmap, op->mb->count);
		mbfree(op->mb);
		op->mb = 0;
		if(uop == 0)
			uop = op;
	}
	if(uop == 0) {
		print("ou: busy!\n");
		goto busy;
	}

	count = mb->count;
	uop->mb = mb;
	uop->mbmap = vme2sysmap(vme->bus, (ulong)mb->data, count);
	wbackcache(mb->data, count);
	invalcache(mb->data, count);

	hc = uop->hc;
	hc->cmd = Ureply;
	hc->param[0] = mb->param;
	hc->param[1] = uop->mbmap;
	hc->param[2] = count;
	hc->param[3] = 0;
	if(SUM)
		hc->param[3] = addsum(mb->data, count);
/*
print("hro: cmd=%.8lux u=%.8lux s=%.8lux\n", &cmd, cmd.param[0], cmd.param[3]);
/**/


	wbackcache(hc, sizeof(*hc));
	invalcache(hc, sizeof(*hc));

	qlock(&h.issueq);
	i = h.issuep;
	uop->ip = &HOT->reqstq[i];

	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	*uop->ip = uop->hcmap;

	goto loop;
}

/*
 * process pick up hotrod filled buffers
 */
int
hrcmd(void *v)
{

	USED(v);
	return h.cmdwait;
}

void
hotrodin(void)
{
	ulong u, s;
	int i, count;
	Chan *cp;
	Msgbuf *mb;
	Hhdr *hc;
	Hcmd *hcmd;

	print("hri: start\n");
	userinit(hotrodou, 0, "hro");
	cp = h.chan;

loop:
	sleep(&h.hrir, hrcmd, 0);
	h.cmdwait = 0;

l1:
	/*
	 * look at reply q from hotrod
	 */
	u = HOT->replyq[h.replyp];
	if(u == 0)
		goto loop;
	HOT->replyq[h.replyp] = 0;
	h.replyp++;
	if(h.replyp >= NRQ)
		h.replyp = 0;

	hc = &h.buf[h.bhash[u%h.vhash]];
	if(hc->hcmap == u)
		goto found;

	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		if(hc->hcmap == u)
			goto found;
	}
	print("hri: spurious %lux\n", u);
	goto l1;

found:
	vmeflush(vme->bus, hc->hcmap, sizeof(Hcmd));
	invalcache(hc, sizeof(Hcmd));

	hcmd = hc->hcmd;
	if(hcmd->cmd != Ubuf) {
		print("hri: cmd not Ubuf %d\n", hcmd->cmd);
		goto l1;
	}

	count = hc->mbsize;
	if(hcmd->param[3] > count) {
		print("hri: rcount too big %d %lux\n", hcmd->param[3], &hcmd->param[3]);
		hcmd->param[3] = count;
	}
	mb = hc->mb;
	vme2sysfree(vme->bus, hc->mbmap, count);
	invalcache(mb->data, count);

	mb->param = hcmd->param[0];	/* u */
	mb->count = hcmd->param[3];	/* actual count */
	if(hcmd->param[4]) {
		s = addsum(mb->data, mb->count);
		if(s != hcmd->param[4]) {
			HOT->error++;
			print("hri: checksum %lux sb %lux\n", s, hcmd->param[4]);
/**/
			s = (mb->count + 3) / 4;
			for(i=0; i<s; i++) {
				print("%.8lux\n", ((ulong*)mb->data)[i]);
				prflush();
			}
/**/
		}
	}
/*
print("hri: buffer u=%.8lux s=%.8lux\n", hcmd->param[0], hcmd->param[4]);
*/
	send(cp->send, mb);

	mb = mballoc(count, cp, Mbhotrod2);
	hc->mbmap = vme2sysmap(vme->bus, (ulong)mb->data, count);
	hc->mb = mb;

	hcmd->cmd = Ubuf;
	hcmd->param[0] = 0;			/* returned u */
	hcmd->param[1] = hc->mbmap;		/* message return */
	hcmd->param[2] = count;			/* size */
	hcmd->param[3] = 0;			/* returned count */
	hcmd->param[4] = 0;			/* returned cksum */

	wbackcache(mb->data, count);
	invalcache(mb->data, count);

	wbackcache(hcmd, sizeof(Hcmd));
	invalcache(hcmd, sizeof(Hcmd));

	qlock(&h.issueq);
	i = h.issuep;
	HOT->reqstq[i] = hc->hcmap;
	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	goto l1;
}

/*
 * process to copy out hotrod print buffer
 */
void
callhp(Alarm *a, void *arg)
{
	cancel(a);
	wakeup(&h.hpr);
}

void
hotprint(void)
{
	int c, f;
	union { ulong x; char y[4]; }u;

	print("hotrod copy process\n");

	f = 1;
	h.noise = 1;
	for(;;) {
		u.x = *(ulong*)((ulong)h.prbufpc & ~3);
		c = u.y[(ulong)h.prbufpc & 3];
		if(!h.noise || c == 0) {
			alarm(1000, callhp, 0);
			sleep(&h.hpr, no, 0);
			continue;
		}
		if(f) {
			print("hotrod: %c", c);
			f = 0;
		} else
			print("%c", c);
		if(c == '\n')
			f = 1;
		h.prbufpc++;
		if(h.prbufpc >= h.prbufpe)
			h.prbufpc = h.prbufp;
	}
}

void
hrodstart(void)
{
	Exec header;
	ulong buf[32];
	long n, o, m, prbufsize;
	int i;
	Hhdr *hc;
	Hcmd *hcmd;
	struct { Hhdr hdr; Hcmd cmd; } cmd;

	if (HOT == 0)
		return;
	for(i=0; i<100; i++)
		sched();
	cmd_exec("cfs");
	if(con_clone(FID1, FID3)) {
		print("cant clone /hobbit/hot\n");
		return;
	}
	if(con_path(FID3, "/hobbit/hot")) {
		print("cant path /hobbit/hot\n");
		return;
	}
	if(con_open(FID3, MREAD)) {
		print("cant open /hobbit/hot\n");
		return;
	}

	n = con_read(FID3, (char*)&header, 0, sizeof(header));
	if(n != sizeof(header)) {
		print("read header %d\n", n);
		return;
	}

	header.magic = lswab(header.magic);
	header.text = lswab(header.text);
	header.data = lswab(header.data);
	header.bss = lswab(header.bss);

	if(header.magic != Z_MAGIC) {
		print("header magic %lux\n", header.magic);
		return;
	}

	/*
	 * make sure its dead
	 */
	HOT->reqstq[0] = 0;	/* command */
	HOT->reqstp = 0;	/* hrod pointer */
	h.issuep = 0;		/* my pointer */

	print("	Ureboot\n");
	cmd.cmd.cmd = Ureboot;
	if(hotissue(&cmd.hdr, &cmd.cmd, 1))
		print("	reboot didnt issue\n");
	h.issuep = 0;		/* my pointer again */

	print("	load %ld+%ld", header.text, header.data);

	m = header.text+header.data;
	o = 0;
	for(;m>0;) {
		n = sizeof(buf);
		if(n > m)
			n = m;
		n = con_read(FID3, (char*)buf, o+sizeof(header), n);
		if(n <= 0)
			break;
		memmove((char*)HOT->ram + o, buf, n);
		m -= n;
		o += n;
	}

	print("+%ld\n", header.bss);

	m = sizeof(HOT->ram)-header.text-header.data;
	if(m < header.bss) {
		print("	bss too big\n");
		return;
	}
	memset((char*)HOT->ram + o, 0, m);

	if(1)
		HOT->entry = header.entry;

	for(m=0; m<100; m++) {
		if(HOT->prbufsize)
			break;
		delay(100);
	}

	if(!HOT->prbufsize) {
		print("	no prbuf\n");
		return;
	}
	h.prbufp = (char*)HOT + HOT->prbufp;
	h.prbufpe = h.prbufp + HOT->prbufsize;
	h.prbufpc = h.prbufp;

	cmd.cmd.cmd = Ureset;
	if(hotissue(&cmd.hdr, &cmd.cmd, 1)) {
		print("	reset didnt issue\n");
		return;
	}
	h.replyp = 0;

	if(!h.hpactive) {
		h.hpactive = 1;
		userinit(hotprint, 0, "hpr");
		userinit(hotrodin, 0, "hri");
	}

	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		hcmd = hc->hcmd;

		hcmd->cmd = Ubuf;
		hcmd->param[0] = 0;			/* returned u */
		hcmd->param[1] = hc->mbmap;		/* message return */
		hcmd->param[2] = hc->mbsize;		/* size */
		hcmd->param[3] = 0;			/* returned count */

		wbackcache(hc->mb->data, hc->mbsize);
		invalcache(hc->mb->data, hc->mbsize);

		if(hotissue(hc, hcmd, 0)) {
			print("	buf[%d] didnt issue\n", i);
			return;
		}
	}
}

void
cmd_hrod(void)
{
	Msgbuf *mb;
	Hcmd *hc;
	struct { Hhdr hdr; Hcmd cmd; } cmd;
	int i;

	switch(number(0, 10)) {
	case 0:
		print("hrod 1: reboot\n");
		print("hrod 3: Utest memdiag\n");
		print("hrod 31: Utest memdiag no fornax\n");
		print("hrod 32: Utest memdiag no hrod\n");
		print("hrod 4: fake interrupt\n");
		print("hrod 5: verbose\n");
		print("hrod 6: Uping\n");
		print("hrod 7: Utest no diag\n");
		print("hrod 10: HOT->error\n");
		print("hrod 11: HOT->error=0\n");
		break;

	case 1:
		hrodstart();
		break;

	case 3:
		print("both\n");
		i = 256*4;
		mb = mballoc(i, 0, Mbhotrod3);
		cmd.hdr.mbmap = vme2sysmap(vme->bus, (ulong)mb->data, i);
		cmd.cmd.cmd = Utest;
		cmd.cmd.param[0] = cmd.hdr.mbmap;
		cmd.cmd.param[1] = i/4;
		if(hotissue(&cmd.hdr, &cmd.cmd, 1))
			print("	test didnt issue\n");
		invalcache(&cmd.cmd, sizeof(Hcmd));
		print("%lux %ld\n", cmd.cmd.param[0], cmd.cmd.param[1]);
		for(;;) {
			print("-");
			mem(HOT, &HOT->ram[(cmd.cmd.param[0]-0x40000)/sizeof(ulong)],
				cmd.cmd.param[1]);
		}
		break;

	case 31:
		print("no fornax access of hrod mem\n");
		i = 256*4;
		mb = mballoc(i, 0, Mbhotrod4);
		cmd.hdr.mbmap = vme2sysmap(vme->bus, (ulong)mb->data, i);
		cmd.cmd.cmd = Utest;
		cmd.cmd.param[0] = cmd.hdr.mbmap;
		cmd.cmd.param[1] = i/4;
		if(hotissue(&cmd.hdr, &cmd.cmd, 1))
			print("	test didnt issue\n");
		invalcache(&cmd.cmd, sizeof(Hcmd));
		print("%lux %ld\n", cmd.cmd.param[0], cmd.cmd.param[1]);
		for(;;) {
		}
		break;

	case 32:
		print("no hrod access of fornax mem\n");
		cmd.cmd.cmd = Utest;
		cmd.cmd.param[0] = 0;
		cmd.cmd.param[1] = 0;
		if(hotissue(&cmd.hdr, &cmd.cmd, 1))
			print("	test didnt issue\n");
		invalcache(&cmd.cmd, sizeof(Hcmd));
		print("%lux %ld\n", cmd.cmd.param[0], cmd.cmd.param[1]);
		for(;;) {
			print("-");
			mem(HOT, &HOT->ram[(cmd.cmd.param[0]-0x40000)/sizeof(ulong)],
				cmd.cmd.param[1]);
		}
		break;

	case 4:
		h.cmdwait = 1;
		wakeup(&h.hrir);
		break;

	case 5:
		h.noise = !h.noise;
		break;

	case 6:
		cmd.cmd.cmd = Uping;
		if(hotissue(&cmd.hdr, &cmd.cmd, 1))
			print("	test didnt issue\n");
		break;

	case 7:
		cmd.cmd.cmd = Ubus;
		if(hotissue(&cmd.hdr, &cmd.cmd, 1))
			print("	test didnt issue\n");
		break;

	case 10:
		print("HOT->error = %d\n", HOT->error);
		break;

	case 11:
		HOT->error = 0;
		break;
	}
}

ulong
lswab(ulong l)
{
	uchar *p;

	p = (uchar*)&l;
	return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3] << 0);
}

int
hotissue(Hhdr *hdr, Hcmd *cmd, int alloc)
{
	int i;
	long t;
	ulong *hp, m, map;

	map = hdr->hcmap;
	if(alloc)
		map = vme2sysmap(vme->bus, (ulong)cmd, sizeof(Hcmd));
	wbackcache(cmd, sizeof(Hcmd));
	invalcache(cmd, sizeof(Hcmd));

	qlock(&h.issueq);
	i = h.issuep;
	hp = &HOT->reqstq[i];
	*hp = map;

	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	t = toytime() + SECOND(5);
	for(;;) {
		m = *hp;
		if(m == 0)
			break;
		if(toytime() >= t)
			break;
	}
	if(alloc)
		vme2sysfree(vme->bus, map, sizeof(Hcmd));
	else
		vmeflush(vme->bus, map, sizeof(Hcmd));
	return m;
}

static
void
mem(Hot *hot, ulong *buf, ulong size)
{
	long i, j, k, l;
	ulong *p, bit, u;
	int q;

	/* one bit */
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		*p = bit;
		bit <<= 1;
	}
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		if(*p != bit) {
			print("A: %lux is %lux sb %lux\n", p, *p, bit);
			hot->error++;
			delay(500);
		}
		bit <<= 1;
	}
	/* all but one bit */
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		*p = ~bit;
		bit <<= 1;
	}
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		if(*p != ~bit) {
			print("B: %lux is %lux sb %lux\n", p, *p, ~bit);
			hot->error++;
			delay(500);
		}
		bit <<= 1;
	}
	/* rand bit */
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		*p = bit;
		bit += PRIME;
	}
	bit = 0;
	p = buf;
	for(i=0; i<size; i++,p++) {
		if(bit == 0)
			bit = 1;
		if(*p != bit) {
			print("C: %lux is %lux sb %lux\n", p, *p, bit);
			hot->error++;
			delay(500);
		}
		bit += PRIME;
	}
part4:
	/* address */
	p = buf;
	for(i=0; i<size; i++,p++)
		*p = i;
	for(j=0; j<200; j++) {
		p = buf;
		for(i=0; i<size; i++,p++) {
			u = *p;
			if(u != i+j) {
				print("D: %lux is %lux sb %lux (%lux)\n", p, u, i+j, *p);
				hot->error++;
				delay(500);
			}
			*p = i+j+1;
		}
	}
}
void
hotpoot(int val)
{
	if(HOT) {
		HOT->error = val;
		print("pooted\n");
	}
}
hotrod.h        684451411   0     0     664     2580      `
/* csr0 */
#define GAZ_SYNC	0x01
#define GAZ_FIFOE	0x02
#define GAZ_FIFOHF	0x04
#define GAZ_FIFOOF	0x08
#define GAZ_ERROR	0x10

/* csr1 */
#define VME_DTRAN	0x01
#define VME_VA01	0x02
#define VME_AM0		0x04
#define VME_AM1		0x08
#define VME_AM2		0x10
#define VME_VA30	0x40
#define VME_VA31	0x80

/* csr2 */
#define VME_LWORD_L	0x01
#define VME_DS0		0x02
#define VME_DS1		0x04
#define VME_AM3		0x08
#define VME_AM4		0x10
#define VME_AM5		0x20

/* csr3 */
#define INT_FIFOE	0x01
#define INT_FIFOHF	0x02
#define INT_FIFOCMD	0x04
#define INT_VME		0x08
#define GAZ_LOOP	0x10
#define GAZ_SPEED	0x20
#define GAZ_ENABLE	0x40

/* csr4 */
#define	VME_IRQ0	0x01
#define	VME_IRQ1	0x02
#define	VME_IRQ2	0x04
#define	VME_INT0	0x08
#define	VME_INT1	0x10
#define	VME_INT2	0x20

enum
{
	VEC	= 0xd2,				/* vme vector */
	NRQ	= 50,				/* host->hotrod request q */
	PRIME	= 1,

	Ureset	= 0,				/* commands */
	Ureboot,
	Uread,
	Uwrite,
	Urpc,
	Ubuf,
	Ureply,
	Utest,
	Uping,
	Ubus,
};

typedef	struct	Hot	Hot;
typedef	struct	Comm	Comm;

/*
 * communication region
 * top of ram. stack just below.
 */
struct	Comm
{
	ulong	base[1];
	/*
	 * write on error
	 */
	ulong	error;
	/*
	 * host->hotrod request q.
	 * circular.
	 */
	ulong	reqstp;
	ulong	reqstq[NRQ];
	/*
	 * hotrod->host reply q.
	 * circular.
	 */
	ulong	replyp;
	ulong	replyq[NRQ];
	/*
	 * printf buffer area
	 */
	ulong	prbufp;
	ulong	prbufsize;
	/*
	 * boot rom jump pc
	 */
	ulong	entry;
};

/*
 * hotrod device structure
 * 4 256K byte regions
 *	1 rom
 *	2 ram
 *	3 hotrod xr
 *	4 csr
 */
struct	Hot
{
	ulong	rom[0x40000/sizeof(ulong)];
	union
	{
		ulong	ram[0x40000/sizeof(ulong)];
		struct
		{
			char	pad[0x40000-sizeof(Comm)];
			Comm;
		};
	};
	union
	{
		struct
		{
			ulong	gdata;
			ulong	gcmd;
		};
		ulong	junk1[0x40000/sizeof(ulong)];
	};
	union
	{
		struct
		{
			union{
				struct{
					uchar	pad0[3];
					uchar	csr0;
				};
				ulong	lcsr0;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr1;
				};
				ulong	lcsr1;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr2;
				};
				ulong	lcsr2;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr3;
				};
				ulong	lcsr3;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr4;
				};
				ulong	lcsr4;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr5;
				};
				ulong	lcsr5;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr6;
				};
				ulong	lcsr6;
			};
			union{
				struct{
					uchar	pad0[3];
					uchar	csr7;
				};
				ulong	lcsr7;
			};
		};
		ulong	junk2[0x40000/sizeof(ulong)];
	};
};
hsvme.c         700196785   0     0     664     11372     `
#include	"all.h"
#include	"dk.h"
#include	"io.h"

/*
 *  initialize the urp transmitter and go into INITING state
 *	called with up locked
 */
void
urpxinit(Chan *up, int wins)
{
	up->dkp.seq = 0;
	up->dkp.rej = 8;
	up->dkp.maxout = 3;		/* default */
	up->dkp.mlen = 256-4;		/* default */
	if(wins >= 16) {
		if(wins >= 16*up->dkp.maxout) {
			wins = (wins/(up->dkp.maxout+1))-4;
			if(wins < up->dkp.mlen)
				up->dkp.mlen = wins;
		} else {
			up->dkp.maxout = 1;
			up->dkp.mlen = wins-4;
		}
		if(up->dkp.mlen > 1010)	/* hsvme protection */
			up->dkp.mlen = 1010;
	}

	CPRINT("dk %d: xINIT1 urpxinit mlen/maxout=%d,%d\n", up->dkp.cno,
		up->dkp.mlen, up->dkp.maxout);
	up->dkp.urpstate = INITING;
	up->dkp.timeout = DKTIME(20);
	dkreply(up, CINIT1, 0);
}

/*
 *  initialize urp receiver
 *	called with up->dkp locked
 */
void
urprinit(Chan *up)
{
	up->dkp.gcc = 0;
	up->dkp.icc = 0;
	up->dkp.bot = 0;
	up->dkp.ack = 0;
	up->dkp.ec = CECHO0;
}

/*
 *  initialize urp xmitter and receiver
 *	called with up->dkp locked
 */
void
urpinit(Chan *up)
{
	urprinit(up);
	urpxinit(up, 0);
}

/*
 * output process for getting filesystem msgs
 *	pack them up into up and send to dk output
 */
void
dkoutput(void)
{
	Chan *up;
	Msgbuf *mb;
	Dk *dk;

	dk = getarg();
	print("dk output %d\n", dk-dkctlr);

loop:
	mb = recv(dk->reply, 0);
	if(mb == 0) {
		print("dkoutput: null\n");
		goto loop;
	}
	up = mb->chan;
	dklock(dk, up);
	dkqmesg(mb, up);
	dkunlock(dk, up);
	goto loop;
}

/*
 * work part of dkoutput
 *	argument is a message that is to
 *	be output. put on queue and
 *	flag the poke process.
 *	called with up locked
 */
void
dkqmesg(Msgbuf *mb, Chan *up)
{
	Msgbuf *mb1;

	DPRINT("dkoutput: %d %d bytes\n",
		up->dkp.cno, mb->count);
	mb->next = 0;
	mb1 = up->dkp.outmsg;
	if(mb1) {
		/*
		 * if already there,
		 * queue on end and let poke
		 * process figure it out
		 */
		while(mb1->next)
			mb1 = mb1->next;
		mb1->next = mb;
	} else {
		up->dkp.outmsg = mb;
		up->dkp.nout = 0;
		up->dkp.mout = 0;
		up->dkp.mcount = mb->count;
	}
	up->dkp.pokeflg = 1;
}

/*
 * output process for poking the dk output driver
 *	messages are channel pointers to decypher
 */
int
halfempty(void *arg)
{
	Hsdev *hsd;

	hsd = arg;
	return hsd->csr & XHF;
}

void
dkpoke(void)
{
	Chan *up;
	Msgbuf *mb;
	Dk *dk;
	Hsdev *hsd;
	char *b;
	int c, n, bot, seq, tot;

	dk = getarg();
	print("dk poke %d\n", dk-dkctlr);

	hsd = dk->vme->address;
	goto loop1;

loop:
	if(tot)
		hsd->data = TXEOD;
	dkunlock(dk, up);

loop1:
	up = recv(dk->poke, 0);
	if(up == 0) {
		print("dkwrite: null\n");
		goto loop1;
	}
	if(!(hsd->csr & XHF))
		sleep(&dk->xren, halfempty, hsd);
	dklock(dk, up);
	tot = 0;	/* number of chars since half empty */

/*
 * put out control characters
 */
	n = up->dkp.repr;
	while(n != up->dkp.repw) {
		if(tot == 0) {
			hsd->data = up->dkp.cno | CHNO;
			tot++;
		}
		hsd->data = (up->dkp.repchar[n] & 0xff) | CTL;
		tot++;
		n++;
		if(n >= sizeof(up->dkp.repchar))
			n = 0;
	}
	up->dkp.repr = n;
	/*
	 *  don't send any messages until the channel
	 *  is inited
	 */
	if(up->dkp.urpstate != INITED) {
		DPRINT("dkpoke %d no output urpstate=%d\n",
			up->dkp.cno, up->dkp.urpstate);
		goto loop;
	}

more:
	up->dkp.timeout = DKTIME(20);
	if(up->dkp.nout >= up->dkp.maxout)
		goto loop;
	n = up->dkp.mout * up->dkp.mlen;
	c = up->dkp.mcount - n;
	mb = up->dkp.outmsg;
	if(c <= 0) {
		if(up->dkp.nout == 0) {
			up->dkp.seq += up->dkp.mout;
			up->dkp.mcount = 0;
			up->dkp.mout = 0;
			if(mb) {
				mb = mb->next;
				mbfree(up->dkp.outmsg);
				up->dkp.outmsg = mb;
				if(mb)
					up->dkp.mcount = mb->count;
				goto more;
			}
		}
		goto loop;
	}
	if(mb == 0) {
		print("dk %d: work, no buffer\n");
		up->dkp.mcount = 0;
		up->dkp.mout = 0;
		up->dkp.nout = 0;
		goto loop;
	}
	b = mb->data + n;
	bot = CTL|CBOT;
	if(c > up->dkp.mlen) {
		c = up->dkp.mlen;
		bot = CTL|CBOTM;
	}

	if(tot+c >= 1018) {
		if(tot != 0)
			hsd->data = TXEOD;
		dkunlock(dk, up);
		if(!(hsd->csr & XHF))
			sleep(&dk->xren, halfempty, hsd);
		dklock(dk, up);
		tot = 0;
		goto more;
	}

	if(tot == 0) {
		hsd->data = up->dkp.cno | CHNO;
		tot++;
	}
	n = c;
	while(n > 0) {
		hsd->data = *b++ & 0xff;
		n--;
	}
	tot += c;
	seq = CSEQ0 + ((up->dkp.seq + up->dkp.mout + 1) & 7);
	CPRINT("dk %d: %d bytes xSEQ%d\n", up->dkp.cno, c, seq&7);
	hsd->data = bot;
	hsd->data = c & 0xff;
	hsd->data = (c >> 8) & 0xff;
	hsd->data = CTL|seq;
	tot += 4;
	dk->orate.count += c;
	up->dkp.nout++;
	up->dkp.mout++;
	goto more;
}

/*
 * the ONE interrupt
 */
void
hsvmeintr(Vmedevice *vp)
{
	Hsdev *hsd;
	Dk *dk;
	int csr;

	hsd = vp->address;
	dk = vp->private;
	csr = hsd->csr;
	if(csr & REF)
		wakeup(&dk->rren);
	if(csr & XHF)
		wakeup(&dk->xren);
}

int
hsvmeinit(Vmedevice *vp)
{
	Dk *dk;
	Hsdev *hsd;
	ushort csr;

	print("hsvme init %d\n", vp->ctlr);
	if(vp->ctlr >= MaxDk)
		return -1;
	dk = &dkctlr[vp->ctlr];
	if(dk->vme)
		return -1;
	hsd = vp->address;

	/*
	 * does the board exist?
	if (probe(&hsd->csr, sizeof(hsd->csr)))
		return -1;
	 */

	hsd->csr = RESET;
	wbflush();
	delay(20);

	csr = hsd->csr;
	USED(csr);
	hsd->vector = vp->vector;
	hsd->csr = NORESET|IPL(vp->irq)|IENABLE|ALIVE;
	wbflush();
	delay(1);

	csr = hsd->csr;
	USED(csr);
	hsd->csr = NORESET|IPL(vp->irq)|FORCEW|IENABLE|ALIVE;
	wbflush();
	delay(1);

	dk->vme = vp;
	vp->private = dk;
	
	return 0;
}

/*
 * receiver loop process
 */
int
notempty(void *arg)
{
	Hsdev *dk;

	dk = arg;
	return dk->csr & REF;
}

void
dkinput(void)
{
	Dk *dk;
	Hsdev *hsd;
	Chan *up;
	Msgbuf *mb;
	int c, i, x;

	dk = getarg();
	print("dk input %d\n", dk-dkctlr);
	hsd = dk->vme->address;

	up = 0;		/* 0 means no channo found */

rint0:
	sleep(&dk->rren, notempty, hsd);

rint1:
	c = hsd->data;

rint2:
	if(c == 0xffff)
		goto rint0;

	/*
	 * channel number byte
	 */
	if(c & CHNO) {
		i = c & 0x1ff;
		if(i >= NDK) {
/*			print("dkinput: %d %x chan out of range\n", i, c); /**/
			up = 0;
			goto rint1;
		}
		up = &dk->dkchan[i];
		goto rint1;
	}
	if(up == 0) {
		DPRINT("dkinput: no chan\n");
		goto rint1;
	}

	dklock(dk, up);

	/*
	 * data byte(s)
	 */
	if(!(c & NND)) {
		mb = up->dkp.inmsg;
		if(mb == 0) {
			mb = mballoc(LARGEBUF, up, Mbdkinput);
			up->dkp.inmsg = mb;
			up->dkp.icc = 0;
			up->dkp.gcc = 0;
		}
		i = up->dkp.icc;
		if(i >= LARGEBUF-5) {		/* overflow */
			print("dkinput %d: input overflow\n", up->dkp.cno);
			i = up->dkp.gcc;
			up->dkp.icc = i;
		}
		for(;;) {
			mb->data[i] = c;
			i++;
			c = hsd->data;
			if(c & (NND|CHNO))
				break;
			if(i >= LARGEBUF-5)
				break;
		}
		up->dkp.icc = i;
		dkunlock(dk, up);
		goto rint2;
	}

	/*
	 * control byte
	 */
	c &= 0xff;
	switch(c) {

	default:		/* characters to ignore */
		break;

	case CECHO0+0:	case CECHO0+4:
	case CECHO0+1:	case CECHO0+5:
	case CECHO0+2:	case CECHO0+6:
	case CECHO0+3:	case CECHO0+7:
		up->dkp.timeout = DKTIME(4);
		up->dkp.rej = 8;
		c &= 7;
		for(i=up->dkp.nout-1; i>=0; i--) {
			if(c == ((up->dkp.seq + up->dkp.mout - i) & 7)) {
				CPRINT("dk %d: rECHO%d\n",
						up->dkp.cno, c);
				up->dkp.nout = i;
				up->dkp.pokeflg = 1;
				goto brk;
			}
		}
		CPRINT("dk %d: rECHO%d, window = (%d,%d]\n",
			up->dkp.cno, c,
			(up->dkp.seq+up->dkp.mout-up->dkp.nout)&7,
			(up->dkp.seq+up->dkp.mout)&7);
		break;

	case CREJ0+0:	case CREJ0+4:
	case CREJ0+1:	case CREJ0+5:
	case CREJ0+2:	case CREJ0+6:
	case CREJ0+3:	case CREJ0+7:
		up->dkp.timeout = DKTIME(4);
		c &= 7;
		if(up->dkp.rej == 8) {
			for(i=up->dkp.nout; i>0; i--) {
				if(c == ((up->dkp.seq + up->dkp.mout - i) & 7)) {
					CPRINT("dk %d: rREJ%d\n",
						up->dkp.cno, c);
					up->dkp.mout -= i;
					up->dkp.nout = 0;
					up->dkp.pokeflg = 1;
					goto brk;
				}
			}
			CPRINT("dk %d: rREJ%d, window = (%d,%d)\n",
				up->dkp.cno, c,
				(up->dkp.seq+up->dkp.mout-up->dkp.nout)&7,
				(up->dkp.seq+up->dkp.mout)&7);
		} else
			CPRINT("dk %d: rREJ%d not following rECHO or xENQ\n",
				up->dkp.cno, c);
		up->dkp.rej = c;
		break;

	case CACK0+0:	case CACK0+4:
	case CACK0+1:	case CACK0+5:
	case CACK0+2:	case CACK0+6:
	case CACK0+3:	case CACK0+7:
		up->dkp.timeout = DKTIME(4);
		up->dkp.rej = 8;
		c &= 7;
		for(i=up->dkp.nout; i>0; i--) {
			if(c == ((up->dkp.seq + up->dkp.mout - i) & 7)) {
				CPRINT("dk %d: rACK%d\n",
					up->dkp.cno, c);
				up->dkp.mout -= i;
				up->dkp.nout = 0;
				up->dkp.pokeflg = 1;
				goto brk;
			}
		}
		CPRINT("dk %d: rACK%d, window = (%d,%d]\n",
			up->dkp.cno, c,
			(up->dkp.seq+up->dkp.mout-up->dkp.nout)&7,
			(up->dkp.seq+up->dkp.mout)&7);
		break;

	case CBOT:
	case CBOTS:
	case CBOTM:
		up->dkp.bot = up->dkp.icc;
		up->dkp.last = c;
		break;

	case CENQ:
		if(CEBUG)
		switch(up->dkp.ec) {
		case CECHO0+0:	case CECHO0+4:
		case CECHO0+1:	case CECHO0+5:
		case CECHO0+2:	case CECHO0+6:
		case CECHO0+3:	case CECHO0+7:
			CPRINT("dk %d: rENQ xECHO%d xACK%d\n",
				up->dkp.cno,
				up->dkp.ec-CECHO0, up->dkp.ack);
			break;

		case CREJ0+0:	case CREJ0+4:
		case CREJ0+1:	case CREJ0+5:
		case CREJ0+2:	case CREJ0+6:
		case CREJ0+3:	case CREJ0+7:
			CPRINT("dk %d: rENQ xECHO%d xACK%d\n",
				up->dkp.cno, up->dkp.ec-CREJ0, up->dkp.ack);
			break;

		default:
			CPRINT("dk %d: rENQ xGOK%d xACK%d\n",
				up->dkp.cno, up->dkp.ec, up->dkp.ack);
		}
		up->dkp.bot = 0;
		up->dkp.icc = up->dkp.gcc;
		dkreply(up, up->dkp.ec, up->dkp.ack+CACK0);
		break;

	case CINIT1:
		urprinit(up);
		CPRINT("dk %d: rINIT1 xAINIT\n",
			up->dkp.cno);
		dkreply(up, CAINIT, 0);
		break;

	case CAINIT:
		CPRINT("dk %d: rAINIT\n",
			up->dkp.cno);
		up->dkp.urpstate = INITED;
		up->dkp.pokeflg = 1;
		break;

	case CINITREQ:
		urpxinit(up, 0);
		break;

	case CSEQ0+0:	case CSEQ0+4:
	case CSEQ0+1:	case CSEQ0+5:
	case CSEQ0+2:	case CSEQ0+6:
	case CSEQ0+3:	case CSEQ0+7:
		if(up->dkp.dkstate != OPENED) {
			/* don't accept messages on any disconnected channel */
			up->dkp.icc = up->dkp.gcc;
			up->dkp.bot = 0;
			break;
		}
		c &= 7;
		i = up->dkp.icc-2;
		if(i != up->dkp.bot){
			x = 1;
			goto bad;
		}
		mb = up->dkp.inmsg;
		if(mb == 0){
			x = 2;
			goto bad;
		}
		x = i - up->dkp.gcc;
		if((((mb->data[i+1]&0xff) << 8) | (mb->data[i+0]&0xff)) != x){
			x = 3;
			goto bad;
		}
		if(up->dkp.last != CBOTS)
		if(c != ((up->dkp.ack+1)&7)){
			x = 4;
			goto bad;
		}
		up->dkp.icc = i;
		up->dkp.gcc = i;
		up->dkp.bot = 0;
		up->dkp.ack = c;
		CPRINT("dk %d: rSEQ%d xECHO%d count=%d last=%d\n",
			up->dkp.cno, c, c, i, up->dkp.last);
		up->dkp.ec = CECHO0 + c;
		dkreply(up, up->dkp.ec, 0);
		if(up->dkp.last != CBOTM) {
			dk->irate.count += i;
			mb->count = i;
			up->dkp.inmsg = 0;
			dkunlock(dk, up);
			send(up->send, mb);	/* full message */
			goto rint1;
		}
		break;

	bad:
		up->dkp.icc = up->dkp.gcc;
		up->dkp.bot = 0;
		CPRINT("dk %d: rSEQ%d xREJ%d reason %d\n",
			up->dkp.cno, c, up->dkp.ack, x);
		up->dkp.ec = CREJ0 + up->dkp.ack;
		dkreply(up, up->dkp.ec, 0);
		break;
	}
brk:
	dkunlock(dk, up);
	goto rint1;
}

/*
 * put control characters in circular
 * buffer for output routine, then poke
 *	up is locked
 */
void
dkreply(Chan *up, int c1, int c2)
{
	int i;

	i = up->dkp.repw;
	if(i < 0 || i >= sizeof(up->dkp.repchar)) {
		i = 0;
		up->dkp.repw = 0;
		up->dkp.repr = 0;
	}
	if(c1) {
		up->dkp.repchar[i] = c1;
		i++;
		if(i >= sizeof(up->dkp.repchar))
			i = 0;
	}
	if(c2) {
		up->dkp.repchar[i] = c2;
		i++;
		if(i >= sizeof(up->dkp.repchar))
			i = 0;
	}
	up->dkp.repw = i;
	up->dkp.pokeflg = 1;
}
il.c            710793971   0     0     664     16172     `
#include "all.h"
#include "mem.h"

#define	DEBUG	if(cons.flags&Fip)print
#define Starttimer(s)	{(s)->timeout = 0; \
			 (s)->fasttime = (Fasttime*(s)->rtt)/Iltickms; \
			 (s)->slowtime = (Slowtime*(s)->rtt)/Iltickms; }

static
struct
{
	Lock;
	Queue*	reply;
	Chan*	ilp;
} il;

static	void	ilout(void);
static	void	ilprocess(Chan*, Msgbuf*);
static	Chan*	getchan(Ifc*, Ilpkt*);
static	void	ilhangup(Chan*, char*);
static	void	ilsendctl(Chan*, Ilpkt*, int, ulong, ulong);
static	void	ilpullup(Chan*);
static	void	ilackto(Chan*, ulong);
static	void	ilrexmit(Ilp*);
static	void	iloutoforder(Chan*, Ilpkt*, Msgbuf*);
static	void	ilfreeq(Chan*);
static	void	ilackq(Chan*, Msgbuf*);
static	void	ilbackoff(Ilp*);
static	void	iltimer(void);

static
char*	ilstate[] =
{
	"Closed",
	"Syncer",
	"Syncee",
	"Established",
	"Listening",
	"Closing",
	"Opening",
};
static
char*	iltype[] =
{
	"sync",
	"data",
	"dataquerey",
	"ack",
	"querey",
	"state",
	"close",
};
static
char*	etime	= "connection timed out";

static	Rendez	ild;

static
void
ilpinit(Ilp *ilp)
{
	ilp->unacked = 0;
	ilp->unackedtail = 0;
	ilp->outoforder = 0;
	ilp->recvd = 0;
	ilp->next = 0;
	ilp->start = 0;
	ilp->rstart = 0;
	ilp->slowtime = 0;
	ilp->acktime = 0;
	ilp->rtt = 0;
	ilp->rttack = 0;
	ilp->ackms = 0;
	ilp->window = 0;
	ilp->querytime = Keepalivetime;
	ilp->deathtime = Keepalivetime;
	Starttimer(ilp);
}

static
Chan*
getchan(Ifc *ifc, Ilpkt *p)
{
	Chan *cp, *xcp;
	int srcp, dstp;

	srcp = nhgets(p->ilsrc);
	dstp = nhgets(p->ildst);

	xcp = 0;
	for(cp = il.ilp; cp; cp = cp->ilp.link) {
		if(cp->ilp.alloc == 0) {
			xcp = cp;
			continue;
		}
		if(srcp == cp->ilp.srcp)
		if(dstp == cp->ilp.dstp)
		if(memcmp(p->src, cp->ilp.iphis, Pasize) == 0)
		if(memcmp(p->dst, cp->ilp.ipmy, Pasize) == 0)
			return cp;
	}

	lock(&il);
	if(il.reply == 0) {
		il.reply = newqueue(Nqueue);
		userinit(ilout, &il, "ilo");
		userinit(iltimer, &il, "ilt");
	}

	if(xcp && xcp->ilp.alloc != 0)
		xcp = 0;
	cp = xcp;
	if(cp == 0) {
		cp = chaninit(Devil, 1);
		cp->ilp.link = il.ilp;
		il.ilp = cp;
	}

	cp->ilp.alloc = 1;
	memmove(cp->ilp.iphis, p->src, Pasize);
	memmove(cp->ilp.ipmy, p->dst, Pasize);
	cp->ilp.srcp = srcp;
	cp->ilp.dstp = dstp;
	cp->ilp.state = Ilopening;

	ilpinit(&cp->ilp);

	memmove(cp->ilp.ipgate, ifc->netgate, Pasize);
	memmove(cp->ilp.ea, ifc->ea, Easize);

	cp->ilp.usegate = 0;
	if((nhgetl(ifc->ipa)&ifc->mask) != (nhgetl(p->src)&ifc->mask))
		cp->ilp.usegate = 1;

	cp->send = serveq;
	cp->reply = il.reply;
	cp->ilp.reply = ifc->reply;
	cp->whotime = 0;
	sprint(cp->whochan, "%I-%d", p->src, srcp);

	unlock(&il);
	print("allocating %s\n", cp->whochan);
	return cp;
}

void
ilrecv(Msgbuf *mb, Ifc *ifc)
{
	Ilpkt *ih;
	Chan *cp;
	Ilp *ilp;
	int illen, plen;

	ih = (Ilpkt*)mb->data;

	plen = mb->count;
	if(plen < Ensize+Ipsize+Ilsize)
		goto drop;

	illen = nhgets(ih->illen);
	if(illen+Ilsize > plen)
		goto drop;

	if(ptclcsum((uchar*)ih+(Ensize+Ipsize), illen) != 0) {
		print("il: cksum error\n");
		ifc->sumerr++;
		goto drop;
	}

	cp = getchan(ifc, ih);
	mb->chan = cp;
	ilp = &cp->ilp;

	if(ilp->state == Ilopening) {
		if(ilp->dstp != Ilfsport) {
			if(ih->iltype != Ilclose)
				ilsendctl(cp, ih, Ilclose, 0, 0);
			goto drop;
		}
		if(ih->iltype != Ilsync) {
			print("il: open not sync %I\n", ih->src);
			if(ih->iltype != Ilclose)
				ilsendctl(cp, ih, Ilclose, 0, 0);
			goto drop;
		}
		ilp->state = Ilsyncee;
		ilp->start = (toytime() * 80021) & 0x3fffffffUL;
		ilp->next = ilp->start+1;
		ilp->recvd = 0;
		ilp->rstart = nhgetl(ih->ilid);
		ilp->slowtime = Slowtime;
		ilp->rtt = Iltickms;
		ilp->window = Defaultwin;
		ilp->querytime = Keepalivetime;
		ilp->deathtime = Keepalivetime;
	}

	ilprocess(cp, mb);
	return;

drop:
	mbfree(mb);
}

/*
 * process to convert p9 to il/ip
 */
void
ilout(void)
{
	Msgbuf *mb;
	Ilp *ilp;
	Ilpkt *ih;
	Chan *cp;
	int dlen;
	ulong id;

loop:
	mb = recv(il.reply, 0);
	if(mb == 0)
		goto loop;

	cp = mb->chan;
	ilp = &cp->ilp;

	switch(ilp->state) {
	case Ilclosed:
	case Illistening:
	case Ilclosing:
		goto err;
	}

	dlen = mb->count;
	mb->data -= Ensize+Ipsize+Ilsize;	/* make room for header */
	mb->count += Ensize+Ipsize+Ilsize;
	if(mb->data < mb->xdata)
		panic("ilout: no room for header");
	ih = (Ilpkt*)mb->data;

	/*
	 * Ip fields
	 */
	memmove(ih->src, ilp->ipmy, Pasize);
	memmove(ih->dst, ilp->iphis, Pasize);
	ih->proto = Ilproto;

	/*
	 * Il fields
	 */
	hnputs(ih->illen, Ilsize+dlen);
	hnputs(ih->ilsrc, ilp->dstp);
	hnputs(ih->ildst, ilp->srcp);
	id = ilp->next++;
	hnputl(ih->ilid, id);
	hnputl(ih->ilack, ilp->recvd);
	ih->iltype = Ildata;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	/*
	 * checksum
	 */
	hnputs(ih->ilsum, ptclcsum((uchar*)ih+(Ensize+Ipsize), dlen+Ilsize));

	ilackq(cp, mb);
	ilp->acktime = Ackkeepalive;

	/* Start the round trip timer for this packet if the timer is free */
	if(ilp->rttack == 0) {
		ilp->rttack = id;
		ilp->ackms = MACHP(0)->ticks;
	}

	ipsend(mb);
	goto loop;

err:
	print("ilout: error\n");
	mbfree(mb);
	goto loop;

}

static
void
ilackq(Chan *cp, Msgbuf *mb)
{
	Msgbuf *nmb;
	Ilp *ilp;

	/*
	 * Enqueue a copy on the unacked queue in case this one gets lost
	 */
/* botch -- a reference count will save this copy */
	nmb = mballoc(mb->count, cp, Mbil2);
	memmove(nmb->data, mb->data, mb->count);
	nmb->next = 0;

	ilp = &cp->ilp;
	lock(&il);
	if(ilp->unacked)
		ilp->unackedtail->next = nmb;
	else {
		/*
		 * Start timer since we may have been idle for some time
		 */
		Starttimer(ilp);
		ilp->unacked = nmb;
	}
	ilp->unackedtail = nmb;
	unlock(&il);
}

static
void
ilprocess(Chan *cp, Msgbuf *mb)
{
	ulong id, ack;
	Ilp* ilp;
	Ilpkt *h;

	ilp = &cp->ilp;
	h = (Ilpkt*)mb->data;

	id = nhgetl(h->ilid);
	ack = nhgetl(h->ilack);

	ilp->querytime = Keepalivetime;
	ilp->deathtime = Keepalivetime;
	switch(ilp->state) {
	default:
		print("il unknown state\n");
	case Ilclosed:
		mbfree(mb);
		break;
	case Ilsyncer:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(ack != ilp->start) {
				ilp->state = Ilclosed;
				ilhangup(cp, "connection rejected");
			} else {
				ilp->recvd = id;
				ilp->rstart = id;
				ilsendctl(cp, 0, Ilack, ilp->next, ilp->recvd);
				ilp->state = Ilestablished;
				wakeup(&ilp->syn);
				ilpullup(cp);
				Starttimer(ilp);
			}
			break;
		case Ilclose:
			if(ack == ilp->start) {
				ilp->state = Ilclosed;
				ilhangup(cp, "remote close");
			}
			break;
		}
		mbfree(mb);
		break;

	case Ilsyncee:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(id != ilp->rstart || ack != 0)
				ilp->state = Ilclosed;
			else {
				ilp->recvd = id;
				ilsendctl(cp, 0, Ilsync, ilp->start, ilp->recvd);
				Starttimer(ilp);
			}
			break;
		case Ilack:
			if(ack == ilp->start) {
				ilp->state = Ilestablished;
				ilpullup(cp);
				Starttimer(ilp);
			}
			break;
		case Ilclose:
			if(id == ilp->next) {
				ilp->state = Ilclosed;
				ilhangup(cp, "remote close");
			}
			break;
		}
		mbfree(mb);
		break;

	case Ilestablished:
		switch(h->iltype) {
		case Ilsync:
			if(id != ilp->start) {
				ilp->state = Ilclosed;
				ilhangup(cp, "remote close");
			} else {
				ilsendctl(cp, 0, Ilack, ilp->next, ilp->rstart);
				Starttimer(ilp);
			}
			mbfree(mb);
			break;
		case Ildata:
			Starttimer(ilp);
			ilackto(cp, ack);
			ilp->acktime = Acktime;
			iloutoforder(cp, h, mb);
			ilpullup(cp);
			break;
		case Ildataquery:
			Starttimer(ilp);
			ilackto(cp, ack);
			ilp->acktime = Acktime;
			iloutoforder(cp, h, mb);
			ilpullup(cp);
			ilsendctl(cp, 0, Ilstate, ilp->next, ilp->recvd);
			break;
		case Ilack:
			ilackto(cp, ack);
			Starttimer(ilp);
			mbfree(mb);
			break;
		case Ilquerey:
			ilackto(cp, ack);
			ilsendctl(cp, 0, Ilstate, ilp->next, ilp->recvd);
			Starttimer(ilp);
			mbfree(mb);
			break;
		case Ilstate:
			ilackto(cp, ack);
			ilrexmit(ilp);
			Starttimer(ilp);
			mbfree(mb);
			break;
		case Ilclose:
			mbfree(mb);
			if(ack < ilp->start || ack > ilp->next)
				break;
			ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd);
			ilp->state = Ilclosing;
			ilfreeq(cp);
			Starttimer(ilp);
			break;
		}
		break;

	case Illistening:
		mbfree(mb);
		break;

	case Ilclosing:
		switch(h->iltype) {
		case Ilclose:
			ilp->recvd = id;
			ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd);
			if(ack == ilp->next) {
				ilp->state = Ilclosed;
				ilhangup(cp, 0);
			}
			Starttimer(ilp);
			break;
		default:
			break;
		}
		mbfree(mb);
		break;
	}
}

static
void
ilsendctl(Chan *cp, Ilpkt *inih, int type, ulong id, ulong ack)
{
	Ilpkt *ih;
	Msgbuf *mb;
	Ilp *ilp;

	ilp = &cp->ilp;
	mb = mballoc(Ensize+Ipsize+Ilsize, cp, Mbil3);

	ih = (Ilpkt*)mb->data;

	ih->proto = Ilproto;
	memmove(ih->src, ilp->ipmy, Pasize);
	hnputs(ih->illen, Ilsize);
	if(inih) {
		memmove(ih->dst, inih->src, Pasize);
		memmove(ih->ilsrc, inih->ildst, sizeof(ih->ilsrc));
		memmove(ih->ildst, inih->ilsrc, sizeof(ih->ildst));
		memmove(ih->ilid, inih->ilack, sizeof(ih->ilid));
		memmove(ih->ilack, inih->ilid, sizeof(ih->ilack));
	} else {
		memmove(ih->dst, ilp->iphis, Pasize);
		hnputs(ih->ilsrc, ilp->dstp);
		hnputs(ih->ildst, ilp->srcp);
		hnputl(ih->ilid, id);
		hnputl(ih->ilack, ack);
		ilp->acktime = Ackkeepalive;
	}
	ih->iltype = type;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	hnputs(ih->ilsum, ptclcsum((uchar*)mb->data+(Ensize+Ipsize), Ilsize));

	ipsend(mb);
}

static
void
ilhangup(Chan *cp, char *msg)
{
	Ilp *ilp;
	int s;

	ilp = &cp->ilp;
	s = ilp->state;
	ilp->state = Ilclosed;
	if(s == Ilsyncer)
		wakeup(&ilp->syn);

	print("hangup! %s %d/%d\n", msg? msg: "??", ilp->srcp, ilp->dstp);
	ilfreeq(cp);

	fileinit(cp);
	cp->whotime = 0;
	strcpy(cp->whoname, "<none>");

	lock(&il);
	ilp->alloc = 0;
	ilp->srcp = 0;
	ilp->dstp = 0;
	memset(ilp->iphis, 0, sizeof(ilp->iphis));
	unlock(&il);
}

static
void
ilpullup(Chan *cp)
{
	Ilpkt *oh;
	Msgbuf *mb;
	Ilp *ilp;
	ulong oid, dlen;

	ilp = &cp->ilp;
	lock(&il);

	while(ilp->outoforder) {

		mb = ilp->outoforder;
		oh = (Ilpkt*)mb->data;
		oid = nhgetl(oh->ilid);
		if(oid <= ilp->recvd) {
			ilp->outoforder = mb->next;
			mbfree(mb);
			continue;
		}
		if(oid != ilp->recvd+1)
			break;

		ilp->recvd = oid;
		ilp->outoforder = mb->next;

		/*
		 * strip off the header
		 */
		dlen = nhgets(oh->illen)-Ilsize;
		mb->data += Ensize+Ipsize+Ilsize;
		mb->count = dlen;
		send(cp->send, mb);
	}
	unlock(&il);
}

static
void
iloutoforder(Chan *cp, Ilpkt *h, Msgbuf *mb)
{
	Msgbuf **l, *f;
	Ilp *ilp;
	ulong id;
	uchar *lid;

	ilp = &cp->ilp;
	id = nhgetl(h->ilid);

	/*
	 * Window checks
	 */
	if(id <= ilp->recvd || id > ilp->recvd+ilp->window) {
		mbfree(mb);
		return;
	}

	/*
	 * Packet is acceptable so
	 * sort onto receive queue for pullup
	 */
	mb->next = 0;
	lock(&il);
	if(ilp->outoforder == 0)
		ilp->outoforder = mb;
	else {
		l = &ilp->outoforder;
		for(f = *l; f; f = f->next) {
			lid = ((Ilpkt*)(f->data))->ilid;
			if(id < nhgetl(lid)) {
				mb->next = f;
				*l = mb;
				unlock(&il);
				return;
			}
			l = &f->next;
		}
		*l = mb;
	}
	unlock(&il);
}

static
void
ilackto(Chan *cp, ulong ackto)
{
	Ilpkt *h;
	Ilp *ilp;
	Msgbuf *mb;
	ulong id, t;

	ilp = &cp->ilp;
	if(ilp->rttack == ackto) {
		t = TK2MS(MACHP(0)->ticks - ilp->ackms);
		/* Guard against the ulong zero wrap */
		if(t < 100*ilp->rtt)
			ilp->rtt = (ilp->rtt*(ILgain-1)+t)/ILgain;
		if(ilp->rtt < Iltickms)
			ilp->rtt = Iltickms;
	}

	/* Cancel if we lost the packet we were interested in */
	if(ilp->rttack <= ackto)
		ilp->rttack = 0;

	lock(&il);
	while(ilp->unacked) {
		h = (Ilpkt*)ilp->unacked->data;
		id = nhgetl(h->ilid);
		if(ackto < id)
			break;

		mb = ilp->unacked;
		ilp->unacked = mb->next;
		mb->next = 0;
		mbfree(mb);
	}
	unlock(&il);
}

static
void
ilrexmit(Ilp *ilp)
{
	Msgbuf *omb, *mb;
	Ilpkt *h;

	lock(&il);
	omb = ilp->unacked;
	if(omb == 0) {
		unlock(&il);
		return;
	}

/* botch -- a reference count will save this copy */
	mb = mballoc(omb->count, omb->chan, Mbil4);
	memmove(mb->data, omb->data, omb->count);
	unlock(&il);
	
	h = (Ilpkt*)mb->data;

	h->iltype = Ildataquery;
	hnputl(h->ilack, ilp->recvd);
	h->ilsum[0] = 0;
	h->ilsum[1] = 0;
	hnputs(h->ilsum, ptclcsum((uchar*)mb->data+(Ensize+Ipsize), nhgets(h->illen)));

	ipsend(mb);
}

void
ilfreeq(Chan *cp)
{
	Ilp *ilp;
	Msgbuf *mb, *next;

	ilp = &cp->ilp;
	lock(&il);
	for(mb = ilp->unacked; mb; mb = next) {
		next = mb->next;
		mbfree(mb);
	}
	ilp->unacked = 0;

	for(mb = ilp->outoforder; mb; mb = next) {
		next = mb->next;
		mbfree(mb);
	}
	ilp->outoforder = 0;
	unlock(&il);
}

static
void
ilbackoff(Ilp *ilp)
{
	if(ilp->fasttime < Slowtime/2)
		ilp->fasttime += Fasttime;
	else
		ilp->fasttime = (ilp->fasttime)*3/2;
}

/*
 * il timer
 * every 100ms
 */
static	Rendez	ilt;

static
void
callil(Alarm *a, void *arg)
{

	USED(arg);
	cancel(a);
	wakeup(&ilt);
}

static
void
iltimer(void)
{
	Chan *cp;
	Ilp *ilp;

loop:
	for(cp = il.ilp; cp; cp = cp->ilp.link) {
		ilp = &cp->ilp;
		if(ilp->alloc == 0)
			continue;

		ilp->timeout += Iltickms;
		switch(ilp->state) {
		case Ilclosed:
		case Illistening:
			break;

		case Ilclosing:
			if(ilp->timeout >= ilp->fasttime) {
				ilsendctl(cp, 0, Ilclose, ilp->next, ilp->recvd);
				ilbackoff(ilp);
			}
			if(ilp->timeout >= ilp->slowtime) {
				ilp->state = Ilclosed;
				ilhangup(cp, 0);
			}
			break;

		case Ilsyncee:
		case Ilsyncer:
			if(ilp->timeout >= ilp->fasttime) {
				ilsendctl(cp, 0, Ilsync, ilp->start, ilp->recvd);
				ilbackoff(ilp);
			}
			if(ilp->timeout >= ilp->slowtime) {
				ilp->state = Ilclosed;
				ilhangup(cp, etime);
			}
			break;

		case Ilestablished:
			ilp->acktime -= Iltickms;
			if(ilp->acktime <= 0)
				ilsendctl(cp, 0, Ilack, ilp->next, ilp->recvd);

			ilp->querytime -= Iltickms;
			if(ilp->querytime <= 0){
				ilp->deathtime -= Querytime;
				if(ilp->deathtime < 0){
					ilhangup(cp, etime);
					break;
				}
				ilsendctl(cp, 0, Ilquerey, ilp->next, ilp->recvd);
				ilp->querytime = Querytime;
			}
			if(ilp->unacked == 0) {
				ilp->timeout = 0;
				break;
			}
			if(ilp->timeout >= ilp->fasttime) {
				ilrexmit(ilp);
				ilbackoff(ilp);
			}
			if(ilp->timeout >= ilp->slowtime) {
				ilp->state = Ilclosed;
				ilhangup(cp, etime);
				break;
			}
			break;
		}
	}
	alarm(Iltickms, callil, 0);
	sleep(&ilt, no, 0);
	goto loop;
}

static
int
notsyncer(void *ic)
{
	return ((Ilp*)ic)->state != Ilsyncer;
}

static
void
callildial(Alarm *a, void *arg)
{

	USED(arg);
	cancel(a);
	wakeup(&ild);
}

Chan *
ildial(uchar *ip, Queue *rcp)
{
	Ifc *i;
	Ilp *ilp;
	Chan *cp;
	static ulong initseq;

	for(i = enets; i; i = i->next)
		if((nhgetl(i->ipa)&i->mask) == (nhgetl(ip)&i->mask))
			goto found;

	print("ilauth: no interface for %I\n", ip);
	return 0;

found:
	for(cp = il.ilp; cp; cp = cp->ilp.link)
		if(cp->ilp.alloc == 0) 
			break;
			
	if(cp == 0) {
		cp = chaninit(Devil, 1);
		cp->ilp.link = il.ilp;
		il.ilp = cp;
	}
	ilp = &cp->ilp;
	ilp->alloc = 1;

	lock(&il);
	if(il.reply == 0) {
		il.reply = newqueue(Nqueue);
		userinit(ilout, &il, "ilo");
		userinit(iltimer, &il, "ilt");
	}

	memmove(ilp->iphis, ip, Pasize);
	memmove(ilp->ipmy, i->ipa, Pasize);
	ilp->srcp = Ilauthport;
	ilp->dstp = Ilfsout;

	ilpinit(ilp);

	memmove(ilp->ipgate, i->netgate, Pasize);
	memmove(ilp->ea, i->ea, Easize);
	ilp->usegate = 0;

	cp->send = rcp;
	cp->reply = il.reply;
	ilp->reply = i->reply;
	cp->whotime = 0;
	unlock(&il);

	sprint(cp->whochan, "%I-%d", ip, Ilauthport);

	initseq += toytime();
	ilp->start = initseq & 0xffffff;
	ilp->next = ilp->start+1;
	ilp->window = Defaultwin;
	ilp->state = Ilsyncer;
	ilp->slowtime = Slowtime;
	ilp->rtt = Iltickms;
	ilsendctl(cp, 0, Ilsync, ilp->start, ilp->recvd);
	sleep(&ilp->syn, notsyncer, ilp);

	if(ilp->state == Ilclosed) {
		print("ilauth: connection failed\n");
		return 0;
	}
	print("allocating %s\n", cp->whochan);
	print("ilauth: connected\n");	
	return cp;
}

void
ilauth(void)
{
	int i;

	if(authchan)
		ilhangup(authchan, "ilauth close");

		print("ilauth: dialing %I\n", authip);

	for(i = 0; i < 3; i++) {
		authchan = ildial(authip, authreply);
		if(authchan)
			break;
		alarm(10000, callildial, 0);
		sleep(&ild, no, 0);
	}
	if(i == 5)
		print("ilauth: dial giving up\n");
}
iobuf.c         714761445   0     0     664     4422      `
#include	"all.h"
#include	"io.h"

#define	DEBUG	0

extern	long	nhiob;
extern	Hiob	*hiob;

Iobuf*
getbuf(Device dev, long addr, int flag)
{
	Iobuf *p, *s;
	Hiob *hp;
	long h;

	if(DEBUG)
		print("getbuf %D(%ld) f=%x\n", dev, addr, flag);
	h = addr +
		dev.type*1009L +
		dev.ctrl*10007L +
		dev.unit*100003L +
		dev.part*1000003L;
	if(h < 0)
		h = ~h;
	h %= nhiob;
	hp = &hiob[h];

loop:
	lock(hp);

/*
 * look for it in the active list
 */
	s = hp->link;
	for(p=s;;) {
		if(p->addr == addr && !devcmp(p->dev, dev)) {
			if(p != s) {
				p->back->fore = p->fore;
				p->fore->back = p->back;
				p->fore = s;
				p->back = s->back;
				s->back = p;
				p->back->fore = p;
				hp->link = p;
			}
			unlock(hp);
			qlock(p);
			if(p->addr != addr || devcmp(p->dev, dev)) {
				qunlock(p);
				goto loop;
			}
			p->flags |= flag;
			cons.bhit.count++;
			p->iobuf = p->xiobuf;
			return p;
		}
		p = p->fore;
		if(p == s)
			break;
	}
	if(flag & Bprobe) {
		unlock(hp);
		return 0;
	}

/*
 * not found
 * take oldest unlocked entry in this queue
 */
xloop:
	p = s->back;
	if(!canqlock(p)) {
		if(p == hp->link) {
			unlock(hp);
			print("iobuf all locked\n");
			goto loop;
		}
		s = p;
		goto xloop;
	}
	/*
	 * its dangerous to flush the pseudo
	 * devices since they recursively call
	 * getbuf/putbuf. deadlock!
	 */
	if(p->flags & Bres) {
		qunlock(p);
		if(p == hp->link) {
			unlock(hp);
			print("iobuf all resed\n");
			goto loop;
		}
		s = p;
		goto xloop;
	}
	if(p->flags & Bmod) {
		unlock(hp);
		if(!devwrite(p->dev, p->addr, p->xiobuf))
			p->flags &= ~(Bimm|Bmod);
		qunlock(p);
		goto loop;
	}
	hp->link = p;
	p->addr = addr;
	p->dev = dev;
	p->flags = flag;
	unlock(hp);
	iobufmap(p);
	if(flag & Bread) {
		if(devread(p->dev, p->addr, p->iobuf)) {
			p->flags = 0;
			p->dev = devnone;
			p->addr = -1;
			p->iobuf = (char*)-1;
			qunlock(p);
			return 0;
		}
		cons.bread.count++;
		return p;
	}
	cons.binit.count++;
	return p;
}

/*
 * syncblock tries to put out a block per hashline
 * returns 0 all done,
 * returns 1 if it missed something
 */
int
syncblock(void)
{
	Iobuf *p, *s, *q;
	Hiob *hp;
	long h;
	int flag;

	flag = 0;
	for(h=0; h<nhiob; h++) {
		q = 0;
		hp = &hiob[h];
		lock(hp);
		s = hp->link;
		for(p=s;;) {
			if(p->flags & Bmod) {
				if(q)
					flag = 1;	/* more than 1 mod/line */
				q = p;
			}
			p = p->fore;
			if(p == s)
				break;
		}
		unlock(hp);
		if(q) {
			if(!canqlock(q)) {
				flag = 1;		/* missed -- was locked */
				continue;
			}
			if(!(q->flags & Bmod)) {
				qunlock(q);
				continue;
			}
			if(!devwrite(q->dev, q->addr, q->xiobuf))
				q->flags &= ~(Bmod|Bimm);
			qunlock(q);
		}
	}
	return flag;
}

void
sync(char *reason)
{
	long i;

	print("sync: %s\n", reason);
	for(i=10*nhiob; i>0; i--)
		if(!syncblock())
			return;
	print("sync shorted\n");
}

void
putbuf(Iobuf *p)
{

	if(canqlock(p))
		print("buffer not locked %D(%ld)\n", p->dev, p->addr);
	if(p->flags & Bimm) {
		if(!(p->flags & Bmod))
			print("imm and no mod %D(%ld)\n", p->dev, p->addr);
		if(!devwrite(p->dev, p->addr, p->iobuf))
			p->flags &= ~(Bmod|Bimm);
	}
	iobufunmap(p);
	qunlock(p);
}

int
checktag(Iobuf *p, int tag, long qpath)
{
	Tag *t;

	t = (Tag*)(p->iobuf+BUFSIZE);
	if(t->tag != tag) {
		if(p->flags & Bmod) {
			print("	tag = %d; expected %d -- not flushed\n",
				t->tag, tag);
			return 2;
		}
		if(p->dev.type == Devcw)
			cwfree(p->dev, p->addr);
		p->dev = devnone;
		p->addr = -1;
		p->flags = 0;
		print("	tag = %d; expected %d -- flushed\n",
			t->tag, tag);
		return 2;
	}
	if(qpath != QPNONE) {
		if((qpath ^ t->path) & ~QPDIR) {
			if(1 || CHAT(0))
				print("	tag/path = %lux; expected %d/%lux\n",
					t->path, tag, qpath);
			return 0;
		}
	}
	return 0;
}

void
settag(Iobuf *p, int tag, long qpath)
{
	Tag *t;

	t = (Tag*)(p->iobuf+BUFSIZE);
	t->tag = tag;
	if(qpath != QPNONE)
		t->path = qpath & ~QPDIR;
	p->flags |= Bmod;
}

int
qlmatch(QLock *q1, QLock *q2)
{

	return q1 == q2;
}

int
iobufql(QLock *q)
{
	Iobuf *p, *s;
	Hiob *hp;
	Tag *t;
	long h;
	int tag;

	for(h=0; h<nhiob; h++) {
		hp = &hiob[h];
		lock(hp);
		s = hp->link;
		for(p=s;;) {
			if(qlmatch(q, p)) {
				t = (Tag*)(p->iobuf+BUFSIZE);
				tag = t->tag;
				if(tag < 0 || tag >= MAXTAG)
					tag = Tnone;
				print("	Iobuf %D(%ld) t=%s\n",
					p->dev, p->addr, tagnames[tag]);
				unlock(hp);
				return 1;
			}
			p = p->fore;
			if(p == s)
				break;
		}
		unlock(hp);
	}
	return 0;
}
ip.c            694237896   0     0     664     3605      `
#include "all.h"

#define	DEBUG	if(1||cons.flags&Fip)print

typedef	struct	Rock	Rock;
typedef	struct	Frag	Frag;

struct	Frag
{
	int	start;
	int	end;
};

struct	Rock
{
	uchar	src[Pasize];
	uchar	dst[Pasize];
	int	id;		/* src,dst,id are address of the rock */
	Msgbuf*	mb;		/* reassembly. if 0, the rock is empty */
	ulong	age;		/* timeout to throw away */
	int	last;		/* set to data size when last frag arrives */
	int	nfrag;
	Frag	frag[Nfrag];
};

static
struct
{
	Lock;
	Rock	rock[Nrock];
} ip;

void
ipreceive(Enpkt *ep, int l, Ifc *ifc)
{
	Ippkt *p;
	Msgbuf *mb;
	Rock *r, *or;
	Frag *f;
	int len, id, frag, off, loff, i, n;
	Ippkt pkt;
	ulong t;

	p = (Ippkt*)ep;
	if(l < Ensize+Ipsize) {
		ifc->sumerr++;
		print("ip: en too small\n");
		return;
	}

	memmove(&pkt, p, Ensize+Ipsize);	/* copy pkt to 'real' memory */
	if(pkt.vihl != (IP_VER|IP_HLEN))
		return;
	if(!ipforme(pkt.dst, ifc))
		return;
	if(ipcsum(&pkt.vihl)) {
		ifc->sumerr++;
		print("ip: checksum error (from %I)\n", pkt.src);
		return;
	}

	frag = nhgets(pkt.frag);
	len = nhgets(pkt.length) - Ipsize;
	id = nhgets(pkt.id);

	/*
	 * total ip msg fits into one frag
	 */
	if((frag & ~IP_DF) == 0) {
		mb = mballoc(l, 0, Mbip3);
		memmove(mb->data, &pkt, Ensize+Ipsize);
		memmove(mb->data + (Ensize+Ipsize),
			(uchar*)p + (Ensize+Ipsize), l-(Ensize+Ipsize));
		goto send;
	}

	/*
	 * reassembly of fragments
	 * look up rock by src, dst, id.
	 * in same pass, throw away old rocks.
	 */
	lock(&ip);
	or = 0;
	t = toytime();
	r = ip.rock;
	for(i=0; i<Nrock; i++,r++) {
		if(r->mb == 0) {
			if(or == 0)
				or = r;
			continue;
		}
		if(t >= r->age) {
			mbfree(r->mb);
			r->mb = 0;
			if(or == 0)
				or = r;
			continue;
		}
		if(id == r->id)
		if(memcmp(r->src, pkt.src, Pasize) == 0)
		if(memcmp(r->dst, pkt.dst, Pasize) == 0)
			goto found;
	}
	r = or;
	if(r == 0) {
		unlock(&ip);
		print("iprecv: out of rocks\n");
		lock(&ip);
		r = ip.rock;
		for(i=0; i<Nrock; i++,r++) {
			mbfree(r->mb);
			r->mb = 0;
		}
		r = ip.rock;
	}
	r->id = id;
	r->mb = mballoc(LARGEBUF, 0, Mbip2);
	memmove(r->src, pkt.src, Pasize);
	memmove(r->dst, pkt.dst, Pasize);
	r->nfrag = 0;
	r->last = 0;

found:
	mb = r->mb;
	r->age = t + SECOND(30);

	off = (frag & ~(IP_DF|IP_MF)) << 3;
	if(len+off+Ensize+Ipsize > mb->count) {
		print("iprecv: ip pkt too big\n");
		mbfree(mb);
		r->mb = 0;
		goto uout;
	}
	if(!(frag & IP_MF))
		r->last = off+len;		/* found the end */

	memmove(mb->data+(Ensize+Ipsize)+off,
		(uchar*)p + (Ensize+Ipsize), len);

	/*
	 * frag algorithm:
	 * first entry is easy
	 */
	n = r->nfrag;
	if(n == 0) {
		r->frag[0].start = off;
		r->frag[0].end = off+len;
		r->nfrag = 1;
		goto span;
	}

	/*
	 * two in a row is easy
	 */
	if(r->frag[n-1].end == off) {
		r->frag[n-1].end += len;
		goto span;
	}

	/*
	 * add this frag
	 */
	if(n >= Nfrag) {
		print("too many frags\n");
		mbfree(mb);
		r->mb = 0;
		goto uout;
	}
	r->frag[n].start = off;
	r->frag[n].end = off+len;
	n++;
	r->nfrag = n;

span:
	/*
	 * see if we span the whole list
	 * can be O(n**2), but usually much smaller
	 */
	if(r->last == 0)
		goto uout;
	off = 0;

spanloop:
	loff = off;
	f = r->frag;
	for(i=0; i<n; i++,f++)
		if(off >= f->start && off < f->end)
			off = f->end;
	if(loff == off)
		goto uout;
	if(off < r->last)
		goto spanloop;

	memmove(mb->data, &pkt, Ensize+Ipsize);
	p = (Ippkt*)mb->data;
	hnputs(p->length, r->last+Ipsize);
	l = r->last + (Ensize+Ipsize);
	mb->count = l;
	r->mb = 0;
	unlock(&ip);

send:
	switch(pkt.proto) {
	default:
		mbfree(mb);
		break;

	case Ilproto:
		ilrecv(mb, ifc);
		break;
	}
	return;

uout:
	unlock(&ip);
}

ip.h            692755337   0     0     664     12425     `
typedef struct Ipconv	Ipconv;
typedef struct Ipifc	Ipifc;
typedef struct Fragq	Fragq;
typedef struct Ipfrag	Ipfrag;
typedef ulong		Ipaddr;
typedef ushort		Port;
typedef struct Udphdr	Udphdr;
typedef struct Etherhdr	Etherhdr;
typedef struct Reseq	Reseq;
typedef struct Tcp	Tcp;
typedef struct Tcpctl	Tcpctl;
typedef struct Tcphdr	Tcphdr;
typedef struct Timer	Timer;
typedef struct Ilhdr	Ilhdr;
typedef struct Ilcb	Ilcb;

struct Etherhdr
{
#define ETHER_HDR	14
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];

	/* Now we have the ip fields */
#define ETHER_IPHDR	20
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */
	uchar	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
};

/* Ethernet packet types */
#define ET_IP	0x0800

/* A userlevel data gram */
struct Udphdr
{
#define UDP_EHSIZE	22
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */

	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */

	/* Udp pseudo ip really starts here */
#define UDP_PHDRSIZE	12
#define UDP_HDRSIZE	20
	uchar	Unused;	
	uchar	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */
	uchar	udpsport[2];	/* Source port */
	uchar	udpdport[2];	/* Destination port */
	uchar	udplen[2];	/* data length */
	uchar	udpcksum[2];	/* Checksum */
};

struct Ilhdr
{
#define IL_EHSIZE	34
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */

	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */
	uchar	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
#define IL_HDRSIZE	18	
	uchar	ilsum[2];	/* Checksum including header */
	uchar	illen[2];	/* Packet length */
	uchar	iltype;		/* Packet type */
	uchar	ilspec;		/* Special */
	uchar	ilsrc[2];	/* Src port */
	uchar	ildst[2];	/* Dst port */
	uchar	ilid[4];	/* Sequence id */
	uchar	ilack[4];	/* Acked sequence */
};

struct Ilcb				/* Control block */
{
	int	state;			/* Connection state */

	QLock	ackq;			/* Unacknowledged queue */
	Block	*unacked;
	Block	*unackedtail;

	QLock	outo;			/* Out of order packet queue */
	Block	*outoforder;
	int	oblks;			/* Number of blocks in queue */

	ulong	next;			/* Id of next to send */
	ulong	recvd;			/* Last packet received */
	ulong	start;			/* Local start id */
	ulong	rstart;			/* Remote start id */

	int	timeout;		/* Time out counter */
	int	slowtime;		/* Slow time counter */
	int	fasttime;		/* Retransmission timer */
	int	acktime;		/* Acknowledge timer */

	int	rtt;			/* Average round trip time */
	ulong	rttack;			/* The ack we are waiting for */
	ulong	ackms;			/* Time we issued */

	int	window;			/* Maximum receive window */
};

enum					/* Packet types */
{
	Ilsync,
	Ildata,
	Ildataquery,
	Ilack,
	Ilquerey,
	Ilstate,
	Ilclose,
};

enum					/* Connection state */
{
	Ilclosed,
	Ilsyncer,
	Ilsyncee,
	Ilestablished,
	Illistening,
	Ilclosing,
};

#define TCP_PKT	(TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE)

struct Tcphdr
{
#define TCP_EHSIZE	14
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
#define TCP_IPLEN	8
	uchar	vihl;		/* Version and header length */
	uchar	tos;		/* Type of service */
	uchar	length[2];	/* packet length */
	uchar	id[2];		/* Identification */
	uchar	frag[2];	/* Fragment information */

#define TCP_PHDRSIZE	12	
	uchar	Unused;
	uchar	proto;
	uchar	tcplen[2];
	uchar	tcpsrc[4];
	uchar	tcpdst[4];

#define TCP_HDRSIZE	20
	uchar	tcpsport[2];
	uchar	tcpdport[2];
	uchar	tcpseq[4];
	uchar	tcpack[4];
	uchar	tcpflag[2];
	uchar	tcpwin[2];
	uchar	tcpcksum[2];
	uchar	tcpurg[2];

	/* Options segment */
	uchar	tcpopt[2];
	uchar	tcpmss[2];
};



struct Timer
{
	Timer	*next;
	Timer	*prev;
	int	state;
	int	start;
	int	count;
	void	(*func)(void*);
	void	*arg;
};

struct Tcpctl
{
	QLock;
	uchar	state;		/* Connection state */
	uchar	type;		/* Listening or active connection */
	uchar	code;		/* Icmp code */		
	struct {
		int una;	/* Unacked data pointer */
		int nxt;	/* Next sequence expected */
		int ptr;	/* Data pointer */
		ushort wnd;	/* Tcp send window */
		int up;		/* Urgent data pointer */
		int wl1;
		int wl2;
	} snd;
	int	iss;
	ushort	cwind;
	ushort	ssthresh;
	int	resent;
	struct {
		int nxt;
		ushort wnd;
		int up;
	} rcv;
	int	irs;
	ushort	mss;
	int	rerecv;
	ushort	window;
	int	max_snd;
	int	last_ack;
	char	backoff;
	char	flags;
	char	tos;

	Block	*rcvq;
	ushort	rcvcnt;

	Block	*sndq;			/* List of data going out */
	ushort	sndcnt;			/* Amount of data in send queue */

	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			 
	Timer	acktimer;		/* Acknoledge timer */
	Timer	rtt_timer;		/* Round trip timer */
	int	rttseq;			/* Round trip sequence */
	int	srtt;			/* Shortened round trip */
	int	mdev;			/* Mean deviation of round trip */
};

struct	Tcp
{
	Port	source;
	Port	dest;
	int	seq;
	int	ack;
	char	flags;
	ushort	wnd;
	ushort	up;
	ushort	mss;
};

struct Reseq
{
	Reseq 	*next;
	Tcp	seg;
	Block	*bp;
	ushort	length;
	char	tos;
};

/* An ip interface used for UDP/TCP/IL */
struct Ipconv
{
	QLock;				/* Ref count lock */
	int 	ref;
	int	index;
	Qinfo	*stproto;		/* Stream protocol for this device */
	Network	*net;			/* user level network interface */
	Ipaddr	dst;			/* Destination from connect */
	Port	psrc;			/* Source port */
	Port	pdst;			/* Destination port */

	Ipifc	*ipinterface;		/* Ip protocol interface */
	Queue	*readq;			/* Pointer to upstream read q */
	QLock	listenq;		/* List of people waiting incoming cons */
	Rendez	listenr;		/* Some where to sleep while waiting */
		
	char	err;			/* Async protocol error */
	int	backlog;		/* Maximum number of waiting connections */
	int	curlog;			/* Number of waiting connections */
	int 	newcon;			/* Flags that this is the start of a connection */

	union {
		Tcpctl	tcpctl;			/* Tcp control block */
		Ilcb	ilctl;			/* Il control block */
	};
};

#define	MAX_TIME	100000000	/* Forever */
#define TCP_ACK		200		/* Timed ack sequence every 200ms */

#define URG	0x20
#define ACK	0x10
#define PSH	0x08
#define RST	0x04
#define SYN	0x02
#define FIN	0x01

#define EOL_KIND	0
#define NOOP_KIND	1
#define MSS_KIND	2

#define MSS_LENGTH	4
#define MSL2		10
#define MSPTICK		200
#define DEF_MSS		1024
#define DEF_RTT		1000

#define TCP_PASSIVE	0
#define TCP_ACTIVE	1
#define IL_PASSIVE	0
#define IL_ACTIVE	1

#define MAXBACKOFF	5
#define FORCE		1
#define	CLONE		2
#define RETRAN		4
#define ACTIVE		8
#define SYNACK		16
#define AGAIN		8
#define DGAIN		4

#define TIMER_STOP	0
#define TIMER_RUN	1
#define TIMER_EXPIRE	2

#define Nreseq		64

#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	dur_timer(t)	((t)->start)
#define	read_timer(t)	((t)->count)
#define	run_timer(t)	((t)->state == TIMER_RUN)

enum
{
	CLOSED = 0,
	LISTEN,
	SYN_SENT,
	SYN_RECEIVED,
	ESTABLISHED,
	FINWAIT1,
	FINWAIT2,
	CLOSE_WAIT,
	CLOSING,
	LAST_ACK,
	TIME_WAIT
};

/*
 * Ip interface structure. We have one for each active protocol driver
 */
struct Ipifc 
{
	QLock;
	int 		ref;
	uchar		protocol;		/* Ip header protocol number */
	char		name[NAMELEN];		/* Protocol name */
	void (*iprcv)	(Ipconv *, Block *);	/* Receive demultiplexor */
	Ipconv		*connections;		/* Connection list */
	int		maxmtu;			/* Maximum transfer unit */
	int		minmtu;			/* Minumum tranfer unit */
	int		hsize;			/* Media header size */	
	Lock;	
};

struct Fragq
{
	QLock;
	Block  *blist;
	Fragq  *next;
	Ipaddr src;
	Ipaddr dst;
	ushort id;
};

struct Ipfrag
{
	ushort	foff;
	ushort	flen;
};

#define IP_VER	0x40			/* Using IP version 4 */
#define IP_HLEN 0x05			/* Header length in characters */
#define IP_DF	0x4000			/* Don't fragment */
#define IP_MF	0x2000			/* More fragments */

#define	ICMP_ECHOREPLY		0	/* Echo Reply */
#define	ICMP_UNREACH		3	/* Destination Unreachable */
#define	ICMP_SOURCEQUENCH	4	/* Source Quench */
#define	ICMP_REDIRECT		5	/* Redirect */
#define	ICMP_ECHO		8	/* Echo Request */
#define	ICMP_TIMXCEED		11	/* Time-to-live Exceeded */
#define	ICMP_PARAMPROB		12	/* Parameter Problem */
#define	ICMP_TSTAMP		13	/* Timestamp */
#define	ICMP_TSTAMPREPLY	14	/* Timestamp Reply */
#define	ICMP_IREQ		15	/* Information Request */
#define	ICMP_IREQREPLY		16	/* Information Reply */

/* Sizes */
#define IP_MAX		(32*1024)		/* Maximum Internet packet size */
#define UDP_MAX		(IP_MAX-ETHER_IPHDR)	/* Maximum UDP datagram size */
#define UDP_DATMAX	(UDP_MAX-UDP_HDRSIZE)	/* Maximum amount of udp data */
#define IL_DATMAX	(IP_MAX-IL_HDRSIZE)	/* Maximum IL data in one ip packet */

/* Protocol numbers */
#define IP_UDPPROTO	17
#define IP_TCPPROTO	6
#define	IP_ILPROTO	190		/* I have no idea */

/* Protocol port numbers */
#define PORTALLOC	5000		/* First automatic allocated port */
#define PRIVPORTALLOC	600		/* First priveleged port allocated */
#define PORTMAX		30000		/* Last port to allocte */

/* Stuff to go in funs.h someday */
Ipifc   *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*);
void	closeipifc(Ipifc*);
ushort	ip_csum(uchar*);
int	arp_lookup(uchar*, uchar*);
Ipaddr	ipparse(char*);
void	hnputs(uchar*, ushort);
void	hnputl(uchar*, ulong);
ulong	nhgetl(uchar*);
ushort	nhgets(uchar*);
ushort	ptcl_csum(Block*bp, int, int);
void	ppkt(Block*);
void	udprcvmsg(Ipconv *, Block*);
Block	*btrim(Block*, int, int);
Block	*ip_reassemble(int, Block*, Etherhdr*);
Ipconv	*portused(Ipconv *, Port);
Port	nextport(Ipconv *, Port);
Fragq   *ipfragallo(void);
void	ipfragfree(Fragq*);
void	iproute(uchar*, uchar*);
void	initfrag(int);
int	ntohtcp(Tcp*, Block**);
void	reset(Ipaddr, Ipaddr, char, ushort, Tcp*);
void	proc_syn(Ipconv*, char, Tcp*);
void	send_syn(Tcpctl*);
void	tcp_output(Ipconv*);
int	seq_within(int, int, int);
void	update(Ipconv *, Tcp *);
int	trim(Tcpctl *, Tcp *, Block **, ushort *);
void	add_reseq(Tcpctl *, char, Tcp *, Block *, ushort);
void	close_self(Ipconv *, int);
int	seq_gt(int, int);
Ipconv	*ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto);
void	ipmkdir(Qinfo *, Dirtab *, Ipconv *);
Ipconv	*ipincoming(Ipconv*, Ipconv*);
int	inb_window(Tcpctl *, int);
Block	*htontcp(Tcp *, Block *, Tcphdr *);
void	start_timer(Timer *);
void	stop_timer(Timer *);
int	copyupb(Block **, uchar *, int);
void	init_tcpctl(Ipconv *);
void	close_self(Ipconv *, int);
int	iss(void);
int	seq_within(int, int, int);
int	seq_lt(int, int);
int	seq_le(int, int);
int	seq_gt(int, int);
int	seq_ge(int, int);
void	setstate(Ipconv *, char);
void	tcpackproc(void*);
Block 	*htontcp(Tcp *, Block *, Tcphdr *);
int	ntohtcp(Tcp *, Block **);
void	extract_oob(Block **, Block **, Tcp *);
void	get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *);
void	state_upcall(Ipconv*, char oldstate, char newstate);
int	backoff(int);
int	dupb(Block **, Block *, int, int);
void	tcp_input(Ipconv *, Block *);
void 	tcprcvwin(Ipconv *);
void	tcpstart(Ipconv *, int, ushort, char);
void	ilstart(Ipconv *, int, int);
void	tcpflow(void*);
void 	tcp_timeout(void *);
void	tcp_acktimer(void *);
int	ipclonecon(Chan *);
int	iplisten(Chan *);
void	iloutoforder(Ipconv*, Ilhdr*, Block*);
void	iplocalfill(Chan*, char*, int);
void	ipremotefill(Chan*, char*, int);
void	ipstatusfill(Chan*, char*, int);
int	ipforme(uchar*);

#define	fmtaddr(xx)	(xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff
#define	MIN(a, b)	((a) < (b) ? (a) : (b))
#define MAX(a, b)	((a) > (b) ? (a) : (b))
#define BLKIP(xp)	((Etherhdr *)((xp)->rptr))
#define BLKFRAG(xp)	((Ipfrag *)((xp)->rptr))
#define PREC(x)		((x)>>5 & 7)

#define WORKBUF		64

extern Ipaddr Myip;
extern Ipaddr Mymask;
extern Ipaddr classmask[4];
extern Ipconv *ipconv[];
extern char *tcpstate[];
extern char *ilstate[];
extern Rendez tcpflowr;
extern Qinfo tcpinfo;
extern Qinfo ipinfo;
extern Qinfo udpinfo;
extern Qinfo ilinfo;
extern Qinfo arpinfo;
extern Queue *Ipoutput;

jaguar.c        715885162   0     0     664     10797     `
#include	"all.h"
#include	"io.h"


typedef struct Jag Jag;
typedef	struct Cmd Cmd;
typedef struct Ctlr Ctlr;

enum
{
	MaxJag		= 4,
};

struct	Cmd
{
	int 	flag;
	long	active;		/* toytime chan went active */
	int	retstat;
	QLock	ulock;		/* to get at each queue */
	Rendez	ren;
};

static
struct	Ctlr
{
	Vmedevice* vme;
	int	nqueue;
	QLock;			/* to get at controller */
	Cmd	cmd[NDRIVE];
	Drive	drive[NDRIVE];
} ctlr[MaxJag];

typedef	struct	Mcsb	Mcsb;
typedef	struct	Cqe	Cqe;
typedef	struct	Cib	Cib;
typedef	struct	Crb	Crb;
typedef	struct	Iopb	Iopb;

#define	NCMD	2				/* makes it easy to count mod 1 */
#define	IOTYPE1	((1<<10)|(2<<8)|(0x0b))		/* block mode, 32-bit, A32 block */
#define	IOTYPE2	((0<<10)|(2<<8)|(0x09))		/* normal mode, 32-bit, A32 nonpriv data */
#define	IOTYPE3	((0<<10)|(1<<8)|(0x09))		/* normal mode, 16-bit, A32 nonpriv data */
#define	IOTYPE	IOTYPE1
#define	GREG	0x40				/* its all greg's fault */

struct	Mcsb
{
	ushort	msr;
		#define	CNA	(1<<0)
		#define	BOK	(1<<1)
		#define	QFC	(1<<2)
	ushort	mcr;
		#define	SQM	(1<<0)
		#define	FLQR	(1<<2)
		#define	FLQ	(1<<11)
		#define	RES	(1<<12)
		#define	SFEN	(1<<13)
	ushort	iqar;
		#define	IQHE	(1<<14)		/* interrupt on q half empty */
		#define	IQEA	(1<<15)		/* interrupt on q entry available */
	ushort	qhp;
	ushort	twqr;
	ushort	pad1[3];
};

struct	Cqe
{
	ushort	qecr;
		#define	GO	(1<<0)
		#define	AA	(1<<1)
		#define	HPC	(1<<2)
	ushort	iopba;
	ushort	tag[2];
	ushort	qnumb;
	ushort	pad2;
};

struct	Iopb
{
	ushort	command;
	ushort	options;
		#define	IE	(1<<0)
	ushort	retstat;
	ushort	pad4;
	ushort	intvec;
	ushort	intlev;
	ushort	pad5;
	ushort	iotype;
	ushort	addr[2];
	ushort	size[2];
	ushort	pad6[2];
		#define	NIOB	(18*2)				/* size to here */
	union
	{
		/* for initialize work queue */
		struct
		{
			ushort	wqnumb;
			ushort	wqoptions;
				#define	AE	(1<<0)
				#define	FZE	(1<<2)
				#define	PE	(1<<3)
				#define	IWQ	(1<<15)
			ushort	wqslots;
			ushort	wqpriority;
				#define	WIOB	(NIOB+4*2)	/* size to here */
		};
		/* for scsi operations */
		struct
		{
			ushort	pad7;
			ushort	unit;
				#define	SIOB	(NIOB+2*2)	/* size to here */
			uchar	param[64-SIOB];			/* total size is 64 */
		};
	};
};

struct	Crb
{
	ushort	crsw;
		#define	CRBV	(1<<0)
		#define	CC	(1<<1)
		#define	ER	(1<<2)
		#define	EX	(1<<3)
		#define	AQ	(1<<4)
		#define	QMS	(1<<5)
		#define	CQA	(1<<6)
	ushort	pad8;
	ushort	tag[2];
	ushort	qnumb;
	ushort	pad9;
	Iopb;
};

struct	Cib
{
	ushort	nqueue;
	ushort	dmacount;
	ushort	normvec;
	ushort	errvec;
	ushort	priscsi;
	ushort	secscsi;
	ushort	crba;
	ushort	scsitim1[2];
	ushort	scsitim2[2];
	ushort	vmetim[2];
	ushort	pada[3];
};

struct	Jag
{
	Mcsb;
	Cqe	cmd[NCMD];			/* command queue, zeroth is mqe */
	Iopb	iopb[NCMD];			/* io parameter blocks 1:1 with cqe's */
	Cib	cib;
	char	padb[2048
			-sizeof(Mcsb)
			-NCMD*sizeof(Cqe)
			-NCMD*sizeof(Iopb)
			-sizeof(Cib)
			-sizeof(Crb)
			-120];
	Crb;
	uchar	css[120];			/* controller space */
};

static	void	cmd_statj(int, char *[]);

static
int
jagstat(Jag *jag)
{
	int c, i, s;

	c = 0;	/* set */
	for(i=100000000; i>0; i--) {
		c = jag->crsw;
		if(c & CRBV)
			break;
	}
	if(i == 0)
		print("jagstat timeout\n");
	s = jag->retstat;
	if((c & (CRBV|CC|ER)) != (CRBV|CC)) {
		if(s == 0)
			s = GREG;
		if(c & QMS)
			s = 0;
		if(0 && s)
			print("crsw = %.4ux; error = %.4ux\n", c, s);
	}
	jag->crsw = 0;
	return s;
}

static
int
jagcmd(Jag *jag)
{
	Cqe *cq;
	int c, i;

	cq = &jag->cmd[0];

	cq->qecr = GO;
	c = 0; /* set */
	for(i=0; i<1000000; i++) {
		c = cq->qecr;
		if(!(c & GO))
			break;
	}
	if(c & GO)
		print("go didnt fall: qcer = %.4ux\n", c);
	c = jagstat(jag);
	return c;
}

static
void
hclear(void *ap, int n)
{
	ushort *p;

	p = ap;
	while(n > 0) {
		*p++ = 0;
		n -= sizeof(ushort);
	}
}

Drive*
scsidrive(Device d)
{
	Ctlr *ctl;

	if(d.ctrl >= MaxJag || d.unit >= NDRIVE)
		return 0;
	ctl = &ctlr[d.ctrl];
	if(ctl->vme == 0)
		return 0;
	ctl->drive[d.unit].dev = d;
	return &ctl->drive[d.unit];
}

int
jaginit(Vmedevice *vp)
{
	Jag *jag;
	Cqe *cq;
	Cib *cb;
	Iopb *ib;
	Cmd *cp;
	int i, j, c, reset;
	Ctlr *ctl;

	print("jaginit %d\n", vp->ctlr);
	if(vp->ctlr >= MaxJag)
		return -1;
	ctl = &ctlr[vp->ctlr];
	if(ctl->vme)
		return -1;
	jag = vp->address;

	/*
	 * does the board exist?
	if(probe(&jag->msr, sizeof(jag->msr)))
		return -1;
	 */
	vp->private = ctl;
	reset = 0;

loop:
	memset(ctl, 0, sizeof(Ctlr));
	ctl->vme = vp;
	qlock(ctl);
	ctl->name = "jagc";
	qunlock(ctl);
	for(i=0; i<NDRIVE; i++) {
		cp = &ctl->cmd[i];
		lock(&cp->ren);
		unlock(&cp->ren);
		qlock(&cp->ulock);
		cp->ulock.name = "jagu";
		qunlock(&cp->ulock);
	}

	/*
	 * reset
	 */
	if(reset) {
		print("	reset\n");
		jag->mcr = RES;
		for(i=0; i<50*200; i++)
			;			/* reset for >50us */
		jag->msr = 0;
	}
	jag->mcr = 0;

	c = 0;	/* set */
	for(i=0; i<10000000; i++) {
		c = jag->msr & (BOK|CNA);
		if(c == BOK)
			break;
	}
	if(c != BOK) {
		print("	jaguar BOK msr = %.4ux\n", jag->msr);
		goto again;
	}

	cq = &jag->cmd[NCMD];
	ib = &jag->iopb[NCMD];
	for(i=0; i<NCMD; i++) {
		cq--;
		ib--;
		hclear(cq, sizeof(Cqe));
		cq->iopba = (char*)ib - (char*)jag;
	}

	cb = &jag->cib;

	/*
	 * initialize
	 */
	print("	init controller\n");
	hclear(ib, sizeof(Iopb));
	ib->command = 0x41;				/* init controller */
	ib->iotype = (0<<10) | (3<<8) | 0;
	ib->addr[1] = (char*)cb - (char*)jag;
	ib->size[1] = sizeof(Cib);

	hclear(cb, sizeof(Cib));
	cb->nqueue = NCMD-1;
	cb->priscsi = (0<<3)|7;				/* jag gets scsi id 7 */
	cb->secscsi = (0<<3)|7;				/* on both busses */
	cb->scsitim1[1] = 10;				/* 10 ms to connect */
	cb->scsitim2[1] = 100;				/* 3.2s to reconnect */
	cb->crba = (char*)&jag->crsw - (char*)jag;
	cb->normvec = (vp->irq<<8) | vp->vector;
	cb->errvec = (vp->irq<<8) | vp->vector;

	cq->qnumb = (NIOB<<8) | 0;			/* size and queue num */
	if(jagcmd(jag)) {
		print("	jag initialize controller\n");
		goto again;
	}

	/*
	 * scsi status command
	 */
	for(i=0; i<NDRIVE; i++) {
		if((i & 7) == 7) {
			ctl->drive[i].status = Dself;	/* sid is controller */
			continue;
		}
		hclear(ib, sizeof(Iopb));
		ib->command = 0x20;			/* scsi pass thru */
		ib->iotype = IOTYPE;
		ib->unit = ((i>>3)<<6)|(0<<3)|(i&7);	/* bus,lun,sid */
		ib->param[0] = 0x00;			/* status scsi cmd */
		cq->qnumb = ((SIOB + 6)<<8) | 0;	/* size and queue num */
		c = jagcmd(jag);

		ctl->drive[i].status = Dready;
		if(c == 0)
			continue;
		if((c & 0xff) == 0) {			/* drive error, jag ok */
			for(j=0; j<10; j++) {
				c = jagcmd(jag);	/* try again to clear sense */
				if(c == 0)
					break;
			}
			if(c != 0) {
				print("jag drive %d status 0x%x\n", i, c);
				ctl->drive[i].status = Dnready;
			}
			continue;
		}
		ctl->drive[i].status = Dunavail;	/* jag error, probably timeout */
	}

	/*
	 * initialize work queues
	 */
	print("	init queues\n");
	ctl->nqueue = 0;
	for(i=0; i<NDRIVE; i++) {
		if(ctl->drive[i].status == Dself)
			continue;
		ctl->nqueue++;
		ctl->drive[i].qnum = ctl->nqueue;
		hclear(ib, sizeof(Iopb));
		ib->command = 0x42;			/* initialize work queue */
		ib->wqnumb = ctl->nqueue;		/* queue number */
		ib->wqoptions = IWQ|PE;			/* really do it, honest */
		ib->wqslots = 10;			/* number of slots */
		ib->wqpriority = 14;			/* priority */
		cq->qnumb = (WIOB<<8) | 0;		/* size and queue num */
		if(jagcmd(jag)) {
			print("	jag queue: %.2o\n", i);
			goto again;
		}
	}
	jag->mcr = SQM;
	if(jagstat(jag)) {
		print("	start queue mode\n");
		goto again;
	}
	if(vp->ctlr == 0) {
		cmd_install("statj", "-- jaguar stats", cmd_statj);
	}
	return 0;

again:
	/*
	 * make 2 passes, first without resetting
	 * the jaguar cpu. this will prevent a
	 * scsi init which makes the sony worm
	 * go cata for 90 sec.
	 */
	if(reset)
		panic("jag init");
	reset = 1;
	goto loop;
}

void
jagintr(Vmedevice *vp)
{
	Jag *jag;
	Cmd *cp;
	int s, t;

	jag = vp->address;
	s = jag->retstat;
	t = jag->tag[0];
	if(jag->crsw != 3 || s != 0)
		print("bad jag intr %.4ux %.4ux\n", jag->crsw, s);
	jag->crsw = 0;

	if(t < 0 || t >= NDRIVE) {
		print("bad jag tag %d\n", t);
		t = 0;
	}
	cp = &(((Ctlr*)vp->private)->cmd[t]);
	if(cp->flag)
		print("jag intr flag set\n");
	cp->retstat = s;
	cp->flag = 1;
	wakeup(&cp->ren);
}

void
pokewcp(void)
{
	int c, u;
	long t, a;
	Ctlr *ctl;
	Cmd *cp;

	t = toytime() - SECOND(60);
	for(c=0; c<MaxJag; c++) {
		ctl = &ctlr[c];
		if(ctl->vme == 0)
			continue;
		for(u=0; u<NDRIVE; u++) {
			cp = &ctl->cmd[u];
			a = cp->active;
			if(a != 0 && a < t) {
				cp->active = toytime();		/* small race */
				if(cp->flag)
					print("jaguar timeout and flag set\n");
				else
					print("jaguar timeout\n");
				cp->retstat = GREG;
				cp->flag = 1;
				wakeup(&cp->ren);
			}
		}
	}
}

static int
jflag(void *v)
{
	return ((struct Cmd*)v)->flag;
}

int
scsiio(Device d, int rw, uchar *param, int nparam, void *addr, int size)
{
	Jag *jag;
	Cqe *cq;
	Iopb *ib;
	Drive *dr;
	Cmd *cp;
	ulong a, syspa;
	int i, s;
	Vmedevice *vp;
	Ctlr *ctl;
	static int uniq;

	if(d.ctrl >= MaxJag)
		return GREG;
	ctl = &ctlr[d.ctrl];
	vp = ctl->vme;
	if(vp == 0)
		return GREG;
	cp = &ctl->cmd[d.unit];

	qlock(&cp->ulock);
	qlock(ctl);

	jag = vp->address;
	ib = &jag->iopb[1];

	hclear(ib, sizeof(Iopb));
	ib->command = 0x20;				/* scsi */
	ib->options = (rw<<8) | IE;
	ib->intvec = (vp->vector<<8) | vp->vector;
	ib->intlev = vp->irq;
	ib->iotype = IOTYPE;

	syspa = vme2sysmap(vp->bus, (ulong)addr, size);
	wbackcache(addr, size);
	invalcache(addr, size);

	ib->addr[0] = syspa >> 16;
	ib->addr[1] = syspa;
	a = size;
	ib->size[0] = a >> 16;
	ib->size[1] = a;

	ib->unit = ((d.unit>>3)<<6) | ((param[1]>>5)<<3) | (d.unit&7);
	for(i=0; i<nparam; i++)
		ib->param[i] = param[i];

	cp->flag = 0;
	cp->active = toytime();
	cq = &jag->cmd[1];
	cq->qnumb = ((SIOB + nparam)<<8) | ctl->drive[d.unit].qnum;
	cq->tag[0] = d.unit;
	cq->qecr = GO;

	dr = &ctl->drive[d.unit];
	if(dr->fflag == 0) {
		dofilter(&dr->rate);
		dofilter(&dr->work);
		dr->fflag = 1;
	}
	dr->work.count++;
	dr->rate.count += size;

	while(cq->qecr & GO)
		;
	qunlock(ctl);

	while(!jflag(cp))
		sleep(&cp->ren, jflag, cp);

	vme2sysfree(vp->bus, syspa, size);
	if(rw == SCSIread)
		invalcache(addr, size);

	s = cp->retstat;
	cp->active = 0;
	qunlock(&cp->ulock);
	return s;
}

static
void
stats(Ctlr *ctl)
{
	Drive *dr;
	int i, s;

	print("jaguar stats %d\n", ctlr - ctl);
	for(i=0; i<NDRIVE; i++) {
		dr = &ctl->drive[i];
		if(dr->fflag == 0)
			continue;
		s = dr->status;
		if(s != Dready)
			print("	drive %.2o (%d)\n", i, s);
		else
			print("	drive %.2o = %10ld\n", i, dr->max);
		print("		work = %F\n", (Filta){&dr->work, 1});
		print("		rate = %F tBps\n", (Filta){&dr->rate, 1000});
	}
}

static
void
cmd_statj(int argc, char *argv[])
{
	Ctlr *ctl;
	int i;

	USED(argc);
	USED(argv);

	for(i=0; i<MaxJag; i++) {
		ctl = &ctlr[i];
		if(ctl->vme)
			stats(ctl);
	}
}

junk.c          692664718   0     0     664     4467      `
#include	"all.h"

void*
recv(Queue *q, int tim)
{
	User *p;
	void *a;
	int i, c;

	if(q == 0)
		panic("recv null q");
loop:
	lock(q);
	c = q->count;
	if(c > 0) {
		i = q->loc;
		a = q->args[i];
		i++;
		if(i >= q->size)
			i = 0;
		q->loc = i;
		q->count = c-1;
		p = q->whead;
		if(p) {
			q->whead = p->qnext;
			if(q->whead == 0)
				q->wtail = 0;
			ready(p);
		}
		unlock(q);
		return a;
	}
	p = q->rtail;
	if(p == 0)
		q->rhead = u;
	else
		p->qnext = u;
	q->rtail = u;
	u->qnext = 0;
	u->state = Recving;
	unlock(q);
	sched();
	goto loop;
}

void
send(Queue *q, void *a)
{
	User *p;
	int i, c;

	if(q == 0)
		panic("send null q");
loop:
	lock(q);
	c = q->count;
	if(c < q->size) {
		i = q->loc + c;
		if(i >= q->size)
			i -= q->size;
		q->args[i] = a;
		q->count = c+1;
		p = q->rhead;
		if(p) {
			q->rhead = p->qnext;
			if(q->rhead == 0)
				q->rtail = 0;
			ready(p);
		}
		unlock(q);
		return;
	}
	p = q->wtail;
	if(p == 0)
		q->whead = u;
	else
		p->qnext = u;
	q->wtail = u;
	u->qnext = 0;
	u->state = Sending;
	unlock(q);
	sched();
	goto loop;
}

Queue*
newqueue(int size)
{
	Queue *q;

	q = ialloc(sizeof(Queue) + (size-1)*sizeof(void*), 0);
	q->size = size;
	lock(q);
	unlock(q);
	return q;
}

no(void *a)
{

	USED(a);
	return 0;
}

int
devread(Device a, long b, void *c)
{

	switch(a.type)
	{
	case Devcw:
		return cwread(a, b, c);

	case Devro:
		return roread(a, b, c);

	case Devwren:
		return wrenread(a, b, c);

	case Devworm:
		return wormread(a, b, c);

	case Devfworm:
		return fwormread(a, b, c);

	case Devmworm:
		return mwormread(a, b, c);

	case Devmwren:
		return mwrenread(a, b, c);
	}
	panic("illegal device in read: %D %ld", a, b);
	return 1;
}

int
devwrite(Device a, long b, void *c)
{

	switch(a.type)
	{
	case Devcw:
		return cwwrite(a, b, c);

	case Devro:
		print("write to ro device %D(%ld)\n", a, b);
		return 1;

	case Devwren:
		return wrenwrite(a, b, c);

	case Devworm:
		return wormwrite(a, b, c);

	case Devfworm:
		return fwormwrite(a, b, c);

	case Devmworm:
		return mwormwrite(a, b, c);

	case Devmwren:
		return mwrenwrite(a, b, c);
	}
	panic("illegal device in write: %D %ld", a, b);
	return 1;
}

long
devsize(Device d)
{

	switch(d.type)
	{
	case Devcw:
	case Devro:
		return cwsize(d);

	case Devwren:
		return wrensize(d);

	case Devworm:
		return wormsize(d);

	case Devfworm:
		return fwormsize(d);

	case Devmworm:
		return mwormsize(d);

	case Devmwren:
		return mwrensize(d);
	}
	panic("illegal device in dev_size: %D", d);
	return 0;
}

long
superaddr(Device d)
{

	switch(d.type) {
	default:
		return SUPER_ADDR;

	case Devcw:
	case Devro:
		return cwsaddr(d);
	}
}

long
getraddr(Device d)
{

	switch(d.type) {
	default:
		return ROOT_ADDR;

	case Devcw:
		return cwraddr(d, 0);

	case Devro:
		return cwraddr(d, 1);
	}
}

void
sysinit(void)
{
	Filsys *fs;
	int i;
	char c[20];

	dofilter(&cons.work);
	dofilter(&cons.rate);
	dofilter(&cons.bhit);
	dofilter(&cons.bread);
	dofilter(&cons.brahead);
	dofilter(&cons.binit);
	cons.chan = chaninit(Devcon, 1);
	for(fs=filsys; fs->name; fs++) {
		print("sysinit: %s\n", fs->name);
		if(fs->flags & FREAM)
			devream(fs->dev);
		if(fs->flags & FRECOVER)
			devrecover(fs->dev);
		devinit(fs->dev);
	}
}

void
devream(Device d)
{
	int i;
	Device wdev;

	print("ream: %D\n", d);
	switch(d.type) {
	default:
	bad:
		print("ream: unknown dev type %D\n", d);
		return;

	case Devcw:
		wdev = WDEV(d);
		if(wdev.type == Devfworm)
			fwormream(wdev);
		wlock(&mainlock);	/* ream cw */
		cwream(d);
		wunlock(&mainlock);
		break;

	case Devmwren:
	case Devmworm:
		for(i=d.unit; i<d.part; i++)
			if(cwdevs[i].type != Devwren)
				goto bad;

	case Devwren:
		devinit(d);
		wlock(&mainlock);	/* ream wren */
		rootream(d, ROOT_ADDR);
		superream(d, SUPER_ADDR);
		wunlock(&mainlock);
		break;
	}
}

void
devrecover(Device d)
{

	print("recover: %D\n", d);
	switch(d.type) {
	default:
		print("recover: unknown dev type %D\n", d);
		return;

	case Devcw:
		wlock(&mainlock);	/* recover */
		cwrecover(d);
		wunlock(&mainlock);
		break;
	}
}

void
devinit(Device d)
{

	print("	devinit %D\n", d);
	switch(d.type) {
	default:
		print("devinit unknown device %D\n", d);

	case Devcw:
		cwinit(d);
		break;

	case Devro:
		break;	/* always paired with cw */

	case Devwren:
		wreninit(d);
		break;

	case Devworm:
		worminit(d);
		break;

	case Devfworm:
		fworminit(d);
		break;

	case Devmworm:
		mworminit(d);
		break;

	case Devmwren:
		mwreninit(d);
		break;
	}
}

lib.h           701463095   0     0     664     1357      `
/*
 * functions (possibly) linked in, complete, from libc.
 */

/*
 * mem routines
 */
extern	void*	memset(void*, int, long);
extern	int	memcmp(void*, void*, long);
extern	void*	memmove(void*, void*, long);
extern	void*	memchr(void*, int, long);

/*
 * string routines
 */
extern	char*	strcat(char*, char*);
extern	char*	strchr(char*, char);
extern	int	strcmp(char*, char*);
extern	char*	strcpy(char*, char*);
extern	char*	strncat(char*, char*, long);
extern	char*	strncpy(char*, char*, long);
extern	int	strncmp(char*, char*, long);
extern	long	strlen(char*);

/*
 * utf routines
 */
extern	char*	utfrune(char*, long);
extern	char*	utfrrune(char*, long);

/*
 * print routines
 */

#define	FLONG	(1<<0)
#define	FSHORT	(1<<1)
#define	FUNSIGN	(1<<2)

typedef struct Op	Op;
struct Op
{
	char	*p;
	char	*ep;
	void	*argp;
	int	f1;
	int	f2;
	int	f3;
};
extern	void	strconv(char*, Op*, int, int);
extern	int	numbconv(Op*, int);
extern	char*	donprint(char*, char*, char*, void*);
extern	int	fmtinstall(char, int(*)(Op*));
extern	int	sprint(char*, char*, ...);
extern	int	print(char*, ...);

/*
 * one-of-a-kind
 */
extern	void	qsort(void*, long, long, int (*)(void*, void*));
extern	char	end[];

#define DESKEYLEN	7		/* length of a des key for encrypt/decrypt */
extern	int	decrypt(void*, void*, int);
extern	int	encrypt(void*, void*, int);
extern	int	nrand(int);

main.c          716086871   0     0     664     5928      `
#include	"all.h"
#include	"mem.h"
#include	"io.h"
#include	"ureg.h"

void
machinit(void)
{
	int n;

	n = m->machno;
	memset(m, 0, sizeof(Mach));
	m->machno = n;
	m->mmask = 1<<m->machno;
	m->lights = 0;

	active.exiting = 0;
	active.machs = 1;
}

static
void
confinit(void)
{
	conf.nmach = 2;
	conf.nproc = 45;

	conf.mem = meminit();
	conf.sparemem = conf.mem/12;		/* 8% spare for chk etc */

	conf.nalarm = 200;
	conf.nuid = 1000;
	conf.nserve = 15;
	conf.nrahead = 3;
	conf.nfile = 30000;
	conf.nlgmsg = 100;
	conf.nsmmsg = 500;
	conf.wcpsize = 170*6*1024 + 500;

	conf.nwpath = conf.nfile*8;
	conf.gidspace = conf.nuid*3;

	cons.flags = 0;
}

void
main(void)
{
	int i;

	echo = 1;
	predawn = 1;
		formatinit();
		machinit();
		vecinit();
		confinit();
		lockinit();
		printinit();
		procinit();
		clockinit();
		alarminit();

		mainlock.wr.name = "mainr";
		mainlock.rd.name = "mainw";
		reflock.name = "ref";

		qlock(&reflock);
		qunlock(&reflock);
		serveq = newqueue(1000);
		raheadq = newqueue(1000);
		authreply = newqueue(Nqueue);

		mbinit();

		otherinit();

		files = ialloc(conf.nfile * sizeof(*files), 0);
		for(i=0; i<conf.nfile; i++) {
			qlock(&files[i]);
			files[i].name = "file";
			qunlock(&files[i]);
		}

		wpaths = ialloc(conf.nwpath * sizeof(*wpaths), 0);
		uid = ialloc(conf.nuid * sizeof(*uid), 0);
		gidspace = ialloc(conf.gidspace * sizeof(*gidspace), 0);

		print("iobufinit\n");
		iobufinit();

		arginit();

		userinit(touser, 0, "ini");

	predawn = 0;
		launchinit();
		schedinit();
}

/*
 * read ahead processes.
 * read message from q and then
 * read the device.
 */
void
rahead(void)
{
	Rabuf *rb;
	Iobuf *p;

loop:
	rb = recv(raheadq, 0);
	if(rb == 0)
		goto loop;
	p = getbuf(rb->dev, rb->addr, Bread);
	if(p)
		putbuf(p);

	lock(&rabuflock);
	rb->link = rabuffree;
	rabuffree = rb;
	unlock(&rabuflock);
	goto loop;
}

/*
 * main filesystem server loop.
 * entered by many processes.
 * they wait for message buffers and
 * then process them.
 */
void
serve(void)
{
	Fcall fi, fo;
	Msgbuf *mb, *mb1;
	Chan *cp;
	int t, n;

loop:
	qlock(&reflock);
	mb = recv(serveq, 0);
	cp = mb->chan;
	rlock(&cp->reflock);
	qunlock(&reflock);

	rlock(&mainlock);

	/*
	 * conversion to structure and
	 * simple syntax checks.
	 */
	if(convM2S(mb->data, &fi, mb->count) == 0) {
		print("bad M2S conversion\n");
		goto error;
	}
	t = fi.type;

	if(t < 0 || t >= MAXSYSCALL || (t&1) || !p9call[t]) {
		print("bad message type\n");
		goto error;
	}

	/*
	 * allocate reply message
	 */
	if(t == Tread) {
		mb1 = mballoc(MAXMSG+MAXDAT, cp, Mbreply2);
		fo.data = mb1->data + 8;
	} else
		mb1 = mballoc(MAXMSG, cp, Mbreply3);

	/*
	 * call the file system
	 */
	cons.work.count++;
	cons.rate.count += mb->count;
	fo.err = 0;

	(*p9call[t])(cp, &fi, &fo);

	fo.type = t+1;
	fo.tag = fi.tag;

	if(fo.err) {
		if(CHAT(cp))
			print("	error: %s\n", errstr[fo.err]);
		fo.type = Rerror;
		strncpy(fo.ename, errstr[fo.err], sizeof(fo.ename));
	}

	n = convS2M(&fo, mb1->data);
	if(n == 0) {
		print("bad S2M conversion\n");
		mbfree(mb1);
		goto error;
	}
	mb1->count = n;
	mb1->param = mb->param;
	cons.rate.count += n;
	send(cp->reply, mb1);

out:
	mbfree(mb);
	runlock(&mainlock);
	runlock(&cp->reflock);
	goto loop;

error:
	print("type=%d count=%d\n", mb->data[0], mb->count);
	print(" %.2x %.2x %.2x %.2x\n",
		mb->data[1]&0xff, mb->data[2]&0xff,
		mb->data[3]&0xff, mb->data[4]&0xff);
	print(" %.2x %.2x %.2x %.2x\n",
		mb->data[5]&0xff, mb->data[6]&0xff,
		mb->data[7]&0xff, mb->data[8]&0xff);
	print(" %.2x %.2x %.2x %.2x\n",
		mb->data[9]&0xff, mb->data[10]&0xff,
		mb->data[11]&0xff, mb->data[12]&0xff);
	mb1 = mballoc(3, cp, Mbreply4);
	mb1->data[0] = Rnop;	/* your nop was ok */
	mb1->data[1] = ~0;
	mb1->data[2] = ~0;
	mb1->count = 3;
	mb1->param = mb->param;
	send(cp->reply, mb1);
	goto out;
}

void
init0(void)
{
	m->proc = u;
	u->state = Running;
	u->mach = m;
	spllo();

	(*u->start)();
}

void*
getarg(void)
{

	return u->arg;
}

void
exit(void)
{
	u = 0;
	lock(&active);
	active.machs &= ~(1<<m->machno);
	active.exiting = 1;
	unlock(&active);
	if(!predawn)
		spllo();
	print("cpu %d exiting\n", m->machno);
	while(active.machs)
		delay(1);
	delay(300);	/* time to drain print q */
	splhi();
	duartreset();
	firmware();
}

/*
 * process to copy dump blocks from
 * cache to worm. it runs flat out when
 * it gets work, but only looks for
 * work every 10 seconds.
 */
static	Rendez	wcp;

static
void
callwc(Alarm *a, void *arg)
{

	USED(arg);
	cancel(a);
	wakeup(&wcp);
}

#define	DUMPTIME	5	/* 5 am */
#define	WEEKMASK	0	/* every day (1=sun, 2=mon 4=tue) */

void
wormcopy(void)
{
	int f;
	Filsys *fs;
	long nddate, t, dt;

recalc:
	/*
	 * calculate the next dump time.
	 * minimum delay is 100 minutes.
	 */
	t = time();
	nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
	print("next dump at %T\n", nddate);

loop:
	dt = time() - t;
	if(dt < 0) {
		print("time went back\n");
		goto recalc;
	}
	if(dt > MINUTE(100)) {
		print("time jumped ahead\n");
		goto recalc;
	}
	t += dt;
	f = 0;

	if(!f) {
		if(t > nddate) {
			print("automatic dump %T\n", t);
			for(fs=filsys; fs->name; fs++)
				if(fs->dev.type == Devcw)
					cfsdump(fs);
			goto recalc;
		}
	}

	rlock(&mainlock);
	for(fs=filsys; fs->name; fs++)
		if(fs->dev.type == Devcw)
			f |= dumpblock(fs->dev);
	runlock(&mainlock);

	if(!f)
		f = dowcp();

	if(!f)
		f = dowcmp();

	if(!f) {
		alarm(10000, callwc, 0);
		sleep(&wcp, no, 0);
	}

	wormprobe();
	goto loop;
}

/*
 * process to synch blocks
 * it puts out a block/line every second
 * it waits 10 seconds if caught up.
 * in both cases, it takes about 10 seconds
 * to get up-to-date.
 */
static	Rendez	scp;

static
void
callsc(Alarm *a, void *arg)
{

	USED(arg);
	cancel(a);
	wakeup(&scp);
}

void
synccopy(void)
{
	int f;

loop:
	rlock(&mainlock);
	f = syncblock();
	runlock(&mainlock);
	alarm(f?1000: 10000, callsc, 0);
	sleep(&scp, no, 0);
	pokewcp();
	goto loop;
}
mworm.c         715911244   0     0     664     2535      `
#include	"all.h"

/*
 * multiple cat devices
 */
void
mcatinit(Device dev)
{
	int lb, hb;

	print("mcat init\n");
	lb = dev.unit;
	hb = dev.part;
	while(lb < hb) {
		devinit(cwdevs[lb]);
		lb++;
	}
}

long
mcatsize(Device dev)
{
	int lb, hb;
	long l;

	lb = dev.unit;
	hb = dev.part;
	l = 0;
	while(lb < hb) {
		l += devsize(cwdevs[lb]);
		lb++;
	}
	return l;
}

int
mcatread(Device dev, long b, void *c)
{
	int lb, hb;
	long l, m;

	lb = dev.unit;
	hb = dev.part;
	l = 0;
	while(lb < hb) {
		m = devsize(cwdevs[lb]);
		if(b < l+m)
			return devread(cwdevs[lb], b-l, c);
		l += m;
		lb++;
	}
	print("mcatread %ld %ld", b, lb);
	return 1;
}

int
mcatwrite(Device dev, long b, void *c)
{
	int lb, hb;
	long l, m;

	lb = dev.unit;
	hb = dev.part;
	l = 0;
	while(lb < hb) {
		m = devsize(cwdevs[lb]);
		if(b < l+m)
			return devwrite(cwdevs[lb], b-l, c);
		l += m;
		lb++;
	}
	print("mcatwrite %ld %ld", b, lb);
	return 1;
}

/*
 * multiple interleave devices
 */
void
mlevinit(Device dev)
{
	int lb, hb;

	print("mlev init\n");
	lb = dev.unit;
	hb = dev.part;
	while(lb < hb) {
		devinit(cwdevs[lb]);
		lb++;
	}
}

long
mlevsize(Device dev)
{
	int lb, hb, n;
	long l, min;

	lb = dev.unit;
	hb = dev.part;
	n = hb-lb;
	min = 0;
	while(lb < hb) {
		l = devsize(cwdevs[lb]);
		if(min == 0 || l < min)
			min = l;
		lb++;
	}
	return n * min;
}

int
mlevread(Device dev, long b, void *c)
{
	int lb, hb, n;

	lb = dev.unit;
	hb = dev.part;
	n = hb-lb;
	return devread(cwdevs[lb+b%n], b/n, c);
}

int
mlevwrite(Device dev, long b, void *c)
{
	int lb, hb, n;

	lb = dev.unit;
	hb = dev.part;
	n = hb-lb;
	return devwrite(cwdevs[lb+b%n], b/n, c);
}

/*
 * partition device
 */
void
partinit(Device dev)
{
	Device d;

	print("part init\n");
	d = cwdevs[dev.ctrl];
	devinit(d);
}

long
partsize(Device dev)
{
	long size, l;
	Device d;

	d = cwdevs[dev.ctrl];
	l = devsize(d) / 100;
	size = dev.part * l;
	if(size == 0)
		size = l*100;
	return size;
}

int
partread(Device dev, long b, void *c)
{
	long base, size, l;
	Device d;

	d = cwdevs[dev.ctrl];
	l = devsize(d) / 100;
	base = dev.unit * l;
	size = dev.part * l;
	if(size == 0)
		size = l*100;
	if(b < size)
		return devread(d, base+b, c);
	print("partread %ld %ld\n", b, size);
	return 1;
}

int
partwrite(Device dev, long b, void *c)
{
	long base, size, l;
	Device d;

	d = cwdevs[dev.ctrl];
	l = devsize(d) / 100;
	base = dev.unit * l;
	size = dev.part * l;
	if(size == 0)
		size = l*100;
	if(b < size)
		return devwrite(d, base+b, c);
	print("partwrite %ld %ld\n", b, size);
	return 1;
}

nonet.c         694206612   0     0     664     6231      `
#include "all.h"

/*
 * Nn stuff
 */
enum
{
	Cksum		= 1,
	Nlan		= 40,

	Fnewcall	= 0x1,	/* flag bit marking a new circuit */
	Fhangup		= 0x2,	/* flag bit requesting hangup */
	Fackme		= 0x4,	/* acknowledge this message */
	Fservice	= 0x8,	/* message includes a service name */
	Freset		= 0x10,	/* reset the connection */
	Fnull		= 0x20,	/* null message */
};

static
struct
{
	Lock;
	Queue*	reply;
	Chan*	lcp;
} nn;

static	Chan*	getchan(Ifc*, Nnpkt*);
static	int	checksum(Nnpkt*, int);
static	void	initchan(Chan*);
static	void	nonetout(void);

static
int
checksum(Nnpkt *p, int l)
{
	uchar *sp, *ep;
	ulong s;

	p->sum[0] = 0;
	p->sum[1] = 0;
	sp = p->circuit;
	l -= 14;
	ep = sp + (l & ~0x07);
	for(s = 0; sp < ep; sp += 8) {
		s = s + s + sp[0];
		s = s + s + sp[1];
		s = s + s + sp[2];
		s = s + s + sp[3];
		s = s + s + sp[4];
		s = s + s + sp[5];
		s = s + s + sp[6];
		s = s + s + sp[7];
		s = (s & 0xFFFF) + (s>>16);
	}
	ep = sp + (l & 0x07);
	while(sp < ep) {
		s = s + s + *sp;
		sp++;
	}
	s = (s & 0xFFFF) + (s>>16);
	s = (s & 0xFFFF) + (s>>16);
	p->sum[1] = s>>8;
	p->sum[0] = s;
	return s & 0xFFFF;
}

static
Chan*
getchan(Ifc *ifc, Nnpkt *p)
{
	Chan *cp;
	long ckt;

	ckt = (p->circuit[2]<<16) |
		(p->circuit[1]<<8) |
		p->circuit[0];
	for(cp = nn.lcp; cp; cp = cp->lcp.link)
		if(memcmp(p->s, cp->lcp.s, Easize) == 0)
		if(memcmp(p->d, cp->lcp.d, Easize) == 0)
		if(ckt == cp->lcp.rckt)
			return cp;

	lock(&nn);
	if(nn.reply == 0) {
		nn.reply = newqueue(Nqueue);
		userinit(nonetout, &nn, "nno");
	}

	cp = chaninit(Devnonet, 1);
	cp->lcp.link = nn.lcp;
	nn.lcp = cp;

	memmove(cp->lcp.s, p->s, Easize);
	memmove(cp->lcp.d, p->d, Easize);
	cp->lcp.rckt = ckt;
	cp->lcp.tckt = ckt + 1;
	cp->send = serveq;
	cp->reply = nn.reply;
	cp->lcp.reply = ifc->reply;
	cp->whotime = 0;
	sprint(cp->whochan, "%E-%ld", p->s, ckt);

	unlock(&nn);
	print("allocating %s\n", cp->whochan);
	return cp;
}

static
void
initchan(Chan *cp)
{
	int i;
	Xp *xp;

	for(i = 0; i < 128; i++) {
		xp = &cp->lcp.msgs[i];
		lock(&nn);
		if(xp->reply) {
			mbfree(xp->reply);
			xp->reply = 0;
		}
		if(xp->running) {
			print("initchan while running\n");
			xp->running = 0;
		}
		unlock(&nn);
	}
	fileinit(cp);
}

static
void
nonetout(void)
{
	Msgbuf *mb, *emb;
	Chan *c;
	Nnpkt *p;
	Xp *xp;
	int r, n, offset, flag;


loop:
	mb = recv(nn.reply, 0);
	if(mb == 0)
		goto loop;

	c = mb->chan;
	r = mb->count;

	flag = Fackme;
	if(r == 0)
		flag = Fnull;
	offset = 0;
	do {
		emb = mballoc(ETHERMAXTU, c, Mbnonet1);
		p = (Nnpkt*)emb->data;

		hnputs(p->type, Nntype);

		memmove(p->d, c->lcp.s, Easize);
		p->circuit[0] = c->lcp.tckt;
		p->circuit[1] = c->lcp.tckt>>8;
		p->circuit[2] = c->lcp.tckt>>16;
		p->flag = flag;
		p->mid = mb->param;
		p->ack = mb->param;

		/*
		 * data and count
		 * small botch, 2's complement assumed
		 */
		n = r;
		if(offset)
			n = -n;
		p->remain[0] = n;
		p->remain[1] = n>>8;

		n = r;
		if(n > ETHERMAXTU-(Ensize+Nnsize))
			n = ETHERMAXTU-(Ensize+Nnsize);
		memmove((char*)p+(Ensize+Nnsize), mb->data+offset, n);

		r -= n;
		offset += n;

		n += Ensize+Nnsize;
		if(n < ETHERMINTU) {
			memset((char*)p+n, 0, ETHERMINTU-n);
			n = ETHERMINTU;
		}
		checksum(p, n);
		emb->count = n;
		send(c->lcp.reply, emb);
	} while(r > 0);
	if(mb->count == 0) {
		mbfree(mb);
		goto loop;
	}
	r = mb->data[0];
	if(r < 0 || r >= MAXSYSCALL || !(r&1)) {
		mbfree(mb);
		goto loop;
	}
	xp = &c->lcp.msgs[mb->param & 127];
	lock(&nn);
	if(xp->reply) {
		mbfree(xp->reply);	/* shouldnt happen */
		xp->reply = 0;
	}
	if(statecall[r-1]) {
		xp->reply = mb;
		mb = 0;
	}
	xp->running = 0;
	unlock(&nn);
	if(mb)
		mbfree(mb);
	goto loop;
}

/*
 * called directly from the ether driver
 */
void
nonetreceive(Enpkt *ep, int l, Ifc *ifc)
{
	Chan *chan;
	Nnpkt *p;
	int i, r;
	Msgbuf *mb, *reply;
	Xp *xp, *xp1;

	p = (Nnpkt*)ep;
	if(memcmp(ifc->ea, p->d, Easize) != 0)
		return;
	r = (p->sum[1]<<8) | p->sum[0];
	if(r && r != checksum(p, l)) {
		ifc->sumerr++;
		return;
	}
	chan = getchan(ifc, p);
	if(p->flag & Fnewcall)
		initchan(chan);
	r = (p->remain[1]<<8) | p->remain[0];
	if(r & 0x8000)
		r = -(r^0xFFFF) - 1;
	i = l - (Ensize+Nnsize);

	/*
	 * r is (+/-) data remaining to end of message
	 * i is data in this pkt
	 */
	if(r >= 0) {					/* first packet */
		if(r < i)
			i = r;
		if(r > LARGEBUF)
			return;
		if(chan->lcp.data) {
			mbfree(chan->lcp.data);
			chan->lcp.data = 0;
			chan->lcp.remain = 0;
		}
		mb = mballoc(r, chan, Mbnonet2);
		mb->param = p->mid;
		memmove(mb->data, (char*)p+(Ensize+Nnsize), i);
		if(r == i)
			goto send1;
		chan->lcp.data = mb;
		chan->lcp.remain = r - i;
		return;
	}
	mb = chan->lcp.data;
	if(mb == 0 || mb->param != p->mid)
		return;
	r = -r;
	if(r != chan->lcp.remain)
		return;
	if(r < i)
		i = r;
	memmove(mb->data+(mb->count-r), (char*)p + (Ensize+Nnsize), i);
	if(r != i) {
		/*
		 * still more
		 */
		chan->lcp.remain = r - i;
		return;
	}

send1:
	/*
	 * we have a packet
	 */
	chan->lcp.data = 0;
	if(mb->count == 0) {
		/*
		 * zero-length pkt is ack only
		 */
		xp1 = &chan->lcp.msgs[(p->ack-1) & 127];
		lock(ifc);
		if(xp1->reply) {
			mbfree(xp1->reply);
			xp1->reply = 0;
		}
		unlock(ifc);
		mbfree(mb);
		return;
	}

	/*
	 * if the message is out of range
	 * ignore it
	 */
	r = mb->data[0];
	if(r < 0 || r >= MAXSYSCALL || (r & 1)) {
		mbfree(mb);
		return;
	}

	/*
	 * if we are processing this message
	 * ignore it
	 */
	xp = &chan->lcp.msgs[p->mid & 127];
	lock(ifc);
	if(xp->running) {
		unlock(ifc);
		mbfree(mb);

		/*
		 * special 0-length msg
		 * causes null-pkt ack to
		 * shut up transmitter
		 */
		mb = mballoc(0, chan, Mbnonet3);
		mb->param = p->mid;
		send(chan->reply, mb);
		return;
	}

	/*
	 * delete older canned messages
	 *	(mid-16) and (ack-1)
	 */
	xp1 = &chan->lcp.msgs[(p->mid-16) & 127];
	if(xp1->reply) {
		mbfree(xp1->reply);
		xp1->reply = 0;
	}
	xp1 = &chan->lcp.msgs[(p->ack-1) & 127];
	if(xp1->reply) {
		mbfree(xp1->reply);
		xp1->reply = 0;
	}

	xp->running = 1;
	reply = xp->reply;

	if(reply) {
		xp->reply = 0;
		if(p->mid == reply->param) {
			/*
			 * reissue reply
			 */
			send(chan->reply, reply);
			unlock(ifc);
			mbfree(mb);
			return;
		}
		mbfree(reply);
	}
	unlock(ifc);
	send(chan->send, mb);
}

portdat.h       716088074   0     0     664     20889     `
/*
 * fundamental constants
 */
#define SUPER_ADDR	2		/* address of superblock */
#define ROOT_ADDR	3		/* address of superblock */
#define	ERRREC		64		/* size of a error record */
#define	DIRREC		116		/* size of a directory ascii record */
#define	NAMELEN		28		/* size of names */
#define	NDBLOCK		6		/* number of direct blocks in Dentry */
#define	MAXDAT		8192		/* max allowable data message */
#define	MAXMSG		128		/* max size protocol message sans data */
#define	OFFMSG		60		/* offset of msg in buffer */
#define NDRIVE		16		/* size of drive structure */
#define NTLOCK		200		/* number of active file Tlocks */
#define	LRES		3		/* profiling resolution */
#define AUTHSTR		8
#define KEYENABLE	NAMELEN+DESKEYLEN
#define KEYEXPIRE	NAMELEN+DESKEYLEN+2

/*
 * derived constants
 */
#define	BUFSIZE		(RBUFSIZE-sizeof(Tag))
#define DIRPERBUF	(BUFSIZE/sizeof(Dentry))
#define INDPERBUF	(BUFSIZE/sizeof(long))
#define INDPERBUF2	(INDPERBUF*INDPERBUF)
#define FEPERBUF	((BUFSIZE-sizeof(Super1)-sizeof(long))/sizeof(long))
#define	SMALLBUF	(MAXMSG)
#define	LARGEBUF	(MAXMSG+MAXDAT+256)
#define	CDEV(dev)	cwdevs[(dev).part]
#define	WDEV(dev)	cwdevs[(dev).part+1]


typedef	struct	Vmedevice Vmedevice;	/* not defined here */

typedef struct	Alarm	Alarm;
typedef	struct	Conf	Conf;
typedef	struct	Label	Label;
typedef	struct	List	List;
typedef	struct	Lock	Lock;
typedef	struct	Mach	Mach;
typedef	struct	QLock	QLock;
typedef	struct	Ureg	Ureg;
typedef	struct	User	User;
typedef	struct	Fcall	Fcall;
typedef	struct	Fbuf	Fbuf;
typedef	struct	Super1	Super1;
typedef	struct	Superb	Superb;
typedef	struct	Filsys	Filsys;
typedef	struct	Dentry	Dentry;
typedef	struct	Tag	Tag;
typedef	struct	Uid	Uid;
typedef struct	Device	Device;
typedef struct	Qid	Qid;
typedef	struct	Iobuf	Iobuf;
typedef	struct	Wpath	Wpath;
typedef	struct	File	File;
typedef	struct	Chan	Chan;
typedef	struct	P9call	P9call;
typedef	struct	Cons	Cons;
typedef	struct	Time	Time;
typedef	struct	Tm	Tm;
typedef	struct	Rtc	Rtc;
typedef	struct	Hiob	Hiob;
typedef	struct	RWlock	RWlock;
typedef	struct	Msgbuf	Msgbuf;
typedef	struct	Queue	Queue;
typedef	struct	Drive	Drive;
typedef	struct	Command	Command;
typedef	struct	Flag	Flag;
typedef	struct	Bp	Bp;
typedef	struct	Bit	Bit;
typedef	struct	Rabuf	Rabuf;
typedef	struct	Rout	Rout;
typedef	struct	Rendez	Rendez;
typedef	struct	Filter	Filter;
typedef		ulong	Float;
typedef	struct	Tlock	Tlock;
typedef	struct	Filta	Filta;
typedef	struct	Enpkt	Enpkt;
typedef	struct	Arppkt	Arppkt;
typedef	struct	Ippkt	Ippkt;
typedef	struct	Ilpkt	Ilpkt;
typedef	struct	Ifc	Ifc;

struct	Lock
{
	ulong*	sbsem;		/* addr of sync bus semaphore */
	ulong	pc;
};

struct	Rendez
{
	Lock;
	User*	p;
};

struct	Filter
{
	ulong	count;			/* count and old count kept separate */
	ulong	oldcount;		/*	so interrput can read them */
	Float	filter[3];		/* filters for 1m 10m 100m */ 
};

struct	Filta
{
	Filter*	f;
	int	scale;
};

struct	QLock
{
	Lock;			/* to use object */
	User*	head;		/* next process waiting for object */
	User*	tail;		/* last process waiting for object */
	char*	name;		/* for diagnostics */
	int	locked;		/* flag, is locked */
};

struct	RWlock
{
	int	nread;
	QLock	wr;
	QLock	rd;
};

/*
 * send/recv queue structure
 */
struct	Queue
{
	Lock;			/* to manipulate values */
	int	size;		/* size of queue */
	int	loc;		/* circular pointer */
	int	count;		/* how many in queue */
	User*	rhead;		/* process's waiting for send */
	User*	rtail;
	User*	whead;		/* process's waiting for recv */
	User*	wtail;
	void*	args[1];	/* list of saved pointers, [->size] */
};

struct	Tag
{
	short	pad;
	short	tag;
	long	path;
};

struct	Device
{
	char	type;
	char	ctrl;
	char	unit;
	char	part;
};

struct	Rabuf
{
	union
	{
		struct
		{
			Device	dev;
			long	addr;
		};
		Rabuf*	link;
	};
};

/* DONT TOUCH, this is the disk structure */
struct	Qid
{
	long	path;
	long	version;
};

struct	Hiob
{
	Iobuf*	link;
	Lock;
};

enum
{
	Easize		= 6,		/* Ether address size */
	Pasize		= 4,		/* IP protocol address size */
};

typedef
struct
{
	Queue*	reply;		/* ethernet output */
	uchar	ea[Easize];	/* my ether address */
	uchar	ipmy[Pasize];	/* my ip address (index) */
	uchar	iphis[Pasize];	/* his ip address (index) */
	uchar	ipgate[Pasize];	/* his gateway address */
	int	usegate;	/* flag set if not on this subnet */
	Chan*	link;		/* list of il channels */
} Enp;

typedef
struct	Ilp
{
	Enp;			/* must be first -- botch */

	int	alloc;		/* 1 means allocated */
	int	srcp;		/* source port (index) */
	int	dstp;		/* source port (index) */
	int	state;		/* connection state */

	Msgbuf*	unacked;
	Msgbuf*	unackedtail;

	Msgbuf*	outoforder;

	ulong	next;		/* id of next to send */
	ulong	recvd;		/* last packet received */
	ulong	start;		/* local start id */
	ulong	rstart;		/* remote start id */

	int	timeout;	/* time out counter */
	int	slowtime;	/* slow time counter */
	int	fasttime;	/* retransmission timer */
	int	acktime;	/* acknowledge timer */
	int	querytime;	/* Query timer */
	int	deathtime;	/* Time to kill connection */

	int	rtt;		/* average round trip time */
	ulong	rttack;		/* the ack we are waiting for */
	ulong	ackms;		/* time we issued */

	int	window;		/* maximum receive window */

	Rendez	syn;		/* connect hang out */
} Ilp;

struct	Chan
{
	char	type;			/* major driver type ie Devdk */
	char	whochan[50];
	char	whoname[NAMELEN];
	ulong	flags;
	int	chan;			/* overall channel number, mostly for printing */
	int	nmsgs;			/* outstanding messages, set under flock -- for flush */
	long	whotime;
	RWlock	reflock;
	Chan*	next;			/* link list of chans */
	Queue*	send;
	Queue*	reply;
	union
	{
		/*
		 * il ether circuit structure
		 */
		Ilp	ilp;

		/*
		 * data kit circuit structure
		 */
		struct
		{
			QLock;
			Msgbuf*	inmsg;		/* input message */
			Msgbuf*	outmsg;		/* output messages */
			Msgbuf*	ccmsg;		/* messages for CCCHAN */
			long	urptimeout;
			long	timeout;
			int	pokeflg;

			int	cno;		/* channel number  */
			int	repw;		/* write ptr */
			int	repr;		/* read ptr */

			int	icc;		/* input character pointer */
			int	gcc;		/* good characters */
			int	bot;		/* #bytes at BOT */
			int	last;		/* which BOT is seen */
			int	ack;
			int	ec;
			int	rej;

			int	urpstate;
			int	dkstate;
			int	calltimeout;
			int	seq;
			int	maxout;
			int	mlen;
			int	nout;
			int	mout;
			int	mcount;

			char	repchar[20];	/* list of echo characters */
		} dkp;
		/*
		 * bit3 circuit structure
		 */
		struct
		{
			void*	bitp;
		} bitp;
		/*
		 * hotrod fiber uart
		 */
		struct
		{
			void*	bitp;
		} hot;
	};
};

struct	Filsys
{
	char*	name;			/* name of filsys */
	char*	conf;			/* symbolic configuration */
	Device	dev;			/* device that filsys is on */
	int	flags;
		#define	FREAM		(1<<0)	/* mkfs */
		#define	FRECOVER	(1<<1)	/* install last dump */
		#define	FEDIT		(1<<2)	/* modified */
};

struct	Time
{
	long	lasttoy;
	long	bias;
	long	offset;
};

/*
 * array of qids that are locked
 */
struct	Tlock
{
	Device	dev;
	long	time;
	long	qpath;
	File*	file;
};

struct	Cons
{
	ulong	flags;		/* overall flags for all channels */
	QLock;			/* generic qlock for mutex */
	int	uid;		/* botch -- used to get uid on cons_create */
	int	gid;		/* botch -- used to get gid on cons_create */
	int	nuid;		/* number of uids */
	int	ngid;		/* number of gids */
	long	offset;		/* used to read files, c.f. fchar */
	int	chano;		/* generator for channel numbers */
	Chan*	chan;		/* console channel */
	Filsys*	curfs;		/* current filesystem */

	int	profile;	/* are we profiling? */
	long*	profbuf;
	ulong	minpc;
	ulong	maxpc;
	ulong	nprofbuf;

	long	nlarge;		/* number of large message buffers */
	long	nsmall;		/* ... small ... */
	long	nwormre;	/* worm read errors */
	long	nwormwe;	/* worm write errors */
	int	noage;		/* dont update cache age, dump and check */
	long	nwrenre;	/* disk read errors */
	long	nwrenwe;	/* disk write errors */
	long	nreseq;		/* cache bucket resequence */

	Filter	work;		/* thruput in messages */
	Filter	rate;		/* thruput in bytes */
	Filter	bhit;		/* getbufs that hit */
	Filter	bread;		/* getbufs that miss and read */
	Filter	brahead;	/* messages to readahead */
	Filter	binit;		/* getbufs that miss and dont read */
};

struct	P9call
{
	uchar	calln;
	uchar	rxflag;
	short	msize;
	void	(*func)(Chan*, int);
};

struct	File
{
	QLock;
	Qid	qid;
	Wpath*	wpath;
	Chan*	cp;		/* null means a free slot */
	Tlock*	tlock;		/* if file is locked */
	File*	next;		/* in cp->flist */
	Filsys*	fs;
	long	addr;
	long	slot;
	long	lastra;		/* read ahead address */
	short	fid;
	short	uid;
	char	ticket[AUTHSTR];/* Authentication key */
	char	open;
		#define	FREAD	1
		#define	FWRITE	2
		#define	FREMOV	4
};

struct	Wpath
{
	Wpath*	up;		/* pointer upwards in path */
	long	addr;		/* directory entry addr */
	long	slot;		/* directory entry slot */
	short	refs;		/* number of files using this structure */
};

struct	Iobuf
{
	QLock;
	Device	dev;
	Iobuf*	next;		/* for hash */
	Iobuf*	fore;		/* for lru */
	Iobuf*	back;		/* for lru */
	char*	iobuf;		/* only active while locked */
	char*	xiobuf;		/* "real" buffer pointer */
	long	addr;
	int	flags;
};

struct	Uid
{
	short	uid;		/* user id */
	short	lead;		/* leader of group */
	short	*gtab;		/* group table */
	int	ngrp;		/* number of group entries */
	char	name[NAMELEN];	/* user name */
	char	key[DESKEYLEN];	/* authentication key */
};

/* DONT TOUCH, this is the disk structure */
struct	Dentry
{
	char	name[NAMELEN];
	short	uid;
	short	gid;
	ushort	mode;
		#define	DALLOC	0x8000
		#define	DDIR	0x4000
		#define	DAPND	0x2000
		#define	DLOCK	0x1000
		#define	DREAD	0x4
		#define	DWRITE	0x2
		#define	DEXEC	0x1
	Qid	qid;
	long	size;
	long	dblock[NDBLOCK];
	long	iblock;
	long	diblock;
	long	atime;
	long	mtime;
};

/* DONT TOUCH, this is the disk structure */
struct	Super1
{
	long	fstart;
	long	fsize;
	long	tfree;
	long	qidgen;		/* generator for unique ids */
	/*
	 * Stuff for WWC device
	 */
	long	cwraddr;	/* cfs root addr */
	long	roraddr;	/* dump root addr */
	long	last;		/* last super block addr */
	long	next;		/* next super block addr */
};

/* DONT TOUCH, this is the disk structure */
struct	Fbuf
{
	long	nfree;
	long	free[FEPERBUF];
};

/* DONT TOUCH, this is the disk structure */
struct	Superb
{
	Fbuf	fbuf;
	Super1;
};

struct	Fcall
{
	char	type;
	short	fid;
	short	err;
	short	tag;
	union
	{
		struct
		{
			short	uid;		/* T-Userstr */
			short	oldtag;		/* T-nFlush */
			Qid	qid;		/* R-Attach, R-Clwalk, R-Walk,
						 * R-Open, R-Create */
		};
		struct
		{
			char	uname[NAMELEN];	/* T-nAttach */
			char	aname[NAMELEN];	/* T-nAttach */
			char	auth[NAMELEN];	/* T-nAttach */
			char	chal[8+NAMELEN];/* T-auth, R-auth */
		};
		struct
		{
			char	ename[ERRREC];	/* R-nError */
		};
		struct
		{
			char	name[NAMELEN];	/* T-Walk, T-Clwalk, T-Create, T-Remove */
			long	perm;		/* T-Create */
			short	newfid;		/* T-Clone, T-Clwalk */
			char	mode;		/* T-Create, T-Open */
		};
		struct
		{
			long	offset;		/* T-Read, T-Write */
			long	count;		/* T-Read, T-Write, R-Read */
			char*	data;		/* T-Write, R-Read */
		};
		struct
		{
			char	stat[DIRREC];	/* T-Wstat, R-Stat */
		};
	};
};

struct	List
{
	void*	next;
};

struct	Label
{
	ulong	pc;
	ulong	sp;
};

struct	Alarm
{
	List;
	Lock;
	int	busy;
	int	dt;		/* in ticks */
	void	(*f)(Alarm*, void*);
	void*	arg;
};

struct	Conf
{
	ulong	nmach;		/* processors */
	ulong	nproc;		/* processes */
	ulong	mem;		/* total physical bytes of memory */
	ulong	sparemem;	/* memory left for check/dump and chans */
	ulong	nalarm;		/* alarms */
	ulong	nuid;		/* distinct uids */
	ulong	nserve;		/* server processes */
	ulong	nrahead;	/* read ahead processes */
	ulong	nfile;		/* number of fid -- system wide */
	ulong	nwpath;		/* number of active paths, derrived from nfile */
	ulong	gidspace;	/* space for gid names -- derrived from nuid */
	ulong	nlgmsg;		/* number of large message buffers */
	ulong	nsmmsg;		/* number of small message buffers */
	ulong	wcpsize;	/* memory for worm copies */
};

/*
 * message buffers
 * 2 types, large and small
 */
struct	Msgbuf
{
	short	count;
	short	flags;
		#define	LARGE	(1<<0)
		#define	FREE	(1<<1)
	Chan*	chan;
	Msgbuf*	next;
	ulong	param;
	int	category;
	char*	data;
	char*	xdata;
};

/*
 * message buffer categories
 */
enum
{
	Mxxx		= 0,
	Mbbit,
	Mbreply1,
	Mbreply2,
	Mbreply3,
	Mbreply4,
	Mbdkxpmesg,
	Mbdkxpcall,
	Mbdksrlisten1,
	Mbdksrlisten2,
	Mbdkinput,
/*	Mbnonet1,
	Mbnonet2,
	Mbnonet3,	*/
/*	Mbhotrod1,
	Mbhotrod2,
	Mbhotrod3,
	Mbhotrod4,	*/
	Mbcycl1,
	Mbcycl2,
	Mbcycl3,
	Mbcycl4,
	Mbarp1,
	Mbarp2,
	Mbip1,
	Mbip2,
	Mbip3,
	Mbil1,
	Mbil2,
	Mbil3,
	Mbil4,
	Mbilauth,
	MAXCAT,
};

struct	Mach
{
	int	machno;		/* physical id of processor */
	int	mmask;		/* 1<<m->machno */
	ulong	ticks;		/* of the clock since boot time */
	int	lights;		/* light lights, this processor */
	User*	proc;		/* current process on this processor */
	Label	sched;		/* scheduler wakeup */
	Lock	alarmlock;	/* access to alarm list */
	void*	alarm;		/* alarms bound to this clock */
	void	(*intr)(Ureg*, ulong);	/* pending interrupt */
	User*	intrp;		/* process that was interrupted */
	ulong	cause;		/* arg to intr */
	Ureg*	ureg;		/* arg to intr */
	uchar	stack[1];
};

#define	MAXSTACK 4000
#define	NHAS	100
struct	User
{
	Label	sched;
	Mach*	mach;		/* machine running this proc */
	User*	rnext;		/* next process in run queue */
	User*	qnext;		/* next process on queue for a QLock */
	void	(*start)(void);	/* startup function */
	char*	text;		/* name of this process */
	void*	arg;
	Filter	time;		/* cpu time used */
	int	exiting;
	int	pid;
	int	state;
	struct
	{
		QLock*	q[NHAS];/* list of locks this process has */
		QLock*	want;	/* lock waiting */
	} has;
	uchar	stack[MAXSTACK];
};

#define	PRINTSIZE	256
struct
{
	Lock;
	int	machs;
	int	exiting;
} active;

struct	Drive
{
	Device	dev;
	char	status;
	char	qnum;
	char	fflag;
	long	block;			/* size of a block -- from config */
	long	nblock;			/* number of blocks -- from config */
	long	mult;			/* multiplier to get physical blocks */
	long	max;			/* number of logical blocks */
	Filter	work;
	Filter	rate;
};

struct	Command
{
	char*	arg0;
	char*	help;
	void	(*func)(int, char*[]);
};

struct	Flag
{
	char*	arg0;
	char*	help;
	ulong	flag;
};

struct	Tm
{
	/* see ctime(3) */
	int	sec;
	int	min;
	int	hour;
	int	mday;
	int	mon;
	int	year;
	int	wday;
	int	yday;
	int	isdst;
};

struct	Rtc
{
	int	sec;
	int	min;
	int	hour;
	int	mday;
	int	mon;
	int	year;
};

/*
 * Drive status
 */
enum
{
	Dunavail,
	Dready,
	Dnready,
	Dself,

	SCSIread = 0,
	SCSIwrite = 1,
};

/*
 * Process states
 */
enum
{
	Dead = 0,
	Moribund,
	Zombie,
	Ready,
	Scheding,
	Running,
	Queueing,
	Sending,
	Recving,
	MMUing,
	Exiting,
	Inwait,
	Wakeme,
	Broken,
};

/*
 * Lights
 */
enum
{
	Lreal	= 0,	/* blink in clock interrupt */
	Lintr,		/* on while in interrupt */
	Lpanic,		/* in panic */
	Lcwmap,		/* in cw lookup */
};

/*
 * devnone block numbers
 */
enum
{
	Cwio1 	= 1,
	Cwio2,
	Cwxx1,
	Cwxx2,
	Cwxx3,
	Cwxx4,
	Cwdump1,
	Cwdump2,
	Cuidbuf,
	Cckbuf,
};

#define	MAXFDATA	8192
/*
 * P9 protocol message types
 */
/* DONT TOUCH, this the P9 protocol */
enum
{
	Tnop =		50,
	Rnop,
	Tsession =	52,
	Rsession,
	Terror =	54,	/* illegal */
	Rerror,
	Tflush =	56,
	Rflush,
	Tattach =	58,
	Rattach,
	Tclone =	60,
	Rclone,
	Twalk =		62,
	Rwalk,
	Topen =		64,
	Ropen,
	Tcreate =	66,
	Rcreate,
	Tread =		68,
	Rread,
	Twrite =	70,
	Rwrite,
	Tclunk =	72,
	Rclunk,
	Tremove =	74,
	Rremove,
	Tstat =		76,
	Rstat,
	Twstat =	78,
	Rwstat,
	Tclwalk =	80,
	Rclwalk,
	Tauth =		82,
	Rauth,

	MAXSYSCALL
};

/*
 * error codes generated from the file server
 */
enum
{
	Ebadspc = 1,
	Efid,
	Echar,
	Eopen,
	Ecount,
	Ealloc,
	Eqid,
	Eaccess,
	Eentry,
	Emode,
	Edir1,
	Edir2,
	Ephase,
	Eexist,
	Edot,
	Eempty,
	Ebadu,
	Enotu,
	Enotg,
	Ename,
	Ewalk,
	Eronly,
	Efull,
	Eoffset,
	Elocked,
	Ebroken,
	Eauth,
	MAXERR
};

/*
 * device types
 */
enum
{
	Devnone 	= 0,
	Devcon,			/* console */
	Devdk,			/* data kit norm chan */
	Devdkcc,		/* data kit common control chan */
	Devdksr,		/* data kit service request chan */
	Devdkck,		/* data kit clock chan */
	Devwren,		/* jaguar disk drive */
	Devworm,		/* jaguar video drive */
	Devfworm,		/* fake read-only device */
	Devcw,			/* cache with worm */
	Devro,			/* readonly worm */
	Devnonet,		/* ether net */
	Devbit,			/* bit3 vme-vme adaptor */
	Devhot,			/* hotrod fiber uart */
	Devcycl,		/* cyclone fiber uart */
	Devmcat,		/* multpile cat devices */
	Devmlev,		/* multpile interleave devices */
	Devil,			/* internet link */
	Devpart,		/* partition */
	MAXDEV
};

/*
 * tags on block
 */
/* DONT TOUCH, this is in disk structures */
enum
{
	Tnone		= 0,
	Tsuper,			/* the super block */
	Tdir,			/* directory contents */
	Tind1,			/* points to blocks */
	Tind2,			/* points to Tind1 */
	Tfile,			/* file contents */
	Tfree,			/* in free list */
	Tbuck,			/* cache fs bucket */
	Tvirgo,			/* fake worm virgin bits */
	Tcache,			/* cw cache things */
	Tconfig,		/* configuration block */
	MAXTAG
};

/*
 * flags to getbuf
 */
enum
{
	Bread	= (1<<0),	/* read the block if miss */
	Bprobe	= (1<<1),	/* return null if miss */
	Bmod	= (1<<2),	/* set modified bit in buffer */
	Bimm	= (1<<3),	/* set immediate bit in buffer */
	Bres	= (1<<4),	/* reserved, never renammed */
};

/*
 * open modes passed into P9 open/create
 */
/* DONT TOUCH, this the P9 protocol */
enum
{
	MREAD	= 0,
	MWRITE,
	MBOTH,
	MEXEC,
	MTRUNC	= (1<<4),	/* truncate on open */
	MCEXEC	= (1<<5),	/* close on exec (host) */
	MRCLOSE	= (1<<6),	/* remove on close */
};


/*
 * Ethernet header
 */
enum
{
	ETHERMINTU	= 60,		/* minimum transmit size */
	ETHERMAXTU	= 1514,		/* maximum transmit size */

	Arptype		= 0x0806,
	Iptype		= 0x0800,
	Ilproto		= 40,
	Udpproto	= 17,
	Nqueue		= 20,
	Nfrag		= 6,		/* max number of non-contig ip fragments */
	Nrock		= 100,		/* number of partial ip assembly stations */
	Ne		= 10,
	Nb		= 211,

	Ensize		= 14,		/* ether header size */
	Ipsize		= 20,		/* ip header size -- doesnt include Ensize */
	Arpsize		= 28,		/* arp header size -- doesnt include Ensize */
	Ilsize		= 18,		/* il header size -- doesnt include Insize/Ensize */

	IP_VER		= 0x40,			/* Using IP version 4 */
	IP_HLEN		= Ipsize/4,		/* Header length in longs */
	IP_DF		= 0x4000,		/* Don't fragment */
	IP_MF		= 0x2000,		/* More fragments */

	Ilsync		= 0,		/* Packet types */
	Ildata,
	Ildataquery,
	Ilack,
	Ilquerey,
	Ilstate,
	Ilclose,

	Ilclosed	= 0,		/* Connection state */
	Ilsyncer,
	Ilsyncee,
	Ilestablished,
	Illistening,
	Ilclosing,
	Ilopening,

	Arprequest	= 1,
	Arpreply,

	Ilfsport	= 17008,
	Ilauthport	= 17020,
	Ilfsout		= 5000,

	Iltickms 	= 100,
	Slowtime 	= 200*Iltickms,
	Fasttime 	= 4*Iltickms,
	Acktime		= 2*Iltickms,
	Ackkeepalive	= 6000*Iltickms,
	Querytime	= 60*Iltickms,		/* time between queries */
	Keepalivetime	= 10*Querytime,		/* keep alive time */
	Defaultwin	= 20,
	ILgain		= 8,
};

struct	Enpkt
{
	uchar	d[Easize];		/* destination address */
	uchar	s[Easize];		/* source address */
	uchar	type[2];		/* packet type */

	uchar	data[ETHERMAXTU-(6+6+2)];
	uchar	crc[4];
};

struct	Arppkt
{
	uchar	d[Easize];			/* ether header */
	uchar	s[Easize];
	uchar	type[2];

	uchar	hrd[2];				/* hardware type, must be ether==1 */
	uchar	pro[2];				/* protocol, must be ip */
	uchar	hln;				/* hardware address len, must be Easize */
	uchar	pln;				/* protocol address len, must be Pasize */
	uchar	op[2];
	uchar	sha[Easize];
	uchar	spa[Pasize];
	uchar	tha[Easize];
	uchar	tpa[Pasize];
};

struct	Ippkt
{
	uchar	d[Easize];		/* ether header */
	uchar	s[Easize];
	uchar	type[2];

	uchar	vihl;			/* Version and header length */
	uchar	tos;			/* Type of service */
	uchar	length[2];		/* packet length */
	uchar	id[2];			/* Identification */
	uchar	frag[2];		/* Fragment information */
	uchar	ttl;			/* Time to live */
	uchar	proto;			/* Protocol */
	uchar	cksum[2];		/* Header checksum */
	uchar	src[Pasize];		/* Ip source */
	uchar	dst[Pasize];		/* Ip destination */
};

struct	Ilpkt
{
	uchar	d[Easize];		/* ether header */
	uchar	s[Easize];
	uchar	type[2];

	uchar	vihl;			/* ip header */
	uchar	tos;
	uchar	length[2];
	uchar	id[2];
	uchar	frag[2];
	uchar	ttl;
	uchar	proto;
	uchar	cksum[2];
	uchar	src[Pasize];
	uchar	dst[Pasize];

	uchar	ilsum[2];		/* Checksum including header */
	uchar	illen[2];		/* Packet length */
	uchar	iltype;			/* Packet type */
	uchar	ilspec;			/* Special */
	uchar	ilsrc[2];		/* Src port */
	uchar	ildst[2];		/* Dst port */
	uchar	ilid[4];		/* Sequence id */
	uchar	ilack[4];		/* Acked sequence */
};

struct	Ifc
{
	Lock;
	Queue*	reply;
	Filter	work;
	Filter	rate;
	ulong	rcverr;
	ulong	txerr;
	ulong	sumerr;
	ulong	rxpkt;
	ulong	txpkt;
	uchar	ea[Easize];		/* our ether address */
	uchar	ipa[Pasize];		/* our ip address, pulled from netdb */
	uchar	netgate[Pasize];	/* ip gateway, pulled from netdb */
	ulong	ipaddr;
	ulong	mask;
	Ifc	*next;			/* List of configured interfaces */
};

extern	register	Mach*	m;
extern	register	User*	u;

Conf	conf;
Cons	cons;
#define	MACHP(n)	((Mach*)(MACHADDR+n*BY2PG))

portfns.h       716098338   0     0     664     7798      `
void	accessdir(Iobuf*, Dentry*, int);
void	addfree(Device, long, Superb*);
Alarm*	alarm(int, void(*)(Alarm*, void*), void*);
void	alarminit(void);
void	arpstart(void);
int	authsrv(Chan*, char*, int);
void	authserver(Chan*);
void	arginit(void);
void	cdiag(char*);
int	cnumb(void);
Device	config(void);
int	rawchar(int);
long	bufalloc(Device, int, long);
void	buffree(Device, long, int);
int	byname(void*, void*);
int	byuid(void*, void*);
void	cancel(Alarm*);
int	canlock(Lock*);
int	canqlock(QLock*);
int	Cconv(Op*);
void	cfsdump(Filsys*);
Chan*	chaninit(int, int);
void	cmd_auth(int, char*[]);
void	cmd_check(int, char*[]);
void	cmd_users(int, char*[]);
void	cmd_newuser(int, char*[]);
void	cmd_netdb(int, char*[]);
void	cmd_passwd(int, char*[]);
int	chartoip(uchar *, char *);
int	checkname(char*);
int	checktag(Iobuf*, int, long);
int	cksum(void*, int, int);
int	cksum0(int, int);
void	clock(ulong, ulong);
void	clockinit(void);
void	clockreload(ulong);
void	cyclstart(void);
void	dotrace(int);
int	conschar(void);
void	consserve(void);
int	convD2M(Dentry*, char*);
int	convM2D(char*, Dentry*);
int	convM2S(char*, Fcall*, int);
int	convS2M(Fcall*, char*);
int	conslock(void);
int	con_attach(int, char*, char*);
int	con_clone(int, int);
int	con_create(int, char*, int, int, long, int);
int	con_clri(int);
int	con_open(int, int);
int	con_read(int, char*, long, int);
int	con_remove(int);
void	con_rintr(int);
int	con_session(void);
int	con_walk(int, char*);
int	con_write(int, char*, long, int);
int	cwgrow(Device, Superb*);
int	cwfree(Device, long);
void	cwinit(Device);
long	cwraddr(Device, int);
int	cwread(Device, long, void*);
void	cwream(Device);
void	cwrecover(Device);
long	cwsaddr(Device);
long	cwsize(Device);
void	dbufread(Iobuf*, Dentry*, long);
int	cwwrite(Device, long, void*);
void	datestr(char*, long);
int	Dconv(Op*);
void	delay(int);
int	devcmp(Device, Device);
void	devream(Device);
void	devrecover(Device);
void	devinit(Device);
int	devread(Device, long, void*);
long	devsize(Device);
int	devwrite(Device, long, void*);
void	dkstart(void);
Iobuf*	dnodebuf(Iobuf*, Dentry*, long, int);
void	dofilter(Filter*);
int	doremove(File*, int);
void	dtrunc(Iobuf*, Dentry*);
void	duartinit(void);
void	duartintr(void);
int	duartputc(int);
void	duartreset(void);
void	duartstart(int);
int	duartrecv(void);
void	duartxmit(int);
int	dumpblock(Device);
void	dumpregs(Ureg*);
void	dumpstack(User*);
void	exit(void);
ulong	fakeqid(Dentry*);
Float	famd(Float, int, int, int);
int	Fconv(Op*);
ulong	fdf(Float, int);
void	fileinit(Chan*);
File*	filep(Chan*, int, int);
void	firmware(void);
int	fname(char*);
int	fpair(char*, char*);
void	formatinit(void);
int	fread(void*, int);
void	freealarm(Alarm*);
void	freefp(File*);
void	freewp(Wpath*);
Filsys*	fsstr(char*);
long	fwormsize(Device);
void	fwormream(Device);
void	fworminit(Device);
int	fwormread(Device, long, void*);
int	fwormwrite(Device, long, void*);
void	f_attach(Chan*, Fcall*, Fcall*);
void	f_auth(Chan*, Fcall*, Fcall*);
void	f_clone(Chan*, Fcall*, Fcall*);
void	f_clunk(Chan*, Fcall*, Fcall*);
void	f_create(Chan*, Fcall*, Fcall*);
void	f_nop(Chan*, Fcall*, Fcall*);
void	f_flush(Chan*, Fcall*, Fcall*);
void	f_open(Chan*, Fcall*, Fcall*);
void	f_read(Chan*, Fcall*, Fcall*);
void	f_remove(Chan*, Fcall*, Fcall*);
void	f_session(Chan*, Fcall*, Fcall*);
void	f_stat(Chan*, Fcall*, Fcall*);
void	f_walk(Chan*, Fcall*, Fcall*);
void	f_clwalk(Chan*, Fcall*, Fcall*);
void	f_write(Chan*, Fcall*, Fcall*);
void	f_wstat(Chan*, Fcall*, Fcall*);
Iobuf*	getbuf(Device, long, int);
void*	getarg(void);
char*	getwd(char*, char*);
int	getc(void);
ulong	getcallerpc(void);
Dentry*	getdir(Iobuf*, int);
Chan*	getlcp(uchar*, long);
void	getpasswd(char*, int);
long	getraddr(Device);
void	gotolabel(Label*);
void	hexdump(void*, int);
int	iaccess(File*, Dentry*, int);
void*	ialloc(ulong, int);
void	ilauth(void);
long	indfetch(Iobuf*, Dentry*, long, long , int, int);
int	ingroup(int, int);
int	inh(int, uchar*);
void	init0(void);
void	iobufinit(void);
void	iobufmap(Iobuf*);
void	iobufunmap(Iobuf*);
int	iobufql(QLock*);
void	kbdchar(int);
void	lights(int, int);
void	launchinit(void);
int	leadgroup(int, int);
void	lock(Lock*);
void	lockinit(void);
void	machinit(void);
Msgbuf*	mballoc(int, Chan*, int);
void	mbinit(void);
void	mbfree(Msgbuf*);
ulong	meminit(void);
Iobuf*	movebuf(Iobuf*);
void	mcatinit(Device);
int	mcatread(Device, long, void*);
long	mcatsize(Device);
int	mcatwrite(Device, long, void*);
void	partinit(Device);
int	partread(Device, long, void*);
long	partsize(Device);
int	partwrite(Device, long, void*);
void	mlevinit(Device);
int	mlevread(Device, long, void*);
long	mlevsize(Device);
int	mlevwrite(Device, long, void*);
int	nametokey(char*, char*);
Alarm*	newalarm(void);
File*	newfp(void);
User*	newproc(void);
Qid	newqid(Device);
Queue*	newqueue(int);
void	newstart(void);
Wpath*	newwp(void);
int	nvread(int, void*, int);
int	nvwrite(int, void*, int);
int	walkto(char*);
int	no(void*);
long	number(char*, int, int);
void	online(void);
void	otherinit(void);
void	p9fcall(Chan*, Fcall*, Fcall*);
void	p9mcall(Chan*, char*, char*);
void	panic(char*, ...);
void	prdate(void);
void	preread(Device, long);
void	prflush(void);
int	prime(long);
void	printinit(void);
void	procinit(void);
void	putbuf(Iobuf*);
void	qlock(QLock*);
void	qunlock(QLock*);
void	rahead(void);
void	ready(User*);
void	ream(Filsys*);
void*	recv(Queue*, int);
void	restartprint(Alarm*);
void	rlock(RWlock*);
void	rootream(Device, long);
int	roread(Device, long, void*);
void	rstate(Chan*, int);
long	rtc2sec(Rtc *);
void	runlock(RWlock*);
User*	runproc(void);
void	sched(void);
void	schedinit(void);
int	scsiio(Device, int, uchar*, int, void*, int);
Drive*	scsidrive(Device);
void	sec2rtc(ulong, Rtc *);
void	send(Queue*, void*);
void	serve(void);
int	setlabel(Label*);
void	settag(Iobuf*, int, long);
void	settime(long);
void	sleep(Rendez*, int(*)(void*), void*);
int	splhi(void);
int	spllo(void);
void	splx(int);
void	startprint(void);
int	strtouid(char*, int);
long	superaddr(Device);
void	superream(Device, long);
void	sync(char*);
int	syncblock(void);
long	syscall(Ureg*);
void	sysinit(void);
int	Tconv(Op*);
long	time(void);
long	nextime(long, int, int);
Tlock*	tlocked(Iobuf*, Dentry*);
void	touser(void);
long	toytime(void);
long	rtctime(void);
void	setrtc(long);
void	uidtostr(char*, int, int);
Uid*	uidpstr(char*);
void	unlock(Lock*);
void	userinit(void(*)(void), void*, char*);
void	vecinit(void);
void	wakeup(Rendez*);
void	wbflush(void);
void	wlock(RWlock*);
void	wormcopy(void);
void	wormprobe(void);
int	dowcp(void);
int	dowcmp(void);
void	synccopy(void);
void	worminit(Device);
long	wormsearch(Device, int, long, long);
int	wormread(Device, long, void*);
long	wormsize(Device);
int	wormwrite(Device, long, void*);
void	wreninit(Device);
int	wrenread(Device, long, void*);
long	wrensize(Device);
int	wrenwrite(Device, long, void*);
void	wunlock(RWlock*);
void 	cmd_exec(char*);
void	cmd_install(char*, char*, void (*)(int, char*[]));
ulong	flag_install(char*, char*);

int	nhgets(uchar*);
long	nhgetl(uchar*);
void	hnputs(uchar*, int);
void	hnputl(uchar*, long);

int	jaginit(Vmedevice*);
void	jagintr(Vmedevice*);

int	hsvmeinit(Vmedevice*);
void	hsvmeintr(Vmedevice*);

int	cyclinit(Vmedevice*);
void	cyclintr(Vmedevice*);

int	bitinit(Vmedevice*);
void	bitintr(Vmedevice*);
void	bitstart(void);

int	eagleinit(Vmedevice*);
void	eagleintr(Vmedevice*);
void	eaglestart(void);

void	lanceintr(uchar);
void	lanceparity(void);
int	lanceinit(uchar);
void	lancestart(void);

void	arpreceive(Enpkt*, int, Ifc*);
void	ipreceive(Enpkt*, int, Ifc*);
void	ilrecv(Msgbuf*, Ifc*);

void	getipa(Ifc*);
int	ipforme(uchar*, Ifc*);
int	ipcsum(uchar*);

int	ptclcsum(uchar*, int);
void	ipsend(Msgbuf*);

int	authorise(File *f, Fcall *in);
void	pokewcp(void);
print.c         692664402   0     0     664     3582      `
#include	"u.h"
#include	"lib.h"

#define	PTR	sizeof(char*)
#define	SHORT	sizeof(int)
#define	INT	sizeof(int)
#define	LONG	sizeof(long)
#define	IDIGIT	30
#define	MAXCON	30

static	int	convcount  = { 10 };

#define	PUT(o, c)	if((o)->p < (o)->ep) *(o)->p++ = c

static	int	noconv(Op*);
static	int	cconv(Op*);
static	int	dconv(Op*);
static	int	hconv(Op*);
static	int	lconv(Op*);
static	int	oconv(Op*);
static	int	sconv(Op*);
static	int	uconv(Op*);
static	int	xconv(Op*);
static	int	percent(Op*);

static
int	(*fmtconv[MAXCON])(Op*) =
{
	noconv,
	cconv, dconv, hconv, lconv,
	oconv, sconv, uconv, xconv,
	percent,
};
static
char	fmtindex[128] =
{
	['c'] 1,
	['d'] 2,
	['h'] 3,
	['l'] 4,
	['o'] 5,
	['s'] 6,
	['u'] 7,
	['x'] 8,
	['%'] 9,
};

int
fmtinstall(char c, int (*f)(Op*))
{

	c &= 0177;
	if(fmtindex[c] == 0) {
		if(convcount >= MAXCON)
			return 1;
		fmtindex[c] = convcount++;
	}
	fmtconv[fmtindex[c]] = f;
	return 0;
}

char*
donprint(char *p, char *ep, char *fmt, void *argp)
{
	int sf1, c;
	Op o;

	o.p = p;
	o.ep = ep;
	o.argp = argp;

loop:
	c = *fmt++;
	if(c != '%') {
		if(c == 0) {
			if(o.p < o.ep)
				*o.p = 0;
			return o.p;
		}
		PUT(&o, c);
		goto loop;
	}
	o.f1 = 0;
	o.f2 = -1;
	o.f3 = 0;
	c = *fmt++;
	sf1 = 0;
	if(c == '-') {
		sf1 = 1;
		c = *fmt++;
	}
	while(c >= '0' && c <= '9') {
		o.f1 = o.f1*10 + c-'0';
		c = *fmt++;
	}
	if(sf1)
		o.f1 = -o.f1;
	if(c != '.')
		goto l1;
	c = *fmt++;
	while(c >= '0' && c <= '9') {
		if(o.f2 < 0)
			o.f2 = 0;
		o.f2 = o.f2*10 + c-'0';
		c = *fmt++;
	}
l1:
	if(c == 0)
		fmt--;
	c = (*fmtconv[fmtindex[c&0177]])(&o);
	if(c < 0) {
		o.f3 |= -c;
		c = *fmt++;
		goto l1;
	}
	o.argp = (char*)o.argp + c;
	goto loop;
}

int
numbconv(Op *op, int base)
{
	char b[IDIGIT];
	int i, f, n, r;
	long v;
	short h;

	f = 0;
	switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) {
	case FLONG:
		v = *(long*)op->argp;
		r = LONG;
		break;

	case FUNSIGN|FLONG:
		v = *(ulong*)op->argp;
		r = LONG;
		break;

	case FSHORT:
		h = *(int*)op->argp;
		v = h;
		r = SHORT;
		break;

	case FUNSIGN|FSHORT:
		h = *(int*)op->argp;
		v = (ushort)h;
		r = SHORT;
		break;

	default:
		v = *(int*)op->argp;
		r = INT;
		break;

	case FUNSIGN:
		v = *(unsigned*)op->argp;
		r = INT;
		break;
	}
	if(!(op->f3 & FUNSIGN) && v < 0) {
		v = -v;
		f = 1;
	}
	b[IDIGIT-1] = 0;
	for(i = IDIGIT-2;; i--) {
		n = (ulong)v % base;
		n += '0';
		if(n > '9')
			n += 'a' - ('9'+1);
		b[i] = n;
		if(i < 2)
			break;
		v = (ulong)v / base;
		if(op->f2 >= 0 && i >= IDIGIT-op->f2)
			continue;
		if(v <= 0)
			break;
	}
sout:
	if(f)
		b[--i] = '-';
	strconv(b+i, op, op->f1, -1);
	return r;
}

void
strconv(char *o, Op *op, int f1, int f2)
{
	int n, c;
	char *p;

	n = strlen(o);
	if(f1 >= 0)
		while(n < f1) {
			PUT(op, ' ');
			n++;
		}
	for(p=o; c = *p++;)
		if(f2 != 0) {
			PUT(op, c);
			f2--;
		}
	if(f1 < 0) {
		f1 = -f1;
		while(n < f1) {
			PUT(op, ' ');
			n++;
		}
	}
}

static	int
noconv(Op *op)
{

	strconv("***", op, 0, -1);
	return 0;
}

static	int
cconv(Op *op)
{
	char b[2];

	b[0] = *(int*)op->argp;
	b[1] = 0;
	strconv(b, op, op->f1, -1);
	return INT;
}

static	int
dconv(Op *op)
{

	return numbconv(op, 10);
}

static	int
hconv(Op *op)
{

	USED(op);
	return -FSHORT;
}

static	int
lconv(Op *op)
{

	USED(op);
	return -FLONG;
}

static	int
oconv(Op *op)
{

	return numbconv(op, 8);
}

static	int
sconv(Op *op)
{

	strconv(*(char**)op->argp, op, op->f1, op->f2);
	return PTR;
}

static	int
uconv(Op *op)
{

	USED(op);
	return -FUNSIGN;
}

static	int
xconv(Op *op)
{

	return numbconv(op, 16);
}

static	int
percent(Op *op)
{

	PUT(op, '%');
	return 0;
}
proc.c          714761128   0     0     664     4988      `
#include	"u.h"
#include	"lib.h"
#include	"dat.h"
#include	"fns.h"
#include	"mem.h"

struct
{
	Lock;
	ulong	pid;
} pidalloc;

struct
{
	Lock;
	User	*arena;
	User	*free;
} procalloc;

struct
{
	Lock;
	User	*head;
	User	*tail;
} runq;

char *statename[] =	/* BUG: generate automatically */
{
	[Dead]		"Dead",
	[Moribund]	"Moribund",
	[Zombie]	"Zombie",
	[Ready]		"Ready",
	[Scheding]	"Scheding",
	[Running]	"Running",
	[Queueing]	"Queueing",
	[Sending]	"Sending",
	[Recving]	"Recving",
	[MMUing]	"MMUing",
	[Exiting]	"Exiting",
	[Inwait]	"Inwait",
	[Wakeme]	"Wakeme",
	[Broken]	"Broken",
};

static
void
cmd_trace(int argc, char *argv[])
{
	int n;

	n = 0;
	if(argc > 1)
		n = number(argv[1], n, 10);
	dotrace(n);
}

static
void
cmd_cpu(int argc, char *argv[])
{
	int i, j;
	User *p;
	char *text;

	p = procalloc.arena;
	for(i=0; i<conf.nproc; i++,p++) {
		text = p->text;
		if(!text)
			continue;
		for(j=1; j<argc; j++)
			if(strcmp(argv[j], text) == 0)
				goto found;
		if(argc > 1)
			continue;
	found:
		print("	pid=%2d.%.3s t=%F\n",
			p->pid, text,
			(Filta){&p->time, 1});
		prflush();
	}
}

static
void
cmd_statp(int argc, char *argv[])
{
	int i, j;
	User *p;
	char *text;

	print("process stats\n");
	p = procalloc.arena;
	for(i=0; i<conf.nproc; i++,p++) {
		text = p->text;
		if(!text)
			continue;
		for(j=1; j<argc; j++)
			if(strcmp(argv[j], text) == 0)
				goto found;
		if(argc > 1)
			continue;
	found:
		print("	pid=%2d.%.3s state=%9s %F\n",
			p->pid, text,
			statename[p->state],
			(Filta){&p->time, 1});
		prflush();
	}
}

/*
 * Always splhi()'ed.
 */
void
schedinit(void)		/* never returns */
{
	User *p;

	setlabel(&m->sched);
	if(u) {
		m->proc = 0;
		p = u;
		u = 0;
		if(p->state == Running)
			ready(p);
		p->mach = 0;
	}
	sched();
}

void
sched(void)
{
	User *p;
	void (*f)(Ureg *, ulong);

	if(u) {
		splhi();
		if(setlabel(&u->sched)) {	/* woke up */
			if(u->mach)
				panic("mach non zero");
			u->state = Running;
			u->mach = m;
			m->proc = u;
			spllo();
			return;
		}
		gotolabel(&m->sched);
	}
	if(f = m->intr){			/* assign = */
		m->intr = 0;
		(*f)(m->ureg, m->cause);
	}
	spllo();
	p = runproc();
	splhi();
	u = p;
	gotolabel(&p->sched);
}

void
ready(User *p)
{
	int s;

	s = splhi();
	lock(&runq);
	p->rnext = 0;
	if(runq.tail)
		runq.tail->rnext = p;
	else
		runq.head = p;
	runq.tail = p;
	p->state = Ready;
	unlock(&runq);
	splx(s);
}

/*
 * Always called spllo
 */
User*
runproc(void)
{
	User *p;

loop:
	while(runq.head == 0)
		;
	splhi();
	lock(&runq);
	p = runq.head;
	if(p==0 || p->mach){
		unlock(&runq);
		spllo();
		goto loop;
	}
	if(p->rnext == 0)
		runq.tail = 0;
	runq.head = p->rnext;
	if(p->state != Ready)
		print("runproc %d %s\n", p->pid, statename[p->state]);
	unlock(&runq);
	p->state = Scheding;
	spllo();
	return p;
}

User*
newproc(void)
{
	User *p;

loop:
	lock(&procalloc);
	if(p = procalloc.free) {		/* assign = */
		procalloc.free = p->qnext;
		p->state = Zombie;
		unlock(&procalloc);
		p->mach = 0;
		p->qnext = 0;
		p->exiting = 0;
		lock(&pidalloc);
		p->pid = ++pidalloc.pid;
		unlock(&pidalloc);
		return p;
	}
	unlock(&procalloc);
	panic("no procs");
	goto loop;
}

void
procinit(void)
{
	User *p;
	int i;

	procalloc.free = ialloc(conf.nproc*sizeof(User), 0);
	procalloc.arena = procalloc.free;

	p = procalloc.free;
	for(i=0; i<conf.nproc-1; i++,p++)
		p->qnext = p+1;
	p->qnext = 0;

	cmd_install("cpu", "[proc] -- process cpu time", cmd_cpu); /**/
	cmd_install("trace", "[number] -- stack trace/qlocks", cmd_trace); /**/
	cmd_install("statp", "[proc] -- process stats", cmd_statp); /**/
}

void
sleep(Rendez *r, int (*f)(void*), void *arg)
{
	User *p;
	int s;

	/*
	 * spl is to allow lock to be called
	 * at interrupt time. lock is mutual exclusion
	 */
	s = splhi();
	lock(r);

	/*
	 * if condition happened, never mind
	 */
	if((*f)(arg)) {	
		unlock(r);
		splx(s);
		return;
	}

	/*
	 * now we are committed to
	 * change state and call scheduler
	 */
	p = r->p;
	if(p)
		print("double sleep %d\n", p->pid);
	u->state = Wakeme;
	r->p = u;

	/*
	 * sched does the unlock(u) when it is safe
	 * it also allows to be called splhi.
	 */
	unlock(r);
	sched();
}

void
wakeup(Rendez *r)
{
	User *p;
	int s;

	s = splhi();
	lock(r);
	p = r->p;
	if(p) {
		r->p = 0;
		if(p->state != Wakeme)
			panic("wakeup: not Wakeme");
		ready(p);
	}
	unlock(r);
	splx(s);
}

void
dotrace(int n)
{
	User *p;
	QLock *q;
	int i, j, f;

	p = procalloc.arena;
	for(i=0; i<conf.nproc; i++, p++) {
		if(n) {
			if(p->pid == n)
				dumpstack(p);
			continue;
		}
		if(q = p->has.want) {
			if(q->name)
				print("pid %d wants %.10s\n",
					p->pid, q->name);
			else
				print("pid %d wants %lux\n",
					p->pid, q);
		}
		f = 0;
		for(j=0; j<NHAS; j++) {
			if(q = p->has.q[j]) {
				if(f == 0) {
					if(p->has.want == 0)
						print("pid %d wants nothing\n", p->pid);
					print("	has");
					f = 1;
				}
				if(q->name)
					print(" %.10s", q->name);
				else
					print(" %lux", q);
			}
		}
		if(f)
			print("\n");
	}
}
stil.c          693215548   0     0     664     14363     `
/*
 * stil - Internet link protocol
 */
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"
#include	"arp.h"
#include 	"ipdat.h"

#define	 DBG	if(0)print
int 		ilcksum = 1;
static 	int 	initseq = 25000;
static	Rendez	ilackr;

char	*ilstate[] = { "Closed", "Syncer", "Syncee", "Established", "Listening", "Closing" };
char	*iltype[] =  { "sync", "data", "dataquerey", "ack", "querey", "state", "close" };
static char *etime = "connection timed out";

/* Always Acktime < Fasttime < Slowtime << Ackkeepalive */
enum
{
	Iltickms 	= 100,
	Slowtime 	= 200*Iltickms,
	Fasttime 	= 4*Iltickms,
	Acktime		= 2*Iltickms,
	Ackkeepalive	= 6000*Iltickms,
	Defaultwin	= 20,
};

#define Starttimer(s)	{(s)->timeout = 0; (s)->fasttime = Fasttime;}
/* Packet dropping putnext for testing */
#define DPUTNEXT(q, b)	if((MACHP(0)->ticks&7) != 3)PUTNEXT(q, b);else{freeb(b);print(".");}

void	ilrcvmsg(Ipconv*, Block*);
void	ilackproc(void*);
void	ilsendctl(Ipconv*, Ilhdr*, int, ulong, ulong);
void	ilackq(Ilcb*, Block*);
void	ilprocess(Ipconv*, Ilhdr*, Block*);
void	ilpullup(Ipconv*);
void	ilhangup(Ipconv*, char *);
void	ilfreeq(Ilcb*);
void	ilrexmit(Ilcb*);
void	ilbackoff(Ilcb*);

void
ilopen(Queue *q, Stream *s)
{
	Ipconv *ipc;
	static int ilkproc;

	if(!Ipoutput) {
		Ipoutput = WR(q);
		s->opens++;
		s->inuse++;
	}

	if(ilkproc == 0) {
		ilkproc = 1;
		kproc("ilack", ilackproc, ipconv[s->dev]);
	}

	ipc = &ipconv[s->dev][s->id];
	ipc->ipinterface = newipifc(IP_ILPROTO, ilrcvmsg, ipconv[s->dev],
			            1500, 512, ETHER_HDR, "IL");

	ipc->readq = RD(q);	
	RD(q)->ptr = (void *)ipc;
	WR(q)->next->ptr = (void *)ipc->ipinterface;
	WR(q)->ptr = (void *)ipc;
}

void
ilclose(Queue *q)
{
	Ipconv *s;
	Ilcb *ic;
	Block *bp, *next;

	s = (Ipconv *)(q->ptr);
	ic = &s->ilctl;
	qlock(s);
	s->readq = 0;
	qunlock(s);

	switch(ic->state) {
	case Ilclosing:
	case Ilclosed:
		break;
	case Ilsyncer:
	case Ilsyncee:
	case Ilestablished:
		ilfreeq(ic);
		ic->state = Ilclosing;
		ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
		break;
	case Illistening:
		ic->state = Ilclosed;
		s->psrc = 0;
		s->pdst = 0;
		s->dst = 0;
		break;
	}
}

void
iloput(Queue *q, Block *bp)
{
	Ipconv *ipc;
	Ilhdr *ih;
	Ilcb *ic;
	int dlen;
	Block *np, *f;

	ipc = (Ipconv *)(q->ptr);
	if(ipc->psrc == 0)
		error(Enoport);

	ic = &ipc->ilctl;
	switch(ic->state) {
	case Ilclosed:
	case Illistening:
	case Ilclosing:
		error(Ehungup);
	}

	if(bp->type != M_DATA) {
		freeb(bp);
		error(Ebadctl);
	}

	/* Only allow atomic Il writes to form datagrams */
	for(f = bp; f->next; f = f->next)
		;
	if((f->flags & S_DELIM) == 0) {
		freeb(bp);
		error(Emsgsize);
	}

	dlen = blen(bp);
	if(dlen > IL_DATMAX) {
		freeb(bp);
		error(Emsgsize);
	}

	/* Make space to fit il & ip & ethernet header */
	bp = padb(bp, IL_EHSIZE+IL_HDRSIZE);
	ih = (Ilhdr *)(bp->rptr);

	/* Ip fields */
	hnputl(ih->src, Myip);
	hnputl(ih->dst, ipc->dst);
	ih->proto = IP_ILPROTO;
	/* Il fields */
	hnputs(ih->illen, dlen+IL_HDRSIZE);
	hnputs(ih->ilsrc, ipc->psrc);
	hnputs(ih->ildst, ipc->pdst);
	hnputl(ih->ilid, ic->next++);
	hnputl(ih->ilack, ic->recvd);
	ih->iltype = Ildata;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	/* Checksum of ilheader plus data (not ip & no pseudo header) */
	if(ilcksum)
		hnputs(ih->ilsum, ptcl_csum(bp, IL_EHSIZE, dlen+IL_HDRSIZE));
	ilackq(ic, bp);
	ic->acktime = Ackkeepalive;

	PUTNEXT(q, bp);
}

void
ilackq(Ilcb *ic, Block *bp)
{
	Block *np;

	/* Enqueue a copy on the unacked queue in case this one gets lost */
	np = copyb(bp, blen(bp));
	qlock(&ic->ackq);
	if(ic->unacked)
		ic->unackedtail->list = np;
	else {
		/* Start timer since we may have been idle for some time */
		Starttimer(ic);
		ic->unacked = np;
	}
	ic->unackedtail = np;
	qunlock(&ic->ackq);
	np->list = 0;
}

void
ilackto(Ilcb *ic, ulong ackto)
{
	Ilhdr *h;
	Block *bp;
	ulong id;

	qlock(&ic->ackq);
	while(ic->unacked) {
		h = (Ilhdr *)ic->unacked->rptr;
		id = nhgetl(h->ilid);
		if(ackto < id)
			break;

		bp = ic->unacked;
		ic->unacked = bp->list;
		bp->list = 0;
		freeb(bp);
	}
	qunlock(&ic->ackq);
}

void
iliput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

void
ilrcvmsg(Ipconv *ipc, Block *bp)
{
	Ilhdr *ih;
	Ilcb *ic;
	int plen, illen;
	Ipconv *s, *etab, *new;
	short sp, dp;
	Ipaddr dst;
	char *st;

	ih = (Ilhdr *)bp->rptr;
	plen = blen(bp);
	if(plen < Ipsize+Ilsize)
		goto drop;

	illen = nhgets(ih->illen);
	if(illen+IL_EHSIZE > plen)
		goto drop;

	sp = nhgets(ih->ildst);
	dp = nhgets(ih->ilsrc);
	dst = nhgetl(ih->src);

	if(ilcksum && ptcl_csum(bp, IL_EHSIZE, illen) != 0) {
		st = (ih->iltype < 0 || ih->iltype > Ilclose) ? "?" : iltype[ih->iltype];
		print("il: cksum error, pkt(%s id %d ack %d %d.%d.%d.%d/%d->%d)\n",
			st, nhgetl(ih->ilid), nhgetl(ih->ilack), fmtaddr(dst), sp, dp);
		goto drop;
	}

	etab = &ipc[conf.ip];
	for(s = ipc; s < etab; s++)
		if(s->psrc == sp)
		if(s->pdst == dp)
		if(s->dst == dst) {
			ilprocess(s, ih, bp);
			return;
		}

	if(ih->iltype != Ilsync)
		goto drop;

	/* Look for a listener */
	for(s = ipc; s < etab; s++) {
		if(s->ilctl.state == Illistening)
		if(s->pdst == 0)
		if(s->dst == 0) {
			if(s->curlog > s->backlog)
				goto reset;

			new = ipincoming(ipc, s);
			if(new == 0)
				goto reset;

			new->newcon = 1;
			new->ipinterface = s->ipinterface;
			new->psrc = sp;
			new->pdst = dp;
			new->dst = nhgetl(ih->src);

			ic = &new->ilctl;
			ic->state = Ilsyncee;
			initseq += TK2MS(MACHP(0)->ticks);
			ic->start = initseq;
			ic->next = ic->start+1;
			ic->recvd = 0;
			ic->rstart = nhgetl(ih->ilid);
			ic->slowtime = Slowtime;
			ic->window = Defaultwin;
			ilprocess(new, ih, bp);

			s->ipinterface->ref++;
			s->curlog++;
			wakeup(&s->listenr);
			return;
		}
	}
drop:
	freeb(bp);
	return;
reset:
	ilsendctl(0, ih, Ilclose, 0, 0);
	freeb(bp);
}

void
_ilprocess(Ipconv *s, Ilhdr *h, Block *bp)
{
	Ilcb *ic;
	Block *nb, *next;
	ulong id, ack, dlen;

	id = nhgetl(h->ilid);
	ack = nhgetl(h->ilack);
	ic = &s->ilctl;

	switch(ic->state) {
	default:
		panic("il unknown state");
	case Ilclosed:
		freeb(bp);
		break;
	case Ilsyncer:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(ack != ic->start) {
				ic->state = Ilclosed;
				ilhangup(s, "connection rejected");
			}
			else {
				ic->recvd = id;
				ic->rstart = id;
				ilsendctl(s, 0, Ilack, ic->next, ic->recvd);
				ic->state = Ilestablished;
				ilpullup(s);
				Starttimer(ic);
			}
			break;
		case Ilclose:
			if(ack == ic->start) {
				ic->state = Ilclosed;
				ilhangup(s, "remote close");
			}
			break;
		}
		freeb(bp);
		break;
	case Ilsyncee:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(id != ic->rstart || ack != 0)
				ic->state = Ilclosed;
			else {
				ic->recvd = id;
				ilsendctl(s, 0, Ilsync, ic->start, ic->recvd);
				Starttimer(ic);
			}
			break;
		case Ilack:
			if(ack == ic->start) {
				ic->state = Ilestablished;
				ilpullup(s);
				Starttimer(ic);
			}
			break;
		case Ilclose:
			if(id == ic->next) {
				ic->state = Ilclosed;
				ilhangup(s, "remote close");
			}
			break;
		}
		freeb(bp);
		break;
	case Ilestablished:
		switch(h->iltype) {
		case Ilsync:
			if(id != ic->start) {
				ic->state = Ilclosed;
				ilhangup(s, "remote close");
			}
			else {
				ilsendctl(s, 0, Ilack, ic->next, ic->rstart);
				Starttimer(ic);
			}
			freeb(bp);	
			break;
		case Ildata:
			Starttimer(ic);
			ilackto(ic, ack);
			ic->acktime = Acktime;
			iloutoforder(s, h, bp);
			ilpullup(s);
			break;
		case Ildataquery:
			Starttimer(ic);
			ilackto(ic, ack);
			ic->acktime = Acktime;
			iloutoforder(s, h, bp);
			ilpullup(s);
			ilsendctl(s, 0, Ilstate, ic->next, ic->recvd);
			break;
		case Ilack:
			ilackto(ic, ack);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilquerey:
			ilackto(ic, ack);
			ilsendctl(s, 0, Ilstate, ic->next, ic->recvd);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilstate:
			ilackto(ic, ack);
			ilrexmit(ic);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilclose:
			freeb(bp);
			if(ack < ic->start || ack > ic->next) 
				break;
			ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
			ic->state = Ilclosing;
			ilfreeq(ic);
			Starttimer(ic);
			break;
		}
		break;
	case Illistening:
		freeb(bp);
		break;
	case Ilclosing:
		switch(h->iltype) {
		case Ilclose:
			ic->recvd = id;
			ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
			if(ack == ic->next) {
				ic->state = Ilclosed;
				ilhangup(s, 0);
			}
			Starttimer(ic);
			break;
		default:
			break;
		}
		freeb(bp);
		break;
	}
}

void
ilrexmit(Ilcb *ic)
{
	Block *nb;
	Ilhdr *h;

	if(ic->unacked == 0)
		return;

	nb = copyb(ic->unacked, blen(ic->unacked));
	h = (Ilhdr*)nb->rptr;
	DBG("rxmit %d.", nhgetl(h->ilid));

	h->iltype = Ildataquery;
	hnputl(h->ilack, ic->recvd);
	h->ilsum[0] = 0;
	h->ilsum[1] = 0;
	if(ilcksum)
		hnputs(h->ilsum, ptcl_csum(nb, IL_EHSIZE, nhgets(h->illen)));

	PUTNEXT(Ipoutput, nb);
}

/* DEBUG */
void
ilprocess(Ipconv *s, Ilhdr *h, Block *bp)
{
	Ilcb *ic = &s->ilctl;

	USED(ic);
	DBG("%11s rcv %d/%d snt %d/%d pkt(%s id %d ack %d %d->%d) ",
		ilstate[ic->state],  ic->rstart, ic->recvd, ic->start, ic->next,
		iltype[h->iltype], nhgetl(h->ilid), nhgetl(h->ilack), 
		nhgets(h->ilsrc), nhgets(h->ildst));

	_ilprocess(s, h, bp);

	DBG("%11s rcv %d snt %d\n", ilstate[ic->state], ic->recvd, ic->next);
}

void
ilhangup(Ipconv *s, char *msg)
{
	Block *nb;
	int l;

	DBG("hangup! %s %d/%d\n", msg ? msg : "??", s->psrc, s->pdst);
	if(s->readq) {
		if(msg) {
			l = strlen(msg);
			nb = allocb(l);
			strcpy((char*)nb->wptr, msg);
			nb->wptr += l;
		}
		else
			nb = allocb(0);
		nb->type = M_HANGUP;
		nb->flags |= S_DELIM;
		PUTNEXT(s->readq, nb);
	}
	s->psrc = 0;
	s->pdst = 0;
	s->dst = 0;
}

void
ilpullup(Ipconv *s)
{
	Ilcb *ic;
	Ilhdr *oh;
	ulong oid, dlen;
	Block *bp;

	if(s->readq == 0)
		return;

	ic = &s->ilctl;
	if(ic->state != Ilestablished)
		return;

	qlock(&ic->outo);
	while(ic->outoforder) {
		bp = ic->outoforder;
		oh = (Ilhdr*)bp->rptr;
		oid = nhgetl(oh->ilid);
		if(oid <= ic->recvd) {
			ic->outoforder = bp->list;
			freeb(bp);
			continue;
		}
		if(oid != ic->recvd+1)
			break;

		ic->recvd = oid;
		ic->outoforder = bp->list;
		ic->oblks--;

		qunlock(&ic->outo);
		bp->list = 0;
		dlen = nhgets(oh->illen)-IL_HDRSIZE;
		bp = btrim(bp, IL_EHSIZE+IL_HDRSIZE, dlen);
		PUTNEXT(s->readq, bp);
		qlock(&ic->outo);
	}
	qunlock(&ic->outo);
}

void
iloutoforder(Ipconv *s, Ilhdr *h, Block *bp)
{
	Block *f, **l;
	Ilcb *ic;
	ulong id;
	uchar *lid;

	ic = &s->ilctl;
	bp->list = 0;


	id = nhgetl(h->ilid);
	/* Window checks */
	if(id <= ic->recvd || ic->oblks > ic->window) {
		freeb(bp);
		return;
	}

	/* Packet is acceptable so sort onto receive queue for pullup */
	qlock(&ic->outo);
	ic->oblks++;
	if(ic->outoforder == 0)
		ic->outoforder = bp;
	else {
		l = &ic->outoforder;
		for(f = *l; f; f = f->list) {
			lid = ((Ilhdr*)(f->rptr))->ilid;
			if(id < nhgetl(lid)) {
				bp->list = f;
				*l = bp;
				qunlock(&ic->outo);
				return;
			}
			l = &f->list;
		}
		*l = bp;
	}
	qunlock(&ic->outo);
}

static
void
ilsendctl(Ipconv *ipc, Ilhdr *inih, int type, ulong id, ulong ack)
{
	Ilhdr *ih;
	Ilcb *ic;
	Block *bp;

	bp = allocb(IL_EHSIZE+IL_HDRSIZE);
	bp->wptr += IL_EHSIZE+IL_HDRSIZE;
	bp->flags |= S_DELIM;

	ih = (Ilhdr *)(bp->rptr);
	ic = &ipc->ilctl;

	/* Ip fields */
	ih->proto = IP_ILPROTO;
	hnputl(ih->src, Myip);
	hnputs(ih->illen, IL_HDRSIZE);
	if(inih) {
		hnputl(ih->dst, nhgetl(inih->src));
		hnputs(ih->ilsrc, nhgets(inih->ildst));
		hnputs(ih->ildst, nhgets(inih->ilsrc));
		hnputl(ih->ilid, nhgetl(inih->ilack));
		hnputl(ih->ilack, nhgetl(inih->ilid));
	}
	else {
		hnputl(ih->dst, ipc->dst);
		hnputs(ih->ilsrc, ipc->psrc);
		hnputs(ih->ildst, ipc->pdst);
		hnputl(ih->ilid, id);
		hnputl(ih->ilack, ack);
		ic->acktime = Ackkeepalive;
	}
	ih->iltype = type;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	if(ilcksum)
		hnputs(ih->ilsum, ptcl_csum(bp, IL_EHSIZE, IL_HDRSIZE));

/*	DBG("\nctl(%s id %d ack %d %d->%d)\n",
		iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), 
		nhgets(ih->ilsrc), nhgets(ih->ildst));
*/
	PUTNEXT(Ipoutput, bp);
}

void
ilackproc(void *a)
{
	Ipconv *base, *end, *s;
	Ilcb *ic;
	Block *bp, *np;

	base = (Ipconv*)a;
	end = &base[conf.ip];

	for(;;) {
		tsleep(&ilackr, return0, 0, Iltickms);
		for(s = base; s < end; s++) {
			ic = &s->ilctl;
			ic->timeout += Iltickms;
			switch(ic->state) {
			case Ilclosed:
			case Illistening:
				break;
			case Ilclosing:
				if(ic->timeout >= ic->fasttime) {
					ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime) {
					ic->state = Ilclosed;
					ilhangup(s, 0);
				}
				break;
			case Ilsyncee:
			case Ilsyncer:
				if(ic->timeout >= ic->fasttime) {
					ilsendctl(s, 0, Ilsync, ic->start, ic->recvd);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime) {
					ic->state = Ilclosed;
					ilhangup(s, etime);
				}
				break;
			case Ilestablished:
				ic->acktime -= Iltickms;
				if(ic->acktime <= 0)
					ilsendctl(s, 0, Ilack, ic->next, ic->recvd);
				if(ic->unacked == 0) {
					ic->timeout = 0;
					break;
				}
				if(ic->timeout >= ic->fasttime) {
					ilrexmit(ic);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime) {
					ic->state = Ilclosed;
					ilhangup(s, etime);
					break;
				}
				break;
			}
		}
	}
}

void
ilbackoff(Ilcb *ic)
{
	if(ic->fasttime < Slowtime/2)
		ic->fasttime += Fasttime;
	else
		ic->fasttime = (ic->fasttime)*3/2;
}

void
ilstart(Ipconv *ipc, int type, int window)
{
	Ilcb *ic = &ipc->ilctl;

	if(ic->state != Ilclosed)
		return;

	ic->timeout = 0;
	ic->unacked = 0;
	ic->outoforder = 0;
	ic->slowtime = Slowtime;


	initseq += TK2MS(MACHP(0)->ticks);
	ic->start = initseq;
	ic->next = ic->start+1;
	ic->recvd = 0;
	ic->window = window;
	ic->oblks = 0;

	switch(type) {
	case IL_PASSIVE:
		ic->state = Illistening;
		break;
	case IL_ACTIVE:
		ic->state = Ilsyncer;
		ilsendctl(ipc, 0, Ilsync, ic->start, ic->recvd);
		break;
	}
}

void
ilfreeq(Ilcb *ic)
{
	Block *bp, *next;

	qlock(&ic->ackq);
	for(bp = ic->unacked; bp; bp = next) {
		next = bp->list;
		freeb(bp);
	}
	ic->unacked = 0;
	qunlock(&ic->ackq);

	qlock(&ic->outo);
	for(bp = ic->outoforder; bp; bp = next) {
		next = bp->list;
		freeb(bp);
	}
	ic->outoforder = 0;
	qunlock(&ic->outo);
}

stip.c          693954042   0     0     664     11293     `
/*
 *  ethernet specific multiplexor for ip
 *
 *  this line discipline gets pushed onto an ethernet channel
 *  to demultiplex/multiplex ip conversations.
 */
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"
#include	"arp.h"
#include 	"ipdat.h"

#define DPRINT if(pip)print
int pip = 0;
int ipcksum = 1;
extern Ipifc *ipifc;
int Id = 1;

Fragq		*flisthead;
Fragq		*fragfree;
QLock		fraglock;

Queue 		*Etherq;

Ipaddr		Myip;
Ipaddr		Mymask;
Ipaddr		Mynetmask;
uchar		Netmyip[4];	/* In Network byte order */
uchar		bcast[4] = { 0xff, 0xff, 0xff, 0xff };

/* Predeclaration */
static void	ipetherclose(Queue*);
static void	ipetheriput(Queue*, Block*);
static void	ipetheropen(Queue*, Stream*);
static void	ipetheroput(Queue*, Block*);

/*
 *  the ethernet multiplexor stream module definition
 */
Qinfo ipinfo =
{
	ipetheriput,
	ipetheroput,
	ipetheropen,
	ipetherclose,
	"internet"
};

void
initfrag(int size)
{
	Fragq *fq, *eq;

	fragfree = (Fragq*)ialloc(sizeof(Fragq) * size, 0);

	eq = &fragfree[size];
	for(fq = fragfree; fq < eq; fq++)
		fq->next = fq+1;

	fragfree[size-1].next = 0;
}

/*
 *  set up an ether interface
 */
static void
ipetheropen(Queue *q, Stream *s)
{
	Ipconv *ipc;

	/* First open is by ipconfig and sets up channel
	 * to ethernet
	 */
	if(!Etherq) {
		Etherq = WR(q);
		s->opens++;		/* Hold this queue in place */
		s->inuse++;
	} else {
		ipc = &ipconv[s->dev][s->id];
		RD(q)->ptr = (void *)ipc;
		WR(q)->ptr = (void *)ipc;
		ipc->ref = 1;
	}

	DPRINT("ipetheropen EQ %lux dev=%d id=%d RD %lux WR %lux\n",
		Etherq, s->dev, s->id, RD(q), WR(q));
}

/*
 * newipifc - Attach to or Create a new protocol interface
 */

Ipifc *
newipifc(uchar ptcl, void (*recvfun)(Ipconv *, Block *bp),
	 Ipconv *con, int max, int min, int hdrsize, char *name)
{
	Ipifc *ifc, *free;
 
	free = 0;
	for(ifc = ipifc; ifc < &ipifc[conf.ipif]; ifc++) {
		qlock(ifc);
		if(ifc->protocol == ptcl) {
			ifc->ref++;
			qunlock(ifc);
			return(ifc);
		}
		if(!free && ifc->ref == 0) {
			ifc->ref = 1;
			free = ifc;
		}
		else
			qunlock(ifc);
	}

	if(!free)
		error(Enoifc);

	free->iprcv = recvfun;

	/* If media supports large transfer units limit maxmtu
	 * to max ip size */
	if(max > IP_MAX)
		max = IP_MAX;
	free->maxmtu = max;
	free->minmtu = min;
	free->hsize = hdrsize;
	free->connections = con;

	free->protocol = ptcl;
	strncpy(free->name, name, NAMELEN);

	qunlock(free);
	return(free);
}

static void
ipetherclose(Queue *q)
{
	Ipconv *ipc;

	ipc = (Ipconv *)(q->ptr);
	if(ipc){
		netdisown(ipc->net, ipc->index);
		ipc->ref = 0;
	}
}

void
closeipifc(Ipifc *ifc)
{
	/* If this is the last reference to the protocol multiplexor
	 * cancel upcalls from this stream
	 */
	qlock(ifc);
	if(--ifc->ref == 0) {
		ifc->protocol = 0;
		ifc->name[0] = 0;
	}
	qunlock(ifc);
}

static void
ipetheroput(Queue *q, Block *bp)
{
	Etherhdr *eh, *feh;
	int	 lid, len, seglen, chunk, dlen, blklen, offset;
	Ipifc	 *ifp;
	ushort	 fragoff;
	Block	 *xp, *nb;
	uchar 	 *ptr;

	if(bp->type != M_DATA){
		/* Allow one setting of the ip address */
		if(!Myip && streamparse("setip", bp)) {
			ptr = bp->rptr;
			Myip = ipparse((char *)ptr);
			Netmyip[0] = (Myip>>24)&0xff;
			Netmyip[1] = (Myip>>16)&0xff;
			Netmyip[2] = (Myip>>8)&0xff;
			Netmyip[3] = Myip&0xff;
			Mymask = classmask[Myip>>30];
			while(*ptr != ' ' && *ptr)
				ptr++;
			if(*ptr)
				Mymask = ipparse((char *)ptr);
			/*
			 * Temporary Until we understand real subnets
			 */
			Mynetmask = Mymask;
			freeb(bp);
		}
		else
			PUTNEXT(Etherq, bp);
		return;
	}

	ifp = (Ipifc *)(q->ptr);

	/* Number of bytes in ip and media header to write */
	len = blen(bp);

	/* Fill out the ip header */
	eh = (Etherhdr *)(bp->rptr);
	eh->vihl = IP_VER|IP_HLEN;
	eh->tos = 0;
	eh->ttl = 255;

	/* If we dont need to fragment just send it */
	if(len <= ifp->maxmtu) {
		hnputs(eh->length, len-ETHER_HDR);
		hnputs(eh->id, Id++);
		eh->frag[0] = 0;
		eh->frag[1] = 0;
		eh->cksum[0] = 0;
		eh->cksum[1] = 0;
		hnputs(eh->cksum, ip_csum(&eh->vihl));

		/* Finally put in the type and pass down to the arp layer */
		hnputs(eh->type, ET_IP);
		PUTNEXT(Etherq, bp);
		return;
	}

	if(eh->frag[0] & (IP_DF>>8))
		goto drop;

	seglen = (ifp->maxmtu - (ETHER_HDR+ETHER_IPHDR)) & ~7;
	if(seglen < 8)
		goto drop;

	/* Make prototype output header */
	hnputs(eh->type, ET_IP);
	
	dlen = len - (ETHER_HDR+ETHER_IPHDR);
	xp = bp;
	lid = Id++;

	offset = ETHER_HDR+ETHER_IPHDR;
	while(xp && offset && offset >= BLEN(xp)) {
		offset -= BLEN(xp);
		xp = xp->next;
	}
	xp->rptr += offset;

	for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
		nb = allocb(ETHER_HDR+ETHER_IPHDR+seglen);
		feh = (Etherhdr *)(nb->rptr);

		memmove(nb->wptr, eh, ETHER_HDR+ETHER_IPHDR);
		nb->wptr += ETHER_HDR+ETHER_IPHDR;

		if((fragoff + seglen) >= dlen) {
			seglen = dlen - fragoff;
			hnputs(feh->frag, fragoff>>3);
		}
		else {	
			hnputs(feh->frag, (fragoff>>3)|IP_MF);
		}

		hnputs(feh->length, seglen + ETHER_IPHDR);
		hnputs(feh->id, lid);

		/* Copy up the data area */
		chunk = seglen;
		while(chunk) {
			if(!xp) {
				freeb(nb);
				goto drop;
			}
			blklen = MIN(BLEN(xp), chunk);
			memmove(nb->wptr, xp->rptr, blklen);
			nb->wptr += blklen;
			xp->rptr += blklen;
			chunk -= blklen;
			if(xp->rptr == xp->wptr)
				xp = xp->next;
		} 
				
		feh->cksum[0] = 0;
		feh->cksum[1] = 0;
		hnputs(feh->cksum, ip_csum(&feh->vihl));
		nb->flags |= S_DELIM;
		PUTNEXT(Etherq, nb);
	}
drop:
	freeb(bp);	
}


/*
 *  Input a packet and use the ip protocol to select the correct
 *  device to pass it to.
 *
 */
static void
ipetheriput(Queue *q, Block *bp)
{
	Ipifc 	 *ep, *ifp;
	Etherhdr *h;
	ushort   frag;

	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	h = (Etherhdr *)(bp->rptr);

	/* Ensure we have enough data to process */
	if(BLEN(bp) < (ETHER_HDR+ETHER_IPHDR)) {
		bp = pullup(bp, ETHER_HDR+ETHER_IPHDR);
		if(!bp)
			return;
	}

	/* Look to see if its for me before we waste time checksuming it */
	if(ipforme(h->dst) == 0)
		goto drop;


	if(ipcksum && ip_csum(&h->vihl)) {
		print("ip: checksum error (from %d.%d.%d.%d ?)\n",
		      h->src[0], h->src[1], h->src[2], h->src[3]);
		goto drop;
	}

	/* Check header length and version */
	if(h->vihl != (IP_VER|IP_HLEN))
		goto drop;

	frag = nhgets(h->frag);
	if(frag) {
		h->tos = frag & IP_MF ? 1 : 0;
		bp = ip_reassemble(frag, bp, h);
		if(!bp)
			return;
	}

	/*
 	 * Look for an ip interface attached to this protocol
	 */
	ep = &ipifc[conf.ipif];
	for(ifp = ipifc; ifp < ep; ifp++)
		if(ifp->protocol == h->proto) {
			(*ifp->iprcv)(ifp->connections, bp);
			return;
		}
			
drop:
	freeb(bp);
}

int
ipforme(uchar *addr)
{
	Ipaddr haddr;

	if(memcmp(addr, Netmyip, sizeof(Netmyip)) == 0)
		return 1;

	haddr = nhgetl(addr);

	/* My subnet broadcast */
	if((haddr&Mymask) == (Myip&Mymask))
		return 1;

	/* My network broadcast */
	if((haddr&Mynetmask) == (Myip&Mynetmask))
		return 1;

	/* Real ip broadcast */
	if(haddr == 0)
		return 1;

	/* Old style 255.255.255.255 address */
	if(haddr == ~0)
		return 1;

	return 0;
}
Block *
ip_reassemble(int offset, Block *bp, Etherhdr *ip)
{
	Fragq *f;
	Ipaddr src, dst;
	ushort id;
	Block *bl, **l, *last, *prev;
	int ovlap, len, fragsize;

	src = nhgetl(ip->src);
	dst = nhgetl(ip->dst);
	id = nhgets(ip->id);

	for(f = flisthead; f; f = f->next) {
		if(f->src == src && f->dst == dst && f->id == id)
			break;
	}

	if(!ip->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
		if(f) {
			qlock(f);
			ipfragfree(f);
		}
		return(bp);
	}

	BLKFRAG(bp)->foff = offset<<3;
	BLKFRAG(bp)->flen = nhgets(ip->length) - ETHER_IPHDR; /* Ip data length */
	bp->flags &= ~S_DELIM;

	/* First fragment allocates a reassembly queue */
	if(f == 0) {
		f = ipfragallo();
		if(f == 0) {
			freeb(bp);
			return 0;
		}
		qlock(f);
		f->id = id;
		f->src = src;
		f->dst = dst;

		f->blist = bp;
		/* Check lance has handed us a contiguous buffer */
		if(bp->next)
			panic("ip: reass ?");

		qunlock(f);
		return 0;
	}
	qlock(f);

	prev = 0;
	l = &f->blist;
	for(bl = f->blist; bl && BLKFRAG(bp)->foff > BLKFRAG(bl)->foff; bl = bl->next) {
		prev = bl;
		l = &bl->next;
	}

	/* Check overlap of a previous fragment - trim away as necessary */
	if(prev) {
		ovlap = BLKFRAG(prev)->foff + BLKFRAG(prev)->flen - BLKFRAG(bp)->foff;
		if(ovlap > 0) {
			if(ovlap > BLKFRAG(bp)->flen) {
				freeb(bp);
				qunlock(f);
				return 0;
			}
			BLKFRAG(bp)->flen -= ovlap;
		}
	}

	/* Link onto assembly queue */
	bp->next = *l;
	*l = bp;

	/* Check to see if suceeding segments overlap */
	if(bp->next) {
		l = &bp->next;
		end = BLKFRAG(bp)->foff + BLKFRAG(bp)->flen;
		/* Take completely covered segements out */
		while(*l && (ovlap = (end - BLKFRAG(*l)->foff)) > 0) {
			if(ovlap < BLKFRAG(*l)->flen) {
				BLKFRAG(*l)->flen -= ovlap;
				(*l)->rptr += ovlap;
				break;
			}	
			last = *l;
			freeb(*l);
			*l = last;
		}
	}

	/* Now look for a completed queue */
	for(bl = f->blist; bl; bl = bl->next) {
		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0)
			goto complete;
		if(bl->next == 0 ||
		   BLKFRAG(bl)->foff+BLKFRAG(bl)->flen != BLKFRAG(bl->next)->foff)
			break;
	}
	qunlock(f);
	return 0;
	
complete:
	bl = f->blist;
	last = bl;
	len = nhgets(BLKIP(bl)->length);
	bl->wptr = bl->rptr + len + ETHER_HDR;

	/* Pullup all the fragment headers and return a complete packet */
	for(bl = bl->next; bl; bl = bl->next) {
		fragsize = BLKFRAG(bl)->flen;
		len += fragsize;
		bl->rptr += (ETHER_HDR+ETHER_IPHDR);
		bl->wptr = bl->rptr + fragsize;
		last = bl;
	}

	last->flags |= S_DELIM;
	bl = f->blist;
	f->blist = 0;
	ipfragfree(f);

	ip = BLKIP(bl);
	hnputs(ip->length, len);

	return(bl);		
}

/*
 * ipfragfree - Free a list of fragments, fragment list must be locked
 */

void
ipfragfree(Fragq *frag)
{
	Block *f, *next;
	Fragq *fl, **l;

	for(f = frag->blist; f; f = next) {
		next = f->next;
		freeb(f);
	}

	frag->src = 0;
	frag->id = 0;
	qunlock(frag);

	qlock(&fraglock);

	l = &flisthead;
	for(fl = *l; fl; fl = fl->next) {
		if(fl == frag) {
			*l = frag->next;
			break;
		}
		l = &fl->next;
	}

	frag->next = fragfree;
	fragfree = frag;

	qunlock(&fraglock);


}

/*
 * ipfragallo - allocate a reassembly queue
 */
Fragq *
ipfragallo(void)
{
	Fragq *f;

	qlock(&fraglock);
	if(!fragfree) {
		qunlock(&fraglock);
		print("ipfragallo: no queue\n");
		return 0;
	}
	f = fragfree;
	fragfree = f->next;
	f->next = flisthead;
	flisthead = f;
	f->age = TK2MS(MACHP(0)->ticks)/1000 + 600;

	qunlock(&fraglock);
	return f;

}

/*
 * ip_csum - Compute internet header checksums
 */
ushort
ip_csum(uchar *addr)
{
	int len;
	ulong sum = 0;

	len = (addr[0]&0xf)<<2;

	while(len > 0) {
		sum += addr[0]<<8 | addr[1] ;
		len -= 2;
		addr += 2;
	}

	sum = (sum & 0xffff) + (sum >> 16);
	sum = (sum & 0xffff) + (sum >> 16);
	return (sum^0xffff);
}

/*
 * ipparse - Parse an ip address out of a string
 */

Ipaddr classmask[4] = {
	0xff000000,
	0xff000000,
	0xffff0000,
	0xffffff00
};

Ipaddr
ipparse(char *ipa)
{
	Ipaddr address = 0;
	int shift;
	Ipaddr net;

	shift = 24;

	while(shift >= 0 && ipa != (char *)1) {
		address |= atoi(ipa) << shift;
		shift -= 8;
		ipa = strchr(ipa, '.')+1;
	}
	net = address & classmask[address>>30];

	shift += 8;
	return net | ((address & ~classmask[address>>30])>>shift);
}

sub.c           716114985   0     0     664     16535     `
#include	"all.h"
#include	"io.h"

Filsys*
fsstr(char *p)
{
	Filsys *fs;

	for(fs=filsys; fs->name; fs++)
		if(strcmp(fs->name, p) == 0)
			return fs;
	return 0;
}

/*
 * allocate 'count' contiguous channels
 * of type 'type' and return pointer to base
 */
Chan*
chaninit(int type, int count)
{
	Chan *cp, *icp;
	int i;

	icp = ialloc(count * sizeof(*icp), 0);
	cp = icp;
	for(i=0; i<count; i++) {
		cp->next = chans;
		chans = cp;
		cp->type = type;
		cp->chan = cons.chano;
		cons.chano++;
		strncpy(cp->whoname, "<none>", sizeof(cp->whoname));
		fileinit(cp);
		wlock(&cp->reflock);
		wunlock(&cp->reflock);
		rlock(&cp->reflock);
		runlock(&cp->reflock);
		cp++;
	}
	return icp;
}

void
fileinit(Chan *cp)
{
	File *f, *prev;
	Tlock *t;
	int h;

loop:
	lock(&flock);
	for(h=0; h<nelem(flist); h++) {
		for(prev=0,f=flist[h]; f; prev=f,f=f->next) {
			if(f->cp != cp)
				continue;
			if(prev) {
				prev->next = f->next;
				f->next = flist[h];
				flist[h] = f;
			}
			goto out;
		}
	}
	unlock(&flock);
	return;

out:
	flist[h] = f->next;
	unlock(&flock);

	qlock(f);
	if(t = f->tlock) {
		t->time = 0;
		f->tlock = 0;
	}
	if(f->open & FREMOV)
		doremove(f, 0);
	freewp(f->wpath);
	f->open = 0;
	f->cp = 0;
	qunlock(f);

	goto loop;
}

/*
 * returns a locked file structure
 */
File*
filep(Chan *cp, int fid, int flag)
{
	File *f;
	int h;

	if(fid == NOF)
		return 0;

	h = (long)cp + fid;
	if(h < 0)
		h = ~h;
	h = h % nelem(flist);

loop:
	lock(&flock);
	for(f=flist[h]; f; f=f->next)
		if(f->fid == fid && f->cp == cp)
			goto out;

	if(flag) {
		f = newfp();
		if(f) {
			f->fid = fid;
			f->cp = cp;
			f->wpath = 0;
			f->tlock = 0;
			f->next = flist[h];
			flist[h] = f;
			goto out;
		}
	}
	unlock(&flock);
	return 0;

out:
	unlock(&flock);
	qlock(f);
	if(f->fid == fid && f->cp == cp)
		return f;
	qunlock(f);
	goto loop;
}

/*
 * always called with flock locked
 */
File*
newfp(void)
{
	static first;
	File *f;
	int start, i;

	i = first;
	start = i;
	do {
		f = &files[i];
		i++;
		if(i >= conf.nfile)
			i = 0;
		if(f->cp)
			continue;

		first = i;

		return f;
	} while(i != start);

	print("out of files\n");
	return 0;
}

void
freefp(File *fp)
{
	Chan *cp;
	File *f, *prev;
	int h;

	if(!fp || !(cp = fp->cp))
		return;

	h = (long)cp + fp->fid;
	if(h < 0)
		h = ~h;
	h = h % nelem(flist);

	lock(&flock);
	for(prev=0,f=flist[h]; f; prev=f,f=f->next)
		if(f == fp) {
			if(prev)
				prev->next = f->next;
			else
				flist[h] = f->next;
			break;
		}
	fp->cp = 0;
	unlock(&flock);
}

int
iaccess(File *f, Dentry *d, int m)
{

	if(wstatallow)
		return 0;
	/*
	 * other is easiest
	 */
	if(m & d->mode)
		return 0;
	/*
	 * owner is next
	 */
	if(f->uid == d->uid)
		if((m<<6) & d->mode)
			return 0;
	/*
	 * group membership is hard
	 */
	if(ingroup(f->uid, d->gid))
		if((m<<3) & d->mode)
			return 0;
	return 1;
}

Tlock*
tlocked(Iobuf *p, Dentry *d)
{
	Tlock *t, *t1;
	long qpath, tim;
	Device dev;

	tim = toytime();
	qpath = d->qid.path;
	dev = p->dev;
	t1 = 0;
	for(t=tlocks+NTLOCK-1; t>=tlocks; t--) {
		if(t->qpath == qpath)
		if(t->time >= tim)
		if(devcmp(t->dev, dev) == 0)
			return 0;		/* its locked */
		if(!t1 && t->time < tim)
			t1 = t;			/* steal first lock */
	}
	if(t1) {
		t1->dev = dev;
		t1->qpath = qpath;
		t1->time = tim + TLOCK;
	}
	/* botch
	 * out of tlock nodes simulates
	 * a locked file
	 */
	return t1;
}

Wpath*
newwp(void)
{
	static int si = 0;
	int i;
	Wpath *w, *sw, *ew;

	i = si + 1;
	if(i < 0 || i >= conf.nwpath)
		i = 0;
	si = i;
	sw = &wpaths[i];
	ew = &wpaths[conf.nwpath];
	for(w=sw;;) {
		w++;
		if(w >= ew)
			w = &wpaths[0];
		if(w == sw) {
			print("out of wpaths\n");
			return 0;
		}
		if(w->refs)
			continue;
		lock(&wpathlock);
		if(w->refs) {
			unlock(&wpathlock);
			continue;
		}
		w->refs = 1;
		w->up = 0;
		unlock(&wpathlock);
		return w;
	}

}

void
freewp(Wpath *w)
{
	lock(&wpathlock);
	for(; w; w=w->up)
		w->refs--;
	unlock(&wpathlock);
}

Qid
newqid(Device dev)
{
	Iobuf *p;
	Superb *sb;
	Qid qid;

	p = getbuf(dev, superaddr(dev), Bread|Bmod);
	if(!p || checktag(p, Tsuper, QPSUPER))
		panic("newqid: super block");
	sb = (Superb*)p->iobuf;
	sb->qidgen++;
	qid.path = sb->qidgen;
	qid.version = 0;
	putbuf(p);
	return qid;
}

void
buffree(Device dev, long addr, int d)
{
	Iobuf *p;
	long a;
	int i;

	if(!addr)
		return;
	if(d > 0) {
		d--;
		p = getbuf(dev, addr, Bread);
		if(p) {
			for(i=INDPERBUF-1; i>=0; i--) {
				a = ((long*)p->iobuf)[i];
				buffree(dev, a, d);
			}
			putbuf(p);
		}
	}
	/*
	 * stop outstanding i/o
	 */
	p = getbuf(dev, addr, Bprobe);
	if(p) {
		p->flags &= ~(Bmod|Bimm);
		putbuf(p);
	}
	/*
	 * dont put written worm
	 * blocks into free list
	 */
	if(dev.type == Devcw) {
		i = cwfree(dev, addr);
		if(i)
			return;
	}
	p = getbuf(dev, superaddr(dev), Bread|Bmod);
	if(!p || checktag(p, Tsuper, QPSUPER))
		panic("buffree: super block");
	addfree(dev, addr, (Superb*)p->iobuf);
	putbuf(p);
}

long
bufalloc(Device dev, int tag, long qid)
{
	Iobuf *bp, *p;
	Superb *sb;
	long a;
	int n;

	p = getbuf(dev, superaddr(dev), Bread|Bmod);
	if(!p || checktag(p, Tsuper, QPSUPER))
		panic("bufalloc: super block");
	sb = (Superb*)p->iobuf;

loop:
	n = --sb->fbuf.nfree;
	sb->tfree--;
	if(n < 0 || n >= FEPERBUF)
		panic("bufalloc: bad freelist");
	a = sb->fbuf.free[n];
	if(n <= 0) {
		if(a == 0) {
			sb->tfree = 0;
			sb->fbuf.nfree = 1;
			if(dev.type == Devcw)
				if(cwgrow(dev, sb))
					goto loop;
			putbuf(p);
			return 0;
		}
		bp = getbuf(dev, a, Bread);
		if(!bp || checktag(bp, Tfree, QPNONE)) {
			if(bp)
				putbuf(bp);
			putbuf(p);
			return 0;
		}
		sb->fbuf = *(Fbuf*)bp->iobuf;
		putbuf(bp);
	}
	bp = getbuf(dev, a, Bmod);
	memset(bp->iobuf, 0, RBUFSIZE);
	settag(bp, tag, qid);
	if(tag == Tind1 || tag == Tind2 || tag == Tdir)
		bp->flags |= Bimm;
	putbuf(bp);
	putbuf(p);
	return a;
}

/*
 * what are legal characters in a name?
 * only disallow control characters.
 * a) utf avoids control characters.
 * b) '/' may not be the separator
 */
int
checkname(char *n)
{
	int i, c;

	for(i=0; i<NAMELEN; i++) {
		c = *n & 0xff;
		if(c == 0) {
			if(i == 0)
				return 1;
			memset(n, 0, NAMELEN-i);
			return 0;
		}
		if(c <= 040)
			return 1;
		n++;
	}
	return 1;	/* too long */
}

void
addfree(Device dev, long addr, Superb *sb)
{
	int n;
	Iobuf *p;

	n = sb->fbuf.nfree;
	if(n < 0 || n > FEPERBUF)
		panic("addfree: bad freelist");
	if(n >= FEPERBUF) {
		p = getbuf(dev, addr, Bmod);
		if(p == 0)
			panic("addfree: getbuf");
		*(Fbuf*)p->iobuf = sb->fbuf;
		settag(p, Tfree, QPNONE);
		putbuf(p);
		n = 0;
	}
	sb->fbuf.free[n++] = addr;
	sb->fbuf.nfree = n;
	sb->tfree++;
	if(addr >= sb->fsize)
		sb->fsize = addr+1;
}

int
Cconv(Op *o)
{
	Chan *cp;
	char s[20];

	cp = *(Chan**)o->argp;
	sprint(s, "C%d.%.3d", cp->type, cp->chan);
	strconv(s, o, o->f1, o->f2);
	return sizeof(cp);
}

int
Dconv(Op *o)
{
	Device d;
	char s[20];

	d = *(Device*)o->argp;
	sprint(s, "D%d.%d.%d.%d", d.type, d.ctrl, d.unit, d.part);
	strconv(s, o, o->f1, o->f2);
	return sizeof(d);
}

int
Fconv(Op *o)
{
	Filta a;
	char s[30];

	a = *(Filta*)o->argp;

	sprint(s, "%6lud %6lud %6lud",
		fdf(a.f->filter[0], a.scale*60),
		fdf(a.f->filter[1], a.scale*600),
		fdf(a.f->filter[2], a.scale*6000));
	strconv(s, o, o->f1, o->f2);
	return sizeof(Filta);
}

int
Gconv(Op *o)
{
	int t;
	char s[20];

	t = *(int*)o->argp;
	strcpy(s, "<badtag>");
	if(t >= 0 && t < MAXTAG)
		sprint(s, "%s", tagnames[t]);
	strconv(s, o, o->f1, o->f2);
	return sizeof(t);
}

int
Econv(Op *o)
{
	char s[64];
	uchar *p;

	p = *((uchar**)o->argp);
	sprint(s, "%.2lux%.2lux%.2lux%.2lux%.2lux%.2lux",
		p[0], p[1], p[2], p[3], p[4], p[5]);
	strconv(s, o, o->f1, o->f2);
	return sizeof(uchar*);
}

int
Iconv(Op *o)
{
	char s[64];
	uchar *p;

	p = *((uchar**)o->argp);
	sprint(s, "%d.%d.%d.%d",
		p[0], p[1], p[2], p[3]);
	strconv(s, o, o->f1, o->f2);
	return sizeof(uchar*);
}

int
Nconv(Op *o)
{
	char s[64];
	uchar *p;
	long n;

	p = *((uchar**)o->argp);
	n = (p[0]<<8) | p[1];
	if(!(o->f3 & FSHORT))
		n = (n<<16) | (p[2]<<8) | p[3];
	sprint(s, "%lud", n);
	strconv(s, o, o->f1, o->f2);
	return sizeof(uchar*);
}

void
formatinit(void)
{

	fmtinstall('C', Cconv);	/* print channels */
	fmtinstall('D', Dconv);	/* print devices */
	fmtinstall('F', Fconv);	/* print filters */
	fmtinstall('G', Fconv);	/* print tags */
	fmtinstall('T', Tconv);	/* print times */
	fmtinstall('E', Econv);	/* print ether addresses */
	fmtinstall('I', Iconv);	/* print ip addresses */
	fmtinstall('N', Nconv);	/* print network order integers */
}

int
devcmp(Device d1, Device d2)
{

	if(d1.type == d2.type)
	if(d1.ctrl == d2.ctrl)
	if(d1.unit == d2.unit)
	if(d1.part == d2.part)
		return 0;
	return 1;
}

void
rootream(Device dev, long addr)
{
	Iobuf *p;
	Dentry *d;

	p = getbuf(dev, addr, Bmod|Bimm);
	memset(p->iobuf, 0, RBUFSIZE);
	settag(p, Tdir, QPROOT);
	d = getdir(p, 0);
	strcpy(d->name, "/");
	d->uid = -1;
	d->gid = -1;
	d->mode = DALLOC | DDIR |
		((DREAD|DWRITE|DEXEC) << 6) |
		((DREAD|DWRITE|DEXEC) << 3) |
		((DREAD|DWRITE|DEXEC) << 0);
	d->qid = QID(QPROOT|QPDIR,0);
	d->atime = time();
	d->mtime = d->atime;
	putbuf(p);
}

void
superream(Device dev, long addr)
{
	Iobuf *p;
	Superb *s;
	long i;

	p = getbuf(dev, addr, Bmod|Bimm);
	memset(p->iobuf, 0, RBUFSIZE);
	settag(p, Tsuper, QPSUPER);

	s = (Superb*)p->iobuf;
	s->fstart = 2;
	s->fsize = devsize(dev);
	s->fbuf.nfree = 1;
	s->qidgen = 10;
	for(i=s->fsize-1; i>=addr+2; i--)
		addfree(dev, i, s);
	putbuf(p);
}

struct
{
	Lock;
	Msgbuf	*smsgbuf;
	Msgbuf	*lmsgbuf;
} msgalloc;

void
mbinit(void)
{
	Msgbuf *mb;
	Rabuf *rb;
	int i;

	lock(&msgalloc);
	unlock(&msgalloc);
	msgalloc.lmsgbuf = 0;
	msgalloc.smsgbuf = 0;
	for(i=0; i<conf.nlgmsg; i++) {
		mb = ialloc(sizeof(Msgbuf), 0);
if(1)
mb->xdata = ialloc(LARGEBUF+256, 256);
else
		mb->xdata = ialloc(LARGEBUF+OFFMSG, LINESIZE);
		mb->flags = LARGE;
		mbfree(mb);
		cons.nlarge++;
	}
	for(i=0; i<conf.nsmmsg; i++) {
		mb = ialloc(sizeof(Msgbuf), 0);
if(1)
mb->xdata = ialloc(SMALLBUF+256, 256);
else
		mb->xdata = ialloc(SMALLBUF+OFFMSG, LINESIZE);
		mb->flags = 0;
		mbfree(mb);
		cons.nsmall++;
	}
	memset(mballocs, 0, sizeof(mballocs));

	lock(&rabuflock);
	unlock(&rabuflock);
	rabuffree = 0;
	for(i=0; i<1000; i++) {
		rb = ialloc(sizeof(*rb), 0);
		rb->link = rabuffree;
		rabuffree = rb;
	}
}

Msgbuf*
mballoc(int count, Chan *cp, int category)
{
	Msgbuf *mb;

	lock(&msgalloc);
	if(count > SMALLBUF) {
		if(count > LARGEBUF)
			panic("msgbuf count");
		mb = msgalloc.lmsgbuf;
		if(mb == 0) {
			mb = ialloc(sizeof(Msgbuf), 0);
if(1)
mb->xdata = ialloc(LARGEBUF+256, 256);
else
			mb->xdata = ialloc(LARGEBUF+OFFMSG, LINESIZE);
			cons.nlarge++;
		} else
			msgalloc.lmsgbuf = mb->next;
		mb->flags = LARGE;
	} else {
		mb = msgalloc.smsgbuf;
		if(mb == 0) {
			mb = ialloc(sizeof(Msgbuf), 0);
if(1)
mb->xdata = ialloc(SMALLBUF+256, 256);
else
			mb->xdata = ialloc(SMALLBUF+OFFMSG, LINESIZE);
			cons.nsmall++;
		} else
			msgalloc.smsgbuf = mb->next;
		mb->flags = 0;
	}
	mballocs[category]++;
	unlock(&msgalloc);
	mb->count = count;
	mb->chan = cp;
	mb->param = 0;
	mb->category = category;
if(1)
mb->data = mb->xdata+256;
else
	mb->data = mb->xdata+OFFMSG;
	return mb;
}

void
mbfree(Msgbuf *mb)
{
	lock(&msgalloc);
	mballocs[mb->category]--;
	mb->category = 0;
	if(mb->flags & FREE)
		panic("mbfree already free");
	mb->flags |= FREE;
	if(mb->flags & LARGE) {
		mb->next = msgalloc.lmsgbuf;
		msgalloc.lmsgbuf = mb;
	} else {
		mb->next = msgalloc.smsgbuf;
		msgalloc.smsgbuf = mb;
	}
	mb->data = 0;
	unlock(&msgalloc);
}

/*
 * returns 1 if n is prime
 * used for adjusting lengths
 * of hashing things.
 * there is no need to be clever
 */
int
prime(long n)
{
	long i;

	if((n%2) == 0)
		return 0;
	for(i=3;; i+=2) {
		if((n%i) == 0)
			return 0;
		if(i*i >= n)
			return 1;
	}
}

char*
getwd(char *word, char *line)
{
	int c, n;

	while(*line == ' ')
		line++;
	for(n=0; n<80; n++) {
		c = *line;
		if(c == ' ' || c == 0 || c == '\n')
			break;
		line++;
		*word++ = c;
	}
	*word = 0;
	return line;
}

void
hexdump(void *a, int n)
{
	char s1[30], s2[4];
	uchar *p;
	int i;

	p = a;
	s1[0] = 0;
	for(i=0; i<n; i++) {
		sprint(s2, " %.2ux", p[i]);
		strcat(s1, s2);
		if((i&7) == 7) {
			print("%s\n", s1);
			s1[0] = 0;
		}
	}
	if(s1[0])
		print("%s\n", s1);
}

void*
recv(Queue *q, int tim)
{
	User *p;
	void *a;
	int i, c;


	USED(tim);
	if(q == 0)
		panic("recv null q");
loop:
	lock(q);
	c = q->count;
	if(c > 0) {
		i = q->loc;
		a = q->args[i];
		i++;
		if(i >= q->size)
			i = 0;
		q->loc = i;
		q->count = c-1;
		p = q->whead;
		if(p) {
			q->whead = p->qnext;
			if(q->whead == 0)
				q->wtail = 0;
			ready(p);
		}
		unlock(q);
		return a;
	}
	p = q->rtail;
	if(p == 0)
		q->rhead = u;
	else
		p->qnext = u;
	q->rtail = u;
	u->qnext = 0;
	u->state = Recving;
	unlock(q);
	sched();
	goto loop;
}

void
send(Queue *q, void *a)
{
	User *p;
	int i, c;

	if(q == 0)
		panic("send null q");
loop:
	lock(q);
	c = q->count;
	if(c < q->size) {
		i = q->loc + c;
		if(i >= q->size)
			i -= q->size;
		q->args[i] = a;
		q->count = c+1;
		p = q->rhead;
		if(p) {
			q->rhead = p->qnext;
			if(q->rhead == 0)
				q->rtail = 0;
			ready(p);
		}
		unlock(q);
		return;
	}
	p = q->wtail;
	if(p == 0)
		q->whead = u;
	else
		p->qnext = u;
	q->wtail = u;
	u->qnext = 0;
	u->state = Sending;
	unlock(q);
	sched();
	goto loop;
}

Queue*
newqueue(int size)
{
	Queue *q;

	q = ialloc(sizeof(Queue) + (size-1)*sizeof(void*), 0);
	q->size = size;
	lock(q);
	unlock(q);
	return q;
}

no(void *a)
{

	USED(a);
	return 0;
}

int
devread(Device a, long b, void *c)
{

	switch(a.type)
	{
	case Devcw:
		return cwread(a, b, c);

	case Devro:
		return roread(a, b, c);

	case Devwren:
		return wrenread(a, b, c);

	case Devworm:
		return wormread(a, b, c);

	case Devfworm:
		return fwormread(a, b, c);

	case Devmcat:
		return mcatread(a, b, c);

	case Devmlev:
		return mlevread(a, b, c);

	case Devpart:
		return partread(a, b, c);
	}
	panic("illegal device in read: %D %ld", a, b);
	return 1;
}

int
devwrite(Device a, long b, void *c)
{

	switch(a.type)
	{
	case Devcw:
		return cwwrite(a, b, c);

	case Devro:
		print("write to ro device %D(%ld)\n", a, b);
		return 1;

	case Devwren:
		return wrenwrite(a, b, c);

	case Devworm:
		return wormwrite(a, b, c);

	case Devfworm:
		return fwormwrite(a, b, c);

	case Devmcat:
		return mcatwrite(a, b, c);

	case Devmlev:
		return mlevwrite(a, b, c);

	case Devpart:
		return partwrite(a, b, c);
	}
	panic("illegal device in write: %D %ld", a, b);
	return 1;
}

long
devsize(Device d)
{

	switch(d.type)
	{
	case Devcw:
	case Devro:
		return cwsize(d);

	case Devwren:
		return wrensize(d);

	case Devworm:
		return wormsize(d);

	case Devfworm:
		return fwormsize(d);

	case Devmcat:
		return mcatsize(d);

	case Devmlev:
		return mlevsize(d);

	case Devpart:
		return partsize(d);
	}
	panic("illegal device in dev_size: %D", d);
	return 0;
}

long
superaddr(Device d)
{

	switch(d.type) {
	default:
		return SUPER_ADDR;

	case Devcw:
	case Devro:
		return cwsaddr(d);
	}
}

long
getraddr(Device d)
{

	switch(d.type) {
	default:
		return ROOT_ADDR;

	case Devcw:
		return cwraddr(d, 0);

	case Devro:
		return cwraddr(d, 1);
	}
}

void
devream(Device d)
{
	Device wdev;

	print("ream: %D\n", d);
	switch(d.type) {
	default:
	bad:
		print("ream: unknown dev type %D\n", d);
		return;

	case Devcw:
		wdev = WDEV(d);
		if(wdev.type == Devfworm)
			fwormream(wdev);
		wlock(&mainlock);	/* ream cw */
		cwream(d);
		wunlock(&mainlock);
		break;

	case Devpart:
	case Devmlev:
	case Devmcat:
	case Devwren:
		devinit(d);
		wlock(&mainlock);	/* ream wren */
		rootream(d, ROOT_ADDR);
		superream(d, SUPER_ADDR);
		wunlock(&mainlock);
		break;
	}
}

void
devrecover(Device d)
{

	print("recover: %D\n", d);
	switch(d.type) {
	default:
		print("recover: unknown dev type %D\n", d);
		return;

	case Devcw:
		wlock(&mainlock);	/* recover */
		cwrecover(d);
		wunlock(&mainlock);
		break;
	}
}

void
devinit(Device d)
{
	Filsys *fs;

	print("	devinit %D\n", d);
	switch(d.type) {
	default:
		print("devinit unknown device %D\n", d);

	case Devcw:
		cwinit(d);
		break;

	case Devro:
		for(fs=filsys; fs->name; fs++)
			if(fs->dev.type == Devcw)
				d = fs->dev;
		d.type = Devro;
		for(fs=filsys; fs->name; fs++)
			if(fs->dev.type == Devro)
				fs->dev = d;
		break;

	case Devwren:
		wreninit(d);
		break;

	case Devworm:
		worminit(d);
		break;

	case Devfworm:
		fworminit(d);
		break;

	case Devmcat:
		mcatinit(d);
		break;

	case Devmlev:
		mlevinit(d);
		break;

	case Devpart:
		partinit(d);
		break;
	}
}

time.c          699847313   0     0     664     6267      `
#include	"all.h"
#include	"mem.h"

long
toytime(void)
{
	return mktime + MACHP(0)->ticks / (1000/MS2HZ);
}

#define SEC2MIN 60L
#define SEC2HOUR (60L*SEC2MIN)
#define SEC2DAY (24L*SEC2HOUR)

/*
 *  days per month plus days/year
 */
int	rtdmsize[] =
{
	365, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int	rtldmsize[] =
{
	366, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

/*
 *  return the days/month for the given year
 */
int*
yrsize(int yr)
{
	if((yr%4) == 0)
		return rtldmsize;
	else
		return rtdmsize;
}

void
sec2rtc(ulong secs, Rtc *rtc)
{
	long hms, day;
	int d, *d2m;

	/*
	 * break initial number into days
	 */
	hms = secs % SEC2DAY;
	day = secs / SEC2DAY;
	if(hms < 0) {
		hms += SEC2DAY;
		day -= 1;
	}

	/*
	 * generate hours:minutes:seconds
	 */
	rtc->sec = hms % 60;
	d = hms / 60;
	rtc->min = d % 60;
	d /= 60;
	rtc->hour = d;

	/*
	 * year number
	 */
	if(day >= 0)
		for(d = 1970; day >= *yrsize(d); d++)
			day -= *yrsize(d);
	else
		for (d = 1970; day < 0; d--)
			day += *yrsize(d-1);
	rtc->year = d;

	/*
	 * generate month
	 */
	d2m = yrsize(rtc->year);
	for(d = 1; day >= d2m[d]; d++)
		day -= d2m[d];
	rtc->mday = day + 1;
	rtc->mon = d;
}

long
rtc2sec(Rtc *rtc)
{
	long secs;
	int i, *d2m;

	secs = 0;

	/*
	 *  seconds per year
	 */
	for(i = 1970; i < rtc->year; i++) {
		d2m = yrsize(i);
		secs += d2m[0] * SEC2DAY;
	}

	/*
	 *  seconds per month
	 */
	d2m = yrsize(rtc->year);
	for(i = 1; i < rtc->mon; i++)
		secs += d2m[i] * SEC2DAY;

	secs += (rtc->mday-1) * SEC2DAY;
	secs += rtc->hour * SEC2HOUR;
	secs += rtc->min * SEC2MIN;
	secs += rtc->sec;

	return secs;
}

long
time(void)
{
	long t, dt;

	t = toytime();
	while(tim.bias != 0) {				/* adjust at rate 1 sec/min */
		dt = t - tim.lasttoy;
		if(dt < MINUTE(1))
			break;
		if(tim.bias >= 0) {
			tim.bias -= SECOND(1);
			tim.offset += SECOND(1);
		} else {
			tim.bias += SECOND(1);
			tim.offset -= SECOND(1);
		}
		tim.lasttoy += MINUTE(1);
	}
	return t + tim.offset;
}

void
settime(long nt)
{
	long dt;

	dt = nt - time();
	tim.lasttoy = toytime();
	if(dt > MAXBIAS || dt < -MAXBIAS) {		/* too much, just set it */
		tim.bias = 0;
		tim.offset = nt - tim.lasttoy;
	} else
		tim.bias = dt;
}

void
prdate(void)
{
	long t;

	t = time();
	if(tim.bias >= 0)
		print("%T + %ld\n", t, tim.bias);
	else
		print("%T - %ld\n", t, -tim.bias);
}


static	int	sunday(Tm *t, int d);
static	int	dysize(int);
static	void	ct_numb(char*, int);
void		localtime(long tim, Tm *ct);
void		gmtime(long tim, Tm *ct);

static	char	dmsize[12] =
{
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

/*
 * The following table is used for 1974 and 1975 and
 * gives the day number of the first day after the Sunday of the
 * change.
 */
static	struct
{
	short	yrfrom;
	short	yrto;
	short	daylb;
	short	dayle;
} daytab[] =
{
	87,	999,	97,	303,
	76,	86,	119,	303,
	75,	75,	58,	303,
	74,	74,	5,	333,
	0,	73,	119,	303,
};

static struct
{
	short	minuteswest;	/* minutes west of Greenwich */
	short	dsttime;	/* dst correction */
} timezone =
{
	5*60, 1
};

void
localtime(long tim, Tm *ct)
{
	int daylbegin, daylend, dayno, i;
	long copyt;

	copyt = tim - timezone.minuteswest*60L;
	gmtime(copyt, ct);
	dayno = ct->yday;
	for(i=0;; i++)
		if(ct->year >= daytab[i].yrfrom &&
		   ct->year <= daytab[i].yrto) {
			daylbegin = sunday(ct, daytab[i].daylb);
			daylend = sunday(ct, daytab[i].dayle);
			break;
		}
	if(timezone.dsttime &&
	    (dayno>daylbegin || (dayno==daylbegin && ct->hour>=2)) &&
	    (dayno<daylend || (dayno==daylend && ct->hour<1))) {
		copyt += 60L*60L;
		gmtime(copyt, ct);
		ct->isdst++;
	}
}

/*
 * The argument is a 0-origin day number.
 * The value is the day number of the last
 * Sunday before or after the day.
 */
static
sunday(Tm *t, int d)
{
	if(d >= 58)
		d += dysize(t->year) - 365;
	return d - (d - t->yday + t->wday + 700) % 7;
}

void
gmtime(long tim, Tm *ct)
{
	int d0, d1;
	long hms, day;

	/*
	 * break initial number into days
	 */
	hms = tim % 86400L;
	day = tim / 86400L;
	if(hms < 0) {
		hms += 86400L;
		day -= 1;
	}

	/*
	 * generate hours:minutes:seconds
	 */
	ct->sec = hms % 60;
	d1 = hms / 60;
	ct->min = d1 % 60;
	d1 /= 60;
	ct->hour = d1;

	/*
	 * day is the day number.
	 * generate day of the week.
	 * The addend is 4 mod 7 (1/1/1970 was Thursday)
	 */

	ct->wday = (day + 7340036L) % 7;

	/*
	 * year number
	 */
	if(day >= 0)
		for(d1 = 70; day >= dysize(d1); d1++)
			day -= dysize(d1);
	else
		for (d1 = 70; day < 0; d1--)
			day += dysize(d1-1);
	ct->year = d1;
	ct->yday = d0 = day;

	/*
	 * generate month
	 */

	if(dysize(d1) == 366)
		dmsize[1] = 29;
	for(d1 = 0; d0 >= dmsize[d1]; d1++)
		d0 -= dmsize[d1];
	dmsize[1] = 28;
	ct->mday = d0 + 1;
	ct->mon = d1;
	ct->isdst = 0;
}

void
datestr(char *s, long t)
{
	Tm tm;

	localtime(t, &tm);
	sprint(s, "%.4d%.2d%.2d", tm.year+1900, tm.mon+1, tm.mday);
}

int
Tconv(Op *o)
{
	char s[30];
	char *cp;
	long t;
	Tm tm;

	t = *(long*)o->argp;
	if(t == 0) {
		strcpy(s, "The Epoch");
		goto out;
	}

	localtime(t, &tm);
	strcpy(s, "Day Mon 00 00:00:00 1900");
	cp = &"SunMonTueWedThuFriSat"[tm.wday*3];
	s[0] = cp[0];
	s[1] = cp[1];
	s[2] = cp[2];
	cp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[tm.mon*3];
	s[4] = cp[0];
	s[5] = cp[1];
	s[6] = cp[2];
	ct_numb(s+8, tm.mday);
	ct_numb(s+11, tm.hour+100);
	ct_numb(s+14, tm.min+100);
	ct_numb(s+17, tm.sec+100);
	if(tm.year >= 100) {
		s[20] = '2';
		s[21] = '0';
	}
	ct_numb(s+22, tm.year+100);

out:
	strconv(s, o, o->f1, o->f2);
	return sizeof(t);
}

static
dysize(int y)
{

	if((y%4) == 0)
		return 366;
	return 365;
}

static
void
ct_numb(char *cp, int n)
{

	if(n >= 10)
		cp[0] = (n/10)%10 + '0';
	else
		cp[0] = ' ';
	cp[1] = n%10 + '0';
}

/*
 * compute the next time after t
 * that has hour hr and is not on
 * day in bitpattern --
 * for automatic dumps
 */
long
nextime(long t, int hr, int day)
{
	Tm tm;
	int nhr;

	if(hr < 0 || hr >= 24)
		hr = 5;
	if((day&0x7f) == 0x7f)
		day = 0;

loop:
	localtime(t, &tm);
	t -= tm.sec;
	t -= tm.min*60;
	nhr = tm.hour;
	do {
		t += 60*60;
		nhr++;
	} while(nhr%24 != hr);
	localtime(t, &tm);
	if(tm.hour != hr) {
		t += 60*60;
		localtime(t, &tm);
		if(tm.hour != hr) {
			t -= 60*60;
			localtime(t, &tm);
		}
	}
	if(day & (1<<tm.wday)) {
		t += 12*60*60;
		goto loop;
	}
	return t;
}

worm.c          715911466   0     0     664     18220     `
#include	"all.h"
#include	"io.h"

#define	SCSInone	SCSIread
#define	NLUN		2
#define	NSHLV		50
#define	NPLAT		(NSHLV*2)
#define	FIXEDSIZE	546000

typedef	struct	plat	Plat;
struct	plat
{
	char	status;		/* Sunload, etc */
	char	lun;		/* if loaded, where */
	long	time;		/* time since last access, to unspin */
	long	nblock;		/* number of native blocks */
	long	block;		/* bytes per native block */
	long	mult;		/* multiplier to get plan9 blocks */
	long	max;		/* max size in plan9 blocks */
};
static
struct
{
	Plat	plat[NPLAT];
	int	shinit;		/* beginning of time flag */
	int	active;		/* flag to slow down wormcopy */
	Device	jagu;		/* current lun */
	int	lungen;		/* lun generator */
	QLock;
} w;

enum
{
	Sunload,	/* not assigned a lun, on the shelf */
	Sstop,		/* loaded, but not spinning */
	Sstart,		/* loaded and spinning */
	Sempty,		/* no disk */
};


static	int	wormsense(int);
static	Plat*	wormunit(Device);
static	void	shelves(void);
static	void	waitworm(void);
static	void	prshit(void);
static	int	ascsiio(int, uchar*, int, void*, int);
static	void	cmd_search(int, char*[]);
static	void	cmd_wormcp(int, char*[]);
static	void	wcpinit(void);

/*
 * mounts and spins up the device
 *	locks the structure
 */
static
Plat*
wormunit(Device d)
{
	int p, i, s;
	Plat *v, *x;
	uchar cmd[10], buf[8];
	Drive *dr;

	qlock(&w);
	if(!w.shinit) {
		w.name = "worm";
		dr = scsidrive(d);
		if(dr == 0)
			panic("worm: jagu %D", d);
		w.jagu = d;
		w.lungen = 0;
		waitworm();
		shelves();
		x = &w.plat[0];			/* BOTCH why are platters empty?? */
		for(i=0; i<NPLAT; i++, x++)
			x->status = Sunload;

		cmd_install("search", "[blkno] [nblock] [b/w] -- search blank on worm", cmd_search);
		cmd_install("wormcp", "funit tunit [nblock] -- worm to worm copy", cmd_wormcp);
		wcpinit();

		w.shinit = 1;
	}
	if(w.jagu.ctrl != d.ctrl || w.jagu.unit != d.unit)
		panic("worm: two juke box units %D %D", d, w.jagu);
	p = d.part;
	if(p < 0 || p > NPLAT)
		panic("worminit partition %D", d);
	v = &w.plat[p];

	/*
	 * if disk is unloaded, must load it
	 * into next (circular) logical unit
	 */
	if(v->status == Sunload) {

		/*
		 * release all disks that have that lun
		 */
		x = &w.plat[0];
		for(i=0; i<NPLAT; i++, x++) {
			if(x->status != Sunload && x->lun == w.lungen) {
				if(x->status == Sstart) {
					print("worm: stop %d; lun %d\n",
						i, x->lun);
					memset(cmd, 0, 6);
					cmd[0] = 0x1b;	/* disk stop */
					cmd[1] = x->lun << 5;
					s = ascsiio(SCSInone, cmd, 6, buf, 0);
					if(s)
						goto sbad;
					x->status = Sstop;
				}
				if(x->status == Sstop) {
					print("worm: release %d; lun %d\n",
						i, x->lun);
					memset(cmd, 0, 6);
					cmd[0] = 0xd7;	/* disk release */
					cmd[1] = x->lun << 5;
					s = ascsiio(SCSInone, cmd, 6, buf, 0);
					if(s)
						goto sbad;
					x->status = Sunload;
				}
				if(x->status != Sunload)
					panic("worm: disk not unload %D", d);
			}
		}

		v->lun = w.lungen;
		w.lungen++;
		if(w.lungen >= NLUN)
			w.lungen = 0;

		print("worm: set %d; lun %d\n", p, v->lun);
		memset(cmd, 0, 6);
		cmd[0] = 0xd6;	/* disk set */
		cmd[1] = v->lun << 5;
		cmd[3] = (p << 1) | 0; /* botch make it side>=50 */
		s = ascsiio(SCSInone, cmd, 6, buf, 0);
		if(s)
			goto sbad;
		v->status = Sstop;

		memset(cmd, 0, 6);
		cmd[0] = 0x15;	/* mode select */
		cmd[1] = v->lun << 5;
		cmd[4] = 6;
		memset(buf, 0, 6);
		buf[2] = 1;	/* ebc */
		buf[4] = 7;	/* ealt,evrf,edtre */
		s = ascsiio(SCSIwrite, cmd, 6, buf, 6);
		if(s)
			goto sbad;
	}
	if(v->status == Sstop) {
		print("worm: start %d; lun %d\n", p, v->lun);
		memset(cmd, 0, 6);
		cmd[0] = 0x1b;	/* disk start */
		cmd[1] = v->lun << 5;
		cmd[4] = 1;
		s = ascsiio(SCSInone, cmd, 6, buf, 0);
		if(s)
			goto sbad;
		v->status = Sstart;
	}

	v->time = toytime();
	if(v->status != Sstart)
		panic("worm: not started %D", d);

	if(v->block)
		return v;

	/*
	 * capacity command
	 */
	memset(cmd, 0, sizeof(cmd));
	cmd[0] = 0x25;	/* read capacity */
	cmd[1] = v->lun << 5;
	s = ascsiio(SCSIread, cmd, sizeof(cmd), buf, sizeof(buf));
	if(s)
		goto sbad;

	v->nblock =
		(buf[0]<<24) |
		(buf[1]<<16) |
		(buf[2]<<8) |
		(buf[3]<<0);
	v->block =
		(buf[4]<<24) |
		(buf[5]<<16) |
		(buf[6]<<8) |
		(buf[7]<<0);
	v->mult =
		(RBUFSIZE + v->block - 1) /
		v->block;
	v->max =
		(v->nblock + 1) / v->mult;

	print("	drive %D: lun %d\n", d, v->lun);
	print("		%ld blocks at %ld bytes each\n",
		v->nblock, v->block);
	print("		%ld logical blocks at %d bytes each\n",
		v->max, RBUFSIZE);
	print("		%ld multiplier\n",
		v->mult);

	if(FIXEDSIZE && v->max != FIXEDSIZE)
		panic("size wrong\n");
	return v;

sbad:
	qunlock(&w);
	panic("worm: no capacity %D", d);
	prshit();
	return 0;
}

/*
 * called periodically
 * to stop drives that have not
 * been used in a while
 */
void
wormprobe(void)
{
	int i;
	long t;
	Plat *x;
	uchar cmd[10], buf[8];

	t = toytime() - TWORM;
	x = &w.plat[0];
	for(i=0; i<NPLAT; i++, x++) {
		if(x->status == Sstart && t > x->time) {
			qlock(&w);
			if(x->status == Sstart && t > x->time) {
				print("worm: stop %d; lun %d\n",
					i, x->lun);
				memset(cmd, 0, 6);
				cmd[0] = 0x1b;	/* disk stop */
				cmd[1] = x->lun << 5;
				ascsiio(SCSInone, cmd, 6, buf, 0);
				x->status = Sstop;
			}
			qunlock(&w);
		}
	}
}

void
worminit(Device d)
{

	USED(d);
}

long
wormsize(Device d)
{
	Plat *v;

	if(FIXEDSIZE)
		return FIXEDSIZE;

	v = wormunit(d);
	qunlock(&w);
	return v->max;
}

int
wormiocmd(Device d, int io, long b, void *c)
{
	int s;
	Plat *v;
	long l, m;
	uchar cmd[10];

	v = wormunit(d);
	w.active = 1;
	if(b >= v->max) {
		qunlock(&w);
		print("worm: wormiocmd out of range %D(%ld)\n", d, b);
		return 0x071;
	}

	cmd[0] = 0x28;		/* extended read */
	if(io != SCSIread)
		cmd[0] = 0x2a;	/* extended write */
	cmd[1] = v->lun << 5;

	m = v->mult;
	l = b * m;
	cmd[2] = l>>24;
	cmd[3] = l>>16;
	cmd[4] = l>>8;
	cmd[5] = l;
	cmd[6] = 0;

	cmd[7] = m>>8;
	cmd[8] = m;
	cmd[9] = 0;

	s = ascsiio(io, cmd, 10, c, RBUFSIZE);
	qunlock(&w);
	return s;
}

int
wormread(Device d, long b, void *c)
{
	int s;

	s = wormiocmd(d, SCSIread, b, c);
	if(s) {
		print("wormread: %D(%ld) bad status %.4x\n", d, b, s);
		cons.nwormre++;
		return s;
	}
	return 0;
}

int
wormwrite(Device d, long b, void *c)
{
	int s;

	s = wormiocmd(d, SCSIwrite, b, c);
	if(s) {
		print("wormwrite: %D(%ld) bad status %.4x\n", d, b, s);
		cons.nwormwe++;
		return s;
	}
	return 0;
}

long
wormsearch(Device d, int io, long b, long c)
{
	Plat *v;
	long l, m;
	uchar cmd[10], buf[6];

	v = wormunit(d);
	if(b >= v->max) {
		print("wormsearch out of range %D(%ld)\n", d, b);
		goto no;
	}

	cmd[0] = io;	/* blank/written sector search */
	cmd[1] = v->lun << 5;

	m = v->mult;
	l = b * m;
	cmd[2] = l>>24;
	cmd[3] = l>>16;
	cmd[4] = l>>8;
	cmd[5] = l;
	cmd[6] = 0;

	l = c * m;
	if(l >= 65535) {
		print("wormsearch nsectors %ld too big\n", c);
		l = 65535;
	}
	cmd[7] = l>>8;
	cmd[8] = l;
	cmd[9] = 0;

	if(ascsiio(SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)))
		goto no;
	if(!(buf[1] & 1))
		goto no;
	qunlock(&w);
	return ((buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5]) / m;

no:
	qunlock(&w);
	return -1;
}

static
void
waitworm(void)
{
	int s;

loop:
	s = wormsense(0);
	if(s == 0x70) {
		delay(3000);
		goto loop;
	}
}

static
int
wormsense(int lun)
{
	uchar cmd[6], buf[4];
	int s;
	char *sc;

	memset(cmd, 0, 6);
	cmd[0] = 0x03;	/* request sense */
	cmd[1] = lun<<5;
	memset(buf, 0, 4);
	s = 0x70;	/* robs fault */
	if(!scsiio(w.jagu, SCSIread, cmd, 6, buf, 4))
		s = buf[0] & 0x7f;
	sc = wormscode[s];
	if(!sc)
		sc = "unknown";
	print("	sense code %.2x %s\n", s, sc);
	return s;
}

static
void
shelves(void)
{
	uchar cmd[6], buf[128], *p;
	int s, i, pass, flag;

	pass = 0;

loop:
	flag = 0;
	memset(cmd, 0, 6);
	cmd[0] = 0x1d;	/* send diagnostics */
	cmd[4] = 10;
	memset(buf, 0, 10);
	buf[0] = 0xe2;	/* read internal status */
	s = ascsiio(SCSIwrite, cmd, 6, buf, 10);
	if(s)
		goto sbad;

	memset(cmd, 0, 6);
	cmd[0] = 0x1c;	/* recv diagnostics */
	cmd[4] = 128;
	memset(buf, 0, 128);
	s = ascsiio(SCSIread, cmd, 6, buf, 128);
	if(s)
		goto sbad;

	for(i=0; i<8; i++) {
		p = buf+16+ 4*i;
		if(p[0] & 0x80) {
			print("	lun %d drive power off\n", i);
			goto bad;
		}
		if(p[0] & 0x40) {
			if(!(p[2] & 0x80)) {
				print("	lun %d drive loaded, no return shelf\n", i);
				goto bad;
			}
			print("	lun %d drive loaded, unloading\n", i);
			flag++;

			memset(cmd, 0, 6);
			cmd[0] = 0xd7;	/* disk release */
			cmd[1] = i << 5;
			s = ascsiio(SCSInone, cmd, 6, buf, 0);
			if(s)
				goto bad;
		}
	}
	for(i=0; i<NSHLV; i++) {
		p = buf+48+i;
		if(!(p[0] & 0x80))		/* empty */
			continue;
		if(p[0] & 0x40)
			continue;		/* assigned */
		print("	shelf %2d unassigned\n", i);
		flag++;

		memset(cmd, 0, 6);
		cmd[0] = 0xd6;			/* disk set */
		cmd[3] = (127<<1);
		s = ascsiio(SCSInone, cmd, 6, buf, 0);
		if(s)
			goto sbad;
	
		memset(cmd, 0, 6);
		cmd[0] = 0xd7;	/* disk release */
		cmd[1] = 1;
		cmd[3] = i << 1;
		s = ascsiio(SCSInone, cmd, 6, buf, 0);
		if(s)
			goto sbad;
	}
	if(flag) {
		if(pass) {
			print("	too many passes\n");
			goto bad;
		}
		pass++;
		goto loop;
	}
	if(buf[98])
		print("	i/o unit status: %.2x\n", buf[98]);
	if(buf[99])
		print("	carrier  status: %.2x\n", buf[99]);
	if(buf[100])
		print("	upper dr status: %.2x\n", buf[100]);
	if(buf[101])
		print("	lower dr status: %.2x\n", buf[101]);
	for(i=0; i<NSHLV; i++)
		if(!(buf[i+48] & 0x80)) {
			w.plat[i].status = Sempty;
			w.plat[NSHLV+i].status = Sempty;
			continue;
		}
	for(i=0; i<NSHLV; i++) {
		if(!(buf[i+48] & 0x80))
			continue;
		s = i;				/* first shelf with disk */
		for(i++; i<NSHLV; i++)
			if(!(buf[i+48] & 0x80))
				break;
		print("	shelves %2d thru %2d contain disks\n", s, i-1);
	}
	return;

sbad:
	wormsense(0);
bad:
	panic("cant fix worm shelves");
}

void
prshit(void)
{
	uchar cmd[6], buf[128], *p;
	int s, i;

	memset(cmd, 0, 6);
	cmd[0] = 0x1d;				/* send diagnostics */
	cmd[4] = 10;
	memset(buf, 0, 10);
	buf[0] = 0xe2;				/* read internal status */
	s = ascsiio(SCSIwrite, cmd, 6, buf, 10);
	if(s)
		goto sbad;

	memset(cmd, 0, 6);
	cmd[0] = 0x1c;				/* recv diagnostics */
	cmd[4] = 128;
	memset(buf, 0, 128);
	s = ascsiio(SCSIread, cmd, 6, buf, 128);
	if(s)
		goto sbad;

	for(i=0; i<8; i++) {
		p = buf+16+ 4*i;
		print("%d: %.2x %.2x %.2x %.2x\n", i,
			p[0], p[1], p[2], p[3]);
	}
	return;

sbad:
	wormsense(0);
}

static
int
ascsiio(int rw, uchar *param, int nparam, void *addr, int size)
{
	int s, l;

	s = scsiio(w.jagu, rw, param, nparam, addr, size);
	if(s) {
		l = (param[1]>>5) & 7;
		s = wormsense(l);
		print("ascsiio: bad status %.2x on opcode %.2x\n",
			s, param[0]);
		if(s == 0x06) {
			print("unit attention, reissue\n");
			s = scsiio(w.jagu, rw, param, nparam, addr, size);
			if(s)
				s = wormsense(l);
		}
	}
	return s;
}

static
void
cmd_search(int argc, char *argv[])
{
	Device dev;
	int lb, hb;
	long l, m, b, c, bw, r;

	dev = cwdevs[1];	/* botch */
	if(dev.type != Devmlev) {
		print("device not Devmlev\n");
		return;
	}
	lb = dev.unit;
	hb = dev.part;

	b = 0;
	c = 100;
	bw = 0;

	if(argc > 1)
		b = number(argv[1], b, 10);
	if(argc > 2)
		c = number(argv[2], c, 10);
	if(argc > 3)
		bw = number(argv[3], bw, 10);

	if(bw)
		bw = 0x2c;	/* blank sector search */
	else
		bw = 0x2d;	/* written sector search */

	l = 0;
	while(lb < hb) {
		m = devsize(cwdevs[lb]);
		if(b < l+m) {
			/* botch -- crossing disks */
			r = wormsearch(cwdevs[lb], bw, b-l, c);
			if(r < 0)
				print("search of %ld %ld not found\n", b, c);
			else
				print("search of %ld %ld = %ld\n", b, c, r+l);
			return;
		}
		l += m;
		lb++;
	}
	print("no search\n");
}

static
struct
{
	int	active;			/* yes, we are copying */
	uchar*	memp;			/* pointer to scratch memory */
	long	memc;			/* size of scratch memory */
	long	memb;			/* size of scratch memory in native blocks */
	Device	fr;			/* 'from' device */
	Device	to;			/* 'to' device */
	long	nblock;			/* number of native blocks */
	long	block;			/* bytes per native block */
	long	off;			/* current native block count */
} wcp;

static
void
wcpinit(void)
{
	if(wcp.memp == 0) {
		wcp.memc = conf.wcpsize;
		wcp.memp = ialloc(wcp.memc, LINESIZE);
	}
}

static
void
cmd_wormcp(int argc, char *argv[])
{
	Device fr, to;
	int lb, hb, i;
	Plat *v;

	to = cwdevs[1];
	if(to.type != Devmlev) {
		print("device not mlev\n");
		return;
	}
	if(argc <= 1) {
		print("wcp active turned off\n");
		wcp.active = 0;
		return;
	}
	if(wcp.active) {
		print("wcp active already on\n");
		return;
	}
	if(wcp.memc == 0 || wcp.memp == 0) {
		print("memory not allocated\n");
		return;
	}

	i = -1;
	if(argc > 1)
		i = number(argv[1], i, 10);

	fr = devnone;
	hb = to.part;
	for(lb = to.unit; lb < hb; lb++)
		if(cwdevs[lb].part == i)
			fr = cwdevs[lb];
	if(devcmp(fr, devnone) == 0) {
		print("'from' device not in mlev %d\n", i);
		return;
	}

	i = -1;
	if(argc > 2)
		i = number(argv[2], i, 10);

	if(i < 0 || i >= NPLAT || w.plat[i].status == Sempty) {
		print("'to' device empty %d\n", i);
		return;
	}
	for(lb = to.unit; lb < hb; lb++)
		if(cwdevs[lb].part == i) {
			print("'to' device is in mlev %d\n", i);
			return;
		}
	to = fr;
	to.part = i;

	wcp.off = 0;
	if(argc > 3)
		wcp.off = number(argv[3], wcp.off, 10);

	print("'from' device %D; 'to' device %D %ld\n", fr, to, wcp.off);
	wcp.fr = fr;
	wcp.to = to;

	worminit(fr);
	v = wormunit(fr);
	wcp.nblock = v->nblock;
	wcp.block = v->block;
	wcp.memb = wcp.memc / v->block;
	qunlock(&w);

	worminit(to);
	v = wormunit(to);
	if(wcp.nblock != v->nblock) {
		print("fr and to have different number of blocks %ld %ld\n",
			wcp.nblock, v->nblock);
		qunlock(&w);
		return;
	}
	if(wcp.block != v->block) {
		print("fr and to have different size of blocks %ld %ld\n",
			wcp.nblock, v->nblock);
		qunlock(&w);
		return;
	}
	qunlock(&w);

	if(argc > 4)
		wcp.nblock = number(argv[4], wcp.nblock, 10);

	wcp.active = 1;
}

/*
 * binary search for non-blank/blank boundary
 * on every non-blank probe, read and compare.
 * assert that lb is non-blank
 */
void
wbsrch(long lb, long ub)
{
	Plat *v;
	long mb;
	int s, nbad;
	uchar cmd[10], buf[6];

	nbad = 0;

loop:
	v = wormunit(wcp.to);

loop1:
	mb = (lb + ub) / 2;
	if(cons.flags & roflag)
		print("probe %ld\n", mb);
	if(mb <= lb) {
		wcp.off = lb+1;
		print("binary search found %ld\n", wcp.off);
		qunlock(&w);
		return;
	}

	/*
	 * non-blank search
	 */
	cmd[0] = 0x2d;	/* written sector search */
	cmd[1] = v->lun << 5;

	cmd[2] = mb>>24;
	cmd[3] = mb>>16;
	cmd[4] = mb>>8;
	cmd[5] = mb;
	cmd[6] = 0;

	cmd[7] = 0;
	cmd[8] = 1;
	cmd[9] = 0;

	s = ascsiio(SCSIread, cmd, 10, buf, 6);
	if(s) {
		print("wbsrch %.2x: non blank search\n", s);
		goto stop;
	}
	if((buf[1]&1) == 0) {
		/*
		 * it is blank, seek back
		 */
		ub = mb;
		goto loop1;
	}
	/*
	 * it is full, read and compare
	 * if match, seek forw
	 */
	cmd[0] = 0x28;	/* read */
	s = ascsiio(SCSIread, cmd, 10, wcp.memp, wcp.block);
	if(s) {
		print("wbsrch: read 'to' status %.2x\n", s);
		goto stop;
	}
	qunlock(&w);

	v = wormunit(wcp.fr);
	cmd[0] = 0x28;	/* read */
	cmd[1] = v->lun << 5;
	s = ascsiio(SCSIread, cmd, 10, wcp.memp+wcp.block, wcp.block);
	if(s) {
		print("wbsrch: read 'fr' status %.2x\n", s);
		goto stop;
	}
	if(memcmp(wcp.memp, wcp.memp+wcp.block, wcp.block) != 0) {
		print("wbsrch: compare %ld\n", mb);
		nbad++;
		if(nbad > 2)
			goto stop;
	}
	qunlock(&w);

	lb = mb;
	goto loop;

stop:
	qunlock(&w);
	wcp.active = 0;
}

/*
 * one wormcp pass
 * will copy one buffer across.
 * return flag is if more to do.
 */
int
dowcp(void)
{
	Plat *v;
	int s;
	long l, n;
	uchar cmd[10], buf[6];

	if(!wcp.active)
		return 0;
	v = wormunit(wcp.fr);
	if(w.active) {
		w.active = 0;
		qunlock(&w);
		return 0;
	}

loop:
	/*
	 * non-blank search
	 */
	if(wcp.off >= wcp.nblock) {
		print("done wormcp %D %D; offset = %ld\n",
			wcp.fr, wcp.to, wcp.off);
		goto stop;
	}
	n = 65535;
	if(wcp.off+n > wcp.nblock)
		n = wcp.nblock - wcp.off;

	cmd[0] = 0x2d;	/* written sector search */
	cmd[1] = v->lun << 5;

	l = wcp.off;
	cmd[2] = l>>24;
	cmd[3] = l>>16;
	cmd[4] = l>>8;
	cmd[5] = l;
	cmd[6] = 0;

	cmd[7] = n>>8;
	cmd[8] = n;
	cmd[9] = 0;

	s = ascsiio(SCSIread, cmd, 10, buf, 6);
	if(s) {
		print("dowcp: non-blank search status %.2x\n", s);
		goto stop;
	}
	if((buf[1]&1) == 0) {
		if(cons.flags & roflag)
			print("non-blank failed %ld(%ld)\n", wcp.off, n);
		wcp.off += n;
		goto loop;
	}
	l = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
	if(cons.flags & roflag)
		print("non-blank found %ld(%ld)\n", wcp.off, l-wcp.off);
	if(l < wcp.off) {
		print("dowcp: non-blank backwards\n");
		goto stop;
	}
	wcp.off = l;

	/*
	 * blank search
	 */
	n = wcp.memb;
	if(wcp.off+n > wcp.nblock)
		n = wcp.nblock - wcp.off;

	cmd[0] = 0x2c;	/* blank sector search */
	cmd[1] = v->lun << 5;

	l = wcp.off;
	cmd[2] = l>>24;
	cmd[3] = l>>16;
	cmd[4] = l>>8;
	cmd[5] = l;
	cmd[6] = 0;

	cmd[7] = n>>8;
	cmd[8] = n;
	cmd[9] = 0;
	s = ascsiio(SCSIread, cmd, 10, buf, 6);
	if(s) {
		print("dowcp: blank search status %.2x\n", s);
		goto stop;
	}
	if((buf[1]&1) == 0) {
		if(cons.flags & roflag)
			print("blank failed %ld(%ld)\n", wcp.off, n);
	} else {
		l = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
		n = l - wcp.off;
		if(cons.flags & roflag)
			print("blank found %ld(%ld)\n", wcp.off, n);
	}

	/*
	 * read
	 */
rloop:
	if(cons.flags & roflag)
		print("read %ld(%ld)\n", wcp.off, n);

	cmd[0] = 0x28;	/* read */

	cmd[7] = n>>8;
	cmd[8] = n;
	cmd[9] = 0;

	s = ascsiio(SCSIread, cmd, 10, wcp.memp, n*wcp.block);
	if(s) {
		if(s != 0x54 && s != 0x50) {	/* unrecov read error */
			print("dowcp: read status %.2x\n", s);
			goto stop;
		}
		if(n == 1) {
			print("dowcp: unrecov read error %ld\n", wcp.off);
			wcp.off++;
			qunlock(&w);
			return 1;
		}
		n = n/2;
		goto rloop;
	}

	/*
	 * write
	 */
	qunlock(&w);
	v = wormunit(wcp.to);

	if(cons.flags & roflag)
		print("write %ld(%ld)\n", wcp.off, n);

	cmd[0] = 0x2a;	/* write */
	cmd[1] = v->lun << 5;

	s = ascsiio(SCSIwrite, cmd, 10, wcp.memp, n*wcp.block);
	if(s) {
		if(s == 0x61) {
			qunlock(&w);
			wbsrch(wcp.off, wcp.nblock);
			return wcp.active;
		}
		print("dowcp: write status %.2x\n", s);
		goto stop;
	}
	wcp.off += n;

	qunlock(&w);
	return 1;

stop:
	wcp.active = 0;
	qunlock(&w);
	return 0;
}
wren.c          715899134   0     0     664     2010      `
#include	"all.h"

void
wreninit(Device d)
{
	int s;
	uchar cmd[10], buf[8];
	Drive *dr;

	dr = scsidrive(d);
	if(dr == 0 || dr->status != Dready) {
		print("	drive %D: not ready\n", d);
		return;
	}

loop:
	if(dr->mult)
		return;
	memset(cmd, 0, sizeof(cmd));
	cmd[0] = 0x25;					/* read capacity */
	s = scsiio(dr->dev, SCSIread, cmd, sizeof(cmd), buf, sizeof(buf));
	if(s) {
		print("wreninit: %D bad status %.4x\n", d, s);
		delay(5000);
		goto loop;
	}
	dr->nblock =
		(buf[0]<<24) |
		(buf[1]<<16) |
		(buf[2]<<8) |
		(buf[3]<<0);
	dr->block =
		(buf[4]<<24) |
		(buf[5]<<16) |
		(buf[6]<<8) |
		(buf[7]<<0);
	dr->mult =
		(RBUFSIZE + dr->block - 1) /
		dr->block;
	dr->max =
		(dr->nblock + 1) / dr->mult;
	print("	drive %D:\n", d);
	print("		%ld blocks at %ld bytes each\n",
		dr->nblock, dr->block);
	print("		%ld logical blocks at %d bytes each\n",
		dr->max, RBUFSIZE);
	print("		%ld multiplier\n",
		dr->mult);
}

long
wrensize(Device a)
{
	Drive *dr;

	dr = scsidrive(a);
	return dr->max;
}

int
wreniocmd(int io, Device a, long b, void *c)
{
	long l, m;
	uchar cmd[10];
	Drive *dr;

	if((dr = scsidrive(a)) == 0) {
		print("wreniocmd: no drive - a=%D b=%ld\n", a, b);
		return 0x40;
	}
	if(b >= dr->max) {
		print("wreniocmd out of range a=%D b=%ld\n", a, b);
		return 0x40;
	}

	cmd[0] = 0x28;	/* extended read */
	if(io != SCSIread)
		cmd[0] = 0x2a;	/* extended write */
	cmd[1] = 0;

	m = dr->mult;
	l = b * m;
	cmd[2] = l>>24;
	cmd[3] = l>>16;
	cmd[4] = l>>8;
	cmd[5] = l;
	cmd[6] = 0;

	cmd[7] = m>>8;
	cmd[8] = m;
	cmd[9] = 0;

	return scsiio(dr->dev, io, cmd, sizeof(cmd), c, RBUFSIZE);
}

int
wrenread(Device a, long b, void *c)
{
	int s;

	s = wreniocmd(SCSIread, a, b, c);
	if(s) {
		print("wrenread: %D(%ld) bad status %.4x\n", a, b, s);
		cons.nwormre++;
		return 1;
	}
	return 0;
}

int
wrenwrite(Device a, long b, void *c)
{
	int s;

	s = wreniocmd(SCSIwrite, a, b, c);
	if(s) {
		print("wrenwrite: %D(%ld) bad status %.4x\n", a, b, s);
		cons.nwormwe++;
		return 1;
	}
	return 0;
}
cw.h            700125416   0     0     664     1441      `
#define	DEBUG		0
#define	FIRST		SUPER_ADDR

#define	ADDFREE		(100)
#define	CACHE_ADDR	SUPER_ADDR
#define	DSIZE		546000
#define	MAXAGE		5000
#define	BKPERBLK	10
#define CEPERBK		((BUFSIZE-BKPERBLK*sizeof(long))/\
				(sizeof(Centry)*BKPERBLK))

/* cache state */
enum
{
	/* states -- beware these are recorded on the cache */
				/*    cache    worm	*/
	Cnone = 0,		/*	0	?	*/
	Cdirty,			/*	1	0	*/
	Cdump,			/*	1	0->1	*/
	Cread,			/*	1	1	*/
	Cwrite,			/*	2	1	*/
	Cdump1,			/* inactive form of dump */
	Cerror,

	/* opcodes -- these are not recorded */
	Onone,
	Oread,
	Owrite,
	Ogrow,
	Odump,
	Orele,
	Ofree,
};

typedef	struct	Cache	Cache;
typedef	struct	Centry	Centry;
typedef	struct	Bucket	Bucket;

struct	Cache
{
	long	maddr;		/* cache map addr */
	long	msize;		/* cache map size in buckets */
	long	caddr;		/* cache addr */
	long	csize;		/* cache size */
	long	fsize;		/* current size of worm */
	long	wsize;		/* max size of the worm */
	long	wmax;		/* highwater write */

	long	sbaddr;		/* super block addr */
	long	cwraddr;	/* cw root addr */
	long	roraddr;	/* dump root addr */

	long	toytime;	/* somewhere convienent */
	long	time;
};

struct	Centry
{
	ushort	age;
	short	state;
	long	waddr;		/* worm addr */
};

struct	Bucket
{
	long	agegen;		/* generator for ages in this bkt */
	Centry	entry[CEPERBK];
};

extern	char*	cwnames[];

Centry*	getcentry(Bucket*, long);
int	cwio(Device, long, void*, int);
void	cmd_cwcmd(int, char*[]);

cw1.c           700125032   0     0     664     24365     `
#include "all.h"
#include "cw.h"

static
struct
{
	Filter	ncwio;
	int	inited;
	int	dbucket;	/* last bucket dumped */
	long	daddr;		/* last block dumped */
	long	ncopy;
	int	nodump;
/*
 * here down are cached variables for dumps
 */
	Device	dev;
	Device	cdev;
	Device	wdev;
	Device	rodev;
	long	fsize;
	long	ndump;
	int	depth;
	int	all;
	struct
	{
		char	name[500];
		char	namepad[NAMELEN+10];
	};
} cw;

/*
 * console command
 * initiate a dump
 */
void
cmd_dump(int argc, char *argv[])
{
	Filsys *fs;

	fs = cons.curfs;
	if(argc > 1)
		fs = fsstr(argv[1]);
	if(fs == 0) {
		print("%s: unknown file system\n", argv[1]);
		return;
	}
	cfsdump(fs);
}

/*
 * console command
 * allow/disallow dribble to worm
 */
static
void
cmd_startdump(int argc, char *argv[])
{
	char *arg;

	arg = 0;
	if(argc >= 1)
		arg = argv[1];
	cw.nodump = number(arg, !cw.nodump, 10);

	if(cw.nodump)
		print("dump stopped\n");
	else
		print("dump allowed\n");
}

/*
 * console command
 * worm stats
 */
static
void
cmd_statw(int argc, char *argv[])
{
	Filsys *fs;
	Iobuf *p, *cb;
	Superb *sb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m, nw, bw, state[Onone];
	Device dev, cdev;
	int s;

	USED(argc);
	USED(argv);

	print("cwstats\n");
	print("	nio   = %F\n", (Filta){&cw.ncwio, 1});

	for(fs=filsys; fs->name; fs++) {
		dev = fs->dev;
		if(dev.type != Devcw)
			continue;

		print("	cache %s\n", fs->name);

		cdev = CDEV(dev);
		p = getbuf(dev, cwsaddr(dev), Bread);
		if(!p || checktag(p, Tsuper, QPSUPER)) {
			print("cwstats: checktag super\n");
			if(p)
				putbuf(p);
			continue;
		}
		sb = (Superb*)p->iobuf;
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
		if(!cb || checktag(cb, Tcache, QPSUPER)) {
			print("cwstats: checktag c bucket\n");
			putbuf(p);
			if(cb)
				putbuf(cb);
			continue;
		}
		h = (Cache*)cb->iobuf;

		print("		maddr  = %8ld\n", h->maddr);
		print("		msize  = %8ld\n", h->msize);
		print("		caddr  = %8ld\n", h->caddr);
		print("		csize  = %8ld\n", h->csize);
		print("		sbaddr = %8ld\n", h->sbaddr);
		print("		craddr = %8ld %8ld\n", h->cwraddr, sb->cwraddr);
		print("		roaddr = %8ld %8ld\n", h->roraddr, sb->roraddr);
		print("		fsize  = %8ld %8ld %2ld+%2ld%%\n", h->fsize, sb->fsize,
					h->fsize/DSIZE,
					(h->fsize%DSIZE)/(DSIZE/100));
		print("		slast  =          %8ld\n", sb->last);
		print("		snext  =          %8ld\n", sb->next);
		print("		wmax   = %8ld          %2ld+%2ld%%\n", h->wmax,
					h->wmax/DSIZE,
					(h->wmax%DSIZE)/(DSIZE/100));
		print("		wsize  = %8ld          %2ld+%2ld%%\n", h->wsize,
					h->wsize/DSIZE,
					(h->wsize%DSIZE)/(DSIZE/100));

		putbuf(p);

		bw = 0;	/* max filled bucket */
		memset(state, 0, sizeof(state));
		for(m=0; m<h->msize; m++) {
			p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bres);
			if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
				panic("cwstats: checktag c bucket");
			b = (Bucket*)p->iobuf + m%BKPERBLK;
			ce = b->entry + CEPERBK;
			nw = 0;
			for(c=b->entry; c<ce; c++) {
				s = c->state;
				state[s]++;
				if(s != Cnone && s != Cread)
					nw++;
			}
			putbuf(p);
			if(nw > bw)
				bw = nw;
		}
		putbuf(cb);
		for(s=Cnone; s<Cerror; s++)
			print("		%6ld %s\n", state[s], cwnames[s]);
		print("		cache %2ld%% full\n", (bw*100)/CEPERBK);
	}
}

int
dumpblock(Device dev)
{
	Device cdev, wdev;
	Iobuf *p, *cb, *p1, *p2;
	Cache *h;
	Centry *c, *ce, *bc;
	Bucket *b;
	long m, a, msize, maddr, wmax, caddr;
	int s1, s2;

	if(cw.nodump)
		return 0;

	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	h = (Cache*)cb->iobuf;
	msize = h->msize;
	maddr = h->maddr;
	wmax = h->wmax;
	caddr = h->caddr;
	putbuf(cb);
	for(m=msize; m>=0; m--) {
		a = cw.dbucket + 1;
		if(a < 0 || a >= msize)
			a = 0;
		cw.dbucket = a;
		p = getbuf(cdev, maddr + a/BKPERBLK, Bread|Bres);
		b = (Bucket*)p->iobuf + a%BKPERBLK;
		ce = b->entry + CEPERBK;
		bc = 0;
		for(c=b->entry; c<ce; c++)
			if(c->state == Cdump) {
				if(bc == 0) {
					bc = c;
					continue;
				}
				if(c->waddr < cw.daddr) {
					if(bc->waddr < cw.daddr && bc->waddr > c->waddr)
						bc = c;
					continue;
				}
				if(bc->waddr < cw.daddr || bc->waddr > c->waddr)
					bc = c;
			}
		if(bc) {
			c = bc;
			goto found;
		}
		putbuf(p);
	}
	if(cw.ncopy) {
		print("%ld blocks copied to worm\n", cw.ncopy);
		cw.ncopy = 0;
	}
	cw.nodump = 1;
	return 0;

found:
	a = a*CEPERBK + (c - b->entry) + caddr;
	p1 = getbuf(devnone, Cwdump1, 0);

retry:
	if(devread(cdev, a, p1->iobuf))
		goto stop;
	m = c->waddr;
	cw.daddr = m;
	wdev = WDEV(dev);
	s1 = devwrite(wdev, m, p1->iobuf);
	if(s1) {
		p2 = getbuf(devnone, Cwdump2, 0);
		s2 = devread(wdev, m, p2->iobuf);
		if(s2) {
			if(s1 == 0x61 && s2 == 0x60) {
				putbuf(p2);
				goto retry;
			}
			goto stop1;
		}
		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
			goto stop1;
		putbuf(p2);
	}
	/*
	 * reread and compare
	 */
	p2 = getbuf(devnone, Cwdump2, 0);
	s1 = devread(wdev, m, p2->iobuf);
	if(s1)
		goto stop1;
	if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
		print("reread C%ld W%ld didnt compare\n", a, m);
		goto stop1;
	}
	putbuf(p2);

	putbuf(p1);
	c->state = Cread;
	p->flags |= Bmod;
	putbuf(p);

	if(m > wmax) {
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
		h = (Cache*)cb->iobuf;
		if(m > h->wmax)
			h->wmax = m;
		putbuf(cb);
	}
	cw.ncopy++;
	return 1;

stop1:
	putbuf(p2);
	putbuf(p1);
	c->state = Cdump1;
	p->flags |= Bmod;
	putbuf(p);
	return 1;

stop:
	putbuf(p1);
	putbuf(p);
	print("stopping dump!!\n");
	cw.nodump = 1;
	return 0;
}

void
cwinit(Device dev)
{
	Cache *h;
	Iobuf *cb, *p;
	long l, m;
	Device cdev;

	if(!cw.inited) {
		cw.inited = 1;
		dofilter(&cw.ncwio);
		cmd_install("startdump", "[01] -- allow copy to worm", cmd_startdump);
		cmd_install("dump", "-- make dump backup to worm", cmd_dump);
		cmd_install("statw", "-- cache/worm stats", cmd_statw);
		cmd_install("cwcmd", "subcommand -- cache/worm eratta", cmd_cwcmd);

		roflag = flag_install("ro", "-- ro reads and writes");
	}
	cdev = CDEV(dev);
	devinit(cdev);
	devinit(WDEV(dev));
	l = devsize(WDEV(dev));

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	h->toytime = toytime() + SECOND(30);
	h->time = time();
	m = h->wsize;
	if(l != m) {
		print("wdev changed size %ld to %ld\n", m, l);
		h->wsize = l;
		cb->flags |= Bmod;
	}

	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwinit: checktag c bucket");
		putbuf(p);
	}

	putbuf(cb);
}

long
cwsaddr(Device dev)
{
	Iobuf *cb;
	long sa;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	sa = ((Cache*)cb->iobuf)->sbaddr;
	putbuf(cb);
	return sa;
}

long
cwraddr(Device dev, int flag)
{
	Iobuf *cb;
	long ra;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	if(flag)
		ra = ((Cache*)cb->iobuf)->roraddr;
	else
		ra = ((Cache*)cb->iobuf)->cwraddr;
	putbuf(cb);
	return ra;
}

long
cwsize(Device dev)
{
	Iobuf *cb;
	long fs;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bres);
	fs = ((Cache*)cb->iobuf)->fsize;
	putbuf(cb);
	return fs;
}

int
cwread(Device a, long b, void *c)
{
	return cwio(a, b, c, Oread) == Cerror;
}

int
cwwrite(Device a, long b, void *c)
{
	return cwio(a, b, c, Owrite) == Cerror;
}

int
roread(Device a, long b, void *c)
{
	Iobuf *p;
	Device d;
	int s;

	d = a;
	d.type = Devcw;

	/*
	 * maybe better is to try buffer pool first
	 */
	s = cwio(d, b, 0, Onone);
	if(s == Cdump || s == Cdump1 || s == Cread) {
		s = cwio(d, b, c, Oread);
		if(s == Cdump || s == Cdump1 || s == Cread) {
			if(cons.flags & roflag)
				print("roread: %D %ld -> %D(hit)\n", a, b, d);
			return 0;
		}
	}
	if(cons.flags & roflag)
		print("roread: %D %ld -> %D(miss)\n", a, b, WDEV(d));
	return devread(WDEV(d), b, c);
}

int
cwio(Device dev, long addr, void *buf, int opcode)
{
	Iobuf *p, *p1, *p2, *cb;
	Cache *h;
	Bucket *b;
	Centry *c;
	Device cdev, wdev;
	long bn, a1, a2, max, newmax;
	int state, s1, s2;

	cw.ncwio.count++;
	cdev = CDEV(dev);
	wdev = WDEV(dev);

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	h = (Cache*)cb->iobuf;
	if(toytime() >= h->toytime) {
		cb->flags |= Bmod;
		h->toytime = toytime() + SECOND(30);
		h->time = time();
	}

	if(addr < 0) {
		putbuf(cb);
		panic("cwio neg address %ld", addr);
	}
/*
	if(addr >= h->fsize)
		print("cwio large address %ld\n", addr);
*/
	bn = addr % h->msize;
	a1 = h->maddr + bn/BKPERBLK;
	a2 = bn*CEPERBK + h->caddr;
	max = h->wmax;

	putbuf(cb);
	newmax = 0;

	p = getbuf(cdev, a1, Bread|Bmod|Bres);
	if(!p || checktag(p, Tbuck, a1))
		panic("cwio: checktag c bucket");
	b = (Bucket*)p->iobuf + bn%BKPERBLK;

	c = getcentry(b, addr);
	if(c == 0) {
		putbuf(p);
		print("disk cache bucket %ld is full\n", a1);
		return Cerror;
	}
	a2 += c - b->entry;

	state = c->state;
	switch(opcode)
	{
	default:
		goto bad;

	case Onone:
		break;

	case Oread:
		switch(state) {
		default:
			goto bad;

		case Cread:
			if(!devread(cdev, a2, buf))
				break;

		case Cnone:
			if(devread(wdev, addr, buf)) {
				state = Cerror;
				break;
			}
			if(addr > max)
				newmax = addr;
			if(!devwrite(cdev, a2, buf))
				c->state = Cread;
			break;


		case Cdirty:
		case Cdump:
		case Cdump1:
		case Cwrite:
			if(devread(cdev, a2, buf))
				state = Cerror;
			break;
		}
		break;

	case Owrite:
		switch(state) {
		default:
			goto bad;

		case Cdump:
			p1 = getbuf(devnone, Cwio1, 0);

		retry:
			p2 = 0;
			if(devread(cdev, a2, p1->iobuf)) {
			err:
				putbuf(p1);
				if(p2)
					putbuf(p2);
				print("cwio: write induced dump error\n");
				state = Cerror;
				break;
			}
			s1 = devwrite(wdev, addr, p1->iobuf);
			if(s1) {
				p2 = getbuf(devnone, Cwio2, 0);
				s2 = devread(wdev, addr, p2->iobuf);
				if(s2) {
					if(s1 == 0x61 && s2 == 0x60) {
						putbuf(p2);
						goto retry;
					}
					goto err;
				}
				if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
					goto err;
				putbuf(p2);
			}
			putbuf(p1);
			c->state = Cread;
			if(addr > max)
				newmax = addr;
			cw.ncopy++;

		case Cnone:
		case Cread:
			if(devwrite(cdev, a2, buf)) {
				state = Cerror;
				break;
			}
			c->state = Cwrite;
			break;

		case Cdump1:
		case Cdirty:
		case Cwrite:
			if(devwrite(cdev, a2, buf))
				state = Cerror;
			break;
		}
		break;

	case Ogrow:
		if(state != Cnone) {
			print("cwgrow with state = %s\n",
				cwnames[state]);
			break;
		}
		c->state = Cdirty;
		break;

	case Odump:
		if(state != Cdirty) {	/* BOTCH */
			print("cwdump with state = %s\n",
				cwnames[state]);
			break;
		}
		c->state = Cdump;
		cw.ndump++;	/* only called from dump command */
		break;

	case Orele:
		if(state != Cwrite) {
			if(state != Cdump1)
				print("cwrele with state = %s\n",
					cwnames[state]);
			break;
		}
		c->state = Cnone;
		break;

	case Ofree:
		if(state == Cwrite || state == Cread)
			c->state = Cnone;
		break;
	}
	if(DEBUG)
		print("cwio: %ld s=%s o=%s ns=%s\n",
			addr, cwnames[state],
			cwnames[opcode],
			cwnames[c->state]);
	putbuf(p);
	if(newmax) {
		cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
		h = (Cache*)cb->iobuf;
		if(newmax > h->wmax)
			h->wmax = newmax;
		putbuf(cb);
	}
	return state;

bad:
	print("cw state = %s; cw opcode = %s",
		cwnames[state], cwnames[opcode]);
	return Cerror;
}

int
cwgrow(Device dev, Superb *sb)
{
	Iobuf *cb;
	Cache *h;
	long fs, nfs, ws;
	int state;

	cb = getbuf(CDEV(dev), CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	ws = h->wsize;
	fs = h->fsize;
	if(fs >= ws)
		return 0;
	nfs = fs + ADDFREE;
	if(nfs >= ws)
		nfs = ws;
	h->fsize = nfs;
	putbuf(cb);

	sb->fsize = nfs;
	print("%D grow from %ld to %ld limit %ld\n", dev, fs, nfs, ws);
	for(nfs--; nfs>=fs; nfs--) {
		state = cwio(dev, nfs, 0, Ogrow);
		if(state == Cnone)
			addfree(dev, nfs, sb);
	}
	return 1;
}

int
cwfree(Device dev, long addr)
{
	int state;

	if(dev.type == Devcw) {
		state = cwio(dev, addr, 0, Ofree);
		if(state != Cdirty)
			return 1;	/* do not put in freelist */
	}
	return 0;			/* put in freelist */
}

void
resequence(Bucket *b)
{
	Centry *c, *ce, *cr;
	int age, i;

	ce = b->entry + CEPERBK;
	b->agegen = CEPERBK;
	cons.nreseq++;
	for(i=0;; i++) {
		cr = 0;
		age = MAXAGE;
		for(c = b->entry; c < ce; c++) {
			if(c->age < i)
				continue;
			if(cr == 0 || c->age < age) {
				cr = c;
				age = c->age;
			}
		}
		if(cr == 0)
			return;
		cr->age = i;
	}
}

Centry*
getcentry(Bucket *b, long addr)
{
	Centry *c, *ce, *cr;
	int s, age;


	/*
	 * search for cache hit
	 * find oldest block as byproduct
	 */
	ce = b->entry + CEPERBK;
	age = MAXAGE;
	cr = 0;
	for(c = b->entry; c < ce; c++) {
		if(c->waddr == addr)
			goto found;
		s = c->state;
		if(s == Cnone) {
			cr = c;
			age = 0;
		} else
		if(s == Cread)
			if(c->age < age) {
				cr = c;
				age = c->age;
			}
	}

	/*
	 * remap entry
	 */
	c = cr;
	if(c == 0)
		return 0;	/* bucket is full */

	c->state = Cnone;
	c->waddr = addr;

found:
	age = b->agegen;
	c->age = age;
	age++;
	b->agegen = age;
	if(age >= MAXAGE)
		resequence(b);
	return c;
}

/*
 * ream the cache
 * calculate new buckets
 */
Iobuf*
cacheinit(Device dev)
{
	Iobuf *cb, *p;
	Cache *h;
	Device cdev;
	long m;

	print("cache init %D\n", dev);
	cdev = CDEV(dev);
	devinit(cdev);

	cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
	memset(cb->iobuf, 0, RBUFSIZE);
	settag(cb, Tcache, QPSUPER);
	h = (Cache*)cb->iobuf;

	/*
	 * calculate csize such that
	 * tsize = msize/BKPERBLK + csize and
	 * msize = csize/CEPERBK
	 */
	h->maddr = CACHE_ADDR + 1;
	m = devsize(cdev) - h->maddr;
	h->csize = ((m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
	h->msize = h->csize/CEPERBK;
	while(!prime(h->msize))
		h->msize--;
	h->csize = h->msize*CEPERBK;
	h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
	h->wsize = devsize(WDEV(dev));

	if(h->msize <= 0)
		panic("cache too small");
	if(h->caddr + h->csize > m)
		panic("cache size error");

	/*
	 * setup cache map
	 */
	for(m=h->maddr; m<h->caddr; m++) {
		p = getbuf(cdev, m, Bmod|Bres);
		memset(p->iobuf, 0, RBUFSIZE);
		settag(p, Tbuck, m);
		putbuf(p);
	}
	print("done cacheinit\n");
	return cb;
}

/*
 * ream the cache
 * calculate new buckets
 * get superblock from
 * last worm dump block.
 */
void
cwrecover(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Superb *s;
	long m, baddr;
	Device wdev;

	wdev = WDEV(dev);

	devinit(wdev);

	p = getbuf(devnone, Cwxx1, 0);
	s = (Superb*)p->iobuf;
	baddr = 0;
	m = FIRST;
	if(startsb)		/* to force a starting superblock */
		m = startsb;
	for(;;) {
		memset(p->iobuf, 0, RBUFSIZE);
		if(devread(wdev, m, p->iobuf) ||
		   checktag(p, Tsuper, QPSUPER))
			break;
		baddr = m;
		m = s->next;
		print("dump %ld is good; %ld next\n", baddr, m);
	}
	putbuf(p);
	if(!baddr)
		panic("recover: no superblock\n");

	p = getbuf(wdev, baddr, Bread);
	s = (Superb*)p->iobuf;

	cb = cacheinit(dev);
	h = (Cache*)cb->iobuf;
	h->sbaddr = baddr;
	h->cwraddr = s->cwraddr;
	h->roraddr = s->roraddr;
	h->fsize = s->fsize;		/* this must be accurate */

	putbuf(cb);
	putbuf(p);

	p = getbuf(dev, baddr, Bread|Bmod);
	s = (Superb*)p->iobuf;

	memset(&s->fbuf, 0, sizeof(s->fbuf));
	s->fbuf.free[0] = 0;
	s->fbuf.nfree = 1;
	s->tfree = 0;

	putbuf(p);
	print("done recover\n");
}
/*
 * ream the cache
 * calculate new buckets
 * initialize superblock.
 */
void
cwream(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Superb *s;
	long m, baddr;
	Device cdev;


	print("cwream %D\n", dev);
	cdev = CDEV(dev);
	devinit(cdev);

	baddr = FIRST;	/*	baddr   = super addr
				baddr+1 = cw root
				baddr+2 = ro root
				baddr+3 = reserved next superblock */

	cb = cacheinit(dev);
	h = (Cache*)cb->iobuf;

	h->sbaddr = baddr;
	h->cwraddr = baddr+1;
	h->roraddr = baddr+2;
	h->fsize = 0;	/* prevents superream from freeing */

	putbuf(cb);

	for(m=0; m<3; m++)
		cwio(dev, baddr+m, 0, Ogrow);
	superream(dev, baddr);
	rootream(dev, baddr+1);			/* cw root */
	rootream(dev, baddr+2);			/* ro root */

	cb = getbuf(cdev, CACHE_ADDR, Bread|Bmod|Bres);
	h = (Cache*)cb->iobuf;
	h->fsize = baddr+4;
	putbuf(cb);

	p = getbuf(dev, baddr, Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->last = baddr;
	s->cwraddr = baddr+1;
	s->roraddr = baddr+2;
	s->next = baddr+3;
	s->fsize = baddr+4;
	putbuf(p);

	for(m=0; m<3; m++)
		cwio(dev, baddr+m, 0, Odump);
}

long
rewalk1(long addr, int slot, Wpath *up)
{
	Iobuf *p, *p1;
	Dentry *d;

	if(up == 0)
		return cwraddr(cw.dev, 0);
	up->addr = rewalk1(up->addr, up->slot, up->up);
	p = getbuf(cw.dev, up->addr, Bread|Bmod);
	d = getdir(p, up->slot);
	if(!d || !(d->mode & DALLOC)) {
		print("rewalk1 1\n");
		if(p)
			putbuf(p);
		return addr;
	}
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0);
	if(!p1) {
		print("rewalk1 2\n");
		if(p)
			putbuf(p);
		return addr;
	}
	if(DEBUG)
		print("rewalk1 %ld to %ld \"%s\"\n",
			addr, p1->addr, d->name);
	addr = p1->addr;
	p1->flags |= Bmod;
	putbuf(p1);
	putbuf(p);
	return addr;
}

long
rewalk2(long addr, int slot, Wpath *up)
{
	Iobuf *p, *p1;
	Dentry *d;

	if(up == 0)
		return cwraddr(cw.dev, 1);
	up->addr = rewalk2(up->addr, up->slot, up->up);
	p = getbuf(cw.rodev, up->addr, Bread);
	d = getdir(p, up->slot);
	if(!d || !(d->mode & DALLOC)) {
		print("rewalk2 1\n");
		if(p)
			putbuf(p);
		return addr;
	}
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0);
	if(!p1) {
		print("rewalk2 2\n");
		if(p)
			putbuf(p);
		return addr;
	}
	if(DEBUG)
		print("rewalk2 %ld to %ld \"%s\"\n",
			addr, p1->addr, d->name);
	addr = p1->addr;
	putbuf(p1);
	putbuf(p);
	return addr;
}

void
rewalk(void)
{
	Chan *cp;
	File *fp;

	for(cp=chans; cp; cp=cp->next) {
		for(fp=cp->flist; fp; fp=fp->next) {
			if(!fp->fs)
				continue;
			if(!devcmp(cw.dev, fp->fs->dev))
				fp->addr = rewalk1(fp->addr, fp->slot, fp->wpath);
			else
			if(!devcmp(cw.rodev, fp->fs->dev))
				fp->addr = rewalk2(fp->addr, fp->slot, fp->wpath);
		}
	}
}

long
split(Iobuf *p, long addr)
{
	long na;
	int state;

	na = 0;
	if(p && (p->flags & Bmod)) {
		p->flags |= Bimm;
		putbuf(p);
		p = 0;
	}
	state = cwio(cw.dev, addr, 0, Onone);	/* read the state (twice?) */
	switch(state)
	{
	default:
		panic("split: unknown state %s", cwnames[state]);

	case Cnone:
	case Cdump:
	case Cread:
		break;

	case Cdump1:
	case Cwrite:
		/*
		 * botch.. could be done by relabeling
		 */
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("split: null getbuf\n");
				break;
			}
		}
		na = cw.fsize;
		cw.fsize = na+1;
		cwio(cw.dev, na, 0, Ogrow);
		cwio(cw.dev, na, p->iobuf, Owrite);
		cwio(cw.dev, na, 0, Odump);
		cwio(cw.dev, addr, 0, Orele);
		break;

	case Cdirty:
		cwio(cw.dev, addr, 0, Odump);
		break;
	}
	if(p)
		putbuf(p);
	return na;
}

int
isdirty(Iobuf *p, long addr)
{
	int s;

	if(p && (p->flags & Bmod))
		return 1;
	s = cwio(cw.dev, addr, 0, Onone);
	if(s == Cdirty || s == Cwrite)
		return 1;
	return 0;
}

long
cwrecur(long addr, int tag, int tag1, long qp)
{
	Iobuf *p, *p1;
	Dentry *d;
	int i, j;
	long na;
	char *np;

	p = getbuf(cw.dev, addr, Bprobe);
	if(!cw.all && tag != Tind1 && tag != Tind2)	/* botch, get these modified */
	if(!isdirty(p, addr)) {
		if(DEBUG)
			print("cwrecur: %s %ld t=%s not dirty\n",
				cw.name, addr, tagnames[tag]);
		if(p)
			putbuf(p);
		return 0;
	}
	if(DEBUG)
		print("cwrecur: %s %ld t=%s\n",
			cw.name, addr, tagnames[tag]);
	if(cw.depth >= 100) {
		print("dump depth too great\n");
		if(p)
			putbuf(p);
		return 0;
	}
	cw.depth++;

	switch(tag)
	{
	default:
		print("cwrecur: unknown tag %d\n", tag);

	case Tfile:
		break;

	case Tsuper:
	case Tdir:
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("cwrecur: Tdir p null\n");
				break;
			}
		}
		if(tag == Tdir) {
			cw.namepad[0] = 0;	/* force room */
			np = strchr(cw.name, 0);
			*np++ = '/';
		} else {
			np = 0;	/* set */
			cw.name[0] = 0;
		}

		for(i=0; i<DIRPERBUF; i++) {
			d = getdir(p, i);
			if(!(d->mode & DALLOC))
				continue;
			qp = d->qid.path & ~QPDIR;
			if(tag == Tdir)
				strncpy(np, d->name, NAMELEN);
			else
			if(i > 0)
				print("cwrecur: root with >1 directory\n");
			tag1 = Tfile;
			if(d->mode & DDIR)
				tag1 = Tdir;
			for(j=0; j<NDBLOCK; j++) {
				if(na = d->dblock[j]) {
					na = cwrecur(na, tag1, 0, qp);
					if(na) {
						d->dblock[j] = na;
						p->flags |= Bmod;
					}
				}
			}
			if(na = d->iblock) {
				na = cwrecur(na, Tind1, tag1, qp);
				if(na) {
					d->iblock = na;
					p->flags |= Bmod;
				}
			}
			if(na = d->diblock) {
				na = cwrecur(na, Tind2, tag1, qp);
				if(na) {
					d->diblock = na;
					p->flags |= Bmod;
				}
			}
		}
		break;

	case Tind1:
		j = tag1;
		tag1 = 0;
		goto tind;

	case Tind2:
		j = Tind1;

	tind:
		if(!p) {
			p = getbuf(cw.dev, addr, Bread);
			if(!p) {
				print("cwrecur: Tind p null\n");
				break;
			}
		}
		for(i=0; i<INDPERBUF; i++) {
			if(na = ((long*)p->iobuf)[i]) {
				na = cwrecur(na, j, tag1, qp);
				if(na) {
					((long*)p->iobuf)[i] = na;
					p->flags |= Bmod;
				}
			}
		}
		break;
	}
	na = split(p, addr);
	cw.depth--;
	return na;
}

void
cfsdump(Filsys *fs)
{
	Iobuf *pr, *p1, *p;
	Dentry *dr, *d1, *d;
	Cache *h;
	Superb *s;
	long orba, rba, oroa, roa, sba, a, m, n, i;
	char tstr[20];

	if(fs->dev.type != Devcw) {
		print("cant dump this: %D\n", fs->dev);
		return;
	}

	wlock(&mainlock);		/* dump */

	/*
	 * set up static structure
	 * with frequent variables
	 */
	cw.dev = fs->dev;
	cw.rodev = cw.dev;
	cw.rodev.type = Devro;
	cw.wdev = WDEV(cw.dev);
	cw.cdev = CDEV(cw.dev);

	cw.ndump = 0;
	cw.name[0] = 0;
	cw.depth = 0;

	/*
	 * cw root
	 */
	sync("before dump");
	cw.fsize = cwsize(cw.dev);
	orba = cwraddr(cw.dev, 0);
	print("cwroot %ld", orba);
	cw.all = 1;
	rba = cwrecur(orba, Tsuper, 0, QPROOT);
	if(rba == 0)
		rba = orba;
	print("->%ld\n", rba);
	sync("after cw");

	/*
	 * partial super block
	 */
	p = getbuf(cw.dev, cwsaddr(cw.dev), Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->fsize = cw.fsize;
	s->cwraddr = rba;
	putbuf(p);

	/*
	 * partial cache block
	 */
	p = getbuf(cw.cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
	h = (Cache*)p->iobuf;
	h->fsize = cw.fsize;
	h->cwraddr = rba;
	putbuf(p);

	/*
	 * ro root
	 */
	oroa = cwraddr(cw.dev, 1);
	pr = getbuf(cw.dev, oroa, Bread|Bmod);
	dr = getdir(pr, 0);

	datestr(tstr, time());	/* tstr = "yyyymmdd" */
	n = 0;
	for(a=0;; a++) {
		p1 = dnodebuf(pr, dr, a, Tdir);
		if(!p1)
			goto bad;
		n++;
		for(i=0; i<DIRPERBUF; i++) {
			d1 = getdir(p1, i);
			if(!d1)
				goto bad;
			if(!(d1->mode & DALLOC))
				goto found1;
			if(!memcmp(d1->name, tstr, 4))
				goto found2;	/* found entry */
		}
		putbuf(p1);
	}

	/*
	 * no year directory, create one
	 */
found1:
	p = getbuf(cw.dev, rba, Bread);
	d = getdir(p, 0);
	d1->qid = d->qid;
	d1->qid.version += n;
	memmove(d1->name, tstr, 4);
	d1->mode = d->mode;
	d1->uid = d->uid;
	d1->gid = d->gid;
	putbuf(p);
	accessdir(p1, d1, FWRITE);

	/*
	 * put mmdd[count] in year directory
	 */
found2:
	accessdir(p1, d1, FREAD);
	putbuf(pr);
	pr = p1;
	dr = d1;

	n = 0;
	m = 0;
	for(a=0;; a++) {
		p1 = dnodebuf(pr, dr, a, Tdir);
		if(!p1)
			goto bad;
		n++;
		for(i=0; i<DIRPERBUF; i++) {
			d1 = getdir(p1, i);
			if(!d1)
				goto bad;
			if(!(d1->mode & DALLOC))
				goto found;
			if(!memcmp(d1->name, tstr+4, 4))
				m++;
		}
		putbuf(p1);
	}

	/*
	 * empty slot put in root
	 */
found:
	if(m)	/* how many dumps this date */
		sprint(tstr+8, "%d", m);

	p = getbuf(cw.dev, rba, Bread);
	d = getdir(p, 0);
	*d1 = *d;				/* qid is QPROOT */
	putbuf(p);
	strcpy(d1->name, tstr+4);
	d1->qid.version += n;
	accessdir(p1, d1, FWRITE);
	putbuf(p1);
	putbuf(pr);

	cw.fsize = cwsize(cw.dev);
	oroa = cwraddr(cw.dev, 1);		/* probably redundant */
	print("roroot %ld", oroa);
	cw.all = 0;
	roa = cwrecur(oroa, Tsuper, 0, QPROOT);
	if(roa == 0) {
		print("[same]");
		roa = oroa;
	}
	print("->%ld /%.4s/%s\n", roa, tstr, tstr+4);
	sync("after ro");

	/*
	 * final super block
	 */
	a = cwsaddr(cw.dev);
	print("sblock %ld", a);
	p = getbuf(cw.dev, a, Bread|Bmod|Bimm);
	s = (Superb*)p->iobuf;
	s->last = a;
	sba = s->next;
	s->next = cw.fsize;
	cw.fsize++;
	s->fsize = cw.fsize;
	s->roraddr = roa;

	cwio(cw.dev, sba, 0, Ogrow);
	cwio(cw.dev, sba, p->iobuf, Owrite);
	cwio(cw.dev, sba, 0, Odump);
	print("->%ld (->%ld)\n", sba, s->next);

	putbuf(p);

	/*
	 * final cache block
	 */
	p = getbuf(cw.cdev, CACHE_ADDR, Bread|Bmod|Bimm|Bres);
	h = (Cache*)p->iobuf;
	h->fsize = cw.fsize;
	h->roraddr = roa;
	h->sbaddr = sba;
	putbuf(p);

	rewalk();
	sync("all done");

	print("%ld blocks queued for worm\n", cw.ndump);
	cw.nodump = 0;

	wunlock(&mainlock);
	return;

bad:
	panic("dump: bad");
}

cw2.c           700128809   0     0     664     8095      `
#include "all.h"
#include "cw.h"

char*	cwnames[] =
{
	[Cnone]		"none",
	[Cdirty]	"dirty",
	[Cdump]		"dump",
	[Cread]		"read",
	[Cwrite]	"write",
	[Cdump1]	"dump1",
	[Cerror]	"error",

	[Onone]		"none",
	[Oread]		"read",
	[Owrite]	"write",
	[Ogrow]		"grow",
	[Odump]		"dump",
	[Orele]		"rele",
};

static
struct
{
	int	flg;
	long	maddr;
	int	caddr;
	int	platter;
	long	count;
	Device	cdev;
	Device	wdev;
} wcmp;

void
mvstates(Device dev, int s1, int s2)
{
	Iobuf *p, *cb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m;
	Device cdev;

	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++)
			if(c->state == s1)
				c->state = s2;
		putbuf(p);
	}
	putbuf(cb);
}

void
prchain(Device dev, long m, int flg)
{
	Iobuf *p;
	Superb *s;

	if(m == 0) {
		if(flg)
			m = cwsaddr(dev);
		else
		if(startsb)
			m = startsb;
		else
			m = FIRST;
	}
	p = getbuf(devnone, Cwxx2, 0);
	s = (Superb*)p->iobuf;
	for(;;) {
		memset(p->iobuf, 0, RBUFSIZE);
		if(devread(WDEV(dev), m, p->iobuf) ||
		   checktag(p, Tsuper, QPSUPER))
			break;
		if(flg) {
			print("dump %ld is good; %ld prev\n", m, s->last);
			if(m >= s->last)
				break;
			m = s->last;
		} else {
			print("dump %ld is good; %ld next\n", m, s->next);
			if(m <= s->next)
				break;
			m = s->next;
		}
	}
	putbuf(p);
}

void
savecache(Device dev)
{
	Iobuf *p, *cb;
	Cache *h;
	Bucket *b;
	Centry *c, *ce;
	long m, maddr, msize, left, *longp, nbyte;
	Device cdev;
	Superb *s;

	if(walkto("/adm/cache"))
		goto bad;
	if(con_open(FID2, MWRITE|MTRUNC))
		goto bad;


	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	msize = h->msize;
	maddr = h->maddr;
	putbuf(cb);

	cb = getbuf(devnone, Cwxx4, 0);
	longp = (long*)cb->iobuf;
	left = BUFSIZE/sizeof(long);
	cons.offset = 0;

	for(m=0; m<msize; m++) {
		if(left < BKPERBLK) {
			nbyte = (BUFSIZE/sizeof(long) - left) * sizeof(long);
			con_write(FID2, cb->iobuf, cons.offset, nbyte);
			cons.offset += nbyte;
			longp = (long*)cb->iobuf;
			left = BUFSIZE/sizeof(long);
		}
		p = getbuf(cdev, maddr + m/BKPERBLK, Bread|Bres);
		if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++)
			if(c->state == Cread) {
				*longp++ = c->waddr;
				left--;
			}
		putbuf(p);
	}
	nbyte = (BUFSIZE/sizeof(long) - left) * sizeof(long);
	con_write(FID2, cb->iobuf, cons.offset, nbyte);
	putbuf(cb);
	return;

bad:
	print("cant open /adm/cache\n");
}

void
loadcache(Device dev, int dskno)
{
	Iobuf *p, *cb;
	long m, msize, nbyte, *longp, count;

	if(walkto("/adm/cache"))
		goto bad;
	if(con_open(FID2, MREAD))
		goto bad;

	cb = getbuf(devnone, Cwxx4, 0);
	cons.offset = 0;
	count = 0;

	for(;;) {
		memset(cb->iobuf, 0, BUFSIZE);
		nbyte = con_read(FID2, cb->iobuf, cons.offset, BUFSIZE) / sizeof(long);
		if(nbyte <= 0)
			break;
		cons.offset += nbyte * sizeof(long);
		longp = (long*)cb->iobuf;
		while(nbyte > 0) {
			m = *longp++;
			nbyte--;
			if(m == 0)
				continue;
			if(dskno < 0 ||
			   m >= dskno*DSIZE && m < (dskno+1)*DSIZE) {
				p = getbuf(dev, m, Bread);
				if(p)
					putbuf(p);
				count++;
			}
		}
	}
	putbuf(cb);
	print("%ld blocks loaded from worm %d\n", count, dskno);
	return;

bad:
	print("cant open /adm/cache\n");
}

int
dowcmp(void)
{
	Iobuf *p, *p1, *p2;
	Cache *h;
	Centry *c;
	Bucket *b;
	long maddr, msize, caddr, a;

	if(wcmp.flg == 0)
		return 0;
	
	p = getbuf(wcmp.cdev, CACHE_ADDR, Bread|Bres);
	if(!p || checktag(p, Tcache, QPSUPER)) {
		print("dowcmp: checktag c bucket\n");
		if(p)
			putbuf(p);
		wcmp.flg = 0;
		return 0;
	}
	h = (Cache*)p->iobuf;
	maddr = h->maddr;
	msize = h->msize;
	caddr = h->caddr;
	putbuf(p);

	wcmp.caddr++;
	if(wcmp.caddr < 0 || wcmp.caddr >= CEPERBK) {
		wcmp.caddr = 0;
		wcmp.maddr++;
	}
	if(wcmp.maddr < 0 || wcmp.maddr >= msize)
		wcmp.maddr = 0;

	p = getbuf(wcmp.cdev, maddr + wcmp.maddr/BKPERBLK, Bread|Bres);
	if(!p || checktag(p, Tbuck, maddr + wcmp.maddr/BKPERBLK)) {
		print("dowcmp: checktag c bucket\n");
		if(p)
			putbuf(p);
		wcmp.flg = 0;
		return 0;
	}

	b = (Bucket*)p->iobuf + wcmp.maddr%BKPERBLK;
	c = b->entry + wcmp.caddr;
	while(c->state == Cread) {
		a = c->waddr;
		if(a < wcmp.platter*DSIZE || a >= (wcmp.platter+1)*DSIZE)
			break;

		wcmp.count++;

		p1 = getbuf(wcmp.wdev, a, Bread);
		if(!p1) {
			print("dowcmp: wdev error\n");
			break;
		}

		a = wcmp.maddr*CEPERBK + wcmp.caddr + caddr;
		p2 = getbuf(wcmp.cdev, a, Bread);
		if(!p2) {
			print("dowcmp: cdev error\n");
			putbuf(p1);
			break;
		}

		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
			print("dowcmp: dont compare w=%ld c=%ld\n", c->waddr, a);
			c->state = Cdump1;
			p->flags |= Bmod;
		}
		putbuf(p1);
		putbuf(p2);
		break;
	}

	putbuf(p);
	if(wcmp.maddr == 0 && wcmp.caddr == 0) {
		print("dowcmp: wrap %ld\n", wcmp.count);
		wcmp.flg = 0;
		wcmp.count = 0;
	}
	return 1;
}

#ifdef	XXX
/* garbage to change sb size
 * probably will need it someday
 */
	fsz = number(0, 0, 10);
	count = 0;
	if(fsz == number(0, -1, 10))
		count = -1;		/* really do it */
	print("fsize = %ld\n", fsz);
	cdev = CDEV(dev);
	cb = getbuf(cdev, CACHE_ADDR, Bread|Bres);
	if(!cb || checktag(cb, Tcache, QPSUPER))
		panic("cwstats: checktag c bucket");
	h = (Cache*)cb->iobuf;
	for(m=0; m<h->msize; m++) {
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Bread|Bmod|Bres);
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
			panic("cwtest: checktag c bucket");
		b = (Bucket*)p->iobuf + m%BKPERBLK;
		ce = b->entry + CEPERBK;
		for(c=b->entry; c<ce; c++) {
			if(c->waddr < fsz)
				continue;
			if(count < 0) {
				c->state = Cnone;
				continue;
			}
			if(c->state != Cdirty)
				count++;
		}
		putbuf(p);
	}
	if(count < 0) {
		print("old cache hsize = %ld\n", h->fsize);
		h->fsize = fsz;
		cb->flags |= Bmod;
		p = getbuf(dev, h->sbaddr, Bread|Bmod);
		s = (Superb*)p->iobuf;
		print("old super hsize = %ld\n", s->fsize);
		s->fsize = fsz;
		putbuf(p);
	}
	putbuf(cb);
	print("count = %ld\n", count);
#endif

int
convstate(char *name)
{
	int i;

	for(i=0; i<nelem(cwnames); i++)
		if(cwnames[i])
			if(strcmp(cwnames[i], name) == 0)
				return i;
	return -1;
}

void
cmd_cwcmd(int argc, char *argv[])
{
	Filsys *fs;
	Device dev;
	char *arg;
	int s1, s2;
	long a;

	if(argc <= 1) {
		print("	cwcmd mvstate state1 state2\n");
		print("	cwcmd prchain [start] [bakflg]\n");
		print("	cwcmd savecache\n");
		print("	cwcmd loadcache [dskno]\n");
		print("	cwcmd wormcmp [dskno]\n");
		return;
	}
	arg = argv[1];
	for(fs=filsys; fs->name; fs++) {
		dev = fs->dev;
		if(dev.type != Devcw)
			continue;

		if(strcmp(arg, "mvstate") == 0) {
			if(argc <= 3)
				goto bad;
			s1 = convstate(argv[2]);
			s2 = convstate(argv[3]);
			if(s1 < 0 || s2 < 0)
				goto bad;
			mvstates(dev, s1, s2);
			continue;
		bad:
			print("cwcmd mvstate: bad args\n");
			continue;
		}
		if(strcmp(arg, "prchain") == 0) {
			a = 0;
			if(argc > 2)
				a = number(argv[2], 0, 10);
			s1 = 0;
			if(argc > 3)
				s1 = 1;
			prchain(dev, a, s1);
			continue;
		}
		if(strcmp(arg, "savecache") == 0) {
			savecache(dev);
			continue;
		}
		if(strcmp(arg, "loadcache") == 0) {
			s1 = -1;
			if(argc > 2)
				s1 = number(argv[2], 0, 10);
			loadcache(dev, s1);
			continue;
		}
		if(strcmp(arg, "wormcmp") == 0) {
			s1 = 0;
			if(argc > 2)
				s1 = number(argv[2], 0, 10);
			if(wcmp.flg == 0) {
				wcmp.cdev = CDEV(dev);
				wcmp.wdev = WDEV(dev);
				wcmp.platter = s1;
				wcmp.flg = 1;
				print("wcmp dskno %d\n", wcmp.platter);
			} else {
				wcmp.flg = 0;
				print("wcmp off\n");
			}
			continue;
		}
		print("unknown cwcmd %s\n", arg);
	}
}

cyclone.c       710793651   0     0     664     9173      `
#include	"all.h"
#include	"../cyc/comm.h"
#include	"io.h"

typedef	struct	Hcmd	Hcmd;
typedef	struct	Hhdr	Hhdr;
typedef	struct	Exec	Exec;
typedef	struct	Out	Out;


enum
{
	SUM	= 0,
	NBUF	= 100,
	NLBUF	= 50,
	NPARAM	= 6,
	NOUT	= 20,
	NHASH	= 290,
};

struct	Hhdr
{
	Hcmd*	hcmd;
	int	mbsize;
	Msgbuf*	mb;
	ulong	hcmap;		/* vme map for this Hcmd structure */
	ulong	mbmap;		/* vme map for this mb->data */
};

struct	Hcmd
{
	ulong	cmd;
	ulong	param[NPARAM];
};

struct Out
{
	Hhdr;
	Msgbuf*	mb;
	Hcmd*	hc;
	ulong*	ip;
};

static
struct
{
	Queue*	reply;
	struct
	{
		Vmedevice* vme;
		int	hpactive;
		int	cmdwait;
		int	verb;
		long	oubusy;
		Rendez	hrir;			/* vme interrupt */
		QLock	issueq;
		ulong	issuep;
		ulong	replyp;
		int	bhash[NHASH];
		int	vhash;
		Hhdr	buf[NBUF];		/* outstanding buffers */
		Chan*	chan;
		Out	out[NOUT];

		Filter	rate;
		Filter	count;
	};
} h;

static	int	cyclissue(Hhdr*, Hcmd*, int);
static	void	cmd_cycl(int, char*[]);
static	void	cmd_stats(int, char*[]);

void
cyclintr(Vmedevice *vp)
{

	USED(vp);
	h.cmdwait = 1;
	wakeup(&h.hrir);
}

int
cyclinit(Vmedevice *vp)
{
	int i, n, bv, bc, c;
	Msgbuf *m;
	Chan *cp;
	Hhdr *hc;

	print("cyclinit\n");

	/*
	 * does the board exist?
	com = vp->address;
	if(probe(&com->rom[0], sizeof(com->rom[0]))) {
		return -1;
	}
	 */

	memset(&h, 0, sizeof(h));
	h.vme = vp;
	h.reply = newqueue(10);
	cp = chaninit(Devcycl, 1);
	h.chan = cp;
	cp->send = serveq;
	cp->reply = h.reply;

	for(i=0; i<NBUF; i++) {
		n = LARGEBUF;
		if(i >= NLBUF)
			n = SMALLBUF;
		m = mballoc(n, h.chan, Mbcycl1);
		hc = &h.buf[i];
		hc->hcmd = ialloc(sizeof(Hcmd), LINESIZE);
		hc->mb = m;
		hc->mbsize = n;
		hc->mbmap = vme2sysmap(h.vme->bus, (ulong)m->data, n);
		hc->hcmap = vme2sysmap(h.vme->bus, (ulong)hc->hcmd, sizeof(Hcmd));
	}

	/*
	 * find best hash divisor
	 */
	bc = NHASH;
	bv = 0;
	for(h.vhash=10; h.vhash<=NHASH; h.vhash++) {
		memset(h.bhash, 0, sizeof(h.bhash));
		c = 0;
		for(i=0; i<NBUF; i++) {
			hc = &h.buf[i];
			n = hc->hcmap%h.vhash;
			if(h.bhash[n])
				c++;
			h.bhash[n] = 1;
		}
		if(c < bc) {
			bc = c;
			bv = h.vhash;
		}
	}
	print("best v = %d %d collisions\n", bv, bc);
	h.vhash = bv;
	memset(h.bhash, 0, sizeof(h.bhash));
	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		h.bhash[hc->hcmap%h.vhash] = i;
	}

	dofilter(&h.count);
	dofilter(&h.rate);

	cmd_install("cycl", "subcommand -- issue cyclone command", cmd_cycl);
	cmd_install("statc", "-- cyclone stats", cmd_stats);
	return 0;
}

ulong
addsum(void *d, int count)
{
	ulong *p, s;
	int i, n;

	n = (count+3)/4;
	p = d;
	s = 0;
	for(i=0; i<n; i++)
		s += *p++;
	return s;
}

void
cyclou(void)
{
	Out *op, *uop;
	Msgbuf *mb;
	Hcmd *hc;
	Comm *com;
	int i, count;

	for(i=0; i<NOUT; i++) {
		op = &h.out[i];
		op->mb = 0;
		op->hc = ialloc(sizeof(Hcmd), LINESIZE);
		op->hcmap = vme2sysmap(h.vme->bus, (ulong)op->hc, sizeof(Hcmd));
	}

	print("cyo: start\n");
	com = h.vme->address;

loop:
	mb = recv(h.reply, 0);
	if(!mb) {
		print("zero message\n");
		goto loop;
	}

	/*
	 * look for done guys
	 * also find spare slot->uop
	 */
busy:
	op = h.out;
	uop = 0;
	for(i=0; i<NOUT; i++, op++) {
		if(op->mb == 0) {
			if(uop == 0)
				uop = op;
			continue;
		}
		if(*op->ip)
			continue;

		vmeflush(h.vme->bus, op->hcmap, sizeof(Hcmd));
		vme2sysfree(h.vme->bus, op->mbmap, op->mb->count);
		mbfree(op->mb);
		op->mb = 0;
		if(uop == 0)
			uop = op;
	}
	if(uop == 0) {
		if(h.oubusy == 0)
			print("ou: busy!\n");
		h.oubusy++;
		if(h.oubusy > 1000000)
			h.oubusy = 0;
		goto busy;
	}

	count = mb->count;
	uop->mb = mb;
	uop->mbmap = vme2sysmap(h.vme->bus, (ulong)mb->data, count);
	wbackcache(mb->data, count);
	invalcache(mb->data, count);

	hc = uop->hc;
	hc->cmd = Ureply;
	hc->param[0] = mb->param;
	hc->param[1] = uop->mbmap;
	hc->param[2] = count;
	hc->param[3] = 0;
	if(SUM)
		hc->param[3] = addsum(mb->data, count);

	if(h.verb) {
		print("cyo: cmd=reply u=%.8lux b=%.8lux c=%.8lux\n",
			hc->param[0], hc->param[1], hc->param[2]);
		prflush();
	}

	wbackcache(hc, sizeof(*hc));
	invalcache(hc, sizeof(*hc));

	qlock(&h.issueq);
	i = h.issuep;
	uop->ip = &com->reqstq[i];

	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	h.count.count++;
	h.rate.count += count;

	*uop->ip = uop->hcmap;

	goto loop;
}

/*
 * process pick up hotrod filled buffers
 */
int
cycmd(void *v)
{

	USED(v);
	return h.cmdwait;
}

void
cyclin(void)
{
	ulong u, s;
	int i, count;
	Chan *cp;
	Msgbuf *mb;
	Hhdr *hc;
	Hcmd *hcmd;
	Comm* com;

	print("cyi: start\n");
	userinit(cyclou, 0, "cyo");
	cp = h.chan;
	com = h.vme->address;

loop:
	sleep(&h.hrir, cycmd, 0);
	h.cmdwait = 0;

l1:
	/*
	 * look at reply q from cyclone
	 */
	u = com->replyq[h.replyp];
	if(u == 0)
		goto loop;
	com->replyq[h.replyp] = 0;
	h.replyp++;
	if(h.replyp >= NRQ)
		h.replyp = 0;

	hc = &h.buf[h.bhash[u%h.vhash]];
	if(hc->hcmap == u)
		goto found;

	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		if(hc->hcmap == u)
			goto found;
	}
	print("hri: spurious %lux\n", u);
	goto l1;

found:
	vmeflush(h.vme->bus, hc->hcmap, sizeof(Hcmd));
	invalcache(hc, sizeof(Hcmd));

	hcmd = hc->hcmd;
	if(hcmd->cmd != Ubuf) {
		print("hri: cmd not Ubuf %d\n", hcmd->cmd);
		goto l1;
	}

	count = hc->mbsize;
	if(hcmd->param[3] > count) {
		print("hri: rcount too big %d %lux\n", hcmd->param[3], &hcmd->param[3]);
		hcmd->param[3] = count;
	}
	mb = hc->mb;
	vme2sysfree(h.vme->bus, hc->mbmap, count);
	invalcache(mb->data, count);

	mb->param = hcmd->param[0];	/* u */
	mb->count = hcmd->param[3];	/* actual count */
	if(hcmd->param[4]) {
		s = addsum(mb->data, mb->count);
		if(s != hcmd->param[4])
			print("hri: checksum %lux sb %lux\n", s, hcmd->param[4]);
	}

	if(h.verb) {
		print("cyi: cmd=buf u=%.8lux b=%.8lux c=%.8lux\n",
			hcmd->param[0], hcmd->param[1], hcmd->param[3]);
		prflush();
	}

	send(cp->send, mb);

	h.count.count++;
	h.rate.count += mb->count;

	mb = mballoc(count, cp, Mbcycl2);
	hc->mbmap = vme2sysmap(h.vme->bus, (ulong)mb->data, count);
	hc->mb = mb;

	hcmd->cmd = Ubuf;
	hcmd->param[0] = 0;			/* returned u */
	hcmd->param[1] = hc->mbmap;		/* message return */
	hcmd->param[2] = count;			/* size */
	hcmd->param[3] = 0;			/* returned count */
	hcmd->param[4] = 0;			/* returned cksum */

	wbackcache(mb->data, count);
	invalcache(mb->data, count);

	wbackcache(hcmd, sizeof(Hcmd));
	invalcache(hcmd, sizeof(Hcmd));

	qlock(&h.issueq);
	i = h.issuep;
	com->reqstq[i] = hc->hcmap;
	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	goto l1;
}

void
cyclstart(void)
{
	int i;
	Hhdr *hc;
	Hcmd *hcmd;
	struct { Hhdr hdr; Hcmd cmd; } cmd;

	if(h.vme == 0) {
		print("no cyclone configured\n");
		return;
	}

	print("cyclone start\n");
	cmd.cmd.cmd = Ureboot;
	if(cyclissue(&cmd.hdr, &cmd.cmd, 1))
		print("	reboot didnt issue\n");

	h.issuep = 0;		/* my pointer again */
	h.replyp = 0;

	if(!h.hpactive) {
		h.hpactive = 1;
		userinit(cyclin, 0, "cyi");
	}
	fileinit(h.chan);

	for(i=0; i<NBUF; i++) {
		hc = &h.buf[i];
		hcmd = hc->hcmd;

		hcmd->cmd = Ubuf;
		hcmd->param[0] = 0;			/* returned u */
		hcmd->param[1] = hc->mbmap;		/* message return */
		hcmd->param[2] = hc->mbsize;		/* size */
		hcmd->param[3] = 0;			/* returned count */

		wbackcache(hc->mb->data, hc->mbsize);
		invalcache(hc->mb->data, hc->mbsize);

		if(cyclissue(hc, hcmd, 0)) {
			print("	buf[%d] didnt issue\n", i);
			return;
		}
	}
}

static
void
cmd_cycl(int argc, char *argv[])
{
	struct { Hhdr hdr; Hcmd cmd; } cmd;
	int i;

	if(argc <= 1) {
		print("cycl reboot -- reboot\n");
		print("cycl intr -- fake an interrupt\n");
		print("cycl verbose -- chatty\n");
		print("cycl ping -- issue Uping\n");
		return;
	}
	for(i=1; i<argc; i++) {
		if(strcmp(argv[i], "reboot") == 0) {
			cyclstart();
			continue;
		}
		if(strcmp(argv[i], "intr") == 0) {
			h.cmdwait = 1;
			wakeup(&h.hrir);
			continue;
		}
		if(strcmp(argv[i], "verbose") == 0) {
			h.verb = !h.verb;
			if(h.verb)
				print("chatty\n");
			else
				print("quiet\n");
			continue;
		}
		if(strcmp(argv[i], "ping") == 0) {
			cmd.cmd.cmd = Uping;
			if(cyclissue(&cmd.hdr, &cmd.cmd, 1))
				print("	ping didnt issue\n");
			continue;
		}
		print("unknown cycl subcommand\n");
	}
}

static
int
cyclissue(Hhdr *hdr, Hcmd *cmd, int alloc)
{
	int i;
	long t;
	ulong *hp, m, map;
	Comm *com;

	map = hdr->hcmap;
	if(alloc)
		map = vme2sysmap(h.vme->bus, (ulong)cmd, sizeof(Hcmd));
	wbackcache(cmd, sizeof(Hcmd));
	invalcache(cmd, sizeof(Hcmd));
	com = h.vme->address;

	qlock(&h.issueq);
	i = h.issuep;
	hp = &com->reqstq[i];
	*hp = map;

	i++;
	if(i >= NRQ)
		i = 0;
	h.issuep = i;
	qunlock(&h.issueq);

	t = toytime() + SECOND(5);
	for(;;) {
		m = *hp;
		if(m == 0)
			break;
		if(toytime() >= t) {
			*hp = 0;
			break;
		}
	}

	if(alloc)
		vme2sysfree(h.vme->bus, map, sizeof(Hcmd));
	else
		vmeflush(h.vme->bus, map, sizeof(Hcmd));

	return m;
}

static
void
cmd_stats(int argc, char *argv[])
{
	Vmedevice *vp;

	USED(argc);
	USED(argv);

	print("cyclone stats\n");
	for(;;) {	/* loop for multipls cyclones */
		vp = h.vme;
		if(vp == 0)
			break;
		print("	%s\n", vp->name);
		print("		work = %F mps\n", (Filta){&h.count, 1});
		print("		rate = %F tBps\n", (Filta){&h.rate, 1000});
		break;
	}
}

mkfile          714776782   0     0     664     213       `
X=`{cd ../port;echo *.c | sed 's/fcall.c //'}
PFILES=`{echo $X | sed 's/ /|/g' | sed 's/\.c//g'}

^($PFILES).$O:R:	'../port/\1.c'
	$CC $CFLAGS ../port/$stem1.c

fcall.$O:R: ../port/fcall.c
	$CC -w ../port/fcall.c

fcmd.c          716098547   0     0     664     2053      `
#include	"all.h"

int
fchar(void)
{
	if(uidgc.find >= uidgc.flen) {
		uidgc.find = 0;
		uidgc.flen = con_read(FID2, uidgc.uidbuf->iobuf, cons.offset, BUFSIZE);
		if(uidgc.flen <= 0)
			return -1;
		cons.offset += uidgc.flen;
	}
	return uidgc.uidbuf->iobuf[uidgc.find++] & 0xff;
}

int
fread(void *buf, int len)
{
	int n, c;
	char *b;

	b = buf;
	for(n = 0; n < len; n++) {
		c = fchar();
		if(c < 0)
			break;
		b[n] = c;
	}

	return n;
}

int
fname(char *name)
{
	int i, c;

	/*
	 * read a name and return first char known not
	 * to be in the name.
	 */
	memset(name, 0, NAMELEN);
	for(i=0;; i++) {
		c = fchar();
		switch(c) {
		case '#':
			for(;;) {
				c = fchar();
				if(c == -1 || c == '\n')
					break;
			}

		case ' ':
		case '\n':

		case ':':
		case ',':
		case '=':
		case 0:
			return c;

		case -1:
			return 0;

		case '\t':
			return ' ';
		}
		if(i < NAMELEN-1)
			name[i] = c;
	}
	return 0;
}

int
fpair(char *n1, char *n2)
{
	int c;

	do {
		c = fname(n1);
		if(c == 0)
			return 1;
	} while(*n1 == 0);


	while(c != '=') {
		c = fname(n2);
		if(c == 0)
			return 1;
		if(*n2 != 0)
			memmove(n1, n2, NAMELEN);
	}

	do {
		c = fname(n2);
		if(c == 0)
			return 1;
	} while(*n2 == 0);
	return 0;
}

int
chartoea(uchar *ea, char *cp)
{
	int i, h, c;

	h = 0;
	for(i=0; i<Easize*2; i++) {
		c = *cp++;
		if(c >= '0' && c <= '9')
			c = c - '0';
		else
		if(c >= 'a' && c <= 'f')
			c = c - 'a' + 10;
		else
		if(c >= 'A' && c <= 'F')
			c = c - 'A' + 10;
		else
			return 1;
		h = (h*16) + c;
		if(i & 1) {
			*ea++ = h;
			h = 0;
		}
	}
	if(*cp != 0)
		return 1;
	return 0;
}

int
chartoip(uchar *pa, char *cp)
{
	int i, c, h;

	for(i=0;;) {
		h = 0;
		for(;;) {
			c = *cp++;
			if(c < '0' || c > '9')
				break;
			h = (h*10) + (c-'0');
		}
		*pa++ = h;
		i++;
		if(i == Pasize) {
			if(c != 0)
				return 1;
			return 0;
		}
		if(c != '.')
			return 1;
	}
}

void
getipa(Ifc *ifc)
{

	memmove(ifc->ipa, sysip, Pasize);
	memmove(ifc->netgate, defgwip, Pasize);
out:
	ifc->ipaddr = nhgetl(ifc->ipa);
	ifc->mask = nhgetl(defmask);
}

auth.c          715919802   0     0     664     3877      `
#include	"all.h"
#include	"io.h"

void
cmd_passwd(int argc, char *argv[])
{
	char passwd[32];
	static char zeros[DESKEYLEN];
	char nkey1[DESKEYLEN], nkey2[DESKEYLEN];

	USED(argc, argv);

	if(memcmp(nvr.authkey, zeros, sizeof(nvr.authkey))) {
		print("Old password:");
		getpasswd(passwd, sizeof(passwd));
		memset(nkey1, 0, DESKEYLEN);
		passtokey(nkey1, passwd);
		if(memcmp(nkey1, nvr.authkey, DESKEYLEN)) {
			print("Bad password\n");
			delay(1000);
			return;
		}
	}

	print("New password:");
	getpasswd(passwd, sizeof(passwd));
	memset(nkey1, 0, DESKEYLEN);
	passtokey(nkey1, passwd);

	print("Confirm password:");
	getpasswd(passwd, sizeof(passwd));
	memset(nkey2, 0, DESKEYLEN);
	passtokey(nkey2, passwd);

	if(memcmp(nkey1, nkey2, DESKEYLEN)) {
		print("don't match\n");
		return;
	}
	memmove(nvr.authkey, nkey1, DESKEYLEN);
	nvr.authsum = nvcsum(nvr.authkey, DESKEYLEN);
	nvwrite(NVRAUTHADDR, &nvr, sizeof(nvr));
}

void
cmd_auth(int argc, char *argv[])
{
	int i, keys;
	Uid *u;
	char *file;
	char buf[KEYDBLEN], *p;
	static int started;

	if(argc > 1) {
		if(strcmp(argv[1], "on") == 0) {
/*
			strcpy(nvr.authcmd, "auth ");
			if(argc == 3)
				strcat(nvr.authcmd, argv[2]);

			nvwrite(NVRAUTHADDR, &nvr, sizeof(nvr));
 */
			return;
		}
		p = strchr(argv[1], '!');
		if(p != 0) {			/* Network address */
			*p++ = '\0';
			if(strcmp(argv[1], "il") != 0) {
				print("bad network\n");
				return;
			}
			if(chartoip(authip, p)) {
				print("bad ip address\n");
				return;
			}
			ilauth();
			return;
		}
	}

	file = "/adm/keys";
	if(argc > 1)
		file = argv[1];

	wlock(&uidgc.uidlock);
	uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0);

	if(walkto(file) || con_open(FID2, 0))
		goto bad;

	uidgc.flen = 0;
	uidgc.find = 0;
	cons.offset = 0;

	keys = 0;
	for(;;) {
		i = fread(buf, KEYDBLEN);
		if(i == 0)
			break;

		if(i < KEYDBLEN)
			goto bad;

		decrypt(nvr.authkey, buf, KEYDBLEN);
		buf[NAMELEN-1] = '\0';
		u = uidpstr(buf);
		if(u && buf[KEYENABLE] == 0) {
			memmove(u->key, buf+NAMELEN, DESKEYLEN);
			keys++;
		}
	}
	print("read %d enabled keys\n", keys);

	authchan = 0;	/* This should close down the channel ! */
out:
	putbuf(uidgc.uidbuf);
	wunlock(&uidgc.uidlock);
	return;
bad:
	print("error in %s\n", file);
	goto out;
}

int
authorise(File *f, Fcall *in)
{

	if(noauth || wstatallow)		/* set to allow entry during boot */
		return 1;

	if(strcmp(in->uname, "none") == 0)
		return 1;

	decrypt(nvr.authkey, in->auth, AUTHSTR+DESKEYLEN);
	if(memcmp(in->auth, f->ticket, AUTHSTR))
		return 0;

	f->ticket[0]++;
	return 1;
}

int
authsrv(Chan *c, char *buf, int i)
{
	Msgbuf *mb;
	int n;

	if(authchan == 0) {
		print("no connection to auth server\n");
		return 0;
	}

	mb = mballoc(MAXMSG, c, Mbilauth);

	memmove(mb->data, buf, i);
	mb->count = i;
	send(c->reply, mb);

	mb = recv(authreply, 0);

	if(mb->count > AUTHLEN+ERRREC) {
		print("authsrv: long read\n");
		mbfree(mb);
		return -1;
	}
	memmove(buf, mb->data, mb->count);
	n = mb->count;
	mbfree(mb);

	return n;
}

void
getpasswd(char *str, int n)
{
	int c;
	char *p, *e;

	memset(str, 0, sizeof(n));
	p = str;
	e = str+n-1;
	echo = 0;
	for(;;) {
		if(p == e) {
			*p = '\0';
			goto out;
		}
		c = getc();
		switch(c) {
		case '\n':
			*p = '\0';
			print("\n");
			goto out;
		case '\b':
			if(p > str)
				p--;
			break;
		case 'U' - '@':
			p = str;
			break;
		default:
			*p++ = c;
		}
	}
out:
	echo = 1;
}

int
conslock(void)
{
	static char zeroes[DESKEYLEN];
	char passwd[128];
	char nkey1[DESKEYLEN];

	if(memcmp(nvr.authkey, zeroes, DESKEYLEN) == 0) {
		print("no password set\n");
		return 0;
	}

	for(;;) {
		print("%s password:", service);
		getpasswd(passwd, sizeof(passwd));
		memset(nkey1, 0, DESKEYLEN);
		passtokey(nkey1, passwd);
		if(memcmp(nkey1, nvr.authkey, DESKEYLEN) == 0) {
			prdate();
			return 1;
		}

		print("Bad password\n");
		delay(1000);
	}
	return 0;
}

devcons.c       716086108   0     0     664     4006      `
#include	"all.h"

static
struct
{
	Lock;
	uchar	buf[4000];
	uchar	*in;
	uchar	*out;
	int	printing;
} printq;

static
struct
{
	Lock;
	Rendez;
	uchar	buf[500];
	uchar	*in;
	uchar	*out;
	int	reading;
} readq;

void
printinit(void)
{

	lock(&printq);		/* allocate lock */
	printq.in = printq.buf;
	printq.out = printq.buf;
	unlock(&printq);

	lock(&readq);		/* allocate lock */
	readq.in = readq.buf;
	readq.out = readq.buf;
	unlock(&readq);

	duartinit();
}

/*
 * Put a string on the console.
 * n bytes of s are guaranteed to fit in the buffer and is ready to print.
 * Must be called splhi() and with printq locked.
 * If early in booting (predawn) poll output.
 */
static void
puts(char *s, int n)
{

	if(predawn) {
		while(n > 0) {
			duartxmit(*s++ & 0xFF);
			delay(5);
			n--;
		}
		return;
	}
	if(!printq.printing){
		printq.printing = 1;
		duartstart(*s++ & 0xFF);
		n--;
	}
	memmove(printq.in, s, n);
	printq.in += n;
	if(printq.in >= printq.buf+sizeof(printq.buf))
		printq.in = printq.buf;
}

/*
 * Print a string on the console.  This is the high level routine
 * with a queue to the interrupt handler.  BUG: There is no check against
 * overflow.
 */
void
putstrn(char *str, int n)
{
	int s, m;
	char *t;

	s = splhi();
	lock(&printq);
	while(n > 0){
		if(*str == '\n')
			puts("\r", 1);
		m = printq.buf+sizeof(printq.buf) - printq.in;
		if(n < m)
			m = n;
		t = memchr(str+1, '\n', m-1);
		if(t)
			if(t-str < m)
				m = t - str;
		puts(str, m);
		n -= m;
		str += m;
	}
	unlock(&printq);
	splx(s);
}

/*
 * get character to print at interrupt time
 * always called splhi from proc 0
 */
conschar(void)
{
	uchar *p;
	int c, s;

	s = splhi();
	lock(&printq);
	p = printq.out;
	if(p == printq.in) {
		printq.printing = 0;
		c = -1;
	} else {
		c = *p++;
		if(p >= printq.buf+sizeof(printq.buf))
			p = printq.buf;
		printq.out = p;
	}
	unlock(&printq);
	splx(s);
	return c;
}

/*
 * dispose of input character at interrupt time
 * always called splhi from proc 0
 */
void
kbdchar(int c)
{
	int s;
	uchar *p;
	uchar ch;

	if(c == ('T' - '@')) {			/* ^t */
		for(s=0; s<=conf.nproc; s++)
			dotrace(0);
		return;
	}
	s = splhi();
	lock(&readq);
	if(readq.reading)
		goto out;
	if(c == ('U' - '@')) {
		if(echo)
			putstrn("^U\n", 3);	/* echo */
		readq.in = readq.out;
		goto out;
	}
	if(c == '\r')
		c = '\n';
	ch = c;
	if(echo)
		putstrn((char*)&ch, 1);		/* echo */
	p = readq.in;
	*p++ = c;
	if(p >= readq.buf+sizeof(readq.buf))
		p = readq.buf;
	readq.in = p;
	if(c == '\n' || c == ('D' - '@')) {
		readq.reading = 1;
		wakeup(&readq);
	}
out:
	unlock(&readq);
	splx(s);
}

int
rawchar(int d)
{
	int c;

	for(;;) {
		c = duartrecv();
		if(c) {
			if(c == '\r')
				c = '\n';
			if(c == '\n') {
				duartxmit('\r');
				delay(10);
			}
			duartxmit(c);
			return c;
		}
		delay(1);
		if(d) {
			d--;
			if(d <= 0)
				break;
		}
	}
	return 0;
}

int
reading(void *a)
{

	USED(a);
	return readq.reading;
}


/*
 * get a character from the console
 * this is the high level routine with
 * a queue to the interrupt handler
 */
int
getc(void)
{
	int c, s;
	uchar *p;

	c = 0;

loop:
	sleep(&readq, reading, 0);
	s = splhi();
	lock(&readq);
	p = readq.out;
	if(readq.in == p) {
		readq.reading = 0;
		unlock(&readq);
		splx(s);
		goto loop;
	}
	if(readq.in != p) {
		c = *p++;
		if(p == readq.buf+sizeof(readq.buf))
			p = readq.buf;
		readq.out = p;
	}
	unlock(&readq);
	splx(s);
	return c;
}

int
sprint(char *s, char *fmt, ...)
{

	return donprint(s, s+PRINTSIZE, fmt, (&fmt+1)) - s;
}

int
print(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int n;

	n = donprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf;
	putstrn(buf, n);
	return n;
}

void
panic(char *fmt, ...)
{
	char buf[PRINTSIZE];
	int n;

	lights(Lpanic, 1);
	dumpstack(u);
	strcpy(buf, "panic: ");
	n = donprint(buf+7, buf+sizeof(buf)-1, fmt, (&fmt+1)) - buf;
	buf[n] = '\n';
	putstrn(buf, n+1);
	if(!predawn){
		spllo();
		prflush();
	}
	exit();
}

void
prflush(void)
{
	if(predawn)
		return;
	while(printq.printing)
		delay(100);
}
config.c        716117074   0     0     664     9680      `
#include	"all.h"
#include	"io.h"

struct
{
	char*	icharp;
	char*	charp;
	int	loc;
	int	error;
	int	newconf;	/* clear befor start */
	int	modconf;	/* write back when done */
} f;

int
nzip(uchar *ip)
{
	if(ip[0] || ip[1] || ip[2] || ip[3])
		return 1;
	return 0;
}

void
cdiag(char *s)
{

	f.charp--;
	if(f.error == 0) {
		print("diag: %s\n", s);
		print("	%.*s<%c>%s\n",
			f.charp-f.icharp, f.icharp,
			*f.charp,
			f.charp+1);
		f.error = 1;
	}
}

int
cnumb(void)
{
	int c, n;

	c = *f.charp++;
	if(c < '0' || c > '9') {
		cdiag("number expected");
		return 0;
	}
	n = 0;
	while(c >= '0' && c <= '9') {
		n = n*10 + (c-'0');
		c = *f.charp++;
	}
	f.charp--;
	return n;
}

Device
config(void)
{
	int c, i, m;
	Device d, dl[100];

	if(f.error)
		goto bad;

	d.type = 0;
	d.ctrl = 0;
	d.unit = 0;
	d.part = 0;

	c = *f.charp++;
	switch(c) {
	default:
		cdiag("unknown type");
		goto bad;

	case '(':	/* (d+) one or multiple cat */
	case '[':	/* [d+] one or multiple interleave */
		for(i=0;; i++) {
			dl[i] = config();
			if(f.error)
				goto bad;
			m = *f.charp;
			if(c == '(' && m == ')')
				break;
			if(c == '[' && m == ']')
				break;
		}
		f.charp++;
		if(i == 0)
			return dl[0];

		d.type = Devmcat;
		if(c == '[')
			d.type = Devmlev;
		d.unit = f.loc;
		for(m=0; m<=i; m++)
			cwdevs[f.loc+m] = dl[m];
		f.loc += m;
		d.part = f.loc;
		break;

	case 'f':	/* fd fake worm */
		d.type = Devfworm;
		dl[0] = config();
		cwdevs[f.loc] = dl[0];
		d.part = f.loc;
		f.loc++;
		break;

	case 'w':	/* w[#.]# wren [ctrl] unit [part] */
	case 'r':	/* o[#.]# worm [ctrl] unit [part] */
		d.type = Devwren;
		if(c == 'r')
			d.type = Devworm;
		d.ctrl = 0;
		d.unit = cnumb();
		d.part = 0;
		m = *f.charp;
		if(m == '.') {
			f.charp++;
			d.part = cnumb();
			m = *f.charp;
			if(m == '.') {
				f.charp++;
				d.ctrl = d.unit;
				d.unit = d.part;
				d.ctrl = cnumb();
			}
		}
		break;

	case 'o':	/* ro part of last cw */
		d.type = Devro;	/* the rest is filled in in devinit */
		break;

	case 'c':	/* cdd cache/worm */
		d.type = Devcw;
		dl[0] = config();
		dl[1] = config();
		cwdevs[f.loc+0] = dl[0];
		cwdevs[f.loc+1] = dl[1];
		d.part = f.loc;
		f.loc += 2;
		break;

	case 'p':	/* pd#.# partition base% size% */
		d.type = Devpart;
		dl[0] = config();
		cwdevs[f.loc] = dl[0];
		d.ctrl = f.loc;
		f.loc++;
		d.unit = cnumb();
		c = *f.charp++;
		if(c != '.')
			cdiag("dot expected");
		d.part = cnumb();
		break;
	}
	return d;

bad:
	d.type = Devnone;
	return d;
}

char*
strdup(char *s)
{
	int n;
	char *s1;

	n = strlen(s);
	s1 = ialloc(n+1, 0);
	strcpy(s1, s);
	return s1;
}

Device
iconfig(char *s)
{
	Device d;

	f.error = 0;
	f.icharp = s;
	f.charp = f.icharp;
	d = config();
	if(*f.charp) {
		cdiag("junk on end");
		f.error = 1;
	}
	return d;
}

int
testconfig(char *s)
{
	int savfl;

	savfl = f.loc;
	iconfig(s);
	f.loc = savfl;
	return f.error;
}

void
mergeconf(Iobuf *p)
{
	char word[100];
	char *cp;
	Filsys *fs;

	cp = p->iobuf;
	goto line;

loop:
	if(*cp != '\n')
		goto bad;
	cp++;

line:
	cp = getwd(word, cp);
	if(strcmp(word, "") == 0)
		return;
	if(strcmp(word, "service") == 0) {
		cp = getwd(word, cp);
		if(service[0] == 0)
			strcpy(service, word);
		goto loop;
	}
	if(strcmp(word, "ip") == 0) {
		cp = getwd(word, cp);
		if(!nzip(sysip))
			if(chartoip(sysip, word))
				goto bad;
		goto loop;
	}
	if(strcmp(word, "ipgw") == 0) {
		cp = getwd(word, cp);
		if(!nzip(defgwip))
			if(chartoip(defgwip, word))
				goto bad;
		goto loop;
	}
	if(strcmp(word, "ipauth") == 0) {
		cp = getwd(word, cp);
		if(!nzip(authip))
			if(chartoip(authip, word))
				goto bad;
		goto loop;
	}
	if(strcmp(word, "ipmask") == 0) {
		cp = getwd(word, cp);
		if(!nzip(defmask))
			if(chartoip(defmask, word))
				goto bad;
		goto loop;
	}
	if(strcmp(word, "filsys") == 0) {
		cp = getwd(word, cp);
		for(fs=filsys; fs->name; fs++)
			if(strcmp(fs->name, word) == 0) {
				if(fs->flags & FEDIT) {
					cp = getwd(word, cp);
					goto loop;
				}
				break;
			}
		fs->name = strdup(word);
		cp = getwd(word, cp);
		fs->conf = strdup(word);
		goto loop;
	}
	goto bad;

bad:
	putbuf(p);
	panic("unknown word in config block: %s", word);
}

void
sysinit(void)
{
	Filsys *fs;
	int i, error;
	Device d;
	Iobuf *p;

	dofilter(&u->time);
	dofilter(&cons.work);
	dofilter(&cons.rate);
	dofilter(&cons.bhit);
	dofilter(&cons.bread);
	dofilter(&cons.brahead);
	dofilter(&cons.binit);
	cons.chan = chaninit(Devcon, 1);

	/*
	 * part 1 -- read the config file
	 */
start:
	f.loc = 0;
	print("config: %s\n", nvr.config);
	d = iconfig(nvr.config);
	devinit(d);
	p = getbuf(d, 0, Bread);
	if(p && f.newconf) {
		memset(p->iobuf, 0, RBUFSIZE);
		settag(p, Tconfig, 0);
		p->flags |= Bmod;
	}
	if(!p || checktag(p, Tconfig, 0))
		panic("config io");
	mergeconf(p);
	if(f.modconf) {
		memset(p->iobuf, 0, BUFSIZE);
		p->flags |= Bmod;
		if(service[0])
			sprint(strchr(p->iobuf, 0), "service %s\n", service);
		for(fs=filsys; fs->name; fs++)
			if(fs->conf)
				sprint(strchr(p->iobuf, 0),
					"filsys %s %s\n", fs->name, fs->conf);
		if(nzip(sysip))
			sprint(strchr(p->iobuf, 0), "ip %I\n", sysip);
		if(nzip(defgwip))
			sprint(strchr(p->iobuf, 0), "ipgw %I\n", defgwip);
		if(nzip(authip))
			sprint(strchr(p->iobuf, 0), "ipauth %I\n", authip);
		if(nzip(defmask))
			sprint(strchr(p->iobuf, 0), "ipmask %I\n", defmask);
		putbuf(p);
		f.modconf = 0;
		f.newconf = 0;
		print("config block written\n");
		goto start;
	}
	putbuf(p);

loop:
	/*
	 * part 2 -- squeeze out the deleted filesystems
	 */
	for(fs=filsys; fs->name; fs++)
		if(fs->conf == 0) {
			for(; fs->name; fs++)
				*fs = *(fs+1);
			goto loop;
		}
	if(filsys[0].name == 0)
		panic("no filsys");

	/*
	 * part 3 -- compile the device expression
	 */
	error = 0;
	for(fs=filsys; fs->name; fs++) {
		print("config: %s %s\n", fs->name, fs->conf);
		i = f.loc;
		fs->dev = iconfig(fs->conf);
		if(f.error) {
			error = 1;
			continue;
		}
		print("fs %s %D\n", fs->name, fs->dev);
		for(; i<f.loc; i++)
			print("	%2d %D\n", i, cwdevs[i]);
	}
	if(error)
		panic("fs config");

	/*
	 * part 4 -- initialize the devices
	 */
	for(fs=filsys; fs->name; fs++) {
		print("sysinit: %s\n", fs->name);
		if(fs->flags & FREAM)
			devream(fs->dev);
		if(fs->flags & FRECOVER)
			devrecover(fs->dev);
		devinit(fs->dev);
	}
}

void
getline(char *line)
{
	char *p;
	int c;

	p = line;
	for(;;) {
		c = rawchar(0);
		if(c == 0 || c == '\n') {
			*p = 0;
			return;
		}
		if(c == '\b') {
			p--;
			continue;
		}
		*p++ = c;
	}
}

void
arginit(void)
{
	int verb, c;
	char line[300], word[100], *cp;
	uchar localip[4];
	Filsys *fs;
	uchar csum;

	print("nvr read\n");
	nvread(NVRAUTHADDR, &nvr, sizeof(nvr));
	csum = nvcsum(nvr.authkey, sizeof(nvr.authkey));
	if(csum != nvr.authsum) {
		print("\n\n ** NVR key checksum is incorrect  **\n");
		print(" ** set password to allow attaches **\n\n");
		memset(nvr.authkey, 0, sizeof(nvr.authkey));
	}
	csum = nvcsum(nvr.config, sizeof(nvr.config));
	if(csum != nvr.configsum) {
		print("\n\n ** NVR config checksum is incorrect  **\n");
		memset(nvr.config, 0, sizeof(nvr.config));
		goto loop;
	}

	print("for config mode hit a key within 5 seconds\n");
	c = rawchar(5*1000);
	if(c == 0) {
		print("	no config\n");
		return;
	}

loop:
	print("config: ");
	getline(line);
	cp = getwd(word, line);
	if(strcmp(word, "end") == 0)
		return;

/*** temp ***/
	if(strcmp(word, "xxx") == 0) {
		strcpy(line, "filsys main cp(w0)50.25f[p(w0)0.25p(w0)25.25p(w0)75.25]");
		cp = getwd(word, line);
	}
	if(strcmp(word, "yyy") == 0) {
		strcpy(line, "config p(w0)50.1");
		cp = getwd(word, line);
	}
/*** temp ***/

	if(strcmp(word, "ream") == 0) {
		verb = FREAM;
		goto gfsname;
	}
	if(strcmp(word, "recover") == 0) {
		verb = FRECOVER;
		goto gfsname;
	}
	if(strcmp(word, "filsys") == 0) {
		verb = FEDIT;
		goto gfsname;
	}
	if(strcmp(word, "allow") == 0) {
		writeallow = 1;
		goto loop;
	}
	if(strcmp(word, "config") == 0) {
		getwd(word, cp);
		if(testconfig(word))
			goto loop;
		c = strlen(word);
		if(c >= sizeof(nvr.config)) {
			print("config string too long\n");
			goto loop;
		}
		memset(nvr.config, 0, sizeof(nvr.config));
		memmove(nvr.config, word, c);
		nvr.configsum = nvcsum(nvr.config, sizeof(nvr.config));
		nvwrite(NVRAUTHADDR, &nvr, sizeof(nvr));
		f.newconf = 1;
		goto loop;
	}
	if(strcmp(word, "service") == 0) {
		getwd(word, cp);
		strcpy(service, word);
		f.modconf = 1;
		goto loop;
	}
	if(strcmp(word, "ip") == 0) {
		verb = 0;
		goto ipname;
	}
	if(strcmp(word, "ipgw") == 0) {
		verb = 1;
		goto ipname;
	}
	if(strcmp(word, "ipauth") == 0) {
		verb = 2;
		goto ipname;
	}
	if(strcmp(word, "ipmask") == 0) {
		verb = 3;
		goto ipname;
	}
	print("unknown config command\n");
	print("	type end to get out\n");
	goto loop;

ipname:
	getwd(word, cp);
	if(chartoip(localip, word)) {
		print("bad ip address\n");
		goto loop;
	}
	switch(verb) {
	case 0:
		memmove(sysip, localip, sizeof(sysip));
		break;
	case 1:
		memmove(defgwip, localip, sizeof(defgwip));
		break;
	case 2:
		memmove(authip, localip, sizeof(authip));
		break;
	case 3:
		memmove(defmask, localip, sizeof(defmask));
		break;
	}
	f.modconf = 1;
	goto loop;

gfsname:
	cp = getwd(word, cp);
	for(fs=filsys; fs->name; fs++)
		if(strcmp(word, fs->name) == 0)
			goto found;
	memset(fs, 0, sizeof(*fs));
	fs->name = strdup(word);

found:
	switch(verb) {
	case FREAM:
		if(strcmp(word, "main") == 0)
			wstatallow = 1;		/* only set, never reset */
	case FRECOVER:
		fs->flags |= verb;
		goto loop;
	case FEDIT:
		f.modconf = 1;
		getwd(word, cp);
		fs->flags |= verb;
		if(word[0] == 0) {
			fs->conf = 0;
			goto loop;
		}
		if(testconfig(word))
			goto loop;
		fs->conf = strdup(word);
		goto loop;
	}
}
uidgid.c        715711015   0     0     664     9050      `
#include	"all.h"

struct
{
	char*	name;
	short	uid;
	short	lead;
} minusers[] =
{
	"adm",		-1,	-1,
	"none",		0,	-1,
	"tor",		1,	1,
	"sys",		10000,	0,
	"map",		10001,	10001,
	"doc",		10002,	0,
	"upas",		10003,	10003,
	"cda",		10004,	0,
	"bootes",	10005,	10005,
	0
};

static char buf[4096];
static Rune ichar[] = L"?=+-/:";

int	fchar(void);
int	readln(char*, int);
char*	getword(char*, Rune, char*, int);
void	setminusers(void);
Uid*	chkuid(char *name, int chk);
void	pentry(char*, Uid*);
Uid*	uidtop(int);
void	do_newuser(int, char*[]);

void
cmd_users(int argc, char *argv[])
{
	Uid *ui;
	int u, g, o, line, n;
	char *file, *p, *uname, *ulead, *unext;


	file = "/adm/users";
	if(argc > 1)
		file = argv[1];

	if(strcmp(file, "default") == 0) {
		setminusers();
		return;
	}

	wlock(&uidgc.uidlock);
	uidgc.uidbuf = getbuf(devnone, Cuidbuf, 0);

	if(walkto(file) || con_open(FID2, 0)) {
		print("cmd_users: cannot access %s\n", file);
		putbuf(uidgc.uidbuf);
		wunlock(&uidgc.uidlock);
		return;
	}

	uidgc.flen = 0;
	uidgc.find = 0;
	cons.offset = 0;

	u = 0;
	line = 0;
	for(;;) {
		line++;
		n = readln(buf, sizeof(buf));
		if(n == 0)
			break;

		p = getword(buf, L':', "no : after number", line);
		if(p == 0)
			continue;
		ulead = getword(p, L':', "no : after name", line);
		if(ulead == 0)
			continue;

		if(strlen(p) > NAMELEN-1) {
			print("%s: name too long\n");
			continue;
		}
		strcpy(uid[u].name, p);
		uid[u].uid = number(buf, 0, 10);
		memset(uid[u].key, 0, DESKEYLEN);
		u++;
		if(u >= conf.nuid) {
			print("conf.nuid too small (%d)\n", conf.nuid);
			break;
		}
	}

	cons.nuid = u;

	/* Parse group table */
	uidgc.flen = 0;
	uidgc.find = 0;
	cons.offset = 0;

	g = 0;
	line = 0;
	for(;;) {
		line++;
		n = readln(buf, sizeof(buf));
		if(n == 0)
			break;

		uname = getword(buf, L':', 0, 0);	/* skip number */
		if(uname == 0)
			continue;

		ulead = getword(uname, L':', 0, 0);	/* skip name */
		if(ulead == 0)
			continue;

		p = getword(ulead, L':', "no : after leader", line);
		if(p == 0)
			continue;

		ui = uidpstr(uname);
		if(ui == 0)
			continue;

		/* set to owner if name not known */
		ui->lead = 0;
		if(ulead[0]) {
			o = strtouid(ulead, 0);
			if(o != 0)
				ui->lead = o;
			else
				ui->lead = ui->uid;
		}
		ui->gtab = &gidspace[g];
		ui->ngrp = 0;
		for(;;) {
			if(p == 0)
				break;
			unext = getword(p, L',', 0, 0);
			o = strtouid(p, 0);
			if(o != 0) {
				gidspace[g++] = o;
				ui->ngrp++;
			}	
			p = unext;
		}
	}

	/* Sorted by uid for use in uidtostr */
	qsort(uid, cons.nuid, sizeof(uid[0]), byuid);

	cons.ngid = g;

	putbuf(uidgc.uidbuf);
	wunlock(&uidgc.uidlock);

	print("%d uids read, %d groups used\n", u, g);
}

void
cmd_newuser(int argc, char *argv[])
{
	if(argc <= 1) {
		print("usage: newuser args\n");
		print("	name -- create a new user\n");
		print("	name : -- create a new group\n");
		print("	name ? -- show entry for user\n");
		print("	name name -- rename\n");
		print("	name =[name] -- add/alter/remove leader\n");
		print("	name +name -- add member\n");
		print("	name -name -- delete member\n");
		return;
	}
	wlock(&uidgc.uidlock);
	do_newuser(argc, argv);
	wunlock(&uidgc.uidlock);
}

void
do_newuser(int argc, char *argv[])
{
	Rune *r;
	int nuid;
	short *s;
	Uid *ui, *u2;
	char *p, *md;
	int i, l, n;


	nuid = 10000;
	md = 0;
	if(argc == 2) {
		nuid = 1;
		argv[2] = ":";
	}

	for(r = ichar; *r; r++)
		if(utfrune(argv[1], *r)) {
			print("illegal character in name\n");
			return;
		}
	if(strlen(argv[1]) > NAMELEN-1) {
		print("name %s too long\n", argv[1]);
		return;
	}
		
	p = argv[2];
	switch(*p) {
	case '?':
		ui = chkuid(argv[1], 1);
		if(ui == 0)
			return;
		pentry(buf, ui);
		print("%s", buf);
		return;

	case ':':
		if(chkuid(argv[1], 0))
			return;
		while(uidtop(nuid) != 0)
			nuid++;
		if(cons.nuid >= conf.nuid) {
			print("conf.nuid too small (%d)\n", conf.nuid);
			return;
		}
		ui = &uid[cons.nuid++];
		ui->uid = nuid;
		ui->lead = 0;
		if(nuid < 10000) {
			ui->lead = ui->uid;
			md = argv[1];
		}
		strcpy(ui->name, argv[1]);
		ui->ngrp = 0;
		break;

	case '=':
		ui = chkuid(argv[1], 1);
		if(ui == 0)
			return;
		p++;
		if(*p == '\0') {
			ui->lead = 0;
			break;			
		}
		u2 = chkuid(p, 1);
		if(u2 == 0)
			return;
		ui->lead = u2->uid;
		break;

	case '+':
		ui = chkuid(argv[1], 1);
		if(ui == 0)
			return;
		p++;
		u2 = chkuid(p, 1);
		if(u2 == 0)
			return;
		if(u2->uid == ui->uid)
			return;
		if(cons.ngid+ui->ngrp+1 >= conf.gidspace) {
			print("conf.gidspace too small (%d)\n", conf.gidspace);
			return;
		}
		for(i = 0; i < ui->ngrp; i++) {
			if(ui->gtab[i] == u2->uid) {
				print("member already in group\n");
				return;
			}
		}
		s = gidspace+cons.ngid;
		memmove(s, ui->gtab, ui->ngrp*sizeof(*s));
		ui->gtab = s;
		s[ui->ngrp++] = u2->uid;
		cons.ngid += ui->ngrp+1;
		break;

	case '-':
		ui = chkuid(argv[1], 1);
		if(ui == 0)
			return;
		p++;
		u2 = chkuid(p, 1);
		if(u2 == 0)
			return;
		for(i = 0; i < ui->ngrp; i++)
			if(ui->gtab[i] == u2->uid)
				break;

		if(i == ui->ngrp) {
			print("%s not in group\n", p);
			return;
		}
		s = ui->gtab+i;
		ui->ngrp--;
		memmove(s, s+1, (ui->ngrp-i)*sizeof(*s));
		break;

	default:
		if(chkuid(argv[2], 0))
			return;

		for(r = ichar; *r; r++)
			if(utfrune(argv[2], *r)) {
				print("illegal character in name\n");
				return;
			}

		ui = chkuid(argv[1], 1);
		if(ui == 0)
			return;

		if(strlen(argv[2]) > NAMELEN-1) {
			print("name %s too long\n", argv[2]);
			return;
		}
		strcpy(ui->name, argv[2]);
		break;
	}

	qsort(uid, cons.nuid, sizeof(uid[0]), byuid);

	if(walkto("/adm/users") || con_open(FID2, MWRITE|MTRUNC)) {
		print("can't open /adm/users for write");
		return;
	}

	cons.offset = 0;
	for(i = 0; i < cons.nuid; i++) {
		pentry(buf, &uid[i]);
		l = strlen(buf);
		n = con_write(FID2, buf, cons.offset, l);
		if(l != n)
			print("short write on /adm/users\n");
		cons.offset += n;
	}

	if(md != 0) {
		wunlock(&uidgc.uidlock);
		sprint(buf, "create /usr/%s %s %s 755 d", md, md, md);
		print("%s\n", buf);
		cmd_exec(buf);
		wlock(&uidgc.uidlock);
	}
}

Uid*
chkuid(char *name, int chk)
{
	Uid *u;

	u = uidpstr(name);
	if(chk == 1) {
		if(u == 0)
			print("%s does not exist\n", name);
	}
	else {
		if(u != 0)
			print("%s already exists\n", name);
	}
	return u;
}

void
pentry(char *buf, Uid *u)
{
	int i, posn;
	Uid *p;

	posn = sprint(buf, "%d:%s:", u->uid, u->name);
	p = uidtop(u->lead);
	if(p && u->lead != 0)
		posn += sprint(buf+posn, "%s", p->name);

	posn += sprint(buf+posn, ":");
	for(i = 0; i < u->ngrp; i++) {
		p = uidtop(u->gtab[i]);
		if(i != 0)
			posn += sprint(buf+posn, ",");
		if(p != 0)
			posn += sprint(buf+posn, "%s", p->name);
		else
			posn += sprint(buf+posn, "%d", u->gtab[i]);
	}
	sprint(buf+posn, "\n");
}

void
setminusers(void)
{
	int u;

	for(u = 0; minusers[u].name; u++) {
		strcpy(uid[u].name, minusers[u].name);
		uid[u].uid = minusers[u].uid;
		uid[u].lead = minusers[u].lead;
		memset(uid[u].key, 0, DESKEYLEN);
	}
	cons.nuid = u;
	qsort(uid, u, sizeof(uid[0]), byuid);
}

Uid*
uidpstr(char *name)
{
	Uid *s, *e;

	s = uid;
	for(e = s+cons.nuid; s < e; s++) {
		if(strcmp(name, s->name) == 0)
			return s;
	}
	return 0;
}

char*
getword(char *buf, Rune delim, char *error, int line)
{
	char *p;

	p = utfrune(buf, delim);
	if(p == 0) {
		if(error)
			print("cmd_users: %s line %d\n", error, line);
		return 0;
	}
	*p = '\0';
	return p+1;
}

int
strtouid(char *name, int dolock)
{
	Uid *u;
	int id;

	if(dolock)
		rlock(&uidgc.uidlock);

	u = uidpstr(name);
	id = 0;
	if(u != 0)
		id = u->uid;

	if(dolock)
		runlock(&uidgc.uidlock);

	return id;
}

Uid*
uidtop(int id)
{
	Uid *bot, *top, *new;

	bot = uid;
	top = bot + cons.nuid-1;

	while(bot <= top){
		new = bot + (top - bot)/2;
		if(new->uid == id)
			return new;
		if(new->uid < id)
			bot = new + 1;
		else
			top = new - 1;
	}
	return 0;
}

void
uidtostr(char *name, int id, int dolock)
{
	Uid *p;

	if(dolock)
		rlock(&uidgc.uidlock);

	p = uidtop(id);
	if(p == 0)
		strcpy(name, "none");
	else	
		strcpy(name, p->name);

	if(dolock)
		runlock(&uidgc.uidlock);
}

int
nametokey(char *name, char *key)
{
	Uid *u;

	u = uidpstr(name);
	if(u != 0) {
		memmove(key, u->key, DESKEYLEN);
		return 0;
	}
	return 1;
}

int
ingroup(int u, int g)
{
	Uid *p;
	short *s, *e;

	if(u == g)
		return 1;

	rlock(&uidgc.uidlock);
	p = uidtop(g);
	if(p != 0) {
		s = p->gtab;
		for(e = s + p->ngrp; s < e; s++) {
			if(*s == u) {
				runlock(&uidgc.uidlock);
				return 1;
			}
		}
	}
	runlock(&uidgc.uidlock);
	return 0;
}

int
leadgroup(int ui, int gi)
{
	int i;
	Uid *u;

	rlock(&uidgc.uidlock);
	u = uidtop(gi);
	if(u == 0) {
		runlock(&uidgc.uidlock);
		return 0;
	}
	i = u->lead;
	runlock(&uidgc.uidlock);
	if(i == ui)
		return 1;
	if(i == 0)
		return ingroup(ui, gi);

	return 0;
}

int
byuid(void *a1, void *a2)
{
	Uid *u1, *u2;

	u1 = a1;
	u2 = a2;
	return u1->uid - u2->uid;
}

int
readln(char *p, int len)
{
	int n, c;

	n = 0;
	while(len--) {
		c = fchar();
		if(c == -1 || c == '\n')
			break;

		n++;
		*p++ = c;
	}
	*p = '\0';
	return n;
}
