#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
static void fsattach(Req *);
static char* fsclone(Fid *, Fid *);
static char* fswalk(Fid *, char *, Qid *);
static void fsopen(Req *);
static void fscreate(Req *);
static void fsremove(Req *);
static void fsread(Req *);
static void fswrite(Req *);
static void fsstat(Req *);
static void fswstat(Req *);
static void fsflush(Req *);
static void fsclunk(Fid *);
static void fsend(Srv *);
enum {
Qroot = 0,
NQIDS,
STACK = 2048,
};
int nextqid = NQIDS;
int ngames = 0;
typedef struct Qinfo Qinfo;
typedef struct GRef GRef;
typedef struct Aux Aux;
typedef struct Move Move;
typedef struct Player Player;
typedef struct Game Game;
typedef struct Reqlist Reqlist;
typedef struct Popmsg Popmsg;
struct Qinfo{
Qid qid, pqid;
char *name;
char *uid;
};
struct GRef{
GRef *next;
GRef *prev;
ulong fid;
};
struct Aux{
char *data;
Game *g;
Player *p;
Player *opp;
};
struct Move{
Move *next;
char *data;
};
struct Player{
Move *mtop, *mlast;
Move *mcur;
int dirty;
Qinfo;
char *gid; /* modifiable only for player files */
};
struct Game{
Game *next;
Game *prev;
Player *players[2]; /* black or white */
Qinfo;
GRef *ref;
int nrefs;
int rm;
};
struct Reqlist{
Reqlist *next;
Reqlist *prev;
Req *r;
};
struct Popmsg{
int op;
void *data;
char *rdata;
};
Reqlist *rtop, *rlast;
Game *groot;
Channel *reqchan;
int gofd; /* srvfd of main service */
Srv gosrv = {
.attach= fsattach,
.clone= fsclone,
.walk1= fswalk,
.open= fsopen,
.create= fscreate,
.remove= fsremove,
.read= fsread,
.write= fswrite,
.stat= fsstat,
.wstat= fswstat,
.flush= fsflush,
.destroyfid= fsclunk,
.end= fsend,
/*
.auth= fsauth,
*/
};
static void
addgame(char *name, char *uid)
{
Player **p;
int i;
if(groot == nil){
groot= emalloc9p(sizeof(*groot));
groot->next = nil;
groot->prev = nil;
} else {
groot->prev = emalloc9p(sizeof(*groot->prev));
groot->prev->next = groot;
groot = groot->prev;
groot->prev = nil;
}
groot->qid = (Qid){nextqid, 0, QTDIR};
groot->name = estrdup9p(name);
groot->uid = estrdup9p(uid);
groot->nrefs = 0;
ngames++;
nextqid++;
p = groot->players;
for(i=0; i < 2; i++){
p[i] = emalloc9p(sizeof(*p[i]));
p[i]->qid = (Qid){nextqid, 0, QTFILE};
p[i]->pqid = groot->qid;
p[i]->uid = estrdup9p(uid);
p[i]->gid = estrdup9p(uid);
if(!i){
p[i]->name = estrdup9p("W");
p[i]->dirty = 1; /* B goes first */
}
else
p[i]->name = estrdup9p("B");
nextqid++;
}
}
static void
gincref(Game *g, ulong fid)
{
if(!g->ref){
g->ref = emalloc9p(sizeof *g->ref);
g->ref->next = nil;
g->ref->prev = nil;
} else {
g->ref->prev = emalloc9p(sizeof *g->ref->next);
g->ref->prev->next = g->ref;
g->ref = g->ref->prev;
g->ref->prev = nil;
}
g->ref->fid = fid;
g->nrefs++;
}
static void
gdecref(Game *g, ulong fid)
{
GRef *top;
if(!g->nrefs)
return;
top = g->ref;
for(; g->ref; g->ref=g->ref->next){
if(g->ref->fid == fid)
break;
}
if(!g->ref){
g->ref = top;
return;
}
if(!g->ref->prev){
if(!g->ref->next){
free(g->ref);
g->ref = nil;
g->nrefs = 0;
return;
}
g->ref = g->ref->next;
free(g->ref->prev);
g->ref->prev = nil;
g->nrefs--;
return;
}
if(!g->ref->next){
g->ref = g->ref->prev;
free(g->ref->next);
g->ref->next = nil;
g->ref = top;
g->nrefs--;
return;
}
g->ref->prev->next = g->ref->next;
g->ref->next->prev = g->ref->prev;
free(g->ref);
g->ref = top;
g->nrefs--;
}
static void
rmmoves(Player *p)
{
Move *m, *mtop;
mtop = p->mtop;
for(m=mtop; m != nil;){
mtop = m->next;
free(m->data);
free(m);
m = mtop;
}
}
static void
rmplayers(Game *g)
{
Player **p;
int i;
p = g->players;
for(i=0; i<2; i++){
rmmoves(p[i]);
free(p[i]->uid);
free(p[i]->gid);
free(p[i]->name);
free(p[i]);
p[i] = nil;
}
}
static void
rmgame(Game *g)
{
ngames--;
rmplayers(g);
free(g->name);
g->name = nil;
free(g->uid);
g->uid = nil;
if(g == groot){
if(!g->next){
groot = nil;
return;
}
groot = g->next;
groot->prev = nil;
return;
}
if(!g->next){
g->prev->next = nil;
return;
}
g->prev->next = g->next;
g->next->prev = g->prev;
}
Game *
findgame(char *name, int path)
{
Game *gl;
if(name==nil && path < NQIDS)
return nil;
for(gl=groot; gl != nil; gl=gl->next){
if(name != nil)
if(strcmp(gl->name, name)==0)
return gl;
if(path >= NQIDS)
if(gl->qid.path == path)
return gl;
}
return nil;
}
static void
addmove(Player *p, char *s)
{
if(p->mtop == nil){
p->mtop = emalloc9p(sizeof(*p->mtop));
p->mlast = p->mtop;
} else {
p->mlast->next = emalloc9p(sizeof(*p->mlast->next));
p->mlast = p->mlast->next;
p->mlast->next = nil;
}
p->mlast->data = estrdup9p(s);
}
static void
reply(Req *r, char *data)
{
if(r == nil) /* unusual case where */
return; /* write flushes early */
if(data == nil){
respond(r, "interrupted");
return;
}
r->ofcall.data = data;
r->ofcall.count = strlen(data);
respond(r, nil);
}
static void
addreq(Req *r)
{
if(rtop == nil){
rtop = emalloc9p(sizeof(*rtop));
rtop->r = r;
rlast = rtop;
rlast->next = nil;
rlast->prev = nil;
return;
}
rlast->next = emalloc9p(sizeof(*rlast->next));
rlast->next->prev = rlast;
rlast = rlast->next;
rlast->r = r;
rlast->next = nil;
}
static void
remreq(Reqlist **rl)
{
Reqlist *tmp;
if(rl == nil)
return;
else if(*rl == nil)
return;
if(*rl==rtop){
if(rtop == rlast){
free(rtop);
*rl = rtop = rlast = nil;
return;
}
rtop=rtop->next;
free(rtop->prev);
rtop->prev = nil;
*rl = rtop;
return;
}
if(*rl==rlast){
*rl = (*rl)->prev;
free(rlast);
(*rl)->next = nil;
rlast = *rl;
return;
}
(*rl)->prev->next = (*rl)->next;
(*rl)->next->prev = (*rl)->prev;
tmp = *rl;
*rl = (*rl)->next;
free(tmp);
}
static void
popreqsop(Popmsg msg)
{
Reqlist *rl;
Req *r;
Qid *qh;
switch(msg.op){
case 0: /* reply to all reads */
while(rtop!=nil){
reply(rtop->r, msg.rdata);
remreq(&rtop);
}
break;
case 1: /* reply to one specified read */
if((r = msg.data) == nil)
break;
for(rl=rtop; rl!=nil; rl=rl->next){
if(rl->r == r){
reply(rl->r, msg.rdata);
remreq(&rl);
break;
}
}
break;
case 2: /* reply to all reads on one file */
if((qh = msg.data) == nil)
break;
for(rl=rtop; rl!=nil;){
if(rl->r != nil && qh->path == rl->r->fid->qid.path){
reply(rl->r, msg.rdata);
remreq(&rl);
continue;
}
rl = rl->next;
}
free(msg.rdata);
break;
}
}
static void
popreqsproc(void *)
{
Popmsg msg;
threadsetname("popreads"); /* indeed, he does */
while(recv(reqchan, &msg))
popreqsop(msg);
}
static void
fsattach(Req *r)
{
Aux *a;
if(!r->fid->uid || strcmp(r->fid->uid, "")==0){
respond(r, "must specify user name");
return;
}
a = emalloc9p(sizeof *a);
a->g = nil;
a->p = nil;
r->fid->aux = a;
r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
r->fid->qid = r->ofcall.qid;
respond(r, nil);
}
static char*
fsclone(Fid *ofid, Fid *fid)
{
Aux *a;
a = emalloc9p(sizeof *a);
*a = *(Aux*)ofid->aux;
fid->aux = a;
return nil;
}
static char*
fswalk(Fid *fid, char *name, Qid *qid)
{
Aux *a;
Game *gl;
int path;
a = fid->aux;
path = (int)fid->qid.path; /* without integral switches */
if(path == Qroot){ /* this is just shabby */
if(strcmp(name, "..")==0)
return nil;
else if((gl = findgame(name, path)) != nil){
fid->qid = gl->qid;
a->g = gl;
gincref(a->g, fid->fid);
}
else
return "path not found";
}
else if(a->g && !a->g->rm){ /* switch style */
if(path == a->g->qid.path){
if(strcmp(name, "W")==0){
a->p = a->g->players[0];
a->opp = a->g->players[1];
fid->qid = a->g->players[0]->qid;
}
else if(strcmp(name, "B")==0){
a->p = a->g->players[1];
a->opp = a->g->players[0];
fid->qid = a->g->players[1]->qid;
}
else if(strcmp(name, "..")==0)
fid->qid = (Qid){Qroot, 0, QTDIR};
else
return "path not found";
}
else if(a->p && path == a->p->qid.path){
if(strcmp(name, "..")==0)
fid->qid = a->p->pqid;
else
return "path not found";
}
else
return "path not found";
}
else
return "path not found";
*qid = fid->qid;
return nil;
}
static void
fsopen(Req *r)
{
Aux *a;
a = r->fid->aux;
if(a->p != nil && r->fid->qid.path == a->p->qid.path){
switch(r->ifcall.mode&OEXEC){ /* disregard higher bits */
case OREAD:
a->p->mcur = a->p->mtop;
break;
case ORDWR:
a->p->mcur = a->p->mtop;
/* follow through */
case OWRITE: /* save fswrite the drama */
if(strcmp(a->p->gid, r->fid->uid)){ /* chmod 464 */
respond(r, "access permission denied");
return;
}
break;
case OEXEC:
respond(r, "access permission denied");
return;
}
}
r->ofcall.qid = r->fid->qid;
r->ofcall.iounit = 10;
respond(r, nil);
}
static void
fscreate(Req *r)
{
if(!r->fid->uid){
respond(r, "guest permission denied");
return;
}
if(r->fid->qid.path != Qroot){
respond(r, "create prohibited");
return;
}
if(r->ifcall.perm&DMDIR){
if(!findgame(r->ifcall.name, -1)){
addgame(r->ifcall.name, r->fid->uid);
r->fid->qid = groot->qid;
r->ofcall.qid = r->fid->qid;
}
respond(r, nil);
return;
}
respond(r, "create prohibited"); /* else, rm condition */
}
static void
fsremove(Req *r)
{
Aux *a;
int path;
a = r->fid->aux;
path = r->fid->qid.path;
if(a->g && a->g->qid.path == path
&& strcmp(a->g->uid, r->fid->uid)==0)
{
if(a->g->nrefs){
a->g->rm = 1;
rmgame(a->g);
respond(r, nil);
return;
}
rmgame(a->g);
free(a->g);
respond(r, nil);
return;
}
respond(r, "only removal of directories is permitted");
}
int rootgen(int off, Dir *d, Game **game)
{
Game *g;
if(ngames <= off)
return -1;
g = *game;
memset(d, 0, sizeof *d);
d->atime = d->mtime = time(0);
if(g->uid){
d->uid = estrdup9p(g->uid);
d->gid = estrdup9p(g->uid);
}else{
d->uid = estrdup9p("igo");
d->gid = estrdup9p("igo");
}
d->muid = estrdup9p("");
d->name = estrdup9p(g->name);
d->mode = DMDIR|0755;
d->qid = g->qid;
*game = (*game)->next;
return 0;
}
int
gamegen(int off, Dir *d, Aux *a)
{
if(off > 1)
return -1;
memset(d, 0, sizeof *d);
d->atime = d->mtime = time(0);
if(a->g->players[off]->uid)
d->uid = estrdup9p(a->g->players[off]->uid);
else
d->uid = estrdup9p("igo");
if(a->g->players[off]->gid)
d->gid = estrdup9p(a->g->players[off]->gid);
else
d->gid = estrdup9p("igo");
d->muid = estrdup9p("");
d->name = estrdup9p(a->g->players[off]->name);
d->mode = DMAPPEND|0464;
d->qid.path = a->g->players[off]->qid.path;
d->qid.vers = 0;
d->qid.type = QTFILE;
return 0;
}
static void
fsread(Req *r)
{
Aux *a;
Game *g;
Move *mv;
int path;
path = r->fid->qid.path;
a = r->fid->aux;
if(a->p && path == a->p->qid.path){
mv = a->p->mcur;
if(mv != nil && mv->data != nil)
{
reply(r, mv->data);
a->p->mcur = mv->next;
return;
}
addreq(r);
return;
}
if(path == Qroot){
g = groot;
dirread9p(r, rootgen, &g);
} else
dirread9p(r, gamegen, a);
respond(r, nil);
}
static void
fswrite(Req *r)
{
Aux *a;
int path;
Qid *qh;
Popmsg hreq;
int count;
path = r->fid->qid.path;
qh = &(r->fid->qid);
count = r->ifcall.count;
a = r->fid->aux;
if(a->p && a->opp && path == a->p->qid.path)
{
if(a->p->dirty){
respond(r, "let opponent move");
return;
}
a->data = emalloc9p(count+1);
strncpy(a->data, r->ifcall.data, count);
a->data[count] = '\0'; /* in case of garbled data */
r->ofcall.count = strlen(a->data);
a->p->dirty = 1;
a->opp->dirty = 0;
addmove(a->p, a->data); /* β: in case Tclunk never comes */
respond(r, nil);
hreq = (Popmsg){2, qh, strdup(a->data)};
send(reqchan, &hreq);
free(a->data); /* see β */
a->data = nil;
return;
}
respond(r, nil);
}
static void
fsstat(Req *r)
{
Aux *a;
int path;
char *uid = nil;
char *gid = nil;
Dir *d;
d = &(r->d);
memset(d, 0, sizeof *d);
path = r->fid->qid.path;
d->qid = r->fid->qid;
a = r->fid->aux;
if(path == Qroot){
d->name = estrdup9p("/");
d->mode = DMDIR|0777;
}
else if(a->g && !a->g->rm){ /* switch style */
if(path == a->g->qid.path){
d->name = estrdup9p(a->g->name);
d->mode = DMDIR|0755;
if(a->g->uid)
uid = estrdup9p(a->g->uid);
gid = estrdup9p(a->g->uid);
} /* with completely unique */
else if(path == a->p->qid.path){ /* QIDs, only one of these */
d->name = estrdup9p(a->p->name); /* is possible */
if(a->p->gid)
gid = estrdup9p(a->p->gid);
if(a->p->uid){
uid = estrdup9p(a->p->uid);
d->mode = DMAPPEND|0464;
}
else
d->mode = DMAPPEND|0444;
}
} else {
respond(r, "path not found");
return;
}
if(!uid)
uid = estrdup9p("igo");
if(!gid)
gid = estrdup9p("igo");
d->atime = d->mtime = time(0);
d->uid = estrdup9p(uid);
d->gid = estrdup9p(gid);
d->muid = estrdup9p("");
free(uid);
free(gid);
respond(r, nil);
}
static void
fswstat(Req *r)
{
Aux *a;
int path;
a = r->fid->aux;
path = r->fid->qid.path;
if(a->g && a->g->rm){
respond(r, "path not found"); /* homogeny of messages */
return;
}
if(a->p && a->p->qid.path == path
&& strcmp(a->p->uid, r->fid->uid)==0
&& r->d.gid && r->d.gid[0])
{
if(a->p->gid && a->p->gid[0])
free(a->p->gid);
a->p->gid = estrdup9p(r->d.gid);
respond(r, nil);
return;
}
respond(r, "wstat prohibited");
}
static void
fsflush(Req *r)
{
Req *or;
int rtype, ftype;
Popmsg hreq;
or = r->oldreq;
rtype = or->ifcall.type;
ftype = or->fid->qid.type;
if(ftype == QTFILE && rtype == Tread){
hreq = (Popmsg){1, or, nil};
send(reqchan, &hreq);
}
else /* flush all else */
reply(or, nil);
respond(r, nil);
}
static void
fsclunk(Fid *f)
{
Aux *a;
a = f->aux;
if(a){
if(a->g && a->g->nrefs){
gdecref(a->g, f->fid);
}
if(a->g && a->g->rm && !a->g->nrefs){
free(a->g);
if(a->data)
free(a->data);
free(a);
return;
}
if(a->data)
free(a->data);
free(a);
}
}
/* if main service, then
* reply to all reads and
* close all threads
*/
static void
fsend(Srv *s)
{
Popmsg hreq;
if(s->srvfd != gofd)
return;
if(rtop != nil){
hreq = (Popmsg){0, nil, nil};
send(reqchan, &hreq);
}
threadexitsall(nil);
}
void
usage(void)
{
fprint(2, "usage: %s [-D] [-a addr] [-m mtpt] [-s srv]\n", argv0);
exits("usage");
}
void
threadmain(int argc, char **argv)
{
char *addr, *service;
char *mtpt;
Srv *s;
addr = nil;
service = nil;
mtpt = "/n/gofs";
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 'a':
addr = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
service = EARGF(usage());
break;
default:
usage();
break;
}ARGEND
reqchan = chancreate(sizeof(Popmsg), 0);
procrfork(popreqsproc, nil, STACK, RFNAMEG);
if(addr)
threadlistensrv(&gosrv, addr);
s = emalloc9p(sizeof *s);
*s = gosrv;
threadpostmountsrv(s, service, mtpt, MREPL|MCREATE);
gofd = s->srvfd;
threadexits(0);
}
|