ref: f801657f77f3923ec2388c25bdcb036c8019ba89
dir: /prog/devicefs.c/
#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;
}