shithub: lpa

ref: 2b23d05d57743af57385cd42c0fd2d223b11d8c8
dir: /fs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

#include "dat.h"
#include "fns.h"

#define Eexist "file does not exist"
#define Enodir "not a directory"
#define Enotyet "not yet implemented"
#define Eoffset "invalid offset"
#define Ewrite "write prohibited"
#define Ecreate "create prohibited"
#define Eremove "remove prohibited"
#define Einvalidname "invalid LPA name"

Qid qroot;
Qid qnew;
char *username;

enum {
	Qroot,
		Qnew,
		Qsession,
			Qctl,
			Qcons,
			Qlog,
			Qmodules,
				Qmodule,
			Qthreads,
	Qlpaobj
};

enum {
	Fctl,
	Fcons,
	Flog,
	Fmodules,
	Fthreads,
};

enum {
	Fnew,
	Fsession,
};

typedef struct Aux Aux;
struct Aux
{
	Session *session;
	Module *module;
	Symbol *symbol;
	char *cachestr;
};

static Aux *
allocaux(void)
{
	Aux *aux = alloc(DataAux);
	setroot(aux, 1);
	return aux;
}

#define QID_TYPE(qid) ((qid.path) & 0xFF)
#define QID_PATH(qid) (qid.path >> 8)

static Qid
mkqid(int type, uvlong id)
{
	Qid qid;
	qid.vers = 0;
	qid.path = (type & 0xFF) | (id << 8);
	switch(type){
	case Qroot:
	case Qsession:
	case Qmodules:
	case Qmodule:
	case Qthreads:
		qid.type = QTDIR;
		break;
	case Qnew:
	case Qctl:
	case Qcons:
	case Qlog:
	case Qlpaobj:
		qid.type = QTFILE;
		break;
	}

	if(type == Qcons)
		qid.type |= QTAPPEND;
	
	return qid;
}

Qid
freshobjqid(void)
{
	static int id = 0;
	Qid qid = mkqid(Qlpaobj, id);
	id++;
	return qid;
}

static void
mkfilestat(Dir *d, char *name, Qid qid, ulong mode)
{
	d->uid = estrdup9p(username);
	d->gid = estrdup9p(username);
	d->muid = estrdup9p(username);
	d->mode = mode;
	d->name = estrdup9p(name);
	d->qid = qid;
}

static void
mkdirstat(Dir *d, char *name, Qid qid)
{
	d->uid = estrdup9p(username);
	d->gid = estrdup9p(username);
	d->muid = estrdup9p(username);
	d->mode = 0555|DMDIR;
	d->name = estrdup9p(name);
	d->qid = qid;
}

static int
roottreegen(int n, Dir *d, void *aux)
{
	Enumeration *sessions = aux;
	int done = 0;

	if(n == Fnew) /* new */
		mkfilestat(d, "new", qnew, 0444);
	else{
		n -= Fsession;
		if(n < sessions->count){
			Session *s = sessions->items[n];
			mkdirstat(d, s->name, s->qsession);
		}else
			done = 1;
	}

	return done ? -1 : 0;
}

static int
sessiongen(int n, Dir *d, void *aux)
{
	Session *s = aux;
	int done = 0;

	switch(n){
	case Fctl:
		mkfilestat(d, "ctl", s->qctl, 0666);
		break;
	case Fcons:
		mkfilestat(d, "cons", s->qctl, DMAPPEND|0555);
		d->length = s->logsize;
		break;
	case Flog:
		mkfilestat(d, "log", s->qlog, 0444);
		d->length = s->logsize;
		break;
	case Fmodules:
		mkdirstat(d, "modules", s->qmodules);
		break;
/*	case Fthreads:
		mkdirstat(d, "threads", s->qthreads);
		break;
*/
	default:
		done = 1;
	}

	return done ? -1 : 0;
}

static int
modulesgen(int n, Dir *d, void *aux)
{
	Enumeration *modules = aux;
	if(n == modules->count)
		return -1;

	Module *m = modules->items[n];
	mkdirstat(d, m->name, m->qmodule);
	return 0;
}

static int
symbolsgen(int n, Dir *d, void *aux)
{
	Enumeration *symbols = aux;
	if(n == symbols->count)
		return -1;

	Symbol *s = symbols->items[n];
	mkfilestat(d, s->name, s->qsymbol, 0666);
	d->length = strlen(printval(s->value));
	return 0;
}

static char *
requeststr(Req *r)
{
	char *buf;
	r->ofcall.count = r->ifcall.count;
	buf = emalloc9p(r->ifcall.count+1);
	memcpy(buf, r->ifcall.data, r->ifcall.count);
	buf[r->ifcall.count] = 0; /* make sure it is 0 terminated */
	return buf;
}

static void
sessioncons(Req *r)
{
	Aux *aux = r->fid->aux;
	Session *s = aux->session;

	srvrelease(r->srv);
	if(r->ifcall.type == Tread){
		qlock(&s->loglock);
		if(r->ifcall.offset >= s->logsize)
			rsleep(&s->logwait);
		readbuf(r, s->log, s->logsize);
		qunlock(&s->loglock);
	}else{ /* Twrite */
		char *buf = requeststr(r);
		send(s->input, &buf);
	}
	srvacquire(r->srv);
}

static void
sessionlog(Req *r)
{
	Aux *aux = r->fid->aux;
	Session *s = aux->session;

	srvrelease(r->srv);
	qlock(&s->loglock);
	readbuf(r, s->log, s->logsize);
	qunlock(&s->loglock);
	srvacquire(r->srv);
}

static void
sessionctl(Req *r)
{
	Aux *aux = r->fid->aux;
	Session *s = aux->session;
	char *buf = requeststr(r);

	srvrelease(r->srv);
	systemcmd(s, buf, 1);
	free(buf);
	srvacquire(r->srv);
}

static char *
symbolrw(Req *r)
{
	Aux *aux = r->fid->aux;
	Session *session = aux->session;
	Symbol *symb = aux->symbol;

	if(r->ifcall.type == Tread){
		/* Pretty print the value and readstr() it. */
		if(aux->cachestr == nil)
			aux->cachestr = printval(symb->value);
		readstr(r, aux->cachestr);
		if(r->ofcall.count == 0){
			free(aux->cachestr);
			aux->cachestr = nil;
		}
	}else{ /* Twrite */
		if(trap(EAny))
			return errmsg();

		char *buf = requeststr(r);
		void *v = parseval(session, buf);
		free(buf);
		if(getalloctag(v) == DataFunction){
			Function *f = v;
			if(strcmp(symb->name, f->ast->funcname->name) != 0)
				error(ESyntax, "Function name must match symbol name");
		}
		symset(symb->table, symb->id, v);
		endtrap();
	}
	return nil;
}

static void
fsattach(Req *r)
{
	r->fid->qid = qroot;
	r->ofcall.qid = r->fid->qid;

	r->fid->aux = allocaux();

	respond(r, nil);
}

static char *
fswalk1(Fid *fid, char *name, Qid *qid)
{
	char *err = nil;
	Enumeration *e = nil;
	Aux *aux = fid->aux;
	Session *s = aux->session;
	Module *m = aux->module;

	switch(QID_TYPE(fid->qid)){
	case Qroot:
		if(strcmp(name, "..") == 0)
			*qid = fid->qid;
		else if(strcmp(name, "new") == 0)
			*qid = qnew;
		else{
			int found = 0;
			e = enumsessions();
			for(uvlong i = 0; i < e->count; i++){
				Session *s = e->items[i];
				if(strcmp(name, s->name) == 0){
					*qid = s->qsession;
					aux->session = s;
					found = 1;
					break;
				}
			}
			if(!found)
				err = Eexist;
		}
		break;
	case Qsession:
		if(strcmp(name, "..") == 0)
			*qid = qroot;
		else if(strcmp(name, "ctl") == 0)
			*qid = s->qctl;
		else if(strcmp(name, "cons") == 0)
			*qid = s->qcons;
		else if(strcmp(name, "log") == 0)
			*qid = s->qlog;
		else if(strcmp(name, "modules") == 0)
			*qid = s->qmodules;
/*		else if(strcmp(name, "threads") == 0)
			*qid = s->qthreads;
*/
		else
			err = Eexist;
		break;
	case Qmodules:
		if(strcmp(name, "..") == 0)
			*qid = s->qsession;
		else{
			int found = 0;
			e = enummodules(s);
			for(uvlong i = 0; i < e->count && !found; i++){
				Module *m = e->items[i];
				if(strcmp(name, m->name) == 0){
					*qid = m->qmodule;
					aux->module = m;
					found = 1;
				}
			}
			if(!found)
				err = Eexist;
		}
		break;
	case Qmodule:
		if(strcmp(name, "..") == 0)
			*qid = m->qsession;
		else{
			int found = 0;
			e = enumsymbols(m->symtab, 1);
			for(uvlong i = 0; i < e->count && !found; i++){
				Symbol *symb = e->items[i];
				if(strcmp(name, symb->name) == 0){
					*qid = symb->qsymbol;
					aux->symbol = symb;
					found = 1;
				}
			}
			if(!found)
				err = Eexist;
		}
		break;
	case Qthreads:
		if(strcmp(name, "..") == 0)
			*qid = s->qsession;
		else
			err = Enotyet;
		break;
	default:
		err = Enodir;
		break;
	}
	if(e != nil)
		setroot(e, 0);

	return err;
}

static char *
fsclone(Fid *old, Fid *new)
{
	new->aux = allocaux();

	Aux *oldaux = old->aux;
	Aux *newaux = new->aux;
	memcpy(newaux, oldaux, sizeof(Aux));

	return nil;
}

static void
fsopen(Req *r)
{
	/* TODO check permissions */
	char *err = nil;
	if(QID_TYPE(r->fid->qid) == Qnew){
		/* Create a new session */
		Session *s = allocsession();
		Module *m = addmodule(s, "main");

		qnew.vers = s->id;
		r->fid->qid = qnew;
		r->ofcall.qid = qnew;

		s->qsession = mkqid(Qsession, s->id);
		s->qctl = mkqid(Qctl, s->id);
		s->qcons = mkqid(Qcons, s->id);
		s->qlog = mkqid(Qlog, s->id);
		s->qmodules = mkqid(Qmodules, s->id);
		s->qthreads = mkqid(Qthreads, s->id);

		m->qsession = s->qsession;
		m->qmodule = mkqid(Qmodule, m->id);
	}

	respond(r, err);
}

static void
fsstat(Req *r)
{
	Aux *aux = r->fid->aux;
	Session *s = aux->session;
	Module *m = aux->module;
	Symbol *symb = aux->symbol;
	char *err = nil;

	switch(QID_TYPE(r->fid->qid)){
	case Qroot:
		mkdirstat(&r->d, "/", qroot);
		break;
	case Qnew:
		roottreegen(Fnew, &r->d, nil);
		break;
	case Qsession:
		mkdirstat(&r->d, s->name, s->qsession);
		break;
	case Qctl:
		sessiongen(Fctl, &r->d, s);
		break;
	case Qcons:
		sessiongen(Fcons, &r->d, s);
		break;
	case Qlog:
		sessiongen(Flog, &r->d, s);
		break;
	case Qmodules:
		sessiongen(Fmodules, &r->d, s);
		break;
	case Qmodule:
		mkdirstat(&r->d, m->name, m->qmodule);
		break;
	case Qthreads:
		sessiongen(Fthreads, &r->d, s);
		break;
	case Qlpaobj:
		mkfilestat(&r->d, symb->name, symb->qsymbol, 0444);
		r->d.length = strlen(printval(symb->value));
		break;
	default:
		err = Enotyet;
	}

	respond(r, err);
}

static void
fsread(Req *r)
{
	char buf[256];
	Enumeration *e = nil;
	Aux *aux = r->fid->aux;
	char *err = nil;

	switch(QID_TYPE(r->fid->qid)){
	case Qroot:
		e = enumsessions();
		dirread9p(r, roottreegen, e);
		break;
	case Qsession:
		dirread9p(r, sessiongen, aux->session);
		break;
	case Qmodules:
		e = enummodules(aux->session);
		dirread9p(r, modulesgen, e);
		break;
	case Qmodule:
		e = enumsymbols(aux->module->symtab, 0);
		dirread9p(r, symbolsgen, e);
		break;
	case Qnew:
		snprint(buf, sizeof(buf), "%uld\n", r->fid->qid.vers);
		readstr(r, buf);
		break;
	case Qcons:
		sessioncons(r);
		break;
	case Qlog:
		sessionlog(r);
		break;
	case Qlpaobj:
		err = symbolrw(r);
		break;
	default:
		err = Enotyet;
		break;
	}
	if(e != nil)
		setroot(e, 0);
	respond(r, err);
}

static void
fswrite(Req *r)
{
	char *err = nil;

	switch(QID_TYPE(r->fid->qid)){
	case Qctl:
		sessionctl(r);
		break;
	case Qcons:
		sessioncons(r);
		break;
	case Qlpaobj:
		err = symbolrw(r);
		break;
	default:
		err = Ewrite;
	}
	respond(r, err);
}

static void
fscreate(Req *r)
{
	char *err = nil;
	Aux *aux = r->fid->aux;
	Module *m = aux->module;
	uvlong symid;
	Symbol *symb;

	switch(QID_TYPE(r->fid->qid)){
	case Qmodule: /* create a new symbol */
		symid = sym(m->symtab, r->ifcall.name);
		if(symid == -1)
			err = Einvalidname;
		symb = symptr(m->symtab, symid);
		aux->symbol = symb;
		r->fid->qid = r->ofcall.qid = symb->qsymbol;
		break;
	default:
		err = Ecreate;
	}
	respond(r, err);
}

static void
fsremove(Req *r)
{
	char *err;

	switch(QID_TYPE(r->fid->qid)){
	case Qlpaobj:
		err = Enotyet;
		break;
	default:
		err = Eremove;
	}
	respond(r, err);
}

static void
fsdestroyfid(Fid *fid)
{
	if(fid->aux)
		setroot(fid->aux, 0);
}

static Srv fs = {
	.attach = fsattach,
	.walk1 = fswalk1,
	.clone = fsclone,
	.open = fsopen,
	.stat = fsstat,
	.read = fsread,
	.write = fswrite,
	.create = fscreate,
	.remove = fsremove,

	.destroyfid = fsdestroyfid,
};

void
startfs(char *name, char *mtpt)
{
	char *srvname = smprint("/srv/%s", name);
	if(access(srvname, AREAD|AWRITE) == 0){
		int fd = open(srvname, ORDWR);
		if(fd < 0)
			sysfatal("open lpa service");
		if(mount(fd, -1, mtpt, MREPL, "") != 0)
			sysfatal("mount lpa service");
		return;
	}

	dataspecs[DataAux].size = sizeof(Aux);

	username = getuser();
	qroot = mkqid(Qroot, 0);
	qnew = mkqid(Qnew, 0);

	threadpostmountsrv(&fs, name, mtpt, MREPL);
}