1 /* 2 * Web file system. Conventionally mounted at /mnt/web 3 * 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) 9 * body retrieved data 10 * content-type mime content-type of body 11 * postbody data to be posted 12 * parsed parsed version of url 13 * url entire url 14 * scheme http, ftp, etc. 15 * host hostname 16 * path path on host 17 * query query after path 18 * fragment #foo anchor reference 19 * user user name (ftp) 20 * password password (ftp) 21 * ftptype transfer mode (ftp) 22 */ 23 24 #include <u.h> 25 #include <libc.h> 26 #include <bio.h> 27 #include <ip.h> 28 #include <plumb.h> 29 #include <thread.h> 30 #include <fcall.h> 31 #include <9p.h> 32 #include "dat.h" 33 #include "fns.h" 34 35 int fsdebug; 36 37 enum 38 { 39 Qroot, 40 Qrootctl, 41 Qclone, 42 Qcookies, 43 Qclient, 44 Qctl, 45 Qbody, 46 Qbodyext, 47 Qcontenttype, 48 Qpostbody, 49 Qparsed, 50 Qurl, 51 Qscheme, 52 Qschemedata, 53 Quser, 54 Qpasswd, 55 Qhost, 56 Qport, 57 Qpath, 58 Qquery, 59 Qfragment, 60 Qftptype, 61 Qend, 62 }; 63 64 #define PATH(type, n) ((type)|((n)<<8)) 65 #define TYPE(path) ((int)(path) & 0xFF) 66 #define NUM(path) ((uint)(path)>>8) 67 68 Channel *creq; 69 Channel *creqwait; 70 Channel *cclunk; 71 Channel *cclunkwait; 72 73 typedef struct Tab Tab; 74 struct Tab 75 { 76 char *name; 77 ulong mode; 78 int offset; 79 }; 80 81 Tab tab[] = 82 { 83 "/", DMDIR|0555, 0, 84 "ctl", 0666, 0, 85 "clone", 0666, 0, 86 "cookies", 0666, 0, 87 "XXX", DMDIR|0555, 0, 88 "ctl", 0666, 0, 89 "body", 0444, 0, 90 "XXX", 0444, 0, 91 "contenttype", 0444, 0, 92 "postbody", 0666, 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), 105 }; 106 107 ulong time0; 108 109 static void 110 fillstat(Dir *d, uvlong path, ulong length, char *ext) 111 { 112 Tab *t; 113 int type; 114 char buf[32]; 115 116 memset(d, 0, sizeof(*d)); 117 d->uid = estrdup("web"); 118 d->gid = estrdup("web"); 119 d->qid.path = path; 120 d->atime = d->mtime = time0; 121 d->length = length; 122 type = TYPE(path); 123 t = &tab[type]; 124 if(type == Qbodyext) { 125 snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext); 126 d->name = estrdup(buf); 127 } 128 else if(t->name) 129 d->name = estrdup(t->name); 130 else{ /* client directory */ 131 snprint(buf, sizeof buf, "%ud", NUM(path)); 132 d->name = estrdup(buf); 133 } 134 d->qid.type = t->mode>>24; 135 d->mode = t->mode; 136 } 137 138 static void 139 fsstat(Req *r) 140 { 141 fillstat(&r->d, r->fid->qid.path, 0, nil); 142 respond(r, nil); 143 } 144 145 static int 146 rootgen(int i, Dir *d, void*) 147 { 148 char buf[32]; 149 150 i += Qroot+1; 151 if(i < Qclient){ 152 fillstat(d, i, 0, nil); 153 return 0; 154 } 155 i -= Qclient; 156 if(i < nclient){ 157 fillstat(d, PATH(Qclient, i), 0, nil); 158 snprint(buf, sizeof buf, "%d", i); 159 free(d->name); 160 d->name = estrdup(buf); 161 return 0; 162 } 163 return -1; 164 } 165 166 static int 167 clientgen(int i, Dir *d, void *aux) 168 { 169 Client *c; 170 171 c = aux; 172 i += Qclient+1; 173 if(i <= Qparsed){ 174 fillstat(d, PATH(i, c->num), 0, c->ext); 175 return 0; 176 } 177 return -1; 178 } 179 180 static int 181 parsedgen(int i, Dir *d, void *aux) 182 { 183 Client *c; 184 185 c = aux; 186 i += Qparsed+1; 187 if(i < Qend){ 188 fillstat(d, PATH(i, c->num), 0, nil); 189 return 0; 190 } 191 return -1; 192 } 193 194 static void 195 fsread(Req *r) 196 { 197 char *s; 198 char e[ERRMAX]; 199 Client *c; 200 ulong path; 201 202 path = r->fid->qid.path; 203 switch(TYPE(path)){ 204 default: 205 snprint(e, sizeof e, "bug in webfs path=%lux\n", path); 206 respond(r, e); 207 break; 208 209 case Qroot: 210 dirread9p(r, rootgen, nil); 211 respond(r, nil); 212 break; 213 214 case Qrootctl: 215 globalctlread(r); 216 break; 217 218 case Qcookies: 219 cookieread(r); 220 break; 221 222 case Qclient: 223 dirread9p(r, clientgen, client[NUM(path)]); 224 respond(r, nil); 225 break; 226 227 case Qctl: 228 ctlread(r, client[NUM(path)]); 229 break; 230 231 case Qcontenttype: 232 c = client[NUM(path)]; 233 if(c->contenttype == nil) 234 r->ofcall.count = 0; 235 else 236 readstr(r, c->contenttype); 237 respond(r, nil); 238 break; 239 240 case Qpostbody: 241 c = client[NUM(path)]; 242 readbuf(r, c->postbody, c->npostbody); 243 respond(r, nil); 244 break; 245 246 case Qbody: 247 case Qbodyext: 248 c = client[NUM(path)]; 249 if(c->iobusy){ 250 respond(r, "already have i/o pending"); 251 break; 252 } 253 c->iobusy = 1; 254 sendp(c->creq, r); 255 break; 256 257 case Qparsed: 258 dirread9p(r, parsedgen, client[NUM(path)]); 259 respond(r, nil); 260 break; 261 262 case Qurl: 263 case Qscheme: 264 case Qschemedata: 265 case Quser: 266 case Qpasswd: 267 case Qhost: 268 case Qport: 269 case Qpath: 270 case Qquery: 271 case Qfragment: 272 case Qftptype: 273 c = client[NUM(path)]; 274 s = *(char**)((ulong)c->url+tab[TYPE(path)].offset); 275 if(s == nil) 276 r->ofcall.count = 0; 277 else 278 readstr(r, s); 279 respond(r, nil); 280 break; 281 } 282 } 283 284 static void 285 fswrite(Req *r) 286 { 287 int m; 288 ulong path; 289 char e[ERRMAX], *buf, *cmd, *arg; 290 Client *c; 291 292 path = r->fid->qid.path; 293 switch(TYPE(path)){ 294 default: 295 snprint(e, sizeof e, "bug in webfs path=%lux\n", path); 296 respond(r, e); 297 break; 298 299 case Qcookies: 300 cookiewrite(r); 301 break; 302 303 case Qrootctl: 304 case Qctl: 305 if(r->ifcall.count >= 1024){ 306 respond(r, "ctl message too long"); 307 return; 308 } 309 buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count); 310 cmd = buf; 311 arg = strpbrk(cmd, "\t "); 312 if(arg){ 313 *arg++ = '\0'; 314 arg += strspn(arg, "\t "); 315 }else 316 arg = ""; 317 r->ofcall.count = r->ifcall.count; 318 if(TYPE(path)==Qrootctl){ 319 if(!ctlwrite(r, &globalctl, cmd, arg) 320 && !globalctlwrite(r, cmd, arg)) 321 respond(r, "unknown control command"); 322 }else{ 323 c = client[NUM(path)]; 324 if(!ctlwrite(r, &c->ctl, cmd, arg) 325 && !clientctlwrite(r, c, cmd, arg)) 326 respond(r, "unknown control command"); 327 } 328 free(buf); 329 break; 330 331 case Qpostbody: 332 c = client[NUM(path)]; 333 if(c->bodyopened){ 334 respond(r, "cannot write postbody after opening body"); 335 break; 336 } 337 if(r->ifcall.offset >= 128*1024*1024){ /* >128MB is probably a mistake */ 338 respond(r, "offset too large"); 339 break; 340 } 341 m = r->ifcall.offset + r->ifcall.count; 342 if(c->npostbody < m){ 343 c->postbody = erealloc(c->postbody, m); 344 memset(c->postbody+c->npostbody, 0, m-c->npostbody); 345 c->npostbody = m; 346 } 347 memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count); 348 r->ofcall.count = r->ifcall.count; 349 respond(r, nil); 350 break; 351 } 352 } 353 354 static void 355 fsopen(Req *r) 356 { 357 static int need[4] = { 4, 2, 6, 1 }; 358 ulong path; 359 int n; 360 Client *c; 361 Tab *t; 362 363 /* 364 * lib9p already handles the blatantly obvious. 365 * we just have to enforce the permissions we have set. 366 */ 367 path = r->fid->qid.path; 368 t = &tab[TYPE(path)]; 369 n = need[r->ifcall.mode&3]; 370 if((n&t->mode) != n){ 371 respond(r, "permission denied"); 372 return; 373 } 374 375 switch(TYPE(path)){ 376 case Qcookies: 377 cookieopen(r); 378 break; 379 380 case Qpostbody: 381 c = client[NUM(path)]; 382 c->havepostbody++; 383 c->ref++; 384 respond(r, nil); 385 break; 386 387 case Qbody: 388 case Qbodyext: 389 c = client[NUM(path)]; 390 if(c->url == nil){ 391 respond(r, "url is not yet set"); 392 break; 393 } 394 c->bodyopened = 1; 395 c->ref++; 396 sendp(c->creq, r); 397 break; 398 399 case Qclone: 400 n = newclient(0); 401 path = PATH(Qctl, n); 402 r->fid->qid.path = path; 403 r->ofcall.qid.path = path; 404 if(fsdebug) 405 fprint(2, "open clone => path=%lux\n", path); 406 t = &tab[Qctl]; 407 /* fall through */ 408 default: 409 if(t-tab >= Qclient) 410 client[NUM(path)]->ref++; 411 respond(r, nil); 412 break; 413 } 414 } 415 416 static void 417 fsdestroyfid(Fid *fid) 418 { 419 sendp(cclunk, fid); 420 recvp(cclunkwait); 421 } 422 423 static void 424 fsattach(Req *r) 425 { 426 if(r->ifcall.aname && r->ifcall.aname[0]){ 427 respond(r, "invalid attach specifier"); 428 return; 429 } 430 r->fid->qid.path = PATH(Qroot, 0); 431 r->fid->qid.type = QTDIR; 432 r->fid->qid.vers = 0; 433 r->ofcall.qid = r->fid->qid; 434 respond(r, nil); 435 } 436 437 static char* 438 fswalk1(Fid *fid, char *name, Qid *qid) 439 { 440 int i, n; 441 ulong path; 442 char buf[32], *ext; 443 444 path = fid->qid.path; 445 if(!(fid->qid.type&QTDIR)) 446 return "walk in non-directory"; 447 448 if(strcmp(name, "..") == 0){ 449 switch(TYPE(path)){ 450 case Qparsed: 451 qid->path = PATH(Qclient, NUM(path)); 452 qid->type = tab[Qclient].mode>>24; 453 return nil; 454 case Qclient: 455 case Qroot: 456 qid->path = PATH(Qroot, 0); 457 qid->type = tab[Qroot].mode>>24; 458 return nil; 459 default: 460 return "bug in fswalk1"; 461 } 462 } 463 464 i = TYPE(path)+1; 465 for(; i<nelem(tab); i++){ 466 if(i==Qclient){ 467 n = atoi(name); 468 snprint(buf, sizeof buf, "%d", n); 469 if(n < nclient && strcmp(buf, name) == 0){ 470 qid->path = PATH(i, n); 471 qid->type = tab[i].mode>>24; 472 return nil; 473 } 474 break; 475 } 476 if(i==Qbodyext){ 477 ext = client[NUM(path)]->ext; 478 snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext); 479 if(strcmp(buf, name) == 0){ 480 qid->path = PATH(i, NUM(path)); 481 qid->type = tab[i].mode>>24; 482 return nil; 483 } 484 } 485 else if(strcmp(name, tab[i].name) == 0){ 486 qid->path = PATH(i, NUM(path)); 487 qid->type = tab[i].mode>>24; 488 return nil; 489 } 490 if(tab[i].mode&DMDIR) 491 break; 492 } 493 return "directory entry not found"; 494 } 495 496 static void 497 fsflush(Req *r) 498 { 499 Req *or; 500 int t; 501 Client *c; 502 ulong path; 503 504 or=r; 505 while(or->ifcall.type==Tflush) 506 or = or->oldreq; 507 508 if(or->ifcall.type != Tread && or->ifcall.type != Topen) 509 abort(); 510 511 path = or->fid->qid.path; 512 t = TYPE(path); 513 if(t != Qbody && t != Qbodyext) 514 abort(); 515 516 c = client[NUM(path)]; 517 sendp(c->creq, r); 518 iointerrupt(c->io); 519 } 520 521 static void 522 fsthread(void*) 523 { 524 ulong path; 525 Alt a[3]; 526 Fid *fid; 527 Req *r; 528 529 threadsetname("fsthread"); 530 plumbstart(); 531 532 a[0].op = CHANRCV; 533 a[0].c = cclunk; 534 a[0].v = &fid; 535 a[1].op = CHANRCV; 536 a[1].c = creq; 537 a[1].v = &r; 538 a[2].op = CHANEND; 539 540 for(;;){ 541 switch(alt(a)){ 542 case 0: 543 path = fid->qid.path; 544 if(TYPE(path)==Qcookies) 545 cookieclunk(fid); 546 if(fid->omode != -1 && TYPE(path) >= Qclient) 547 closeclient(client[NUM(path)]); 548 sendp(cclunkwait, nil); 549 break; 550 case 1: 551 switch(r->ifcall.type){ 552 case Tattach: 553 fsattach(r); 554 break; 555 case Topen: 556 fsopen(r); 557 break; 558 case Tread: 559 fsread(r); 560 break; 561 case Twrite: 562 fswrite(r); 563 break; 564 case Tstat: 565 fsstat(r); 566 break; 567 case Tflush: 568 fsflush(r); 569 break; 570 default: 571 respond(r, "bug in fsthread"); 572 break; 573 } 574 sendp(creqwait, 0); 575 break; 576 } 577 } 578 } 579 580 static void 581 fssend(Req *r) 582 { 583 sendp(creq, r); 584 recvp(creqwait); /* avoids need to deal with spurious flushes */ 585 } 586 587 void 588 initfs(void) 589 { 590 time0 = time(0); 591 creq = chancreate(sizeof(void*), 0); 592 creqwait = chancreate(sizeof(void*), 0); 593 cclunk = chancreate(sizeof(void*), 0); 594 cclunkwait = chancreate(sizeof(void*), 0); 595 procrfork(fsthread, nil, STACK, RFNAMEG); 596 } 597 598 void 599 takedown(Srv*) 600 { 601 closecookies(); 602 threadexitsall("done"); 603 } 604 605 Srv fs = 606 { 607 .attach= fssend, 608 .destroyfid= fsdestroyfid, 609 .walk1= fswalk1, 610 .open= fssend, 611 .read= fssend, 612 .write= fssend, 613 .stat= fssend, 614 .flush= fssend, 615 .end= takedown, 616 }; 617 618