2 * Web file system. Conventionally mounted at /mnt/web
4 * ctl send control messages (might go away)
5 * cookies list of cookies, editable
6 * clone open and read to obtain new connection
7 * n connection directory
8 * ctl control messages (like get url)
10 * content-type mime content-type of body
11 * postbody data to be posted
12 * parsed parsed version of url
14 * scheme http, ftp, etc.
17 * query query after path
18 * fragment #foo anchor reference
19 * user user name (ftp)
20 * password password (ftp)
21 * ftptype transfer mode (ftp)
64 #define PATH(type, n) ((type)|((n)<<8))
65 #define TYPE(path) ((int)(path) & 0xFF)
66 #define NUM(path) ((uint)(path)>>8)
73 typedef struct Tab Tab;
91 "contenttype", 0444, 0,
93 "parsed", DMDIR|0555, 0,
94 "url", 0444, offsetof(Url, url),
95 "scheme", 0444, offsetof(Url, scheme),
96 "schemedata", 0444, offsetof(Url, schemedata),
97 "user", 0444, offsetof(Url, user),
98 "passwd", 0444, offsetof(Url, passwd),
99 "host", 0444, offsetof(Url, host),
100 "port", 0444, offsetof(Url, port),
101 "path", 0444, offsetof(Url, path),
102 "query", 0444, offsetof(Url, query),
103 "fragment", 0444, offsetof(Url, fragment),
104 "ftptype", 0444, offsetof(Url, ftp.type),
110 fillstat(Dir *d, uvlong path, ulong length, char *ext)
116 memset(d, 0, sizeof(*d));
117 d->uid = estrdup("web");
118 d->gid = estrdup("web");
120 d->atime = d->mtime = time0;
124 if(type == Qbodyext) {
125 snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
126 d->name = estrdup(buf);
129 d->name = estrdup(t->name);
130 else{ /* client directory */
131 snprint(buf, sizeof buf, "%ud", NUM(path));
132 d->name = estrdup(buf);
134 d->qid.type = t->mode>>24;
141 fillstat(&r->d, r->fid->qid.path, 0, nil);
146 rootgen(int i, Dir *d, void*)
152 fillstat(d, i, 0, nil);
157 fillstat(d, PATH(Qclient, i), 0, nil);
158 snprint(buf, sizeof buf, "%d", i);
160 d->name = estrdup(buf);
167 clientgen(int i, Dir *d, void *aux)
174 fillstat(d, PATH(i, c->num), 0, c->ext);
181 parsedgen(int i, Dir *d, void *aux)
188 fillstat(d, PATH(i, c->num), 0, nil);
202 path = r->fid->qid.path;
205 snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
210 dirread9p(r, rootgen, nil);
223 dirread9p(r, clientgen, client[NUM(path)]);
228 ctlread(r, client[NUM(path)]);
232 c = client[NUM(path)];
233 if(c->contenttype == nil)
236 readstr(r, c->contenttype);
241 c = client[NUM(path)];
242 readbuf(r, c->postbody, c->npostbody);
248 c = client[NUM(path)];
250 respond(r, "already have i/o pending");
258 dirread9p(r, parsedgen, client[NUM(path)]);
273 c = client[NUM(path)];
276 && (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil)
288 char e[ERRMAX], *buf, *cmd, *arg;
291 path = r->fid->qid.path;
294 snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
304 if(r->ifcall.count >= 1024){
305 respond(r, "ctl message too long");
308 buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count);
310 arg = strpbrk(cmd, "\t ");
313 arg += strspn(arg, "\t ");
316 r->ofcall.count = r->ifcall.count;
317 if(TYPE(path)==Qrootctl){
318 if(!ctlwrite(r, &globalctl, cmd, arg)
319 && !globalctlwrite(r, cmd, arg))
320 respond(r, "unknown control command");
322 c = client[NUM(path)];
323 if(!ctlwrite(r, &c->ctl, cmd, arg)
324 && !clientctlwrite(r, c, cmd, arg))
325 respond(r, "unknown control command");
331 c = client[NUM(path)];
333 respond(r, "cannot write postbody after opening body");
336 if(r->ifcall.offset >= 128*1024*1024){ /* >128MB is probably a mistake */
337 respond(r, "offset too large");
340 m = r->ifcall.offset + r->ifcall.count;
341 if(c->npostbody < m){
342 c->postbody = erealloc(c->postbody, m);
343 memset(c->postbody+c->npostbody, 0, m-c->npostbody);
346 memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
347 r->ofcall.count = r->ifcall.count;
356 static int need[4] = { 4, 2, 6, 1 };
363 * lib9p already handles the blatantly obvious.
364 * we just have to enforce the permissions we have set.
366 path = r->fid->qid.path;
367 t = &tab[TYPE(path)];
368 n = need[r->ifcall.mode&3];
369 if((n&t->mode) != n){
370 respond(r, "permission denied");
380 c = client[NUM(path)];
388 c = client[NUM(path)];
390 respond(r, "url is not yet set");
400 path = PATH(Qctl, n);
401 r->fid->qid.path = path;
402 r->ofcall.qid.path = path;
404 fprint(2, "open clone => path=%lux\n", path);
409 client[NUM(path)]->ref++;
416 fsdestroyfid(Fid *fid)
425 if(r->ifcall.aname && r->ifcall.aname[0]){
426 respond(r, "invalid attach specifier");
429 r->fid->qid.path = PATH(Qroot, 0);
430 r->fid->qid.type = QTDIR;
431 r->fid->qid.vers = 0;
432 r->ofcall.qid = r->fid->qid;
437 fswalk1(Fid *fid, char *name, Qid *qid)
443 path = fid->qid.path;
444 if(!(fid->qid.type&QTDIR))
445 return "walk in non-directory";
447 if(strcmp(name, "..") == 0){
450 qid->path = PATH(Qclient, NUM(path));
451 qid->type = tab[Qclient].mode>>24;
455 qid->path = PATH(Qroot, 0);
456 qid->type = tab[Qroot].mode>>24;
459 return "bug in fswalk1";
464 for(; i<nelem(tab); i++){
467 snprint(buf, sizeof buf, "%d", n);
468 if(n < nclient && strcmp(buf, name) == 0){
469 qid->path = PATH(i, n);
470 qid->type = tab[i].mode>>24;
476 ext = client[NUM(path)]->ext;
477 snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
478 if(strcmp(buf, name) == 0){
479 qid->path = PATH(i, NUM(path));
480 qid->type = tab[i].mode>>24;
484 else if(strcmp(name, tab[i].name) == 0){
485 qid->path = PATH(i, NUM(path));
486 qid->type = tab[i].mode>>24;
489 if(tab[i].mode&DMDIR)
492 return "directory entry not found";
504 while(or->ifcall.type==Tflush)
507 if(or->ifcall.type != Tread && or->ifcall.type != Topen)
510 path = or->fid->qid.path;
512 if(t != Qbody && t != Qbodyext)
515 c = client[NUM(path)];
528 threadsetname("fsthread");
542 path = fid->qid.path;
543 if(TYPE(path)==Qcookies)
545 if(fid->omode != -1 && TYPE(path) >= Qclient)
546 closeclient(client[NUM(path)]);
547 sendp(cclunkwait, nil);
550 switch(r->ifcall.type){
570 respond(r, "bug in fsthread");
583 recvp(creqwait); /* avoids need to deal with spurious flushes */
590 creq = chancreate(sizeof(void*), 0);
591 creqwait = chancreate(sizeof(void*), 0);
592 cclunk = chancreate(sizeof(void*), 0);
593 cclunkwait = chancreate(sizeof(void*), 0);
594 procrfork(fsthread, nil, STACK, RFNAMEG);
601 threadexitsall("done");
607 .destroyfid= fsdestroyfid,