fs.c 11 KB

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