shithub: p9-stm32-example-os

ref: f801657f77f3923ec2388c25bdcb036c8019ba89
dir: /prog/devicefs.c/

View raw version
#include	<u.h>
#include	"dat.h"
#include	"fns.h"
#include	"mem.h"
#include	"libkern/kern.h"

#include	"prog/prog.h"
#include	"prog/progfns.h"

#define DIRSIZE		(DFSMSIZE - IOHDRSZ)
#define FNLEN		8

typedef struct Fid Fid;
struct Fid
{
	u32int	fid;
	uvlong	qid;
	uchar	isopen;
	uchar	openm;
	Fid		*next;
};

typedef
struct DevFsSrv
{
	Queue	*din;
	Queue	*dout;
	char	*buf;
	Fid		fidpool;
	Fcall	r;
	Fcall	f;
} DevFsSrv;

static char Egeneric[] = "server error";
static char Eclient[] = "client error";
static char Eimpl[] = "not implemented";
static char Enoauth[] = "no auth";
static char Eperm[] = "permission denied";
static char Efid[] = "fid error";
static char Eopen[] = "open or mode error";
static char Eseek[] = "bad seek or offset";

/*	trees	*/
typedef struct FileTree FileTree;
struct FileTree
{
	uvlong	parent;
//	uvlong	path;
	char*	name;
	u32int	perm;
};

enum {
	Qroot = 0,
	Qdev,
	Qsysctl,
	Qcount,
};

enum {
	Qproglo	= 100,
	Qproghi	= 200,
};

static
FileTree tree[] =
{
	/*	Qroot	*/	{	Qroot,	".",		0555|DMDIR,	},
	/*	Qdev	*/	{	Qroot,	"dev",		0444|DMDIR,	},
	/*	Qsysctl	*/	{	Qdev,	"sysctl",	0444,		},
};

/*	fid operations	*/
Fid*
fid_create(DevFsSrv* srv, u32int fid)
{
	Fid *c, *p, *new;

	for(c = &srv->fidpool; c != nil; c = c->next) {
		if(c->fid == fid)
			return nil;
		p = c; 
	}

	new = malloc(sizeof(Fid));
	new->fid = fid;

	p->next = new;
	return new;
}

Fid*
fid_get(DevFsSrv* srv, u32int fid)
{
	Fid *c;

	for(c = &srv->fidpool; c != nil; c = c->next)
		if(c->fid == fid)
			return c;
	return nil;
}

Fid*
fid_clunk(DevFsSrv* srv, u32int fid)
{
	Fid *c, *p;

	if(fid == NOFID)
		return nil;	// can't clunk the sentinel

	for(c = &srv->fidpool; c != nil && c->fid != fid; c = c->next) p = c;
	if(c == nil)
		return nil;

	p->next = c->next;
	free(c);
	return &srv->fidpool;
}

/*	generic replies	*/
void
rerror(DevFsSrv *srv, char *ename)
{
	uint n;
	srv->r.type = Rerror;
	srv->r.tag = srv->f.tag;
	srv->r.ename = ename;
	n = convS2M(&srv->r, (uchar*)srv->buf, DFSMSIZE);
//	kprint("devicefs: error %s\n", ename);
	qwrite(srv->dout, srv->buf, n);
}

void
rreply(DevFsSrv *srv)
{
	uint n;
	if((n = convS2M(&srv->r, (uchar*)srv->buf, DFSMSIZE)) == 0) {
		rerror(srv, Egeneric);
		return;
	}
	qwrite(srv->dout, srv->buf, n);
}

/*	generic message handlers	*/
void
devicefs_tversion(DevFsSrv *srv)
{
	srv->r.type = Rversion;
	srv->r.tag = srv->f.tag;
	srv->r.msize = DFSMSIZE;
	srv->r.version = VERSION9P;

	rreply(srv);
}

void
devicefs_tauth(DevFsSrv *srv)
{
	rerror(srv, Enoauth);
}

void
devicefs_tattach(DevFsSrv *srv)
{
	Fid* fid;
//	Qid qid;

	if(srv->f.afid != NOFID) {
		rerror(srv, Enoauth);
		return;
	}

	if((fid = fid_create(srv, srv->f.fid)) == nil) {
		rerror(srv, Efid);
		return;
	}

	fid->fid = srv->f.fid;
	fid->qid = Qroot;

	srv->r.type = Rattach;
	srv->r.tag = srv->f.tag;
	srv->r.qid.path = fid->qid;
	srv->r.qid.vers = 1;
	srv->r.qid.type = tree[Qroot].perm >> 24;

	kprint("devicefs: attached %s", srv->f.uname);
	rreply(srv);
}

void
devicefs_tflush(DevFsSrv *srv)
{
	rerror(srv, Eimpl);
}

void
devicefs_twalk(DevFsSrv *srv)
{
	Fid *fid, *newfid;
	Qid qid;

	if(srv->f.nwname > MAXWELEM) {
		rerror(srv, Eclient);
		return;
	}

	if((fid = fid_get(srv, srv->f.fid)) == nil) {
		rerror(srv, Efid);
		return;
	}

	if(srv->f.fid != srv->f.newfid) {
		if((newfid = fid_create(srv, srv->f.newfid)) == nil) {
			rerror(srv, Efid);
			return;
		}
	} else
		newfid = fid;

	srv->r.type = Rwalk;
	srv->r.tag = srv->f.tag;
	srv->r.nwqid = 0;

	if(srv->f.nwname == 0) {
		newfid->qid = fid->qid;
		rreply(srv);
		return;
	}

	qid.path = fid->qid;
	for(int i = 0; i < srv->f.nwname; i++) {
		for(int j = 0; j < Qcount; j++) {
			if(tree[j].parent == qid.path && strcmp(srv->f.wname[i], tree[j].name) == 0) {
				qid.path = j;
				qid.vers = 1;
				qid.type = tree[j].perm >> 24;
				srv->r.wqid[i] = qid;
				srv->r.nwqid++;

				// TODO delegate
				break;
			}
		}
		if(srv->r.nwqid != i + 1) {
			srv->r.nwqid++;
			break;
		}
	}

	if(srv->r.nwqid == srv->f.nwname)
		newfid->qid = srv->r.wqid[srv->r.nwqid - 1].path;
	else
		fid_clunk(srv, newfid->fid);

	rreply(srv);
}

void
devicefs_topen(DevFsSrv *srv)
{
	Fid* fid;
	u32int perm;

	if((fid = fid_get(srv, srv->f.fid)) == nil) {
		rerror(srv, Efid);
		return;
	}

	if(fid->isopen) {
		rerror(srv, Eopen);
		return;
	}

	// TODO delegation
	perm = tree[fid->qid].perm;

	if((srv->f.mode & 0xf == OREAD) && !(perm & 0400)) {
		rerror(srv, Eperm);
		return;
	}
	if((srv->f.mode & 0xf == OWRITE) && !(perm & 0200)) {
		rerror(srv, Eperm);
		return;
	}
	if((srv->f.mode & 0xf == ORDWR) && !(perm & 0200 && perm & 0400)) {
		rerror(srv, Eperm);
		return;
	}
	if((srv->f.mode & 0xf == OEXEC) && !(perm & 0100 && perm & 0400)) {
		rerror(srv, Eperm);
		return;
	}
	if(srv->f.mode & ORCLOSE) {
		rerror(srv, Eperm);
		return;
	}
	if(srv->f.mode & OTRUNC && (!(perm & 0200) || (perm | DMDIR))) {
		rerror(srv, Eperm);
		return;
	}

	fid->isopen = 1;
	fid->openm = srv->f.mode & 0xff;

	srv->r.type = Ropen;
	srv->r.tag = srv->f.tag;
	srv->r.qid.path = fid->qid;
	srv->r.qid.vers = 1;
	srv->r.qid.type = perm >> 24;
	srv->r.iounit = DFSIOUNIT;

	rreply(srv);
}

void
devicefs_tcreate(DevFsSrv *srv)
{
	rerror(srv, Eperm);
}

void
devicefs_tread(DevFsSrv *srv)
{
	Fid* fid;
	Dir* dir;
	char *name, *data;
	u32int perm, n;

	if((fid = fid_get(srv, srv->f.fid)) == nil) {
		rerror(srv, Efid);
		return;
	}

	if((!fid->isopen) || (fid->openm & 0xf == OWRITE)) {
		rerror(srv, Eopen);
		return;
	}

	// TODO delegation
	name = tree[fid->qid].name;
	perm = tree[fid->qid].perm;

	if(perm & DMDIR) {
		if(srv->f.offset != 0) {
			rerror(srv, Eperm);
			return;
		}

		data = malloc(DIRSIZE);

		dir = malloc(sizeof(Dir));
		dir->qid.path = fid->qid;
		dir->qid.vers = 1;
		dir->qid.type = perm >> 24;
		dir->mode = perm;
		dir->atime = 0;
		dir->mtime = 0;
		dir->length = FNLEN;
		dir->name = name;
		dir->uid = "none";
		dir->gid = "none";
		dir->muid = "none";

		n = convD2M(dir, (uchar*)data, DIRSIZE);
		if(n == 0) {
			free(dir);
			free(data);
			rerror(srv, Egeneric);
			return;
		}

		srv->r.type = Rread;
		srv->r.tag = srv->f.tag;
		srv->r.count = n;
		srv->r.data = data;

		rreply(srv);

		free(dir);
		free(data);
		return;
	}

	// TODO delegation
	data = "bye bye";

	if(srv->f.offset > 7)
		rerror(srv, Eseek);

	srv->r.type = Rread;
	srv->r.tag = srv->f.tag;
	srv->r.count = 7 - srv->f.offset;
	srv->r.data = &data[srv->f.offset];

	rreply(srv);
}

void
devicefs_twrite(DevFsSrv *srv)
{
	rerror(srv, Eperm);
}

void
devicefs_tclunk(DevFsSrv *srv)
{
	if(fid_clunk(srv, srv->f.fid) == nil) {
		rerror(srv, Efid);
		return;
	}

	srv->r.type = Rclunk;
	srv->r.tag = srv->f.tag;

	rreply(srv);
}

void
devicefs_tremove(DevFsSrv *srv)
{
	rerror(srv, Eperm);
}

void
devicefs_tstat(DevFsSrv *srv)
{
	Fid* fid;
	Dir* stat;
	char *name;
	uchar *data;
	u32int perm;
	uint n;

	if((fid = fid_get(srv, srv->f.fid)) == nil) {
		rerror(srv, Efid);
		return;
	}

	// TODO delegation
	name = tree[fid->qid].name;
	perm = tree[fid->qid].perm;

	data = malloc(DIRSIZE);
	stat = malloc(sizeof(Dir));
	stat->qid.path = fid->qid;
	stat->qid.vers = 1;
	stat->qid.type = perm >> 24;
	stat->mode = perm;
	stat->atime = 0;
	stat->mtime = 0;
	stat->length = FNLEN;
	stat->name = name;
	stat->uid = "none";
	stat->gid = "none";
	stat->muid = "none";

	n = convD2M(stat, data, DIRSIZE);
	if(n == 0) {
		free(stat);
		free(data);
		rerror(srv, Egeneric);
		return;
	}

	srv->r.type = Rstat;
	srv->r.tag = srv->f.tag;
	srv->r.nstat = n;
	srv->r.stat = data;

	rreply(srv);

	free(stat);
	free(data);
}

void
devicefs_twstat(DevFsSrv *srv)
{
	rerror(srv, Eperm);
}

/*	program entrypoint	*/
void
devicefs(void *srvptr)
{
	kprint("devicefs: started");
	uint n, m;
	DevFsSrv *srv = srvptr;

	// start processing messages
	qflush(srv->din);
	qflush(srv->dout);

	kprint("devicefs: serving via UART2");

	while(1) {
		n = BIT32SZ;
		while(n > 0)	// read Tmsg size
			n -= qread(srv->din, &srv->buf[BIT32SZ - n], n);
		n = GBIT32(srv->buf);

		if(n > DFSMSIZE - BIT32SZ) {	// drain buffer
			kprint("devicefs: error: Tmsg too big: %ud", n);
			while(n > 0)
				n -= qread(srv->din, srv->buf,
							(n > DFSMSIZE) ? DFSMSIZE : n);
			continue;
		}

		m = n - BIT32SZ;
		while(m > 0)
			m -= qread(srv->din, &srv->buf[n - m], m);
		if(convM2S((uchar*)srv->buf, n, &srv->f) == 0) {	// resulting size?
			kprint("devicefs: warn: malformed Tmsg");
			rerror(srv, Egeneric);
			continue;
		}

		kprint("devicefs: Tmsg type %04x", srv->f.type);
		switch(srv->f.type) {
		case Tversion:	devicefs_tversion(srv);	break;
		case Tauth:		devicefs_tauth(srv);	break;
		case Tattach:	devicefs_tattach(srv);	break;
		case Tflush:	devicefs_tflush(srv);	break;
		case Twalk:		devicefs_twalk(srv);	break;
		case Topen:		devicefs_topen(srv);	break;
		case Tcreate:	devicefs_tcreate(srv);	break;
		case Tread:		devicefs_tread(srv);	break;
		case Twrite:	devicefs_twrite(srv);	break;
		case Tclunk:	devicefs_tclunk(srv);	break;
		case Tremove:	devicefs_tremove(srv);	break;
		case Tstat:		devicefs_tstat(srv);	break;
		case Twstat:	devicefs_twstat(srv);	break;
		default:
			kprint("devicefs: warn: wrong Tmsg type: %d", srv->f.type);
			rerror(srv, Eclient);
		}

		// clear data
//		if(srv->f.uname)	free(srv->f.uname);
//		if(srv->f.aname)	free(srv->f.aname);
//		if(srv->f.name)		free(srv->f.name);
//		if(srv->f.data)		free(srv->f.data);
//		if(srv->f.stat)		free(srv->f.stat);
//		for(int i = 0; i < MAXWELEM; i++) {
//			if(srv->f.wname[i])	free(srv->f.wname[i]);
//		}
	}

	kprint("devicefs: in queue closed, exiting");

	qfree(srv->din);
	qfree(srv->dout);
	free(srv);

	kprint("devicefs: ended");
	pexit(nil, 0);
}

Proc*
prog_devicefs(Queue* fsiq, Queue* fsoq)
{
	Proc *fsproc;
	Egrp *eg;

	DevFsSrv *srv = malloc(sizeof(DevFsSrv));
	srv->din = fsiq;
	srv->dout = fsoq;
	srv->buf = malloc(DFSMSIZE);
	srv->fidpool.fid = NOFID;
	srv->fidpool.next = nil;

	fsproc = newprog("devicefs", devicefs, srv, 0, 1280);
	fsproc->env->egrp = eg;

	return fsproc;
}