#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
nzip(uchar ip[Pasize])
{
	if(ip[0] || ip[1] || ip[2] || ip[3])
		return 1;
	return 0;
}

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;
	}
}