diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /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-x | sys/src/cmd/webfs/fs.c | 616 |
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, +}; + |