fs.c 11 KB

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