#include <9pm/u.h>
#include <9pm/libc.h>
#include "dat.h"
#include "fns.h"

enum{
	Qdir=	0,

	Nfiles=64,	/* max root files */
};

extern ulong	bootlen;
extern uchar	bootcode[];

Dirtab rootdir[Nfiles] = {
	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
};

static uchar	*rootdata[Nfiles];
static int	nroot = 1;

/*
 *  add a root file
 */
static void
addroot(char *name, uchar *contents, ulong len, int perm)
{
	Dirtab *d;

	if(nroot >= Nfiles)
		panic("too many root files");
	rootdata[nroot] = contents;
	d = &rootdir[nroot];
	strcpy(d->name, name);
	d->length = len;
	d->perm = perm;
	d->qid.type = 0;
	d->qid.vers = 0;
	d->qid.path = nroot+1;
	if(perm & DMDIR)
		d->qid.type |= QTDIR;
	nroot++;
}

/*
 *  add a root file
 */
void
addrootfile(char *name, uchar *contents, ulong len)
{
	addroot(name, contents, len, 0555);
}

/*
 *  add a root file
 */
void
addrootdir(char *name)
{
	addroot(name, nil, 0, DMDIR|0555);
}

static void
rootreset(void)
{
	addrootdir("9pm");
	addrootdir("bin");
	addrootdir("dev");
	addrootdir("env");
	addrootdir("srv");
}

static Chan*
rootattach(char *spec)
{
	return devattach('/', spec);
}

static Walkqid*
rootwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c,  nc, name, nname, rootdir, nroot, devgen);
}

static int
rootstat(Chan *c, uchar *dp, int n)
{
	return devstat(c, dp, n, rootdir, nroot, devgen);
}

static Chan*
rootopen(Chan *c, int omode)
{
	switch((ulong)c->qid.path) {
	default:
		break;
	}

	return devopen(c, omode, rootdir, nroot, devgen);
}

/*
 * sysremove() knows this is a nop
 */
static void
rootclose(Chan *c)
{
	USED(c);
}

static long
rootread(Chan *c, void *buf, long n, vlong off)
{
	ulong t;
	Dirtab *d;
	uchar *data;
	ulong offset = off;

	t = c->qid.path;
	switch(t){
	case Qdir:
		return devdirread(c, buf, n, rootdir, nroot, devgen);
	}

	d = &rootdir[t-1];
	data = rootdata[t-1];
	if(offset >= d->length)
		return 0;
	if(offset+n > d->length)
		n = d->length - offset;
	memmove(buf, data+offset, n);
	return n;
}

static long
rootwrite(Chan *c, void *a, long n, vlong o)
{
	USED(c);
	USED(a);
	USED(n);
	USED(o);

	error(Egreg);
	return 0;
}

Dev devroot = {
	'/',
	"root",

	rootreset,
	rootattach,
	rootwalk,
	rootstat,
	rootopen,
	devcreate,
	rootclose,
	rootread,
	devbread,
	rootwrite,
	devbwrite,
	devremove,
	devwstat,
};
