ftpfs.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <auth.h>
  4. #include <fcall.h>
  5. #include <String.h>
  6. #include "ftpfs.h"
  7. /* an active fid */
  8. typedef struct Fid Fid;
  9. struct Fid
  10. {
  11. int fid;
  12. Node *node; /* path to remote file */
  13. int busy;
  14. Fid *next;
  15. int open;
  16. };
  17. Fid *fids; /* linked list of fids */
  18. char errstring[128]; /* error to return */
  19. int mfd; /* fd for 9fs */
  20. int messagesize = 4*1024*IOHDRSZ;
  21. uchar mdata[8*1024*IOHDRSZ];
  22. uchar mbuf[8*1024*IOHDRSZ];
  23. Fcall rhdr;
  24. Fcall thdr;
  25. int debug;
  26. int usenlst;
  27. char *ext;
  28. int quiet;
  29. int kapid = -1;
  30. int dying; /* set when any process decides to die */
  31. int dokeepalive;
  32. char *rflush(Fid*), *rnop(Fid*), *rversion(Fid*),
  33. *rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),
  34. *rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),
  35. *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
  36. *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
  37. *rauth(Fid*);;
  38. void mountinit(char*);
  39. void io(void);
  40. int readpdir(Node*);
  41. char *(*fcalls[])(Fid*) = {
  42. [Tflush] rflush,
  43. [Tversion] rversion,
  44. [Tattach] rattach,
  45. [Tauth] rauth,
  46. [Twalk] rwalk,
  47. [Topen] ropen,
  48. [Tcreate] rcreate,
  49. [Tread] rread,
  50. [Twrite] rwrite,
  51. [Tclunk] rclunk,
  52. [Tremove] rremove,
  53. [Tstat] rstat,
  54. [Twstat] rwstat,
  55. };
  56. OS oslist[] = {
  57. { Plan9, "Plan 9", },
  58. { Plan9, "Plan9", },
  59. { Plan9, "UNIX Type: L8 Version: Plan 9", },
  60. { Unix, "SUN", },
  61. { Unix, "UNIX", },
  62. { VMS, "VMS", },
  63. { VM, "VM", },
  64. { Tops, "TOPS", },
  65. { MVS, "MVS", },
  66. { NetWare, "NetWare", },
  67. { NetWare, "NETWARE", },
  68. { OS½, "OS/2", },
  69. { TSO, "TSO", },
  70. { NT, "Windows_NT", }, /* DOS like interface */
  71. { NT, "WINDOWS_NT", }, /* Unix like interface */
  72. { Unknown, 0 },
  73. };
  74. char *nouid = "?uid?";
  75. #define S2P(x) (((ulong)(x)) & 0xffffff)
  76. char *nosuchfile = "file does not exist";
  77. void
  78. usage(void)
  79. {
  80. fprint(2, "ftpfs [-/dqn] [-a passwd] [-m mountpoint] [-e ext] [-o os] [-r root] [net!]address\n");
  81. exits("usage");
  82. }
  83. void
  84. main(int argc, char *argv[])
  85. {
  86. char *mountroot = 0;
  87. char *mountpoint = "/n/ftp";
  88. char *cpassword = 0;
  89. char *cp;
  90. int p[2];
  91. OS *o;
  92. defos = Unix;
  93. user = strdup(getuser());
  94. ARGBEGIN {
  95. case '/':
  96. mountroot = "/";
  97. break;
  98. case 'a':
  99. cpassword = ARGF();
  100. break;
  101. case 'd':
  102. debug = 1;
  103. break;
  104. case 'k':
  105. dokeepalive = 1;
  106. break;
  107. case 'm':
  108. mountpoint = ARGF();
  109. break;
  110. case 'n':
  111. usenlst = 1;
  112. break;
  113. case 'e':
  114. ext = ARGF();
  115. break;
  116. case 'o':
  117. cp = ARGF();
  118. for(o = oslist; o->os != Unknown; o++)
  119. if(strncmp(cp, o->name, strlen(o->name)) == 0){
  120. defos = o->os;
  121. break;
  122. }
  123. break;
  124. case 'r':
  125. mountroot = ARGF();
  126. break;
  127. case 'q':
  128. quiet = 1;
  129. break;
  130. } ARGEND
  131. if(argc != 1)
  132. usage();
  133. /* get a pipe to mount and run 9fs on */
  134. if(pipe(p) < 0)
  135. fatal("pipe failed: %r");
  136. mfd = p[0];
  137. /* initial handshakes with remote side */
  138. hello(*argv);
  139. if (cpassword == 0)
  140. rlogin(*argv);
  141. else
  142. clogin("anonymous", cpassword);
  143. preamble(mountroot);
  144. /* start the 9fs protocol */
  145. switch(rfork(RFPROC|RFNAMEG|RFENVG|RFFDG|RFNOTEG|RFREND)){
  146. case -1:
  147. fatal("fork: %r");
  148. case 0:
  149. /* seal off standard input/output */
  150. close(0);
  151. open("/dev/null", OREAD);
  152. close(1);
  153. open("/dev/null", OWRITE);
  154. close(p[1]);
  155. fmtinstall('F', fcallfmt); /* debugging */
  156. io();
  157. quit();
  158. break;
  159. default:
  160. close(p[0]);
  161. if(mount(p[1], -1, mountpoint, MREPL|MCREATE, "") < 0)
  162. fatal("mount failed: %r");
  163. }
  164. exits(0);
  165. }
  166. /*
  167. * lookup an fid. if not found, create a new one.
  168. */
  169. Fid *
  170. newfid(int fid)
  171. {
  172. Fid *f, *ff;
  173. ff = 0;
  174. for(f = fids; f; f = f->next){
  175. if(f->fid == fid){
  176. if(f->busy)
  177. return f;
  178. else{
  179. ff = f;
  180. break;
  181. }
  182. } else if(!ff && !f->busy)
  183. ff = f;
  184. }
  185. if(ff == 0){
  186. ff = mallocz(sizeof(*f), 1);
  187. ff->next = fids;
  188. fids = ff;
  189. }
  190. ff->node = nil;
  191. ff->fid = fid;
  192. return ff;
  193. }
  194. /*
  195. * a process that sends keep alive messages to
  196. * keep the server from shutting down the connection
  197. */
  198. int
  199. kaproc(void)
  200. {
  201. int pid;
  202. if(!dokeepalive)
  203. return -1;
  204. switch(pid = rfork(RFPROC|RFMEM)){
  205. case -1:
  206. return -1;
  207. case 0:
  208. break;
  209. default:
  210. return pid;
  211. }
  212. while(!dying){
  213. sleep(5000);
  214. nop();
  215. }
  216. _exits(0);
  217. return -1;
  218. }
  219. void
  220. io(void)
  221. {
  222. char *err;
  223. int n;
  224. kapid = kaproc();
  225. while(!dying){
  226. n = read9pmsg(mfd, mdata, messagesize);
  227. if(n <= 0)
  228. fatal("mount read");
  229. if(convM2S(mdata, n, &thdr) == 0)
  230. continue;
  231. if(debug)
  232. fprint(2, "<-%F\n", &thdr);/**/
  233. if(!fcalls[thdr.type])
  234. err = "bad fcall type";
  235. else
  236. err = (*fcalls[thdr.type])(newfid(thdr.fid));
  237. if(err){
  238. rhdr.type = Rerror;
  239. rhdr.ename = err;
  240. }else{
  241. rhdr.type = thdr.type + 1;
  242. rhdr.fid = thdr.fid;
  243. }
  244. rhdr.tag = thdr.tag;
  245. if(debug)
  246. fprint(2, "->%F\n", &rhdr);/**/
  247. n = convS2M(&rhdr, mdata, messagesize);
  248. if(write(mfd, mdata, n) != n)
  249. fatal("mount write");
  250. }
  251. }
  252. char*
  253. rnop(Fid *f)
  254. {
  255. USED(f);
  256. return 0;
  257. }
  258. char*
  259. rversion(Fid*)
  260. {
  261. if(thdr.msize > sizeof(mdata))
  262. rhdr.msize = messagesize;
  263. else
  264. rhdr.msize = thdr.msize;
  265. messagesize = thdr.msize;
  266. if(strncmp(thdr.version, "9P2000", 6) != 0)
  267. return "unknown 9P version";
  268. rhdr.version = "9P2000";
  269. return nil;
  270. }
  271. char*
  272. rflush(Fid*)
  273. {
  274. return 0;
  275. }
  276. char*
  277. rauth(Fid*)
  278. {
  279. return "auth unimplemented";
  280. }
  281. char*
  282. rattach(Fid *f)
  283. {
  284. f->busy = 1;
  285. f->node = remroot;
  286. rhdr.qid = f->node->d->qid;
  287. return 0;
  288. }
  289. char*
  290. rwalk(Fid *f)
  291. {
  292. Node *np;
  293. Fid *nf;
  294. char **elems;
  295. int i, nelems;
  296. char *err;
  297. Node *node;
  298. /* clone fid */
  299. nf = nil;
  300. if(thdr.newfid != thdr.fid){
  301. nf = newfid(thdr.newfid);
  302. if(nf->busy)
  303. return "newfid in use";
  304. nf->busy = 1;
  305. nf->node = f->node;
  306. f = nf;
  307. }
  308. err = nil;
  309. elems = thdr.wname;
  310. nelems = thdr.nwname;
  311. node = f->node;
  312. rhdr.nwqid = 0;
  313. if(nelems > 0){
  314. /* walk fid */
  315. for(i=0; i<nelems && i<MAXWELEM; i++){
  316. if((node->d->qid.type & QTDIR) == 0){
  317. err = "not a directory";
  318. break;
  319. }
  320. if(strcmp(elems[i], ".") == 0){
  321. Found:
  322. rhdr.wqid[i] = node->d->qid;
  323. rhdr.nwqid++;
  324. continue;
  325. }
  326. if(strcmp(elems[i], "..") == 0){
  327. node = node->parent;
  328. goto Found;
  329. }
  330. if(strcmp(elems[i], ".flush.ftpfs") == 0){
  331. /* hack to flush the cache */
  332. invalidate(node);
  333. readdir(node);
  334. goto Found;
  335. }
  336. /* some top level names are special */
  337. if((os == Tops || os == VM || os == VMS) && node == remroot){
  338. np = newtopsdir(elems[i]);
  339. if(np){
  340. node = np;
  341. goto Found;
  342. } else {
  343. err = nosuchfile;
  344. break;
  345. }
  346. }
  347. /* everything else */
  348. node = extendpath(node, s_copy(elems[i]));
  349. if(ISCACHED(node->parent)){
  350. /* the cache of the parent is good, believe it */
  351. if(!ISVALID(node)){
  352. err = nosuchfile;
  353. break;
  354. }
  355. if(node->parent->chdirunknown || (node->d->mode & DMSYML))
  356. fixsymbolic(node);
  357. } else if(!ISVALID(node)){
  358. /* this isn't a valid node, try cd'ing */
  359. if(changedir(node) == 0){
  360. node->d->qid.type = QTDIR;
  361. node->d->mode |= DMDIR;
  362. }else{
  363. node->d->qid.type = QTFILE;
  364. node->d->mode &= ~DMDIR;
  365. }
  366. }
  367. goto Found;
  368. }
  369. if(i == 0 && err == 0)
  370. err = "file does not exist";
  371. }
  372. /* clunk a newly cloned fid if the walk didn't succeed */
  373. if(nf != nil && (err != nil || rhdr.nwqid<nelems)){
  374. nf->busy = 0;
  375. nf->fid = 0;
  376. }
  377. /* if it all worked, point the fid to the enw node */
  378. if(err == nil)
  379. f->node = node;
  380. return err;
  381. }
  382. char *
  383. ropen(Fid *f)
  384. {
  385. int mode;
  386. mode = thdr.mode;
  387. if(f->node->d->qid.type & QTDIR)
  388. if(mode != OREAD)
  389. return "permission denied";
  390. if(mode & OTRUNC){
  391. uncache(f->node);
  392. uncache(f->node->parent);
  393. filedirty(f->node);
  394. } else {
  395. /* read the remote file or directory */
  396. if(!ISCACHED(f->node)){
  397. filefree(f->node);
  398. if(f->node->d->qid.type & QTDIR){
  399. invalidate(f->node);
  400. if(readdir(f->node) < 0)
  401. return nosuchfile;
  402. } else {
  403. if(readfile(f->node) < 0)
  404. return errstring;
  405. }
  406. CACHED(f->node);
  407. }
  408. }
  409. rhdr.qid = f->node->d->qid;
  410. f->open = 1;
  411. f->node->opens++;
  412. return 0;
  413. }
  414. char*
  415. rcreate(Fid *f)
  416. {
  417. char *name;
  418. if((f->node->d->qid.type&QTDIR) == 0)
  419. return "not a directory";
  420. name = thdr.name;
  421. f->node = extendpath(f->node, s_copy(name));
  422. uncache(f->node);
  423. if(thdr.perm & DMDIR){
  424. if(createdir(f->node) < 0)
  425. return "permission denied";
  426. } else
  427. filedirty(f->node);
  428. invalidate(f->node->parent);
  429. uncache(f->node->parent);
  430. rhdr.qid = f->node->d->qid;
  431. f->open = 1;
  432. f->node->opens++;
  433. return 0;
  434. }
  435. char*
  436. rread(Fid *f)
  437. {
  438. long off;
  439. int n, cnt, rv;
  440. Node *np;
  441. rhdr.count = 0;
  442. off = thdr.offset;
  443. cnt = thdr.count;
  444. if(cnt > messagesize-IOHDRSZ)
  445. cnt = messagesize-IOHDRSZ;
  446. if(f->node->d->qid.type & QTDIR){
  447. rv = 0;
  448. for(np = f->node->children; np != nil; np = np->sibs){
  449. if(!ISVALID(np))
  450. continue;
  451. if(off <= BIT16SZ)
  452. break;
  453. n = convD2M(np->d, mbuf, messagesize-IOHDRSZ);
  454. off -= n;
  455. }
  456. for(; rv < cnt && np != nil; np = np->sibs){
  457. if(!ISVALID(np))
  458. continue;
  459. if(np->d->mode & DMSYML)
  460. fixsymbolic(np);
  461. n = convD2M(np->d, mbuf + rv, cnt-rv);
  462. if(n <= BIT16SZ)
  463. break;
  464. rv += n;
  465. }
  466. } else {
  467. /* reread file if it's fallen out of the cache */
  468. if(!ISCACHED(f->node))
  469. if(readfile(f->node) < 0)
  470. return errstring;
  471. CACHED(f->node);
  472. rv = fileread(f->node, (char*)mbuf, off, cnt);
  473. if(rv < 0)
  474. return errstring;
  475. }
  476. rhdr.data = (char*)mbuf;
  477. rhdr.count = rv;
  478. return 0;
  479. }
  480. char*
  481. rwrite(Fid *f)
  482. {
  483. long off;
  484. int cnt;
  485. if(f->node->d->qid.type & QTDIR)
  486. return "directories are not writable";
  487. rhdr.count = 0;
  488. off = thdr.offset;
  489. cnt = thdr.count;
  490. cnt = filewrite(f->node, thdr.data, off, cnt);
  491. if(cnt < 0)
  492. return errstring;
  493. filedirty(f->node);
  494. rhdr.count = cnt;
  495. return 0;
  496. }
  497. char *
  498. rclunk(Fid *f)
  499. {
  500. if(fileisdirty(f->node)){
  501. if(createfile(f->node) < 0)
  502. fprint(2, "ftpfs: couldn't create %s\n", f->node->d->name);
  503. fileclean(f->node);
  504. uncache(f->node);
  505. }
  506. if(f->open){
  507. f->open = 0;
  508. f->node->opens--;
  509. }
  510. f->busy = 0;
  511. return 0;
  512. }
  513. /*
  514. * remove is an implicit clunk
  515. */
  516. char *
  517. rremove(Fid *f)
  518. {
  519. if(QTDIR & f->node->d->qid.type){
  520. if(removedir(f->node) < 0)
  521. return errstring;
  522. } else {
  523. if(removefile(f->node) < 0)
  524. return errstring;
  525. }
  526. uncache(f->node->parent);
  527. uncache(f->node);
  528. INVALID(f->node);
  529. f->busy = 0;
  530. return 0;
  531. }
  532. char *
  533. rstat(Fid *f)
  534. {
  535. Node *p;
  536. p = f->node->parent;
  537. if(!ISCACHED(p)){
  538. invalidate(p);
  539. readdir(p);
  540. CACHED(p);
  541. }
  542. if(!ISVALID(f->node))
  543. return nosuchfile;
  544. if(p->chdirunknown)
  545. fixsymbolic(f->node);
  546. rhdr.nstat = convD2M(f->node->d, mbuf, messagesize-IOHDRSZ);
  547. rhdr.stat = mbuf;
  548. return 0;
  549. }
  550. char *
  551. rwstat(Fid *f)
  552. {
  553. USED(f);
  554. return "wstat not implemented";
  555. }
  556. /*
  557. * print message and die
  558. */
  559. void
  560. fatal(char *fmt, ...)
  561. {
  562. va_list arg;
  563. char buf[8*1024];
  564. dying = 1;
  565. va_start(arg, fmt);
  566. vseprint(buf, buf + (sizeof(buf)-1) / sizeof(*buf), fmt, arg);
  567. va_end(arg);
  568. fprint(2, "ftpfs: %s\n", buf);
  569. if(kapid > 0)
  570. postnote(PNGROUP, kapid, "die");
  571. exits(buf);
  572. }
  573. /*
  574. * like strncpy but make sure there's a terminating null
  575. */
  576. void*
  577. safecpy(void *to, void *from, int n)
  578. {
  579. char *a = ((char*)to) + n - 1;
  580. strncpy(to, from, n);
  581. *a = 0;
  582. return to;
  583. }
  584. /*
  585. * set the error string
  586. */
  587. int
  588. seterr(char *fmt, ...)
  589. {
  590. va_list arg;
  591. va_start(arg, fmt);
  592. vsnprint(errstring, sizeof errstring, fmt, arg);
  593. va_end(arg);
  594. return -1;
  595. }
  596. /*
  597. * create a new node
  598. */
  599. Node*
  600. newnode(Node *parent, String *name)
  601. {
  602. Node *np;
  603. static ulong path;
  604. Dir d;
  605. np = mallocz(sizeof(Node), 1);
  606. if(np == 0)
  607. fatal("out of memory");
  608. np->children = 0;
  609. if(parent){
  610. np->parent = parent;
  611. np->sibs = parent->children;
  612. parent->children = np;
  613. np->depth = parent->depth + 1;
  614. d.dev = 0; /* not stat'd */
  615. } else {
  616. /* the root node */
  617. np->parent = np;
  618. np->sibs = 0;
  619. np->depth = 0;
  620. d.dev = 1;
  621. }
  622. np->remname = name;
  623. d.name = s_to_c(name);
  624. d.atime = time(0);
  625. d.mtime = d.atime;
  626. d.uid = nouid;
  627. d.gid = nouid;
  628. d.muid = nouid;
  629. np->fp = 0;
  630. d.qid.path = ++path;
  631. d.qid.vers = 0;
  632. d.qid.type = QTFILE;
  633. d.type = 0;
  634. np->d = reallocdir(&d, 0);
  635. return np;
  636. }
  637. /*
  638. * walk one down the local mirror of the remote directory tree
  639. */
  640. Node*
  641. extendpath(Node *parent, String *elem)
  642. {
  643. Node *np;
  644. for(np = parent->children; np; np = np->sibs)
  645. if(strcmp(s_to_c(np->remname), s_to_c(elem)) == 0){
  646. s_free(elem);
  647. return np;
  648. }
  649. return newnode(parent, elem);
  650. }
  651. /*
  652. * flush the cached file, write it back if it's dirty
  653. */
  654. void
  655. uncache(Node *np)
  656. {
  657. if(fileisdirty(np))
  658. createfile(np);
  659. filefree(np);
  660. UNCACHED(np);
  661. }
  662. /*
  663. * invalidate all children of a node
  664. */
  665. void
  666. invalidate(Node *node)
  667. {
  668. Node *np;
  669. if(node->opens)
  670. return; /* don't invalidate something that's open */
  671. uncachedir(node, 0);
  672. /* invalidate children */
  673. for(np = node->children; np; np = np->sibs){
  674. if(np->opens)
  675. continue; /* don't invalidate something that's open */
  676. UNCACHED(np);
  677. invalidate(np);
  678. np->d->dev = 0;
  679. }
  680. }
  681. /*
  682. * make a top level tops-20 directory. They are automaticly valid.
  683. */
  684. Node*
  685. newtopsdir(char *name)
  686. {
  687. Node *np;
  688. np = extendpath(remroot, s_copy(name));
  689. if(!ISVALID(np)){
  690. np->d->qid.type = QTDIR;
  691. np->d->atime = time(0);
  692. np->d->mtime = np->d->atime;
  693. np->d->uid = "?uid?";
  694. np->d->gid = "?uid?";
  695. np->d->muid = "?uid?";
  696. np->d->mode = DMDIR|0777;
  697. np->d->length = 0;
  698. np->d = reallocdir(np->d, 1);
  699. if(changedir(np) >= 0)
  700. VALID(np);
  701. }
  702. return np;
  703. }
  704. /*
  705. * figure out if a symbolic link is to a directory or a file
  706. */
  707. void
  708. fixsymbolic(Node *node)
  709. {
  710. if(changedir(node) == 0){
  711. node->d->mode |= DMDIR;
  712. node->d->qid.type = QTDIR;
  713. } else
  714. node->d->qid.type = QTFILE;
  715. node->d->mode &= ~DMSYML;
  716. }