Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/8021x/v212/fs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"

typedef struct Aux Aux;
struct Aux {
	int off;
};

typedef struct Fs Fs;
struct Fs {
	long inittime;
	Netlog log;
	Netlog tlslog;
};

static Fs theFs;
static char sbuf[1024];
static char tbuf[1024];


typedef struct Tab Tab;
struct Tab
{
	char *name;
	ulong mode;
	uchar type;
	
	// for directories: entries
	int begin;
	int end;

	int parent;
};

Tab tab[] =
{
/*			name	mode		type		beg	end		parent */
/* Qroot */	"/",		DMDIR|0555,	QTDIR,	Qdir, Qdir+1,	Qroot,

/* in Qroot: */
/* Qdir */		"8021x",	DMDIR|0555,	QTDIR,	Qctl,	Qn,		Qroot,

/* in Qdir: */
/* Qctl */		"ctl",		0666,		0,		0,	0,		Qdir,
/* Qkeys */	"keys",	0444,		0,		0,	0,		Qdir,
/* Qlog */		"log",	0444,		0,		0,	0,		Qdir,
/* Qnote */	"note",	0444,		0,		0,	0,		Qdir,
/* Qstats */	"stats",	0444,		0,		0,	0,		Qdir,
/* Qstatus */	"status",	0444,		0,		0,	0,		Qdir,
/* Qtslog */	"tlslog",	0444,		0,		0,	0,		Qdir,

};
#define PATH(type, n)		((type)|((n)<<8))
#define TYPE(path)			((int)(path) & 0xFF)
#define NUM(path)			((uint)(path)>>8)

Channel *fsreqchan;			/* chan(Req*) */
Channel *fsreqwaitchan;		/* chan(nil) */
Channel *fsclunkchan;		/* chan(Fid*) */
Channel *fsclunkwaitchan;	/* chan(nil) */

enum
{
	Closed,
	Open,
};

char *statestr[] = {
	"Closed",
	"Open",
};

typedef struct Reader Reader;
struct Reader
{
	int ref;
	int state;
	int num;
	Req *rq;
	Req **erq;
	int qid;
};


int nreader;
Reader **reader;

static int newreader(void);
static void teardownreader(Netlog*, Reader*);
static void queuereq(Reader *, Req *);

static int
fillstat(ulong qid, Dir *d)
{
	Tab *t;
	ReadBuf *b;

	if (qid < 0 || qid >= Qn)
		return 0;

	memset(d, 0, sizeof(Dir));
	d->uid = "8021x";
	d->gid = "8021x";
	d->muid = "";
	d->qid = (Qid){qid, 0, 0};
	d->atime = time(0);
	d->mtime = theFs.inittime;

	t = &tab[qid];
	d->name = t->name;
	d->mode = t->mode;
	d->qid.type = t->type;

	switch(qid){
	case Qstatus:
	case Qkeys:
	case Qnote:
		d->mtime = getChangetime(qid);
		break;
	}

	switch(qid){
	case Qkeys:
		b = getKeysbuf();
		d->length = b->ndata;
		break;
	case Qnote:
		b = getNotesbuf();
		d->length = b->ndata;
		break;
	case Qstatus:
		getPAEStatus(sbuf, sizeof(sbuf));
		d->length = strlen(sbuf);
		break;
	}

	return 1;
}


static void
fsattach(Req *r)
{
	r->fid->qid = (Qid){Qroot, 0, QTDIR};
	r->ofcall.qid = r->fid->qid;
	r->fid->aux = emalloc9p(sizeof(Aux));
	respond(r, nil);
}

static char*
fsclone(Fid *old, Fid *new)
{
	Aux *na;

	na = emalloc9p(sizeof(Aux));
	*na = *((Aux*)old->aux);
	new->aux = na;
	return nil;
}

static int
lookupqid(char *name, Tab *dir)
{
	Tab *t, *b, *e;

	if (dir == nil)
		return -1;

	if(strcmp(name, "..") == 0)
		return dir->parent;

	b = &tab[dir->begin];
	e = &tab[dir->end];
	for (t = b; t < e; t++)
		if (strcmp(name, t->name) == 0)
			return t - &tab[0];

	return -1;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	ulong p;
	int i;
	Tab *t;

	p = fid->qid.path;

	if (p < 0 || p >= Qn)
		return "no such file or directory";

	t = &tab[p];
	if ((t->type & QTDIR) != QTDIR)
		return "walk in non-directory";
		
	i = lookupqid(name, t);
	if (i >= 0){
		*qid = (Qid){i, 0, tab[i].type};
		return nil;
	}

	return "no such file or directory";
}

static void
readctl(Req *r)
{
	char s[1024];

	sprint(s, "802.1x ctl bla\n");
	readstr(r, s);
}


static void
fsread(Req *r)
{
	int j, n;
	Fid *fid;
	vlong offset;
	uchar *p, *ep;
	void *buf;
	long count;
	Dir d;
	Aux *a;
	ReadBuf *b;
	ulong path;

	fid = r->fid;
	path = fid->qid.path;
	offset = r->ifcall.offset;
	count = r->ifcall.count;
	buf = r->ofcall.data;

	switch(TYPE(path)) {
	case Qroot:
		p = buf;
		ep = p+count;
		if(offset == 0) {
			if(fillstat(Qdir, &d) && (n = convD2M(&d, p, ep-p)) > BIT16SZ)
				p += n;
			r->ofcall.count = p-(uchar*)buf;
		}
		respond(r, nil);
		return;

	case Qdir:
		p = buf;
		ep = p+count;
		a = fid->aux;
		if(offset == 0)
			a->off = 2;	/* skip root and Qdir */

		for(j=a->off; j<Qn; j++) {
			if(fillstat(j, &d)) {
				if((n = convD2M(&d, p, ep-p)) <= BIT16SZ)
					break;
				p += n;
			}
		}
		a->off = j;

		r->ofcall.count = p-(uchar*)buf;
		respond(r, nil);
		return;

	case Qkeys:
		b = getKeysbuf();
		readbuf(r, b->data, b->ndata);
		respond(r, nil);
		return;

	case Qlog:
		a = fid->aux;
		r->ofcall.count = netlogread(&theFs.log, buf, count, offset, &a->off);
		if (r->ofcall.count == 0)
			queuereq(reader[NUM(path)], r);
		else
			respond(r, nil);
		return;

	case Qtlslog:
		a = fid->aux;
		r->ofcall.count = netlogread(&theFs.tlslog, buf, count, offset, &a->off);
		if (r->ofcall.count == 0)
			queuereq(reader[NUM(path)], r);
		else
			respond(r, nil);
		return;

	case Qnote:
		b = getNotesbuf();
		readbuf(r, b->data, b->ndata);
		respond(r, nil);
		return;

	case Qstats:
		r->ofcall.count = 0;
		respond(r, nil);
		return;

	case Qstatus:
		getPAEStatus(sbuf, sizeof(sbuf));
		readstr(r, sbuf);
		respond(r, nil);
		return;

	case Qctl:
		readctl(r);
		respond(r, nil);
		return;
	}
}

static char*
writectl(void *v, long count)
{
	USED(v);
	USED(count);
	return nil;
}

static void
fswrite(Req *r)
{
	Fid *fid;

	fid = r->fid;
	r->ofcall.count = r->ifcall.count;
	if(fid->qid.path == Qctl) {
		respond(r, writectl(r->ifcall.data, r->ifcall.count));
		return;
	}

	respond(r, "permission denied");
	return;
}

static void
fsstat(Req *r)
{
	if (fillstat(TYPE((ulong)r->fid->qid.path), &r->d)) {
		r->d.name = estrdup9p(r->d.name);
		r->d.uid = estrdup9p(r->d.uid);
		r->d.gid = estrdup9p(r->d.gid);
		r->d.muid = estrdup9p(r->d.muid);
		respond(r, nil);
	} else
		respond(r, "no such file or directory");
}

static void
fsopen(Req *r)
{
	int omode;
	Fid *fid;
	ulong path;
	int n;
	Aux *a;
	Reader *c;

	fid = r->fid;
	omode = r->ifcall.mode;
	r->ofcall.qid = (Qid){fid->qid.path, 0, fid->qid.type};

	switch((ulong)fid->qid.path){
	case Qctl:
		if(omode&~(OTRUNC|OREAD|OWRITE|ORDWR))
			respond(r, "permission denied");
		else
			respond(r, nil);
		return;

	case Qlog:
		if(omode != OREAD)
			respond(r, "permission denied");
		n = newreader();
		c = reader[n];
		c->qid = Qlog;
		c->state = Open;
		path = PATH(Qlog, n);
		netlogopen(&theFs.log);
		r->fid->qid.path = path;
		r->ofcall.qid.path = path;
		a = r->fid->aux;
		a->off = 0;
		if(chatty9p)
			fprint(2, "open log => path=%lux\n", path);
		reader[NUM(path)]->ref++;
		respond(r, nil);
		return;

	case Qtlslog:
		if(omode != OREAD)
			respond(r, "permission denied");
		n = newreader();
		c = reader[n];
		c->qid = Qtlslog;
		c->state = Open;
		path = PATH(Qtlslog, n);
		netlogopen(&theFs.tlslog);
		r->fid->qid.path = path;
		r->ofcall.qid.path = path;
		a = r->fid->aux;
		a->off = 0;
		debugTLS++;
		if(chatty9p)
			fprint(2, "open tlslog => path=%lux   debugTLS=%d\n", path, debugTLS);
		reader[NUM(path)]->ref++;
		respond(r, nil);
		return;

	case Qroot:
	case Qdir:
	case Qkeys:
	case Qnote:
	case Qstats:
	case Qstatus:
		if(omode == OREAD)
			respond(r, nil);
		else
			respond(r, "permission denied");
		return;

	default:
		respond(r, "no such file or directory");
	}
}

static int
newreader(void)
{
	int i;
	Reader *c;

	for(i=0; i<nreader; i++)
		if(reader[i]->ref==0 && reader[i]->state == Closed)
			return i;

	if(nreader%16 == 0)
		reader = erealloc9p(reader, (nreader+16)*sizeof(reader[0]));

	c = emalloc9p(sizeof(Reader));
	memset(c, 0, sizeof(*c));
	c->num = nreader;
	reader[nreader++] = c;
	return c->num;
}

static void queuereq(Reader *c, Req *r)
{
	if(c->rq==nil)
		c->erq = &c->rq;
	*c->erq = r;
	r->aux = nil;
	c->erq = (Req**)&r->aux;
}

Req*
findreq(Reader *c, Req *r)
{
	Req **l;

	for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
		if(*l == r){
			*l = r->aux;
			if(*l == nil)
				c->erq = l;
			return r;
		}
	}
	return nil;
}

void
closereader(Netlog *l, Reader *c)
{
	if(--c->ref)
		return;

	if(c->rq != nil)
		logfatal(0, "ref count reached zero with requests pending (BUG)");

	if(c->state != Closed)
		teardownreader(l, c);
}

static void
teardownreader(Netlog *l, Reader *c)
{
	c->state = Closed;
	netlogclose(l);
}

static void
fsflush(Req *r)
{
	int i;

	for(i=0; i<nreader; i++)
		if(findreq(reader[i], r->oldreq))
			respond(r->oldreq, "interrupted");
	respond(r, nil);
}

void
handlereader(Netlog *l, int qid)
{
	Req *r;
	int i;
	void *buf;
	long count;
	vlong offset;
	Reader *c;
	Aux *a;

//	syslog(0, logname, "handlereader %d nreader=%d", qid, nreader);
	for(i=0; i<nreader; i++) {
		c = reader[i];
//		syslog(0, logname, "handlereader %d:%d state=%s qid=%d", qid, i, statestr[c->state], c->qid);
		if (c->state == Closed)
			continue;
		else if (c->qid == qid) {
			r = c->rq;
			if(r != nil){
				if(r->aux != nil)
					logfatal(0, "more than one outstanding reader request (BUG)");
				offset = r->ifcall.offset;
				count = r->ifcall.count;
				buf = r->ofcall.data;
				a = r->fid->aux;
				r->ofcall.count = netlogread(l, buf, count, offset, &a->off);
				if (r->ofcall.count != 0) {
					c->rq = nil;
					respond(r, nil);
				}
			}
		}
	}
}

static void
fsnetproc(void*)
{
	ulong path;
	Alt a[5];
	Fid *fid;
	Req *r;

	threadsetname("fsthread");

	a[0].op = CHANRCV;
	a[0].c = fsclunkchan;
	a[0].v = &fid;
	a[1].op = CHANRCV;
	a[1].c = fsreqchan;
	a[1].v = &r;
	a[2].op = CHANRCV;
	a[2].c = theFs.log.ping;
	a[2].v = nil;
	a[3].op = CHANRCV;
	a[3].c = theFs.tlslog.ping;
	a[3].v = nil;
	a[4].op = CHANEND;
	a[4].op = CHANEND;

	for(;;){
		switch(alt(a)){
		case 0:
			path = fid->qid.path;
			if(fid->aux) {
				free(fid->aux);
				fid->aux = 0;
			}
			switch(TYPE(path)){
			case Qlog:
				if(chatty9p)
					fprint(2, "close log => path=%lux omode=%d \n", path, fid->omode);
				if(fid->omode != -1)
					closereader(&theFs.log, reader[NUM(path)]);
				break;
			case Qtlslog:
				if(chatty9p)
					fprint(2, "close tlslog => path=%lux omode=%d \n", path, fid->omode);
				if(fid->omode != -1)
					closereader(&theFs.tlslog, reader[NUM(path)]);
				break;
			}
			sendp(fsclunkwaitchan, nil);
			break;
		case 1:
			switch(r->ifcall.type){
			case Tattach:
				fsattach(r);
				break;
			case Topen:
				fsopen(r);
				break;
			case Tread:
				fsread(r);
				break;
			case Twrite:
				fswrite(r);
				break;
			case Tstat:
				fsstat(r);
				break;
			case Tflush:
				fsflush(r);
				break;
			default:
				respond(r, "bug in fsthread");
				break;
			}
			sendp(fsreqwaitchan, 0);
			break;
		case 2:
			handlereader(&theFs.log, Qlog);
			break;
		case 3:
			handlereader(&theFs.tlslog, Qtlslog);
			break;
		}
	}
}

static void
fssend(Req *r)
{
	sendp(fsreqchan, r);
	recvp(fsreqwaitchan);	/* avoids need to deal with spurious flushes */
}

static void
fsdestroyfid(Fid *fid)
{
	sendp(fsclunkchan, fid);
	recvp(fsclunkwaitchan);
}

static void
takedown(Srv*)
{
	threadexitsall("done");
}

Srv fs = {
.attach=	fsattach,
.clone=	fsclone,
.walk1=	fswalk1,
.open=	fsopen,
.read=	fsread,
.write=	fswrite,
.stat=	fsstat,
.flush=		fssend,
.destroyfid=	fsdestroyfid,
.end=		takedown,
};

void
initFs(void)
{
	theFs.inittime = time(0);

	fsreqchan = chancreate(sizeof(Req*), 0);
	fsreqwaitchan = chancreate(sizeof(void*), 0);
	fsclunkchan = chancreate(sizeof(Fid*), 0);
	fsclunkwaitchan = chancreate(sizeof(void*), 0);

	netloginit(&theFs.log, "log", 4*1024);
	netloginit(&theFs.tlslog, "tlslog", Nlog);

//	syslog(0, logname, "initFs &theFs.log=%p &theFs.tlslog=%p", &theFs.log, &theFs.tlslog);

	procrfork(fsnetproc, nil, 8192, RFNAMEG|RFNOTEG);
}

int
loglog(char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = netlog(&theFs.log, fmt, arg);
	va_end(arg);
	return n;
}

int
tlslog(char *fmt, ...)
{
	int n;
	va_list arg;

	va_start(arg, fmt);
	n = netlog(&theFs.tlslog, fmt, arg);
	va_end(arg);
	return n;
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.