123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616 |
- /*
- * 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,
- };
|