#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"
enum
{
Qroot = 0,
Qctl,
Qdata,
QEND,
};
#define PATH(type, n) ((type)|((n)<<8))
#define TYPE(path) ((int)(path) & 0xFF)
#define NUM(path) ((uint)(path)>>8)
typedef struct Tab Tab;
struct Tab
{
char* name;
ulong mode;
} tab[] = {
"/", DMDIR|0555,
"ctl", 0666,
"data", 0444,
};
Conn conn;
static char* fillstat(Dir* d, uvlong path);
static int rootgen(int i, Dir* d, void* aux);
static void ctlread(Req* r);
static void ctlwrite(Req* r);
static void dataread(Req* r);
static void
fsattach(Req* r)
{
Conn* c;
c = &conn;
if(r->ifcall.aname && *r->ifcall.aname){
respond(r, "fsattach: invalid attach specifier");
return;
}
memset(c, 0, sizeof(Conn));
r->fid->qid.path = PATH(Qroot, 0);
r->fid->qid.type = QTDIR;
r->fid->qid.vers = 0;
r->ofcall.qid = r->fid->qid;
respond(r, nil);
}
static void
fsstat(Req *r)
{
respond(r, fillstat(&r->d, r->fid->qid.path));
}
static void
fsdestroyfid(Fid*)
{
Conn* c;
c = &conn;
free(c->datbuf);
c->datbuf = nil;
}
static void
fsopen(Req* r)
{
static int need[4] = { 4, 2, 6, 1 };
Conn* c;
Tab* t;
uvlong path;
int n, fd;
path = r->fid->qid.path;
c = &conn;
t = &tab[TYPE(path)];
n = need[r->ifcall.mode&3];
if((n&t->mode) != n){
respond(r, "fsopen: permission denied");
return;
}
switch(TYPE(path)){
default:
break;
case Qdata:
if((fd = gphrdial(c->host, c->net, c->service)) < 0){
c->error = "fsopen: dial failed";
respond(r, c->error);
return;
}
free(c->datbuf);
c->datbuf = nil;
if((c->n = gphrget(fd, c->selector, &c->datbuf)) < 0){
c->error = "fsopen: get failed";
respond(r, c->error);
return;
}
}
respond(r, nil);
}
static void
fsread(Req* r)
{
Conn* c;
c = &conn;
switch(TYPE(r->fid->qid.path)){
default:
c->error = "fsread: invalid file type";
respond(r, c->error);
return;
case Qroot:
dirread9p(r, rootgen, nil);
respond(r, nil);
break;
case Qctl:
ctlread(r);
break;
case Qdata:
dataread(r);
break;
}
}
static void
fswrite(Req* r)
{
static int need[4] = { 4, 2, 6, 1 };
Conn* c;
uvlong path;
path = r->fid->qid.path;
c = &conn;
switch(TYPE(path)){
default:
c->error = "fswrite: invalid file type";
respond(r, c->error);
return;
case Qctl:
ctlwrite(r);
break;
}
}
char*
fswalk1(Fid* fid, char* name, Qid* qid)
{
Conn* c;
uvlong path;
Tab* t;
int i;
path = fid->qid.path;
c = &conn;
if(!(fid->qid.type&QTDIR)){
c->error = "fswalk1: walk in non-directory";
return c->error;
}
if(strcmp(name, "..") == 0){
switch(TYPE(path)){
default:
c->error = "fswalk1: should not happen";
return c->error;
case Qroot:
qid->path = PATH(Qroot, 0);
qid->type = tab[Qroot].mode>>24;
fid->qid = *qid;
return nil;
}
}
for(i=TYPE(path)+1, t=&tab[i]; i<nelem(tab); i++, t++)
if(strcmp(name, t->name) == 0){
qid->path = PATH(i, NUM(path));
qid->type = t->mode>>24;
fid->qid = *qid;
return nil;
}
c->error = "fswalk1: cannot find directory entry";
return c->error;
}
static char*
fsclone(Fid *ofid, Fid *fid)
{
if(chatty9p) fprint(2, "fsclone: %d -> %d\n", TYPE(ofid->qid.path), TYPE(fid->qid.path));
return nil;
}
static char*
fillstat(Dir* d, uvlong path)
{
Tab* t;
t = &tab[TYPE(path)];
memset(d, 0, sizeof(Dir));
if((d->uid = strdup("gphr")) == nil)
goto statoom;
if((d->gid = strdup("gphr")) == nil)
goto statoom;
if((d->name = strdup(t->name)) == nil)
goto statoom;
d->qid.path = path;
d->qid.type = t->mode>>24;
d->mode = t->mode;
/* XXX time should be set to match last update (happens at open or write) */
d->atime = d->mtime = time(0);
return nil;
statoom:
free(d->uid);
free(d->gid);
free(d->name);
return "fillstat: out of memory";
}
static int
rootgen(int i, Dir* d, void*)
{
i += Qroot+1;
if(i >= QEND) return -1;
fillstat(d, i);
return 0;
}
static void
ctlread(Req* r)
{
Conn* c;
char buf[1024];
c = &conn;
snprint(buf, 1024, "addr\t%s!%s!%s\nselector\t%s\n", c->net, c->host, c->service, c->selector);
readstr(r, buf);
respond(r, nil);
}
static void
ctlwrite(Req* r)
{
Conn* c;
char *buf, *cmd, *arg;
c = &conn;
if(r->ifcall.count >= 1024){
c->error = "ctlwrite: message too long";
respond(r, c->error);
return;
}
if((buf = stredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count)) == nil){
c->error = "ctlwrite: out of memory";
respond(r, c->error);
return;
}
r->ofcall.count = r->ifcall.count;
if(r->ifcall.count >= 1 && buf[r->ifcall.count-1] == '\n')
buf[r->ifcall.count-1] = '\0';
cmd = buf;
arg = strpbrk(cmd, "\t ");
if(arg){
*arg++ = '\0';
arg += strspn(arg, "\t ");
}else
arg = "";
if(strcmp(cmd, "addr") == 0){
free(c->addrbuf);
c->addrbuf = buf;
c->net = nil;
c->host = nil;
c->service = nil;
if(strncmp(arg, "tcp!", 4) == 0){
arg[3] = '\0';
c->net = arg;
arg+=4;
}else{
c->net = "tcp";
}
c->host = arg;
c->service = strpbrk(arg, "!");
if(c->service) *c->service++ = '\0';
else c->service = "70";
if(chatty9p) fprint(2, "net: %s, host: %s, service: %s", c->net, c->host, c->service);
}else if(strcmp(cmd, "selector") == 0){
free(c->selbuf);
c->selbuf = buf;
c->selector = arg;
if(chatty9p) fprint(2, "selector: %s", c->selector);
}else{
c->error = "ctlwrite: invalid command";
respond(r, c->error);
return;
}
respond(r, nil);
}
static void
dataread(Req* r)
{
Conn* c;
long count, offset;
c = &conn;
count = r->ifcall.count;
offset = r->ifcall.offset;
offset = offset > c->n ? c->n : offset;
count = offset+count > c->n ? c->n-offset : count;
r->ifcall.count = count;
r->ifcall.offset = offset;
readbuf(r, c->datbuf, c->n);
c->offset = offset;
respond(r, nil);
}
Srv fs = {
.attach = fsattach,
.open = fsopen,
.read = fsread,
.write = fswrite,
.stat = fsstat,
.walk1 = fswalk1,
.clone = fsclone,
.destroyfid = fsdestroyfid,
};
|