2011-03-30 12:46:40 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <bio.h>
|
|
|
|
#include <regexp.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <auth.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <plumb.h>
|
|
|
|
#include "plumber.h"
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
2012-03-09 08:11:12 +00:00
|
|
|
Stack = 16*1024
|
2011-03-30 12:46:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct Dirtab Dirtab;
|
|
|
|
typedef struct Fid Fid;
|
|
|
|
typedef struct Holdq Holdq;
|
|
|
|
typedef struct Readreq Readreq;
|
|
|
|
typedef struct Sendreq Sendreq;
|
|
|
|
|
|
|
|
struct Dirtab
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
uchar type;
|
|
|
|
uint qid;
|
|
|
|
uint perm;
|
|
|
|
int nopen; /* #fids open on this port */
|
|
|
|
Fid *fopen;
|
|
|
|
Holdq *holdq;
|
|
|
|
Readreq *readq;
|
|
|
|
Sendreq *sendq;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Fid
|
|
|
|
{
|
|
|
|
int fid;
|
|
|
|
int busy;
|
|
|
|
int open;
|
|
|
|
int mode;
|
|
|
|
Qid qid;
|
|
|
|
Dirtab *dir;
|
|
|
|
long offset; /* zeroed at beginning of each message, read or write */
|
|
|
|
char *writebuf; /* partial message written so far; offset tells how much */
|
|
|
|
Fid *next;
|
|
|
|
Fid *nextopen;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Readreq
|
|
|
|
{
|
|
|
|
Fid *fid;
|
|
|
|
Fcall *fcall;
|
|
|
|
uchar *buf;
|
|
|
|
Readreq *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Sendreq
|
|
|
|
{
|
|
|
|
int nfid; /* number of fids that should receive this message */
|
|
|
|
int nleft; /* number left that haven't received it */
|
|
|
|
Fid **fid; /* fid[nfid] */
|
|
|
|
Plumbmsg *msg;
|
|
|
|
char *pack; /* plumbpack()ed message */
|
|
|
|
int npack; /* length of pack */
|
|
|
|
Sendreq *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Holdq
|
|
|
|
{
|
|
|
|
Plumbmsg *msg;
|
|
|
|
Holdq *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct /* needed because incref() doesn't return value */
|
|
|
|
{
|
|
|
|
Lock;
|
|
|
|
int ref;
|
|
|
|
} rulesref;
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
DEBUG = 0,
|
|
|
|
NDIR = 50,
|
|
|
|
Nhash = 16,
|
|
|
|
|
|
|
|
Qdir = 0,
|
|
|
|
Qrules = 1,
|
|
|
|
Qsend = 2,
|
|
|
|
Qport = 3,
|
|
|
|
NQID = Qport
|
|
|
|
};
|
|
|
|
|
|
|
|
static Dirtab dir[NDIR] =
|
|
|
|
{
|
|
|
|
{ ".", QTDIR, Qdir, 0500|DMDIR },
|
|
|
|
{ "rules", QTFILE, Qrules, 0600 },
|
|
|
|
{ "send", QTFILE, Qsend, 0200 },
|
|
|
|
};
|
|
|
|
static int ndir = NQID;
|
|
|
|
|
|
|
|
static int srvfd;
|
|
|
|
static int srvclosefd; /* rock for end of pipe to close */
|
|
|
|
static int clockfd;
|
|
|
|
static int clock;
|
|
|
|
static Fid *fids[Nhash];
|
|
|
|
static QLock readlock;
|
|
|
|
static QLock queue;
|
|
|
|
static char srvfile[128];
|
|
|
|
static int messagesize = 8192+IOHDRSZ; /* good start */
|
|
|
|
|
|
|
|
static void fsysproc(void*);
|
|
|
|
static void fsysrespond(Fcall*, uchar*, char*);
|
|
|
|
static Fid* newfid(int);
|
|
|
|
|
|
|
|
static Fcall* fsysflush(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysversion(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysauth(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysattach(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysopen(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysread(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysremove(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsysstat(Fcall*, uchar*, Fid*);
|
|
|
|
static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
|
|
|
|
|
|
|
|
Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) =
|
|
|
|
{
|
|
|
|
[Tflush] = fsysflush,
|
|
|
|
[Tversion] = fsysversion,
|
|
|
|
[Tauth] = fsysauth,
|
|
|
|
[Tattach] = fsysattach,
|
|
|
|
[Twalk] = fsyswalk,
|
|
|
|
[Topen] = fsysopen,
|
|
|
|
[Tcreate] = fsyscreate,
|
|
|
|
[Tread] = fsysread,
|
|
|
|
[Twrite] = fsyswrite,
|
|
|
|
[Tclunk] = fsysclunk,
|
|
|
|
[Tremove]= fsysremove,
|
|
|
|
[Tstat] = fsysstat,
|
|
|
|
[Twstat] = fsyswstat,
|
|
|
|
};
|
|
|
|
|
|
|
|
char Ebadfcall[] = "bad fcall type";
|
|
|
|
char Eperm[] = "permission denied";
|
|
|
|
char Enomem[] = "malloc failed for buffer";
|
|
|
|
char Enotdir[] = "not a directory";
|
|
|
|
char Enoexist[] = "plumb file does not exist";
|
|
|
|
char Eisdir[] = "file is a directory";
|
|
|
|
char Ebadmsg[] = "bad plumb message format";
|
|
|
|
char Enosuchport[] ="no such plumb port";
|
|
|
|
char Enoport[] = "couldn't find destination for message";
|
|
|
|
char Einuse[] = "file already open";
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add new port. A no-op if port already exists or is the null string
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
addport(char *port)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if(port == nil)
|
|
|
|
return;
|
|
|
|
for(i=NQID; i<ndir; i++)
|
|
|
|
if(strcmp(port, dir[i].name) == 0)
|
|
|
|
return;
|
|
|
|
if(i == NDIR){
|
|
|
|
fprint(2, "plumb: too many ports; max %d\n", NDIR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ndir++;
|
|
|
|
dir[i].name = estrdup(port);
|
|
|
|
dir[i].qid = i;
|
|
|
|
dir[i].perm = 0400;
|
|
|
|
nports++;
|
|
|
|
ports = erealloc(ports, nports*sizeof(char*));
|
|
|
|
ports[nports-1] = dir[i].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ulong
|
|
|
|
getclock(void)
|
|
|
|
{
|
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
seek(clockfd, 0, 0);
|
|
|
|
read(clockfd, buf, sizeof buf);
|
|
|
|
return atoi(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
startfsys(void)
|
|
|
|
{
|
|
|
|
int p[2], fd;
|
|
|
|
|
|
|
|
fmtinstall('F', fcallfmt);
|
|
|
|
clockfd = open("/dev/time", OREAD|OCEXEC);
|
|
|
|
clock = getclock();
|
|
|
|
if(pipe(p) < 0)
|
|
|
|
error("can't create pipe: %r");
|
|
|
|
/* 0 will be server end, 1 will be client end */
|
|
|
|
srvfd = p[0];
|
|
|
|
srvclosefd = p[1];
|
|
|
|
sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
|
|
|
|
if(putenv("plumbsrv", srvfile) < 0)
|
|
|
|
error("can't write $plumbsrv: %r");
|
|
|
|
fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
|
|
|
|
if(fd < 0)
|
|
|
|
error("can't create /srv file: %r");
|
|
|
|
if(fprint(fd, "%d", p[1]) <= 0)
|
|
|
|
error("can't write /srv/file: %r");
|
|
|
|
/* leave fd open; ORCLOSE will take care of it */
|
|
|
|
|
|
|
|
procrfork(fsysproc, nil, Stack, RFFDG);
|
|
|
|
|
|
|
|
close(p[0]);
|
|
|
|
if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
|
|
|
|
error("can't mount /mnt/plumb: %r");
|
|
|
|
close(p[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fsysproc(void*)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
Fcall *t;
|
|
|
|
Fid *f;
|
|
|
|
uchar *buf;
|
|
|
|
|
|
|
|
close(srvclosefd);
|
|
|
|
srvclosefd = -1;
|
|
|
|
t = nil;
|
|
|
|
for(;;){
|
|
|
|
buf = malloc(messagesize); /* avoid memset of emalloc */
|
|
|
|
if(buf == nil)
|
|
|
|
error("malloc failed: %r");
|
|
|
|
qlock(&readlock);
|
2013-06-16 04:26:31 +00:00
|
|
|
while((n = read9pmsg(srvfd, buf, messagesize)) == 0)
|
|
|
|
;
|
|
|
|
if(n < 0)
|
2011-03-30 12:46:40 +00:00
|
|
|
threadexitsall("unmounted");
|
|
|
|
if(readlock.head == nil) /* no other processes waiting to read; start one */
|
|
|
|
proccreate(fsysproc, nil, Stack);
|
|
|
|
qunlock(&readlock);
|
|
|
|
if(t == nil)
|
|
|
|
t = emalloc(sizeof(Fcall));
|
|
|
|
if(convM2S(buf, n, t) != n)
|
|
|
|
error("convert error in convM2S");
|
|
|
|
if(DEBUG)
|
|
|
|
fprint(2, "<= %F\n", t);
|
|
|
|
if(fcall[t->type] == nil)
|
|
|
|
fsysrespond(t, buf, Ebadfcall);
|
|
|
|
else{
|
|
|
|
if(t->type==Tversion || t->type==Tauth)
|
|
|
|
f = nil;
|
|
|
|
else
|
|
|
|
f = newfid(t->fid);
|
|
|
|
t = (*fcall[t->type])(t, buf, f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fsysrespond(Fcall *t, uchar *buf, char *err)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if(err){
|
|
|
|
t->type = Rerror;
|
|
|
|
t->ename = err;
|
|
|
|
}else
|
|
|
|
t->type++;
|
|
|
|
if(buf == nil)
|
|
|
|
buf = emalloc(messagesize);
|
|
|
|
n = convS2M(t, buf, messagesize);
|
|
|
|
if(n < 0)
|
|
|
|
error("convert error in convS2M");
|
|
|
|
if(write(srvfd, buf, n) != n)
|
|
|
|
error("write error in respond");
|
|
|
|
if(DEBUG)
|
|
|
|
fprint(2, "=> %F\n", t);
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
Fid*
|
|
|
|
newfid(int fid)
|
|
|
|
{
|
|
|
|
Fid *f, *ff, **fh;
|
|
|
|
|
|
|
|
qlock(&queue);
|
|
|
|
ff = nil;
|
|
|
|
fh = &fids[fid&(Nhash-1)];
|
|
|
|
for(f=*fh; f; f=f->next)
|
|
|
|
if(f->fid == fid)
|
|
|
|
goto Return;
|
|
|
|
else if(ff==nil && !f->busy)
|
|
|
|
ff = f;
|
|
|
|
if(ff){
|
|
|
|
ff->fid = fid;
|
|
|
|
f = ff;
|
|
|
|
goto Return;
|
|
|
|
}
|
|
|
|
f = emalloc(sizeof *f);
|
|
|
|
f->fid = fid;
|
|
|
|
f->next = *fh;
|
|
|
|
*fh = f;
|
|
|
|
Return:
|
|
|
|
qunlock(&queue);
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint
|
|
|
|
dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
|
|
|
|
{
|
|
|
|
Dir d;
|
|
|
|
|
|
|
|
d.qid.type = dir->type;
|
|
|
|
d.qid.path = dir->qid;
|
|
|
|
d.qid.vers = 0;
|
|
|
|
d.mode = dir->perm;
|
|
|
|
d.length = 0; /* would be nice to do better */
|
|
|
|
d.name = dir->name;
|
|
|
|
d.uid = user;
|
|
|
|
d.gid = user;
|
|
|
|
d.muid = user;
|
|
|
|
d.atime = clock;
|
|
|
|
d.mtime = clock;
|
|
|
|
return convD2M(&d, buf, nbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
queuesend(Dirtab *d, Plumbmsg *m)
|
|
|
|
{
|
|
|
|
Sendreq *s, *t;
|
|
|
|
Fid *f;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
s = emalloc(sizeof(Sendreq));
|
|
|
|
s->nfid = d->nopen;
|
|
|
|
s->nleft = s->nfid;
|
|
|
|
s->fid = emalloc(s->nfid*sizeof(Fid*));
|
|
|
|
i = 0;
|
|
|
|
/* build array of fids open on this channel */
|
|
|
|
for(f=d->fopen; f!=nil; f=f->nextopen)
|
|
|
|
s->fid[i++] = f;
|
|
|
|
s->msg = m;
|
|
|
|
s->next = nil;
|
|
|
|
/* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
|
|
|
|
for(t=d->sendq; t!=nil; t=t->next)
|
|
|
|
if(t->next == nil)
|
|
|
|
break;
|
|
|
|
if(t == nil)
|
|
|
|
d->sendq = s;
|
|
|
|
else
|
|
|
|
t->next = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
Readreq *r;
|
|
|
|
|
|
|
|
r = emalloc(sizeof(Readreq));
|
|
|
|
r->fcall = t;
|
|
|
|
r->buf = buf;
|
|
|
|
r->fid = f;
|
|
|
|
r->next = d->readq;
|
|
|
|
d->readq = r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drainqueue(Dirtab *d)
|
|
|
|
{
|
|
|
|
Readreq *r, *nextr, *prevr;
|
|
|
|
Sendreq *s, *nexts, *prevs;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
prevs = nil;
|
|
|
|
for(s=d->sendq; s!=nil; s=nexts){
|
|
|
|
nexts = s->next;
|
|
|
|
for(i=0; i<s->nfid; i++){
|
|
|
|
prevr = nil;
|
|
|
|
for(r=d->readq; r!=nil; r=nextr){
|
|
|
|
nextr = r->next;
|
|
|
|
if(r->fid == s->fid[i]){
|
|
|
|
/* pack the message if necessary */
|
|
|
|
if(s->pack == nil)
|
|
|
|
s->pack = plumbpack(s->msg, &s->npack);
|
|
|
|
/* exchange the stuff... */
|
|
|
|
r->fcall->data = s->pack+r->fid->offset;
|
|
|
|
n = s->npack - r->fid->offset;
|
|
|
|
if(n > messagesize-IOHDRSZ)
|
|
|
|
n = messagesize-IOHDRSZ;
|
|
|
|
if(n > r->fcall->count)
|
|
|
|
n = r->fcall->count;
|
|
|
|
r->fcall->count = n;
|
|
|
|
fsysrespond(r->fcall, r->buf, nil);
|
|
|
|
r->fid->offset += n;
|
|
|
|
if(r->fid->offset >= s->npack){
|
|
|
|
/* message transferred; delete this fid from send queue */
|
|
|
|
r->fid->offset = 0;
|
|
|
|
s->fid[i] = nil;
|
|
|
|
s->nleft--;
|
|
|
|
}
|
|
|
|
/* delete read request from queue */
|
|
|
|
if(prevr)
|
|
|
|
prevr->next = r->next;
|
|
|
|
else
|
|
|
|
d->readq = r->next;
|
|
|
|
free(r->fcall);
|
|
|
|
free(r);
|
|
|
|
break;
|
|
|
|
}else
|
|
|
|
prevr = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* if no fids left, delete this send from queue */
|
|
|
|
if(s->nleft == 0){
|
|
|
|
free(s->fid);
|
|
|
|
plumbfree(s->msg);
|
|
|
|
free(s->pack);
|
|
|
|
if(prevs)
|
|
|
|
prevs->next = s->next;
|
|
|
|
else
|
|
|
|
d->sendq = s->next;
|
|
|
|
free(s);
|
|
|
|
}else
|
|
|
|
prevs = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* can't flush a send because they are always answered synchronously */
|
|
|
|
static void
|
|
|
|
flushqueue(Dirtab *d, int oldtag)
|
|
|
|
{
|
|
|
|
Readreq *r, *prevr;
|
|
|
|
|
|
|
|
prevr = nil;
|
|
|
|
for(r=d->readq; r!=nil; r=r->next){
|
|
|
|
if(oldtag == r->fcall->tag){
|
|
|
|
/* delete read request from queue */
|
|
|
|
if(prevr)
|
|
|
|
prevr->next = r->next;
|
|
|
|
else
|
|
|
|
d->readq = r->next;
|
|
|
|
free(r->fcall);
|
|
|
|
free(r->buf);
|
|
|
|
free(r);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prevr = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove messages awaiting delivery to now-closing fid */
|
|
|
|
static void
|
|
|
|
removesenders(Dirtab *d, Fid *fid)
|
|
|
|
{
|
|
|
|
Sendreq *s, *nexts, *prevs;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
prevs = nil;
|
|
|
|
for(s=d->sendq; s!=nil; s=nexts){
|
|
|
|
nexts = s->next;
|
|
|
|
for(i=0; i<s->nfid; i++)
|
|
|
|
if(fid == s->fid[i]){
|
|
|
|
/* delete this fid from send queue */
|
|
|
|
s->fid[i] = nil;
|
|
|
|
s->nleft--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* if no fids left, delete this send from queue */
|
|
|
|
if(s->nleft == 0){
|
|
|
|
free(s->fid);
|
|
|
|
plumbfree(s->msg);
|
|
|
|
free(s->pack);
|
|
|
|
if(prevs)
|
|
|
|
prevs->next = s->next;
|
|
|
|
else
|
|
|
|
d->sendq = s->next;
|
|
|
|
free(s);
|
|
|
|
}else
|
|
|
|
prevs = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
hold(Plumbmsg *m, Dirtab *d)
|
|
|
|
{
|
|
|
|
Holdq *h, *q;
|
|
|
|
|
|
|
|
h = emalloc(sizeof(Holdq));
|
|
|
|
h->msg = m;
|
|
|
|
/* add to end of queue */
|
|
|
|
if(d->holdq == nil)
|
|
|
|
d->holdq = h;
|
|
|
|
else{
|
|
|
|
for(q=d->holdq; q->next!=nil; q=q->next)
|
|
|
|
;
|
|
|
|
q->next = h;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
queueheld(Dirtab *d)
|
|
|
|
{
|
|
|
|
Holdq *h;
|
|
|
|
|
|
|
|
while(d->holdq != nil){
|
|
|
|
h = d->holdq;
|
|
|
|
d->holdq = h->next;
|
|
|
|
queuesend(d, h->msg);
|
|
|
|
/* no need to drain queue because we know no-one is reading yet */
|
|
|
|
free(h);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *err;
|
|
|
|
|
|
|
|
qlock(&queue);
|
|
|
|
err = nil;
|
|
|
|
if(m->dst==nil || m->dst[0]=='\0'){
|
|
|
|
err = Enoport;
|
|
|
|
if(rs != nil)
|
|
|
|
err = startup(rs, e);
|
|
|
|
plumbfree(m);
|
|
|
|
}else
|
|
|
|
for(i=NQID; i<ndir; i++)
|
|
|
|
if(strcmp(m->dst, dir[i].name) == 0){
|
|
|
|
if(dir[i].nopen == 0){
|
|
|
|
err = startup(rs, e);
|
|
|
|
if(e!=nil && e->holdforclient)
|
|
|
|
hold(m, &dir[i]);
|
|
|
|
else
|
|
|
|
plumbfree(m);
|
|
|
|
}else{
|
|
|
|
queuesend(&dir[i], m);
|
|
|
|
drainqueue(&dir[i]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
freeexec(e);
|
|
|
|
qunlock(&queue);
|
|
|
|
fsysrespond(t, buf, err);
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysversion(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
if(t->msize < 256){
|
|
|
|
fsysrespond(t, buf, "version: message size too small");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
if(t->msize < messagesize)
|
|
|
|
messagesize = t->msize;
|
|
|
|
t->msize = messagesize;
|
|
|
|
if(strncmp(t->version, "9P2000", 6) != 0){
|
|
|
|
fsysrespond(t, buf, "unrecognized 9P version");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
t->version = "9P2000";
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysauth(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
fsysrespond(t, buf, "plumber: authentication not required");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysattach(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
Fcall out;
|
|
|
|
|
|
|
|
if(strcmp(t->uname, user) != 0){
|
|
|
|
fsysrespond(&out, buf, Eperm);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
f->busy = 1;
|
|
|
|
f->open = 0;
|
|
|
|
f->qid.type = QTDIR;
|
|
|
|
f->qid.path = Qdir;
|
|
|
|
f->qid.vers = 0;
|
|
|
|
f->dir = dir;
|
|
|
|
memset(&out, 0, sizeof(Fcall));
|
|
|
|
out.type = t->type;
|
|
|
|
out.tag = t->tag;
|
|
|
|
out.fid = f->fid;
|
|
|
|
out.qid = f->qid;
|
|
|
|
fsysrespond(&out, buf, nil);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysflush(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
qlock(&queue);
|
|
|
|
for(i=NQID; i<ndir; i++)
|
|
|
|
flushqueue(&dir[i], t->oldtag);
|
|
|
|
qunlock(&queue);
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsyswalk(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
Fcall out;
|
|
|
|
Fid *nf;
|
|
|
|
ulong path;
|
|
|
|
Dirtab *d, *dir;
|
|
|
|
Qid q;
|
|
|
|
int i;
|
|
|
|
uchar type;
|
|
|
|
char *err;
|
|
|
|
|
|
|
|
if(f->open){
|
|
|
|
fsysrespond(t, buf, "clone of an open fid");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
nf = nil;
|
|
|
|
if(t->fid != t->newfid){
|
|
|
|
nf = newfid(t->newfid);
|
|
|
|
if(nf->busy){
|
|
|
|
fsysrespond(t, buf, "clone to a busy fid");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
nf->busy = 1;
|
|
|
|
nf->open = 0;
|
|
|
|
nf->dir = f->dir;
|
|
|
|
nf->qid = f->qid;
|
|
|
|
f = nf; /* walk f */
|
|
|
|
}
|
|
|
|
|
|
|
|
out.nwqid = 0;
|
|
|
|
err = nil;
|
|
|
|
dir = f->dir;
|
|
|
|
q = f->qid;
|
|
|
|
|
|
|
|
if(t->nwname > 0){
|
|
|
|
for(i=0; i<t->nwname; i++){
|
|
|
|
if((q.type & QTDIR) == 0){
|
|
|
|
err = Enotdir;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(strcmp(t->wname[i], "..") == 0){
|
|
|
|
type = QTDIR;
|
|
|
|
path = Qdir;
|
|
|
|
Accept:
|
|
|
|
q.type = type;
|
|
|
|
q.vers = 0;
|
|
|
|
q.path = path;
|
|
|
|
out.wqid[out.nwqid++] = q;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
d = dir;
|
|
|
|
d++; /* skip '.' */
|
|
|
|
for(; d->name; d++)
|
|
|
|
if(strcmp(t->wname[i], d->name) == 0){
|
|
|
|
type = d->type;
|
|
|
|
path = d->qid;
|
|
|
|
dir = d;
|
|
|
|
goto Accept;
|
|
|
|
}
|
|
|
|
err = Enoexist;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out.type = t->type;
|
|
|
|
out.tag = t->tag;
|
|
|
|
if(err!=nil || out.nwqid<t->nwname){
|
|
|
|
if(nf)
|
|
|
|
nf->busy = 0;
|
|
|
|
}else if(out.nwqid == t->nwname){
|
|
|
|
f->qid = q;
|
|
|
|
f->dir = dir;
|
|
|
|
}
|
|
|
|
|
|
|
|
fsysrespond(&out, buf, err);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysopen(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
int m, clearrules, mode;
|
|
|
|
|
|
|
|
clearrules = 0;
|
|
|
|
if(t->mode & OTRUNC){
|
|
|
|
if(f->qid.path != Qrules)
|
|
|
|
goto Deny;
|
|
|
|
clearrules = 1;
|
|
|
|
}
|
|
|
|
/* can't truncate anything, so just disregard */
|
|
|
|
mode = t->mode & ~(OTRUNC|OCEXEC);
|
|
|
|
/* can't execute or remove anything */
|
|
|
|
if(mode==OEXEC || (mode&ORCLOSE))
|
|
|
|
goto Deny;
|
|
|
|
switch(mode){
|
|
|
|
default:
|
|
|
|
goto Deny;
|
|
|
|
case OREAD:
|
|
|
|
m = 0400;
|
|
|
|
break;
|
|
|
|
case OWRITE:
|
|
|
|
m = 0200;
|
|
|
|
break;
|
|
|
|
case ORDWR:
|
|
|
|
m = 0600;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
|
|
|
|
goto Deny;
|
|
|
|
if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
|
|
|
|
lock(&rulesref);
|
|
|
|
if(rulesref.ref++ != 0){
|
|
|
|
rulesref.ref--;
|
|
|
|
unlock(&rulesref);
|
|
|
|
fsysrespond(t, buf, Einuse);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
unlock(&rulesref);
|
|
|
|
}
|
|
|
|
if(clearrules){
|
|
|
|
writerules(nil, 0);
|
|
|
|
rules[0] = nil;
|
|
|
|
}
|
|
|
|
t->qid = f->qid;
|
|
|
|
t->iounit = 0;
|
|
|
|
qlock(&queue);
|
|
|
|
f->mode = mode;
|
|
|
|
f->open = 1;
|
|
|
|
f->dir->nopen++;
|
|
|
|
f->nextopen = f->dir->fopen;
|
|
|
|
f->dir->fopen = f;
|
|
|
|
queueheld(f->dir);
|
|
|
|
qunlock(&queue);
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
return t;
|
|
|
|
|
|
|
|
Deny:
|
|
|
|
fsysrespond(t, buf, Eperm);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsyscreate(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
fsysrespond(t, buf, Eperm);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysreadrules(Fcall *t, uchar *buf)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
p = printrules();
|
|
|
|
n = strlen(p);
|
|
|
|
t->data = p;
|
|
|
|
if(t->offset >= n)
|
|
|
|
t->count = 0;
|
|
|
|
else{
|
|
|
|
t->data = p+t->offset;
|
|
|
|
if(t->offset+t->count > n)
|
|
|
|
t->count = n-t->offset;
|
|
|
|
}
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
free(p);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysread(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
uchar *b;
|
|
|
|
int i, n, o, e;
|
|
|
|
uint len;
|
|
|
|
Dirtab *d;
|
|
|
|
uint clock;
|
|
|
|
|
|
|
|
if(f->qid.path != Qdir){
|
|
|
|
if(f->qid.path == Qrules)
|
|
|
|
return fsysreadrules(t, buf);
|
|
|
|
/* read from port */
|
|
|
|
if(f->qid.path < NQID){
|
|
|
|
fsysrespond(t, buf, "internal error: unknown read port");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
qlock(&queue);
|
|
|
|
queueread(f->dir, t, buf, f);
|
|
|
|
drainqueue(f->dir);
|
|
|
|
qunlock(&queue);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
o = t->offset;
|
|
|
|
e = t->offset+t->count;
|
|
|
|
clock = getclock();
|
|
|
|
b = malloc(messagesize-IOHDRSZ);
|
|
|
|
if(b == nil){
|
|
|
|
fsysrespond(t, buf, Enomem);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
n = 0;
|
|
|
|
d = dir;
|
|
|
|
d++; /* first entry is '.' */
|
|
|
|
for(i=0; d->name!=nil && i<e; i+=len){
|
|
|
|
len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
|
|
|
|
if(len <= BIT16SZ)
|
|
|
|
break;
|
|
|
|
if(i >= o)
|
|
|
|
n += len;
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
t->data = (char*)b;
|
|
|
|
t->count = n;
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
free(b);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsyswrite(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
Plumbmsg *m;
|
|
|
|
int i, n;
|
|
|
|
long count;
|
|
|
|
char *data;
|
|
|
|
Exec *e;
|
|
|
|
|
|
|
|
switch((int)f->qid.path){
|
|
|
|
case Qdir:
|
|
|
|
fsysrespond(t, buf, Eisdir);
|
|
|
|
return t;
|
|
|
|
case Qrules:
|
|
|
|
clock = getclock();
|
|
|
|
fsysrespond(t, buf, writerules(t->data, t->count));
|
|
|
|
return t;
|
|
|
|
case Qsend:
|
|
|
|
if(f->offset == 0){
|
|
|
|
data = t->data;
|
|
|
|
count = t->count;
|
|
|
|
}else{
|
|
|
|
/* partial message already assembled */
|
|
|
|
f->writebuf = erealloc(f->writebuf, f->offset + t->count);
|
|
|
|
memmove(f->writebuf+f->offset, t->data, t->count);
|
|
|
|
data = f->writebuf;
|
|
|
|
count = f->offset+t->count;
|
|
|
|
}
|
|
|
|
m = plumbunpackpartial(data, count, &n);
|
|
|
|
if(m == nil){
|
|
|
|
if(n == 0){
|
|
|
|
f->offset = 0;
|
|
|
|
free(f->writebuf);
|
|
|
|
f->writebuf = nil;
|
|
|
|
fsysrespond(t, buf, Ebadmsg);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
/* can read more... */
|
|
|
|
if(f->offset == 0){
|
|
|
|
f->writebuf = emalloc(t->count);
|
|
|
|
memmove(f->writebuf, t->data, t->count);
|
|
|
|
}
|
|
|
|
/* else buffer has already been grown */
|
|
|
|
f->offset += t->count;
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
/* release partial buffer */
|
|
|
|
f->offset = 0;
|
|
|
|
free(f->writebuf);
|
|
|
|
f->writebuf = nil;
|
|
|
|
for(i=0; rules[i]; i++)
|
|
|
|
if((e=matchruleset(m, rules[i])) != nil){
|
|
|
|
dispose(t, buf, m, rules[i], e);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if(m->dst != nil){
|
|
|
|
dispose(t, buf, m, nil, nil);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
fsysrespond(t, buf, "no matching plumb rule");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
fsysrespond(t, buf, "internal error: write to unknown file");
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysstat(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
t->stat = emalloc(messagesize-IOHDRSZ);
|
|
|
|
t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
free(t->stat);
|
|
|
|
t->stat = nil;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsyswstat(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
fsysrespond(t, buf, Eperm);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysremove(Fcall *t, uchar *buf, Fid*)
|
|
|
|
{
|
|
|
|
fsysrespond(t, buf, Eperm);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Fcall*
|
|
|
|
fsysclunk(Fcall *t, uchar *buf, Fid *f)
|
|
|
|
{
|
|
|
|
Fid *prev, *p;
|
|
|
|
Dirtab *d;
|
|
|
|
|
|
|
|
qlock(&queue);
|
|
|
|
if(f->open){
|
|
|
|
d = f->dir;
|
|
|
|
d->nopen--;
|
|
|
|
if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
|
|
|
|
/*
|
|
|
|
* just to be sure last rule is parsed; error messages will be lost, though,
|
|
|
|
* unless last write ended with a blank line
|
|
|
|
*/
|
|
|
|
writerules(nil, 0);
|
|
|
|
lock(&rulesref);
|
|
|
|
rulesref.ref--;
|
|
|
|
unlock(&rulesref);
|
|
|
|
}
|
|
|
|
prev = nil;
|
|
|
|
for(p=d->fopen; p; p=p->nextopen){
|
|
|
|
if(p == f){
|
|
|
|
if(prev)
|
|
|
|
prev->nextopen = f->nextopen;
|
|
|
|
else
|
|
|
|
d->fopen = f->nextopen;
|
|
|
|
removesenders(d, f);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
f->busy = 0;
|
|
|
|
f->open = 0;
|
|
|
|
f->offset = 0;
|
|
|
|
if(f->writebuf != nil){
|
|
|
|
free(f->writebuf);
|
|
|
|
f->writebuf = nil;
|
|
|
|
}
|
|
|
|
qunlock(&queue);
|
|
|
|
fsysrespond(t, buf, nil);
|
|
|
|
return t;
|
|
|
|
}
|