123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820 |
- #include <u.h>
- #include <libc.h>
- #include <auth.h>
- #include <fcall.h>
- #include <String.h>
- #include "ftpfs.h"
- /* an active fid */
- typedef struct Fid Fid;
- struct Fid
- {
- int fid;
- Node *node; /* path to remote file */
- int busy;
- Fid *next;
- int open;
- };
- Fid *fids; /* linked list of fids */
- char errstring[128]; /* error to return */
- int mfd; /* fd for 9fs */
- int messagesize = 4*1024*IOHDRSZ;
- uchar mdata[8*1024*IOHDRSZ];
- uchar mbuf[8*1024*IOHDRSZ];
- Fcall rhdr;
- Fcall thdr;
- int debug;
- int usenlst;
- int usetls;
- char *ext;
- int quiet;
- int kapid = -1;
- int dying; /* set when any process decides to die */
- int dokeepalive;
- char *rflush(Fid*), *rnop(Fid*), *rversion(Fid*),
- *rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),
- *rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),
- *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
- *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
- *rauth(Fid*);;
- void mountinit(char*);
- void io(void);
- int readpdir(Node*);
- char *(*fcalls[])(Fid*) = {
- [Tflush] rflush,
- [Tversion] rversion,
- [Tattach] rattach,
- [Tauth] rauth,
- [Twalk] rwalk,
- [Topen] ropen,
- [Tcreate] rcreate,
- [Tread] rread,
- [Twrite] rwrite,
- [Tclunk] rclunk,
- [Tremove] rremove,
- [Tstat] rstat,
- [Twstat] rwstat,
- };
- OS oslist[] = {
- { Plan9, "Plan 9", },
- { Plan9, "Plan9", },
- { Plan9, "UNIX Type: L8 Version: Plan 9", },
- { Unix, "SUN", },
- { Unix, "UNIX", },
- { VMS, "VMS", },
- { VM, "VM", },
- { Tops, "TOPS", },
- { MVS, "MVS", },
- { NetWare, "NetWare", },
- { NetWare, "NETWARE", },
- { OS½, "OS/2", },
- { TSO, "TSO", },
- { NT, "Windows_NT", }, /* DOS like interface */
- { NT, "WINDOWS_NT", }, /* Unix like interface */
- { Unknown, 0 },
- };
- char *nouid = "?uid?";
- #define S2P(x) (((ulong)(x)) & 0xffffff)
- char *nosuchfile = "file does not exist";
- char *keyspec = "";
- void
- usage(void)
- {
- fprint(2, "ftpfs [-/dqnt] [-a passwd] [-m mountpoint] [-e ext] [-o os] [-r root] [net!]address\n");
- exits("usage");
- }
- void
- main(int argc, char *argv[])
- {
- char *mountroot = 0;
- char *mountpoint = "/n/ftp";
- char *cpassword = 0;
- char *cp;
- int p[2];
- OS *o;
- defos = Unix;
- user = strdup(getuser());
- usetls = 0;
- ARGBEGIN {
- case '/':
- mountroot = "/";
- break;
- case 'a':
- cpassword = ARGF();
- break;
- case 'd':
- debug = 1;
- break;
- case 'k':
- keyspec = EARGF(usage());
- break;
- case 'K':
- dokeepalive = 1;
- break;
- case 'm':
- mountpoint = ARGF();
- break;
- case 'n':
- usenlst = 1;
- break;
- case 'e':
- ext = ARGF();
- break;
- case 't':
- usetls = 1;
- break;
- case 'o':
- cp = ARGF();
- for(o = oslist; o->os != Unknown; o++)
- if(strncmp(cp, o->name, strlen(o->name)) == 0){
- defos = o->os;
- break;
- }
- break;
- case 'r':
- mountroot = ARGF();
- break;
- case 'q':
- quiet = 1;
- break;
- } ARGEND
- if(argc != 1)
- usage();
- /* get a pipe to mount and run 9fs on */
- if(pipe(p) < 0)
- fatal("pipe failed: %r");
- mfd = p[0];
- /* initial handshakes with remote side */
- hello(*argv);
- if(cpassword == 0)
- rlogin(*argv, keyspec);
- else
- clogin("anonymous", cpassword);
- preamble(mountroot);
- /* start the 9fs protocol */
- switch(rfork(RFPROC|RFNAMEG|RFENVG|RFFDG|RFNOTEG|RFREND)){
- case -1:
- fatal("fork: %r");
- case 0:
- /* seal off standard input/output */
- close(0);
- open("/dev/null", OREAD);
- close(1);
- open("/dev/null", OWRITE);
- close(p[1]);
- fmtinstall('F', fcallfmt); /* debugging */
- fmtinstall('D', dirfmt); /* expected by %F */
- fmtinstall('M', dirmodefmt); /* expected by %F */
- io();
- quit();
- break;
- default:
- close(p[0]);
- if(mount(p[1], -1, mountpoint, MREPL|MCREATE, "") < 0)
- fatal("mount failed: %r");
- }
- exits(0);
- }
- /*
- * lookup an fid. if not found, create a new one.
- */
- Fid *
- newfid(int fid)
- {
- Fid *f, *ff;
- ff = 0;
- for(f = fids; f; f = f->next){
- if(f->fid == fid){
- if(f->busy)
- return f;
- else{
- ff = f;
- break;
- }
- } else if(!ff && !f->busy)
- ff = f;
- }
- if(ff == 0){
- ff = mallocz(sizeof(*f), 1);
- ff->next = fids;
- fids = ff;
- }
- ff->node = nil;
- ff->fid = fid;
- return ff;
- }
- /*
- * a process that sends keep alive messages to
- * keep the server from shutting down the connection
- */
- int
- kaproc(void)
- {
- int pid;
- if(!dokeepalive)
- return -1;
- switch(pid = rfork(RFPROC|RFMEM)){
- case -1:
- return -1;
- case 0:
- break;
- default:
- return pid;
- }
- while(!dying){
- sleep(5000);
- nop();
- }
- _exits(0);
- return -1;
- }
- void
- io(void)
- {
- char *err, buf[ERRMAX];
- int n;
- kapid = kaproc();
- while(!dying){
- n = read9pmsg(mfd, mdata, messagesize);
- if(n <= 0){
- errstr(buf, sizeof buf);
- if(buf[0]=='\0' || strstr(buf, "hungup"))
- exits("");
- fatal("mount read: %s\n", buf);
- }
- if(convM2S(mdata, n, &thdr) == 0)
- continue;
- if(debug)
- fprint(2, "<-%F\n", &thdr);/**/
- if(!fcalls[thdr.type])
- err = "bad fcall type";
- else
- err = (*fcalls[thdr.type])(newfid(thdr.fid));
- if(err){
- rhdr.type = Rerror;
- rhdr.ename = err;
- }else{
- rhdr.type = thdr.type + 1;
- rhdr.fid = thdr.fid;
- }
- rhdr.tag = thdr.tag;
- if(debug)
- fprint(2, "->%F\n", &rhdr);/**/
- n = convS2M(&rhdr, mdata, messagesize);
- if(write(mfd, mdata, n) != n)
- fatal("mount write");
- }
- }
- char*
- rnop(Fid *f)
- {
- USED(f);
- return 0;
- }
- char*
- rversion(Fid*)
- {
- if(thdr.msize > sizeof(mdata))
- rhdr.msize = messagesize;
- else
- rhdr.msize = thdr.msize;
- messagesize = thdr.msize;
- if(strncmp(thdr.version, "9P2000", 6) != 0)
- return "unknown 9P version";
- rhdr.version = "9P2000";
- return nil;
- }
- char*
- rflush(Fid*)
- {
- return 0;
- }
- char*
- rauth(Fid*)
- {
- return "auth unimplemented";
- }
- char*
- rattach(Fid *f)
- {
- f->busy = 1;
- f->node = remroot;
- rhdr.qid = f->node->d->qid;
- return 0;
- }
- char*
- rwalk(Fid *f)
- {
- Node *np;
- Fid *nf;
- char **elems;
- int i, nelems;
- char *err;
- Node *node;
- /* clone fid */
- nf = nil;
- if(thdr.newfid != thdr.fid){
- nf = newfid(thdr.newfid);
- if(nf->busy)
- return "newfid in use";
- nf->busy = 1;
- nf->node = f->node;
- f = nf;
- }
- err = nil;
- elems = thdr.wname;
- nelems = thdr.nwname;
- node = f->node;
- rhdr.nwqid = 0;
- if(nelems > 0){
- /* walk fid */
- for(i=0; i<nelems && i<MAXWELEM; i++){
- if((node->d->qid.type & QTDIR) == 0){
- err = "not a directory";
- break;
- }
- if(strcmp(elems[i], ".") == 0){
- Found:
- rhdr.wqid[i] = node->d->qid;
- rhdr.nwqid++;
- continue;
- }
- if(strcmp(elems[i], "..") == 0){
- node = node->parent;
- goto Found;
- }
- if(strcmp(elems[i], ".flush.ftpfs") == 0){
- /* hack to flush the cache */
- invalidate(node);
- readdir(node);
- goto Found;
- }
- /* some top level names are special */
- if((os == Tops || os == VM || os == VMS) && node == remroot){
- np = newtopsdir(elems[i]);
- if(np){
- node = np;
- goto Found;
- } else {
- err = nosuchfile;
- break;
- }
- }
- /* everything else */
- node = extendpath(node, s_copy(elems[i]));
- if(ISCACHED(node->parent)){
- /* the cache of the parent is good, believe it */
- if(!ISVALID(node)){
- err = nosuchfile;
- break;
- }
- if(node->parent->chdirunknown || (node->d->mode & DMSYML))
- fixsymbolic(node);
- } else if(!ISVALID(node)){
- /* this isn't a valid node, try cd'ing */
- if(changedir(node) == 0){
- node->d->qid.type = QTDIR;
- node->d->mode |= DMDIR;
- }else{
- node->d->qid.type = QTFILE;
- node->d->mode &= ~DMDIR;
- }
- }
- goto Found;
- }
- if(i == 0 && err == 0)
- err = "file does not exist";
- }
- /* clunk a newly cloned fid if the walk didn't succeed */
- if(nf != nil && (err != nil || rhdr.nwqid<nelems)){
- nf->busy = 0;
- nf->fid = 0;
- }
- /* if it all worked, point the fid to the enw node */
- if(err == nil)
- f->node = node;
- return err;
- }
- char *
- ropen(Fid *f)
- {
- int mode;
- mode = thdr.mode;
- if(f->node->d->qid.type & QTDIR)
- if(mode != OREAD)
- return "permission denied";
- if(mode & OTRUNC){
- uncache(f->node);
- uncache(f->node->parent);
- filedirty(f->node);
- } else {
- /* read the remote file or directory */
- if(!ISCACHED(f->node)){
- filefree(f->node);
- if(f->node->d->qid.type & QTDIR){
- invalidate(f->node);
- if(readdir(f->node) < 0)
- return nosuchfile;
- } else {
- if(readfile(f->node) < 0)
- return errstring;
- }
- CACHED(f->node);
- }
- }
- rhdr.qid = f->node->d->qid;
- f->open = 1;
- f->node->opens++;
- return 0;
- }
- char*
- rcreate(Fid *f)
- {
- char *name;
- if((f->node->d->qid.type&QTDIR) == 0)
- return "not a directory";
- name = thdr.name;
- f->node = extendpath(f->node, s_copy(name));
- uncache(f->node);
- if(thdr.perm & DMDIR){
- if(createdir(f->node) < 0)
- return "permission denied";
- } else
- filedirty(f->node);
- invalidate(f->node->parent);
- uncache(f->node->parent);
- rhdr.qid = f->node->d->qid;
- f->open = 1;
- f->node->opens++;
- return 0;
- }
- char*
- rread(Fid *f)
- {
- long off;
- int n, cnt, rv;
- Node *np;
- rhdr.count = 0;
- off = thdr.offset;
- cnt = thdr.count;
- if(cnt > messagesize-IOHDRSZ)
- cnt = messagesize-IOHDRSZ;
- if(f->node->d->qid.type & QTDIR){
- rv = 0;
- for(np = f->node->children; np != nil; np = np->sibs){
- if(!ISVALID(np))
- continue;
- if(off <= BIT16SZ)
- break;
- n = convD2M(np->d, mbuf, messagesize-IOHDRSZ);
- off -= n;
- }
- for(; rv < cnt && np != nil; np = np->sibs){
- if(!ISVALID(np))
- continue;
- if(np->d->mode & DMSYML)
- fixsymbolic(np);
- n = convD2M(np->d, mbuf + rv, cnt-rv);
- if(n <= BIT16SZ)
- break;
- rv += n;
- }
- } else {
- /* reread file if it's fallen out of the cache */
- if(!ISCACHED(f->node))
- if(readfile(f->node) < 0)
- return errstring;
- CACHED(f->node);
- rv = fileread(f->node, (char*)mbuf, off, cnt);
- if(rv < 0)
- return errstring;
- }
- rhdr.data = (char*)mbuf;
- rhdr.count = rv;
- return 0;
- }
- char*
- rwrite(Fid *f)
- {
- long off;
- int cnt;
- if(f->node->d->qid.type & QTDIR)
- return "directories are not writable";
- rhdr.count = 0;
- off = thdr.offset;
- cnt = thdr.count;
- cnt = filewrite(f->node, thdr.data, off, cnt);
- if(cnt < 0)
- return errstring;
- filedirty(f->node);
- rhdr.count = cnt;
- return 0;
- }
- char *
- rclunk(Fid *f)
- {
- if(fileisdirty(f->node)){
- if(createfile(f->node) < 0)
- fprint(2, "ftpfs: couldn't create %s\n", f->node->d->name);
- fileclean(f->node);
- uncache(f->node);
- }
- if(f->open){
- f->open = 0;
- f->node->opens--;
- }
- f->busy = 0;
- return 0;
- }
- /*
- * remove is an implicit clunk
- */
- char *
- rremove(Fid *f)
- {
- char *e;
- e = nil;
- if(QTDIR & f->node->d->qid.type){
- if(removedir(f->node) < 0)
- e = errstring;
- } else {
- if(removefile(f->node) < 0)
- e = errstring;
- }
- uncache(f->node->parent);
- uncache(f->node);
- if(e == nil)
- INVALID(f->node);
- f->busy = 0;
- return e;
- }
- char *
- rstat(Fid *f)
- {
- Node *p;
- p = f->node->parent;
- if(!ISCACHED(p)){
- invalidate(p);
- readdir(p);
- CACHED(p);
- }
- if(!ISVALID(f->node))
- return nosuchfile;
- if(p->chdirunknown)
- fixsymbolic(f->node);
- rhdr.nstat = convD2M(f->node->d, mbuf, messagesize-IOHDRSZ);
- rhdr.stat = mbuf;
- return 0;
- }
- char *
- rwstat(Fid *f)
- {
- USED(f);
- return "wstat not implemented";
- }
- /*
- * print message and die
- */
- void
- fatal(char *fmt, ...)
- {
- va_list arg;
- char buf[8*1024];
- dying = 1;
- va_start(arg, fmt);
- vseprint(buf, buf + (sizeof(buf)-1) / sizeof(*buf), fmt, arg);
- va_end(arg);
- fprint(2, "ftpfs: %s\n", buf);
- if(kapid > 0)
- postnote(PNGROUP, kapid, "die");
- exits(buf);
- }
- /*
- * like strncpy but make sure there's a terminating null
- */
- void*
- safecpy(void *to, void *from, int n)
- {
- char *a = ((char*)to) + n - 1;
- strncpy(to, from, n);
- *a = 0;
- return to;
- }
- /*
- * set the error string
- */
- int
- seterr(char *fmt, ...)
- {
- va_list arg;
- va_start(arg, fmt);
- vsnprint(errstring, sizeof errstring, fmt, arg);
- va_end(arg);
- return -1;
- }
- /*
- * create a new node
- */
- Node*
- newnode(Node *parent, String *name)
- {
- Node *np;
- static ulong path;
- Dir d;
- np = mallocz(sizeof(Node), 1);
- if(np == 0)
- fatal("out of memory");
- np->children = 0;
- if(parent){
- np->parent = parent;
- np->sibs = parent->children;
- parent->children = np;
- np->depth = parent->depth + 1;
- d.dev = 0; /* not stat'd */
- } else {
- /* the root node */
- np->parent = np;
- np->sibs = 0;
- np->depth = 0;
- d.dev = 1;
- }
- np->remname = name;
- d.name = s_to_c(name);
- d.atime = time(0);
- d.mtime = d.atime;
- d.uid = nouid;
- d.gid = nouid;
- d.muid = nouid;
- np->fp = 0;
- d.qid.path = ++path;
- d.qid.vers = 0;
- d.qid.type = QTFILE;
- d.type = 0;
- np->d = reallocdir(&d, 0);
- return np;
- }
- /*
- * walk one down the local mirror of the remote directory tree
- */
- Node*
- extendpath(Node *parent, String *elem)
- {
- Node *np;
- for(np = parent->children; np; np = np->sibs)
- if(strcmp(s_to_c(np->remname), s_to_c(elem)) == 0){
- s_free(elem);
- return np;
- }
- return newnode(parent, elem);
- }
- /*
- * flush the cached file, write it back if it's dirty
- */
- void
- uncache(Node *np)
- {
- if(fileisdirty(np))
- createfile(np);
- filefree(np);
- UNCACHED(np);
- }
- /*
- * invalidate all children of a node
- */
- void
- invalidate(Node *node)
- {
- Node *np;
- if(node->opens)
- return; /* don't invalidate something that's open */
- uncachedir(node, 0);
- /* invalidate children */
- for(np = node->children; np; np = np->sibs){
- if(np->opens)
- continue; /* don't invalidate something that's open */
- UNCACHED(np);
- invalidate(np);
- np->d->dev = 0;
- }
- }
- /*
- * make a top level tops-20 directory. They are automaticly valid.
- */
- Node*
- newtopsdir(char *name)
- {
- Node *np;
- np = extendpath(remroot, s_copy(name));
- if(!ISVALID(np)){
- np->d->qid.type = QTDIR;
- np->d->atime = time(0);
- np->d->mtime = np->d->atime;
- np->d->uid = "?uid?";
- np->d->gid = "?uid?";
- np->d->muid = "?uid?";
- np->d->mode = DMDIR|0777;
- np->d->length = 0;
- np->d = reallocdir(np->d, 1);
-
- if(changedir(np) >= 0)
- VALID(np);
- }
- return np;
- }
- /*
- * figure out if a symbolic link is to a directory or a file
- */
- void
- fixsymbolic(Node *node)
- {
- if(changedir(node) == 0){
- node->d->mode |= DMDIR;
- node->d->qid.type = QTDIR;
- } else
- node->d->qid.type = QTFILE;
- node->d->mode &= ~DMSYML;
- }
|