summaryrefslogtreecommitdiff
path: root/sys/src/cmd/webfs/fs.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/webfs/fs.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/webfs/fs.c')
-rwxr-xr-xsys/src/cmd/webfs/fs.c616
1 files changed, 616 insertions, 0 deletions
diff --git a/sys/src/cmd/webfs/fs.c b/sys/src/cmd/webfs/fs.c
new file mode 100755
index 000000000..087390d72
--- /dev/null
+++ b/sys/src/cmd/webfs/fs.c
@@ -0,0 +1,616 @@
+/*
+ * Web file system. Conventionally mounted at /mnt/web
+ *
+ * ctl send control messages (might go away)
+ * cookies list of cookies, editable
+ * clone open and read to obtain new connection
+ * n connection directory
+ * ctl control messages (like get url)
+ * body retrieved data
+ * content-type mime content-type of body
+ * postbody data to be posted
+ * parsed parsed version of url
+ * url entire url
+ * scheme http, ftp, etc.
+ * host hostname
+ * path path on host
+ * query query after path
+ * fragment #foo anchor reference
+ * user user name (ftp)
+ * password password (ftp)
+ * ftptype transfer mode (ftp)
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ip.h>
+#include <plumb.h>
+#include <thread.h>
+#include <fcall.h>
+#include <9p.h>
+#include "dat.h"
+#include "fns.h"
+
+int fsdebug;
+
+enum
+{
+ Qroot,
+ Qrootctl,
+ Qclone,
+ Qcookies,
+ Qclient,
+ Qctl,
+ Qbody,
+ Qbodyext,
+ Qcontenttype,
+ Qpostbody,
+ Qparsed,
+ Qurl,
+ Qscheme,
+ Qschemedata,
+ Quser,
+ Qpasswd,
+ Qhost,
+ Qport,
+ Qpath,
+ Qquery,
+ Qfragment,
+ Qftptype,
+ Qend,
+};
+
+#define PATH(type, n) ((type)|((n)<<8))
+#define TYPE(path) ((int)(path) & 0xFF)
+#define NUM(path) ((uint)(path)>>8)
+
+Channel *creq;
+Channel *creqwait;
+Channel *cclunk;
+Channel *cclunkwait;
+
+typedef struct Tab Tab;
+struct Tab
+{
+ char *name;
+ ulong mode;
+ int offset;
+};
+
+Tab tab[] =
+{
+ "/", DMDIR|0555, 0,
+ "ctl", 0666, 0,
+ "clone", 0666, 0,
+ "cookies", 0666, 0,
+ "XXX", DMDIR|0555, 0,
+ "ctl", 0666, 0,
+ "body", 0444, 0,
+ "XXX", 0444, 0,
+ "contenttype", 0444, 0,
+ "postbody", 0666, 0,
+ "parsed", DMDIR|0555, 0,
+ "url", 0444, offsetof(Url, url),
+ "scheme", 0444, offsetof(Url, scheme),
+ "schemedata", 0444, offsetof(Url, schemedata),
+ "user", 0444, offsetof(Url, user),
+ "passwd", 0444, offsetof(Url, passwd),
+ "host", 0444, offsetof(Url, host),
+ "port", 0444, offsetof(Url, port),
+ "path", 0444, offsetof(Url, path),
+ "query", 0444, offsetof(Url, query),
+ "fragment", 0444, offsetof(Url, fragment),
+ "ftptype", 0444, offsetof(Url, ftp.type),
+};
+
+ulong time0;
+
+static void
+fillstat(Dir *d, uvlong path, ulong length, char *ext)
+{
+ Tab *t;
+ int type;
+ char buf[32];
+
+ memset(d, 0, sizeof(*d));
+ d->uid = estrdup("web");
+ d->gid = estrdup("web");
+ d->qid.path = path;
+ d->atime = d->mtime = time0;
+ d->length = length;
+ type = TYPE(path);
+ t = &tab[type];
+ if(type == Qbodyext) {
+ snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
+ d->name = estrdup(buf);
+ }
+ else if(t->name)
+ d->name = estrdup(t->name);
+ else{ /* client directory */
+ snprint(buf, sizeof buf, "%ud", NUM(path));
+ d->name = estrdup(buf);
+ }
+ d->qid.type = t->mode>>24;
+ d->mode = t->mode;
+}
+
+static void
+fsstat(Req *r)
+{
+ fillstat(&r->d, r->fid->qid.path, 0, nil);
+ respond(r, nil);
+}
+
+static int
+rootgen(int i, Dir *d, void*)
+{
+ char buf[32];
+
+ i += Qroot+1;
+ if(i < Qclient){
+ fillstat(d, i, 0, nil);
+ return 0;
+ }
+ i -= Qclient;
+ if(i < nclient){
+ fillstat(d, PATH(Qclient, i), 0, nil);
+ snprint(buf, sizeof buf, "%d", i);
+ free(d->name);
+ d->name = estrdup(buf);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+clientgen(int i, Dir *d, void *aux)
+{
+ Client *c;
+
+ c = aux;
+ i += Qclient+1;
+ if(i <= Qparsed){
+ fillstat(d, PATH(i, c->num), 0, c->ext);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+parsedgen(int i, Dir *d, void *aux)
+{
+ Client *c;
+
+ c = aux;
+ i += Qparsed+1;
+ if(i < Qend){
+ fillstat(d, PATH(i, c->num), 0, nil);
+ return 0;
+ }
+ return -1;
+}
+
+static void
+fsread(Req *r)
+{
+ char *s;
+ char e[ERRMAX];
+ Client *c;
+ ulong path;
+
+ path = r->fid->qid.path;
+ switch(TYPE(path)){
+ default:
+ snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
+ respond(r, e);
+ break;
+
+ case Qroot:
+ dirread9p(r, rootgen, nil);
+ respond(r, nil);
+ break;
+
+ case Qrootctl:
+ globalctlread(r);
+ break;
+
+ case Qcookies:
+ cookieread(r);
+ break;
+
+ case Qclient:
+ dirread9p(r, clientgen, client[NUM(path)]);
+ respond(r, nil);
+ break;
+
+ case Qctl:
+ ctlread(r, client[NUM(path)]);
+ break;
+
+ case Qcontenttype:
+ c = client[NUM(path)];
+ if(c->contenttype == nil)
+ r->ofcall.count = 0;
+ else
+ readstr(r, c->contenttype);
+ respond(r, nil);
+ break;
+
+ case Qpostbody:
+ c = client[NUM(path)];
+ readbuf(r, c->postbody, c->npostbody);
+ respond(r, nil);
+ break;
+
+ case Qbody:
+ case Qbodyext:
+ c = client[NUM(path)];
+ if(c->iobusy){
+ respond(r, "already have i/o pending");
+ break;
+ }
+ c->iobusy = 1;
+ sendp(c->creq, r);
+ break;
+
+ case Qparsed:
+ dirread9p(r, parsedgen, client[NUM(path)]);
+ respond(r, nil);
+ break;
+
+ case Qurl:
+ case Qscheme:
+ case Qschemedata:
+ case Quser:
+ case Qpasswd:
+ case Qhost:
+ case Qport:
+ case Qpath:
+ case Qquery:
+ case Qfragment:
+ case Qftptype:
+ c = client[NUM(path)];
+ r->ofcall.count = 0;
+ if(c->url != nil
+ && (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil)
+ readstr(r, s);
+ respond(r, nil);
+ break;
+ }
+}
+
+static void
+fswrite(Req *r)
+{
+ int m;
+ ulong path;
+ char e[ERRMAX], *buf, *cmd, *arg;
+ Client *c;
+
+ path = r->fid->qid.path;
+ switch(TYPE(path)){
+ default:
+ snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
+ respond(r, e);
+ break;
+
+ case Qcookies:
+ cookiewrite(r);
+ break;
+
+ case Qrootctl:
+ case Qctl:
+ if(r->ifcall.count >= 1024){
+ respond(r, "ctl message too long");
+ return;
+ }
+ buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count);
+ cmd = buf;
+ arg = strpbrk(cmd, "\t ");
+ if(arg){
+ *arg++ = '\0';
+ arg += strspn(arg, "\t ");
+ }else
+ arg = "";
+ r->ofcall.count = r->ifcall.count;
+ if(TYPE(path)==Qrootctl){
+ if(!ctlwrite(r, &globalctl, cmd, arg)
+ && !globalctlwrite(r, cmd, arg))
+ respond(r, "unknown control command");
+ }else{
+ c = client[NUM(path)];
+ if(!ctlwrite(r, &c->ctl, cmd, arg)
+ && !clientctlwrite(r, c, cmd, arg))
+ respond(r, "unknown control command");
+ }
+ free(buf);
+ break;
+
+ case Qpostbody:
+ c = client[NUM(path)];
+ if(c->bodyopened){
+ respond(r, "cannot write postbody after opening body");
+ break;
+ }
+ if(r->ifcall.offset >= 128*1024*1024){ /* >128MB is probably a mistake */
+ respond(r, "offset too large");
+ break;
+ }
+ m = r->ifcall.offset + r->ifcall.count;
+ if(c->npostbody < m){
+ c->postbody = erealloc(c->postbody, m);
+ memset(c->postbody+c->npostbody, 0, m-c->npostbody);
+ c->npostbody = m;
+ }
+ memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
+ r->ofcall.count = r->ifcall.count;
+ respond(r, nil);
+ break;
+ }
+}
+
+static void
+fsopen(Req *r)
+{
+ static int need[4] = { 4, 2, 6, 1 };
+ ulong path;
+ int n;
+ Client *c;
+ Tab *t;
+
+ /*
+ * lib9p already handles the blatantly obvious.
+ * we just have to enforce the permissions we have set.
+ */
+ path = r->fid->qid.path;
+ t = &tab[TYPE(path)];
+ n = need[r->ifcall.mode&3];
+ if((n&t->mode) != n){
+ respond(r, "permission denied");
+ return;
+ }
+
+ switch(TYPE(path)){
+ case Qcookies:
+ cookieopen(r);
+ break;
+
+ case Qpostbody:
+ c = client[NUM(path)];
+ c->havepostbody++;
+ c->ref++;
+ respond(r, nil);
+ break;
+
+ case Qbody:
+ case Qbodyext:
+ c = client[NUM(path)];
+ if(c->url == nil){
+ respond(r, "url is not yet set");
+ break;
+ }
+ c->bodyopened = 1;
+ c->ref++;
+ sendp(c->creq, r);
+ break;
+
+ case Qclone:
+ n = newclient(0);
+ path = PATH(Qctl, n);
+ r->fid->qid.path = path;
+ r->ofcall.qid.path = path;
+ if(fsdebug)
+ fprint(2, "open clone => path=%lux\n", path);
+ t = &tab[Qctl];
+ /* fall through */
+ default:
+ if(t-tab >= Qclient)
+ client[NUM(path)]->ref++;
+ respond(r, nil);
+ break;
+ }
+}
+
+static void
+fsdestroyfid(Fid *fid)
+{
+ sendp(cclunk, fid);
+ recvp(cclunkwait);
+}
+
+static void
+fsattach(Req *r)
+{
+ if(r->ifcall.aname && r->ifcall.aname[0]){
+ respond(r, "invalid attach specifier");
+ return;
+ }
+ 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 char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+ int i, n;
+ ulong path;
+ char buf[32], *ext;
+
+ path = fid->qid.path;
+ if(!(fid->qid.type&QTDIR))
+ return "walk in non-directory";
+
+ if(strcmp(name, "..") == 0){
+ switch(TYPE(path)){
+ case Qparsed:
+ qid->path = PATH(Qclient, NUM(path));
+ qid->type = tab[Qclient].mode>>24;
+ return nil;
+ case Qclient:
+ case Qroot:
+ qid->path = PATH(Qroot, 0);
+ qid->type = tab[Qroot].mode>>24;
+ return nil;
+ default:
+ return "bug in fswalk1";
+ }
+ }
+
+ i = TYPE(path)+1;
+ for(; i<nelem(tab); i++){
+ if(i==Qclient){
+ n = atoi(name);
+ snprint(buf, sizeof buf, "%d", n);
+ if(n < nclient && strcmp(buf, name) == 0){
+ qid->path = PATH(i, n);
+ qid->type = tab[i].mode>>24;
+ return nil;
+ }
+ break;
+ }
+ if(i==Qbodyext){
+ ext = client[NUM(path)]->ext;
+ snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
+ if(strcmp(buf, name) == 0){
+ qid->path = PATH(i, NUM(path));
+ qid->type = tab[i].mode>>24;
+ return nil;
+ }
+ }
+ else if(strcmp(name, tab[i].name) == 0){
+ qid->path = PATH(i, NUM(path));
+ qid->type = tab[i].mode>>24;
+ return nil;
+ }
+ if(tab[i].mode&DMDIR)
+ break;
+ }
+ return "directory entry not found";
+}
+
+static void
+fsflush(Req *r)
+{
+ Req *or;
+ int t;
+ Client *c;
+ ulong path;
+
+ or=r;
+ while(or->ifcall.type==Tflush)
+ or = or->oldreq;
+
+ if(or->ifcall.type != Tread && or->ifcall.type != Topen)
+ abort();
+
+ path = or->fid->qid.path;
+ t = TYPE(path);
+ if(t != Qbody && t != Qbodyext)
+ abort();
+
+ c = client[NUM(path)];
+ sendp(c->creq, r);
+ iointerrupt(c->io);
+}
+
+static void
+fsthread(void*)
+{
+ ulong path;
+ Alt a[3];
+ Fid *fid;
+ Req *r;
+
+ threadsetname("fsthread");
+ plumbstart();
+
+ a[0].op = CHANRCV;
+ a[0].c = cclunk;
+ a[0].v = &fid;
+ a[1].op = CHANRCV;
+ a[1].c = creq;
+ a[1].v = &r;
+ a[2].op = CHANEND;
+
+ for(;;){
+ switch(alt(a)){
+ case 0:
+ path = fid->qid.path;
+ if(TYPE(path)==Qcookies)
+ cookieclunk(fid);
+ if(fid->omode != -1 && TYPE(path) >= Qclient)
+ closeclient(client[NUM(path)]);
+ sendp(cclunkwait, 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(creqwait, 0);
+ break;
+ }
+ }
+}
+
+static void
+fssend(Req *r)
+{
+ sendp(creq, r);
+ recvp(creqwait); /* avoids need to deal with spurious flushes */
+}
+
+void
+initfs(void)
+{
+ time0 = time(0);
+ creq = chancreate(sizeof(void*), 0);
+ creqwait = chancreate(sizeof(void*), 0);
+ cclunk = chancreate(sizeof(void*), 0);
+ cclunkwait = chancreate(sizeof(void*), 0);
+ procrfork(fsthread, nil, STACK, RFNAMEG);
+}
+
+void
+takedown(Srv*)
+{
+ closecookies();
+ threadexitsall("done");
+}
+
+Srv fs =
+{
+.attach= fssend,
+.destroyfid= fsdestroyfid,
+.walk1= fswalk1,
+.open= fssend,
+.read= fssend,
+.write= fssend,
+.stat= fssend,
+.flush= fssend,
+.end= takedown,
+};
+