123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- /*
- * exportfs - Export a plan 9 name space across a network
- */
- #include <u.h>
- #include <libc.h>
- #include <auth.h>
- #include <fcall.h>
- #include <libsec.h>
- #define Extern
- #include "exportfs.h"
- #define QIDPATH ((1LL<<48)-1)
- vlong newqid = 0;
- enum {
- Encnone,
- Encssl,
- Enctls,
- };
- void (*fcalls[])(Fsrpc*) =
- {
- [Tversion] Xversion,
- [Tauth] Xauth,
- [Tflush] Xflush,
- [Tattach] Xattach,
- [Twalk] Xwalk,
- [Topen] slave,
- [Tcreate] Xcreate,
- [Tclunk] Xclunk,
- [Tread] slave,
- [Twrite] slave,
- [Tremove] Xremove,
- [Tstat] Xstat,
- [Twstat] Xwstat,
- };
- /* accounting and debugging counters */
- int filecnt;
- int freecnt;
- int qidcnt;
- int qfreecnt;
- int ncollision;
- int netfd;
- int srvfd = -1;
- int nonone = 1;
- char *filterp;
- char *ealgs = "rc4_256 sha1";
- char *aanfilter = "/bin/aan";
- int encproto = Encnone;
- int readonly;
- static void mksecret(char *, uchar *);
- static int localread9pmsg(int, void *, uint, ulong *);
- static char *anstring = "tcp!*!0";
- int filter(int, char *);
- void
- usage(void)
- {
- fprint(2, "usage: %s [-adnsR] [-f dbgfile] [-m msize] [-r root] [-S srvfile] [-e 'crypt hash'] [-P exclusion-file] [-A announce-string] [-B address]\n", argv0);
- fatal("usage");
- }
- void
- main(int argc, char **argv)
- {
- char buf[ERRMAX], ebuf[ERRMAX], *srvfdfile;
- Fsrpc *r;
- int doauth, n, fd;
- char *dbfile, *srv, *na, *nsfile, *keyspec;
- AuthInfo *ai;
- ulong initial;
- dbfile = "/tmp/exportdb";
- srv = nil;
- srvfd = -1;
- srvfdfile = nil;
- na = nil;
- nsfile = nil;
- keyspec = "";
- doauth = 0;
- ai = nil;
- ARGBEGIN{
- case 'a':
- doauth = 1;
- break;
- case 'd':
- dbg++;
- break;
- case 'e':
- ealgs = EARGF(usage());
- if(*ealgs == 0 || strcmp(ealgs, "clear") == 0)
- ealgs = nil;
- break;
- case 'f':
- dbfile = EARGF(usage());
- break;
- case 'k':
- keyspec = EARGF(usage());
- break;
- case 'm':
- messagesize = strtoul(EARGF(usage()), nil, 0);
- break;
- case 'n':
- nonone = 0;
- break;
- case 'r':
- srv = EARGF(usage());
- break;
- case 's':
- srv = "/";
- break;
- case 'A':
- anstring = EARGF(usage());
- break;
- case 'B':
- na = EARGF(usage());
- break;
- case 'F':
- /* accepted but ignored, for backwards compatibility */
- break;
- case 'N':
- nsfile = EARGF(usage());
- break;
- case 'P':
- patternfile = EARGF(usage());
- break;
- case 'R':
- readonly = 1;
- break;
- case 'S':
- if(srvfdfile)
- usage();
- srvfdfile = EARGF(usage());
- break;
- default:
- usage();
- }ARGEND
- USED(argc, argv);
- if(doauth){
- /*
- * We use p9any so we don't have to visit this code again, with the
- * cost that this code is incompatible with the old world, which
- * requires p9sk2. (The two differ in who talks first, so compatibility
- * is awkward.)
- */
- ai = auth_proxy(0, auth_getkey, "proto=p9any role=server %s", keyspec);
- if(ai == nil)
- fatal("auth_proxy: %r");
- if(nonone && strcmp(ai->cuid, "none") == 0)
- fatal("exportfs by none disallowed");
- if(auth_chuid(ai, nsfile) < 0)
- fatal("auth_chuid: %r");
- putenv("service", "exportfs");
- }
- if(srvfdfile){
- if((srvfd = open(srvfdfile, ORDWR)) < 0)
- sysfatal("open '%s': %r", srvfdfile);
- }
- if(na){
- if(srv == nil)
- sysfatal("-B requires -s");
- if((fd = dial(netmkaddr(na, 0, "importfs"), 0, 0, 0)) < 0)
- sysfatal("can't dial %s: %r", na);
-
- ai = auth_proxy(fd, auth_getkey, "proto=p9any role=client %s", keyspec);
- if(ai == nil)
- sysfatal("%r: %s", na);
- dup(fd, 0);
- dup(fd, 1);
- close(fd);
- }
- exclusions();
- if(dbg) {
- n = create(dbfile, OWRITE|OTRUNC, 0666);
- dup(n, DFD);
- close(n);
- }
- if(srvfd >= 0 && srv){
- fprint(2, "exportfs: -S cannot be used with -r or -s\n");
- usage();
- }
- DEBUG(DFD, "exportfs: started\n");
- rfork(RFNOTEG);
- if(messagesize == 0){
- messagesize = iounit(netfd);
- if(messagesize == 0)
- messagesize = 8192+IOHDRSZ;
- }
- Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs);
- // for(i=0; i<Nr_workbufs; i++)
- // Workq[i].buf = emallocz(messagesize);
- fhash = emallocz(sizeof(Fid*)*FHASHSIZE);
- fmtinstall('F', fcallfmt);
- /*
- * Get tree to serve from network connection,
- * check we can get there and ack the connection
- */
- if(srvfd != -1) {
- /* do nothing */
- }
- else if(srv) {
- chdir(srv);
- DEBUG(DFD, "invoked as server for %s", srv);
- strncpy(buf, srv, sizeof buf);
- }
- else {
- buf[0] = 0;
- n = read(0, buf, sizeof(buf)-1);
- if(n < 0) {
- errstr(buf, sizeof buf);
- fprint(0, "read(0): %s", buf);
- DEBUG(DFD, "read(0): %s", buf);
- exits(buf);
- }
- buf[n] = 0;
- if(chdir(buf) < 0) {
- errstr(ebuf, sizeof ebuf);
- fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
- DEBUG(DFD, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
- exits(ebuf);
- }
- }
- DEBUG(DFD, "\niniting root\n");
- initroot();
- DEBUG(DFD, "exportfs: %s\n", buf);
- if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2)
- fatal("open ack write");
- if (readn(netfd, &initial, sizeof(ulong)) < sizeof(ulong))
- fatal("can't read initial string: %r\n");
- if (!strncmp((char *)&initial, "impo", sizeof(ulong))) {
- char buf[128], *p, *args[3];
- // New import. Read import's parameters...
- initial = 0;
- p = buf;
- while (p - buf < sizeof buf) {
- if ((n = read(netfd, p, 1)) < 0)
- fatal("can't read impo arguments: %r\n");
- if (n == 0)
- fatal("connection closed while reading arguments\n");
- if (*p == '\n')
- *p = '\0';
- if (*p++ == '\0')
- break;
- }
-
- if (tokenize(buf, args, nelem(args)) != 2)
- fatal("impo arguments invalid: impo%s...\n", buf);
- if (!strcmp(args[0], "aan"))
- filterp = aanfilter;
- else if (strcmp(args[0], "nofilter"))
- fatal("import filter argument unsupported: %s\n", args[0]);
- if (!strcmp(args[1], "ssl"))
- encproto = Encssl;
- else if (!strcmp(args[1], "tls"))
- encproto = Enctls;
- else if (strcmp(args[1], "clear"))
- fatal("import encryption proto unsupported: %s\n", args[1]);
- if (encproto == Enctls)
- sysfatal("%s: tls has not yet been implemented\n", argv[0]);
- }
- if (encproto != Encnone && ealgs && ai) {
- uchar key[16];
- uchar digest[SHA1dlen];
- char fromclientsecret[21];
- char fromserversecret[21];
- int i;
- memmove(key+4, ai->secret, ai->nsecret);
- /* exchange random numbers */
- srand(truerand());
- for(i = 0; i < 4; i++)
- key[i+12] = rand();
- if (initial)
- fatal("Protocol botch: old import\n");
- if(readn(netfd, key, 4) != 4)
- fatal("can't read key part; %r\n");
- if(write(netfd, key+12, 4) != 4)
- fatal("can't write key part; %r\n");
- /* scramble into two secrets */
- sha1(key, sizeof(key), digest, nil);
- mksecret(fromclientsecret, digest);
- mksecret(fromserversecret, digest+10);
- if (filterp)
- netfd = filter(netfd, filterp);
- switch (encproto) {
- case Encssl:
- netfd = pushssl(netfd, ealgs, fromserversecret,
- fromclientsecret, nil);
- break;
- case Enctls:
- default:
- fatal("Unsupported encryption protocol\n");
- }
- if(netfd < 0)
- fatal("can't establish ssl connection: %r");
- }
- else if (filterp) {
- if (initial)
- fatal("Protocol botch: don't know how to deal with this\n");
- netfd = filter(netfd, filterp);
- }
- /*
- * Start serving file requests from the network
- */
- for(;;) {
- r = getsbuf();
- if(r == 0)
- fatal("Out of service buffers");
-
- n = localread9pmsg(netfd, r->buf, messagesize, &initial);
- if(n <= 0)
- fatal(nil);
- if(convM2S(r->buf, n, &r->work) == 0)
- fatal("convM2S format error");
- DEBUG(DFD, "%F\n", &r->work);
- (fcalls[r->work.type])(r);
- }
- }
- // WARNING: Replace this with the original version as soon as all
- // _old_ imports have been replaced with negotiating imports. Also
- // cpu relies on this (which needs to be fixed!) -- pb.
- static int
- localread9pmsg(int fd, void *abuf, uint n, ulong *initial)
- {
- int m, len;
- uchar *buf;
- buf = abuf;
- /* read count */
- assert(BIT32SZ == sizeof(ulong));
- if (*initial) {
- memcpy(buf, initial, BIT32SZ);
- *initial = 0;
- }
- else {
- m = readn(fd, buf, BIT32SZ);
- if(m != BIT32SZ){
- if(m < 0)
- return -1;
- return 0;
- }
- }
- len = GBIT32(buf);
- if(len <= BIT32SZ || len > n){
- werrstr("bad length in 9P2000 message header");
- return -1;
- }
- len -= BIT32SZ;
- m = readn(fd, buf+BIT32SZ, len);
- if(m < len)
- return 0;
- return BIT32SZ+m;
- }
- void
- reply(Fcall *r, Fcall *t, char *err)
- {
- uchar *data;
- int n;
- t->tag = r->tag;
- t->fid = r->fid;
- if(err) {
- t->type = Rerror;
- t->ename = err;
- }
- else
- t->type = r->type + 1;
- DEBUG(DFD, "\t%F\n", t);
- data = malloc(messagesize); /* not mallocz; no need to clear */
- if(data == nil)
- fatal(Enomem);
- n = convS2M(t, data, messagesize);
- if(write(netfd, data, n)!=n)
- {syslog(0, "exportfs", "short write: %r");
- fatal("mount write");
- }
- free(data);
- }
- Fid *
- getfid(int nr)
- {
- Fid *f;
- for(f = fidhash(nr); f; f = f->next)
- if(f->nr == nr)
- return f;
- return 0;
- }
- int
- freefid(int nr)
- {
- Fid *f, **l;
- char buf[128];
- l = &fidhash(nr);
- for(f = *l; f; f = f->next) {
- if(f->nr == nr) {
- if(f->mid) {
- sprint(buf, "/mnt/exportfs/%d", f->mid);
- unmount(0, buf);
- psmap[f->mid] = 0;
- }
- if(f->f) {
- freefile(f->f);
- f->f = nil;
- }
- if(f->dir){
- free(f->dir);
- f->dir = nil;
- }
- *l = f->next;
- f->next = fidfree;
- fidfree = f;
- return 1;
- }
- l = &f->next;
- }
- return 0;
- }
- Fid *
- newfid(int nr)
- {
- Fid *new, **l;
- int i;
- l = &fidhash(nr);
- for(new = *l; new; new = new->next)
- if(new->nr == nr)
- return 0;
- if(fidfree == 0) {
- fidfree = emallocz(sizeof(Fid) * Fidchunk);
- for(i = 0; i < Fidchunk-1; i++)
- fidfree[i].next = &fidfree[i+1];
- fidfree[Fidchunk-1].next = 0;
- }
- new = fidfree;
- fidfree = new->next;
- memset(new, 0, sizeof(Fid));
- new->next = *l;
- *l = new;
- new->nr = nr;
- new->fid = -1;
- new->mid = 0;
- return new;
- }
- Fsrpc *
- getsbuf(void)
- {
- static int ap;
- int look, rounds;
- Fsrpc *wb;
- int small_instead_of_fast = 1;
- if(small_instead_of_fast)
- ap = 0; /* so we always start looking at the beginning and reuse buffers */
- for(rounds = 0; rounds < 10; rounds++) {
- for(look = 0; look < Nr_workbufs; look++) {
- if(++ap == Nr_workbufs)
- ap = 0;
- if(Workq[ap].busy == 0)
- break;
- }
- if(look == Nr_workbufs){
- sleep(10 * rounds);
- continue;
- }
- wb = &Workq[ap];
- wb->pid = 0;
- wb->canint = 0;
- wb->flushtag = NOTAG;
- wb->busy = 1;
- if(wb->buf == nil) /* allocate buffers dynamically to keep size down */
- wb->buf = emallocz(messagesize);
- return wb;
- }
- fatal("No more work buffers");
- return nil;
- }
- void
- freefile(File *f)
- {
- File *parent, *child;
- Loop:
- f->ref--;
- if(f->ref > 0)
- return;
- freecnt++;
- if(f->ref < 0) abort();
- DEBUG(DFD, "free %s\n", f->name);
- /* delete from parent */
- parent = f->parent;
- if(parent->child == f)
- parent->child = f->childlist;
- else{
- for(child=parent->child; child->childlist!=f; child=child->childlist)
- if(child->childlist == nil)
- fatal("bad child list");
- child->childlist = f->childlist;
- }
- freeqid(f->qidt);
- free(f->name);
- f->name = nil;
- free(f);
- f = parent;
- if(f != nil)
- goto Loop;
- }
- File *
- file(File *parent, char *name)
- {
- Dir *dir;
- char *path;
- File *f;
- DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
- path = makepath(parent, name);
- if(patternfile != nil && excludefile(path)){
- free(path);
- return nil;
- }
- dir = dirstat(path);
- free(path);
- if(dir == nil)
- return nil;
- for(f = parent->child; f; f = f->childlist)
- if(strcmp(name, f->name) == 0)
- break;
- if(f == nil){
- f = emallocz(sizeof(File));
- f->name = estrdup(name);
- f->parent = parent;
- f->childlist = parent->child;
- parent->child = f;
- parent->ref++;
- f->ref = 0;
- filecnt++;
- }
- f->ref++;
- f->qid.type = dir->qid.type;
- f->qid.vers = dir->qid.vers;
- f->qidt = uniqueqid(dir);
- f->qid.path = f->qidt->uniqpath;
- f->inval = 0;
- free(dir);
- return f;
- }
- void
- initroot(void)
- {
- Dir *dir;
- root = emallocz(sizeof(File));
- root->name = estrdup(".");
- dir = dirstat(root->name);
- if(dir == nil)
- fatal("root stat");
- root->ref = 1;
- root->qid.vers = dir->qid.vers;
- root->qidt = uniqueqid(dir);
- root->qid.path = root->qidt->uniqpath;
- root->qid.type = QTDIR;
- free(dir);
- psmpt = emallocz(sizeof(File));
- psmpt->name = estrdup("/");
- dir = dirstat(psmpt->name);
- if(dir == nil)
- return;
- psmpt->ref = 1;
- psmpt->qid.vers = dir->qid.vers;
- psmpt->qidt = uniqueqid(dir);
- psmpt->qid.path = psmpt->qidt->uniqpath;
- free(dir);
- psmpt = file(psmpt, "mnt");
- if(psmpt == 0)
- return;
- psmpt = file(psmpt, "exportfs");
- }
- char*
- makepath(File *p, char *name)
- {
- int i, n;
- char *c, *s, *path, *seg[256];
- seg[0] = name;
- n = strlen(name)+2;
- for(i = 1; i < 256 && p; i++, p = p->parent){
- seg[i] = p->name;
- n += strlen(p->name)+1;
- }
- path = malloc(n);
- if(path == nil)
- fatal("out of memory");
- s = path;
- while(i--) {
- for(c = seg[i]; *c; c++)
- *s++ = *c;
- *s++ = '/';
- }
- while(s[-1] == '/')
- s--;
- *s = '\0';
- return path;
- }
- int
- qidhash(vlong path)
- {
- int h, n;
- h = 0;
- for(n=0; n<64; n+=Nqidbits){
- h ^= path;
- path >>= Nqidbits;
- }
- return h & (Nqidtab-1);
- }
- void
- freeqid(Qidtab *q)
- {
- ulong h;
- Qidtab *l;
- q->ref--;
- if(q->ref > 0)
- return;
- qfreecnt++;
- h = qidhash(q->path);
- if(qidtab[h] == q)
- qidtab[h] = q->next;
- else{
- for(l=qidtab[h]; l->next!=q; l=l->next)
- if(l->next == nil)
- fatal("bad qid list");
- l->next = q->next;
- }
- free(q);
- }
- Qidtab*
- qidlookup(Dir *d)
- {
- ulong h;
- Qidtab *q;
- h = qidhash(d->qid.path);
- for(q=qidtab[h]; q!=nil; q=q->next)
- if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
- return q;
- return nil;
- }
- int
- qidexists(vlong path)
- {
- int h;
- Qidtab *q;
- for(h=0; h<Nqidtab; h++)
- for(q=qidtab[h]; q!=nil; q=q->next)
- if(q->uniqpath == path)
- return 1;
- return 0;
- }
- Qidtab*
- uniqueqid(Dir *d)
- {
- ulong h;
- vlong path;
- Qidtab *q;
- q = qidlookup(d);
- if(q != nil){
- q->ref++;
- return q;
- }
- path = d->qid.path;
- while(qidexists(path)){
- DEBUG(DFD, "collision on %s\n", d->name);
- /* collision: find a new one */
- ncollision++;
- path &= QIDPATH;
- ++newqid;
- if(newqid >= (1<<16)){
- DEBUG(DFD, "collision wraparound\n");
- newqid = 1;
- }
- path |= newqid<<48;
- DEBUG(DFD, "assign qid %.16llux\n", path);
- }
- q = mallocz(sizeof(Qidtab), 1);
- if(q == nil)
- fatal("no memory for qid table");
- qidcnt++;
- q->ref = 1;
- q->type = d->type;
- q->dev = d->dev;
- q->path = d->qid.path;
- q->uniqpath = path;
- h = qidhash(d->qid.path);
- q->next = qidtab[h];
- qidtab[h] = q;
- return q;
- }
- void
- fatal(char *s, ...)
- {
- char buf[ERRMAX];
- va_list arg;
- Proc *m;
- if (s) {
- va_start(arg, s);
- vsnprint(buf, ERRMAX, s, arg);
- va_end(arg);
- }
- /* Clear away the slave children */
- for(m = Proclist; m; m = m->next)
- postnote(PNPROC, m->pid, "kill");
- DEBUG(DFD, "%s\n", buf);
- if (s)
- sysfatal(buf);
- else
- exits(nil);
- }
- void*
- emallocz(uint n)
- {
- void *p;
- p = mallocz(n, 1);
- if(p == nil)
- fatal(Enomem);
- return p;
- }
- char*
- estrdup(char *s)
- {
- char *t;
- t = strdup(s);
- if(t == nil)
- fatal(Enomem);
- return t;
- }
- /* Network on fd1, mount driver on fd0 */
- int
- filter(int fd, char *cmd)
- {
- int p[2], lfd, len, nb, argc;
- char newport[128], buf[128], devdir[40], *s, *file, *argv[16];
- // Get a free port and post it to the client.
- if (announce(anstring, devdir) < 0)
- sysfatal("filter: Cannot announce %s: %r\n", anstring);
- snprint(buf, sizeof(buf), "%s/local", devdir);
- buf[sizeof buf - 1] = '\0';
- if ((lfd = open(buf, OREAD)) < 0)
- sysfatal("filter: Cannot open %s: %r\n", buf);
- if ((len = read(lfd, newport, sizeof newport - 1)) < 0)
- sysfatal("filter: Cannot read %s: %r\n", buf);
- close(lfd);
- newport[len] = '\0';
- if ((s = strchr(newport, '\n')) != nil)
- *s = '\0';
- if ((nb = write(fd, newport, len)) < 0)
- sysfatal("getport; cannot write port; %r");
- assert(nb == len);
- argc = tokenize(cmd, argv, nelem(argv)-2);
- if (argc == 0)
- sysfatal("filter: empty command");
- argv[argc++] = buf;
- argv[argc] = nil;
- file = argv[0];
- if (s = strrchr(argv[0], '/'))
- argv[0] = s+1;
- if(pipe(p) < 0)
- fatal("pipe");
- switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
- case -1:
- fatal("rfork record module");
- case 0:
- if (dup(p[0], 1) < 0)
- fatal("filter: Cannot dup to 1; %r\n");
- if (dup(p[0], 0) < 0)
- fatal("filter: Cannot dup to 0; %r\n");
- close(p[0]);
- close(p[1]);
- exec(file, argv);
- fatal("exec record module");
- default:
- close(fd);
- close(p[0]);
- }
- return p[1];
- }
- static void
- mksecret(char *t, uchar *f)
- {
- sprint(t, "%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
- f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9]);
- }
|