diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/exportfs |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/exportfs')
-rwxr-xr-x | sys/src/cmd/exportfs/exportfs.c | 937 | ||||
-rwxr-xr-x | sys/src/cmd/exportfs/exportfs.h | 144 | ||||
-rwxr-xr-x | sys/src/cmd/exportfs/exportsrv.c | 764 | ||||
-rwxr-xr-x | sys/src/cmd/exportfs/mkfile | 18 | ||||
-rwxr-xr-x | sys/src/cmd/exportfs/pattern.c | 154 |
5 files changed, 2017 insertions, 0 deletions
diff --git a/sys/src/cmd/exportfs/exportfs.c b/sys/src/cmd/exportfs/exportfs.c new file mode 100755 index 000000000..6022e78b7 --- /dev/null +++ b/sys/src/cmd/exportfs/exportfs.c @@ -0,0 +1,937 @@ +/* + * 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; /* initially stdin */ +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"; + +char *netdir = "", *local = "", *remote = ""; + +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"); +} + +static void +noteconn(int fd) +{ + NetConnInfo *nci; + + nci = getnetconninfo(nil, fd); + if (nci == nil) + return; + netdir = strdup(nci->dir); + local = strdup(nci->lsys); + remote = strdup(nci->rsys); + freenetconninfo(nci); +} + +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"); + + local = "me"; + remote = na; + 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 { + noteconn(netfd); + 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)) == 0) { + 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") == 0) + filterp = aanfilter; + else if (strcmp(args[0], "nofilter") != 0) + fatal("import filter argument unsupported: %s\n", args[0]); + + if (strcmp(args[1], "ssl") == 0) + encproto = Encssl; + else if (strcmp(args[1], "tls") == 0) + encproto = Enctls; + else if (strcmp(args[1], "clear") != 0) + fatal("import encryption proto unsupported: %s\n", args[1]); + + if (encproto == Enctls) + sysfatal("%s: tls has not yet been implemented", 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("%s", buf); /* caution: buf could contain '%' */ + 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", 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", buf); + if ((len = read(lfd, newport, sizeof newport - 1)) < 0) + sysfatal("filter: Cannot read %s: %r", 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]); +} diff --git a/sys/src/cmd/exportfs/exportfs.h b/sys/src/cmd/exportfs/exportfs.h new file mode 100755 index 000000000..0f7ba3e71 --- /dev/null +++ b/sys/src/cmd/exportfs/exportfs.h @@ -0,0 +1,144 @@ +/* + * exportfs.h - definitions for exporting file server + */ + +#define DEBUG if(!dbg){}else fprint +#define DFD 9 +#define fidhash(s) fhash[s%FHASHSIZE] + +typedef struct Fsrpc Fsrpc; +typedef struct Fid Fid; +typedef struct File File; +typedef struct Proc Proc; +typedef struct Qidtab Qidtab; + +struct Fsrpc +{ + int busy; /* Work buffer has pending rpc to service */ + uintptr pid; /* Pid of slave process executing the rpc */ + int canint; /* Interrupt gate */ + int flushtag; /* Tag on which to reply to flush */ + Fcall work; /* Plan 9 incoming Fcall */ + uchar *buf; /* Data buffer */ +}; + +struct Fid +{ + int fid; /* system fd for i/o */ + File *f; /* File attached to this fid */ + int mode; + int nr; /* fid number */ + int mid; /* Mount id */ + Fid *next; /* hash link */ + + /* for preaddir -- ARRGH! */ + Dir *dir; /* buffer for reading directories */ + int ndir; /* number of entries in dir */ + int cdir; /* number of consumed entries in dir */ + int gdir; /* glue index */ + vlong offset; /* offset in virtual directory */ +}; + +struct File +{ + char *name; + int ref; + Qid qid; + Qidtab *qidt; + int inval; + File *parent; + File *child; + File *childlist; +}; + +struct Proc +{ + uintptr pid; + int busy; + Proc *next; +}; + +struct Qidtab +{ + int ref; + int type; + int dev; + vlong path; + vlong uniqpath; + Qidtab *next; +}; + +enum +{ + MAXPROC = 50, + FHASHSIZE = 64, + Nr_workbufs = 50, + Fidchunk = 1000, + Npsmpt = 32, + Nqidbits = 5, + Nqidtab = (1<<Nqidbits), +}; + +char Ebadfid[]; +char Enotdir[]; +char Edupfid[]; +char Eopen[]; +char Exmnt[]; +char Enomem[]; +char Emip[]; +char Enopsmt[]; + +Extern Fsrpc *Workq; +Extern int dbg; +Extern File *root; +Extern File *psmpt; +Extern Fid **fhash; +Extern Fid *fidfree; +Extern Proc *Proclist; +Extern char psmap[Npsmpt]; +Extern Qidtab *qidtab[Nqidtab]; +Extern ulong messagesize; +Extern char Enomem[]; +Extern int srvfd; +Extern char* patternfile; + +/* File system protocol service procedures */ +void Xattach(Fsrpc*); +void Xauth(Fsrpc*); +void Xclunk(Fsrpc*); +void Xcreate(Fsrpc*); +void Xflush(Fsrpc*); +void Xnop(Fsrpc*); +void Xremove(Fsrpc*); +void Xstat(Fsrpc*); +void Xversion(Fsrpc*); +void Xwalk(Fsrpc*); +void Xwstat(Fsrpc*); +void slave(Fsrpc*); + +void reply(Fcall*, Fcall*, char*); +Fid *getfid(int); +int freefid(int); +Fid *newfid(int); +Fsrpc *getsbuf(void); +void initroot(void); +void fatal(char*, ...); +char* makepath(File*, char*); +File *file(File*, char*); +void freefile(File*); +void slaveopen(Fsrpc*); +void slaveread(Fsrpc*); +void slavewrite(Fsrpc*); +void blockingslave(void); +void reopen(Fid *f); +void noteproc(int, char*); +void flushaction(void*, char*); +void pushfcall(char*); +Qidtab* uniqueqid(Dir*); +void freeqid(Qidtab*); +char* estrdup(char*); +void* emallocz(uint); +int readmessage(int, char*, int); +void exclusions(void); +int excludefile(char*); +int preaddir(Fid*, uchar*, int, vlong); diff --git a/sys/src/cmd/exportfs/exportsrv.c b/sys/src/cmd/exportfs/exportsrv.c new file mode 100755 index 000000000..842880529 --- /dev/null +++ b/sys/src/cmd/exportfs/exportsrv.c @@ -0,0 +1,764 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#define Extern extern +#include "exportfs.h" + +extern char *netdir, *local, *remote; + +char Ebadfid[] = "Bad fid"; +char Enotdir[] = "Not a directory"; +char Edupfid[] = "Fid already in use"; +char Eopen[] = "Fid already opened"; +char Exmnt[] = "Cannot .. past mount point"; +char Emip[] = "Mount in progress"; +char Enopsmt[] = "Out of pseudo mount points"; +char Enomem[] = "No memory"; +char Eversion[] = "Bad 9P2000 version"; +char Ereadonly[] = "File system read only"; + +ulong messagesize; +int readonly; + +void +Xversion(Fsrpc *t) +{ + Fcall rhdr; + + if(t->work.msize > messagesize) + t->work.msize = messagesize; + messagesize = t->work.msize; + if(strncmp(t->work.version, "9P2000", 6) != 0){ + reply(&t->work, &rhdr, Eversion); + return; + } + rhdr.version = "9P2000"; + rhdr.msize = t->work.msize; + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xauth(Fsrpc *t) +{ + Fcall rhdr; + + reply(&t->work, &rhdr, "exportfs: authentication not required"); + t->busy = 0; +} + +void +Xflush(Fsrpc *t) +{ + Fsrpc *w, *e; + Fcall rhdr; + + e = &Workq[Nr_workbufs]; + + for(w = Workq; w < e; w++) { + if(w->work.tag == t->work.oldtag) { + DEBUG(DFD, "\tQ busy %d pid %p can %d\n", w->busy, w->pid, w->canint); + if(w->busy && w->pid) { + w->flushtag = t->work.tag; + DEBUG(DFD, "\tset flushtag %d\n", t->work.tag); + if(w->canint) + postnote(PNPROC, w->pid, "flush"); + t->busy = 0; + return; + } + } + } + + reply(&t->work, &rhdr, 0); + DEBUG(DFD, "\tflush reply\n"); + t->busy = 0; +} + +void +Xattach(Fsrpc *t) +{ + int i, nfd; + Fcall rhdr; + Fid *f; + char buf[128]; + + f = newfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + if(srvfd >= 0){ + if(psmpt == 0){ + Nomount: + reply(&t->work, &rhdr, Enopsmt); + t->busy = 0; + freefid(t->work.fid); + return; + } + for(i=0; i<Npsmpt; i++) + if(psmap[i] == 0) + break; + if(i >= Npsmpt) + goto Nomount; + sprint(buf, "%d", i); + f->f = file(psmpt, buf); + if(f->f == nil) + goto Nomount; + sprint(buf, "/mnt/exportfs/%d", i); + nfd = dup(srvfd, -1); + if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){ + errstr(buf, sizeof buf); + reply(&t->work, &rhdr, buf); + t->busy = 0; + freefid(t->work.fid); + close(nfd); + return; + } + psmap[i] = 1; + f->mid = i; + }else{ + f->f = root; + f->f->ref++; + } + + rhdr.qid = f->f->qid; + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +Fid* +clonefid(Fid *f, int new) +{ + Fid *n; + + n = newfid(new); + if(n == 0) { + n = getfid(new); + if(n == 0) + fatal("inconsistent fids"); + if(n->fid >= 0) + close(n->fid); + freefid(new); + n = newfid(new); + if(n == 0) + fatal("inconsistent fids2"); + } + n->f = f->f; + n->f->ref++; + return n; +} + +void +Xwalk(Fsrpc *t) +{ + char err[ERRMAX], *e; + Fcall rhdr; + Fid *f, *nf; + File *wf; + int i; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + nf = nil; + if(t->work.newfid != t->work.fid){ + nf = clonefid(f, t->work.newfid); + f = nf; + } + + rhdr.nwqid = 0; + e = nil; + for(i=0; i<t->work.nwname; i++){ + if(i == MAXWELEM){ + e = "Too many path elements"; + break; + } + + if(strcmp(t->work.wname[i], "..") == 0) { + if(f->f->parent == nil) { + e = Exmnt; + break; + } + wf = f->f->parent; + wf->ref++; + goto Accept; + } + + wf = file(f->f, t->work.wname[i]); + if(wf == 0){ + errstr(err, sizeof err); + e = err; + break; + } + Accept: + freefile(f->f); + rhdr.wqid[rhdr.nwqid++] = wf->qid; + f->f = wf; + continue; + } + + if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname)) + freefid(t->work.newfid); + if(rhdr.nwqid > 0) + e = nil; + reply(&t->work, &rhdr, e); + t->busy = 0; +} + +void +Xclunk(Fsrpc *t) +{ + Fcall rhdr; + Fid *f; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + if(f->fid >= 0) + close(f->fid); + + freefid(t->work.fid); + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xstat(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + Dir *d; + int s; + uchar *statbuf; + + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + if(f->fid >= 0) + d = dirfstat(f->fid); + else { + path = makepath(f->f, ""); + d = dirstat(path); + free(path); + } + + if(d == nil) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + d->qid.path = f->f->qidt->uniqpath; + s = sizeD2M(d); + statbuf = emallocz(s); + s = convD2M(d, statbuf, s); + free(d); + rhdr.nstat = s; + rhdr.stat = statbuf; + reply(&t->work, &rhdr, 0); + free(statbuf); + t->busy = 0; +} + +static int +getiounit(int fd) +{ + int n; + + n = iounit(fd); + if(n > messagesize-IOHDRSZ) + n = messagesize-IOHDRSZ; + return n; +} + +void +Xcreate(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + File *nf; + + if(readonly) { + reply(&t->work, &rhdr, Ereadonly); + t->busy = 0; + return; + } + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + + path = makepath(f->f, t->work.name); + f->fid = create(path, t->work.mode, t->work.perm); + free(path); + if(f->fid < 0) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + nf = file(f->f, t->work.name); + if(nf == 0) { + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + + f->mode = t->work.mode; + freefile(f->f); + f->f = nf; + rhdr.qid = f->f->qid; + rhdr.iounit = getiounit(f->fid); + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xremove(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + + if(readonly) { + reply(&t->work, &rhdr, Ereadonly); + t->busy = 0; + return; + } + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + + path = makepath(f->f, ""); + DEBUG(DFD, "\tremove: %s\n", path); + if(remove(path) < 0) { + free(path); + errstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + return; + } + free(path); + + f->f->inval = 1; + if(f->fid >= 0) + close(f->fid); + freefid(t->work.fid); + + reply(&t->work, &rhdr, 0); + t->busy = 0; +} + +void +Xwstat(Fsrpc *t) +{ + char err[ERRMAX], *path; + Fcall rhdr; + Fid *f; + int s; + char *strings; + Dir d; + + if(readonly) { + reply(&t->work, &rhdr, Ereadonly); + t->busy = 0; + return; + } + f = getfid(t->work.fid); + if(f == 0) { + reply(&t->work, &rhdr, Ebadfid); + t->busy = 0; + return; + } + strings = emallocz(t->work.nstat); /* ample */ + if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){ + rerrstr(err, sizeof err); + reply(&t->work, &rhdr, err); + t->busy = 0; + free(strings); + return; + } + + if(f->fid >= 0) + s = dirfwstat(f->fid, &d); + else { + path = makepath(f->f, ""); + s = dirwstat(path, &d); + free(path); + } + if(s < 0) { + rerrstr(err, sizeof err); + reply(&t->work, &rhdr, err); + } + else { + /* wstat may really be rename */ + if(strcmp(d.name, f->f->name)!=0 && strcmp(d.name, "")!=0){ + free(f->f->name); + f->f->name = estrdup(d.name); + } + reply(&t->work, &rhdr, 0); + } + free(strings); + t->busy = 0; +} + +/* + * based on libthread's threadsetname, but drags in less library code. + * actually just sets the arguments displayed. + */ +void +procsetname(char *fmt, ...) +{ + int fd; + char *cmdname; + char buf[128]; + va_list arg; + + va_start(arg, fmt); + cmdname = vsmprint(fmt, arg); + va_end(arg); + if (cmdname == nil) + return; + snprint(buf, sizeof buf, "#p/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + write(fd, cmdname, strlen(cmdname)+1); + close(fd); + } + free(cmdname); +} + +void +slave(Fsrpc *f) +{ + Proc *p; + uintptr pid; + Fcall rhdr; + static int nproc; + + if(readonly){ + switch(f->work.type){ + case Twrite: + reply(&f->work, &rhdr, Ereadonly); + f->busy = 0; + return; + case Topen: + if((f->work.mode&3) == OWRITE || (f->work.mode&OTRUNC)){ + reply(&f->work, &rhdr, Ereadonly); + f->busy = 0; + return; + } + } + } + for(;;) { + for(p = Proclist; p; p = p->next) { + if(p->busy == 0) { + f->pid = p->pid; + p->busy = 1; + pid = (uintptr)rendezvous((void*)p->pid, f); + if(pid != p->pid) + fatal("rendezvous sync fail"); + return; + } + } + + if(++nproc > MAXPROC) + fatal("too many procs"); + + pid = rfork(RFPROC|RFMEM); + switch(pid) { + case -1: + fatal("rfork"); + + case 0: + if (local[0] != '\0') + if (netdir[0] != '\0') + procsetname("%s: %s -> %s", netdir, + local, remote); + else + procsetname("%s -> %s", local, remote); + blockingslave(); + fatal("slave"); + + default: + p = malloc(sizeof(Proc)); + if(p == 0) + fatal("out of memory"); + + p->busy = 0; + p->pid = pid; + p->next = Proclist; + Proclist = p; + + rendezvous((void*)pid, p); + } + } +} + +void +blockingslave(void) +{ + Fsrpc *p; + Fcall rhdr; + Proc *m; + uintptr pid; + + notify(flushaction); + + pid = getpid(); + + m = rendezvous((void*)pid, 0); + + for(;;) { + p = rendezvous((void*)pid, (void*)pid); + if(p == (void*)~0) /* Interrupted */ + continue; + + DEBUG(DFD, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid); + if(p->flushtag != NOTAG) + goto flushme; + + switch(p->work.type) { + case Tread: + slaveread(p); + break; + + case Twrite: + slavewrite(p); + break; + + case Topen: + slaveopen(p); + break; + + default: + reply(&p->work, &rhdr, "exportfs: slave type error"); + } + if(p->flushtag != NOTAG) { +flushme: + p->work.type = Tflush; + p->work.tag = p->flushtag; + reply(&p->work, &rhdr, 0); + } + p->busy = 0; + m->busy = 0; + } +} + +int +openmount(int sfd) +{ + int p[2]; + char *arg[10], fdbuf[20], mbuf[20]; + + if(pipe(p) < 0) + return -1; + + switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG)){ + case -1: + return -1; + + default: + close(sfd); + close(p[0]); + return p[1]; + + case 0: + break; + } + + close(p[1]); + + arg[0] = "exportfs"; + snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd); + arg[1] = fdbuf; + snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ); + arg[2] = mbuf; + arg[3] = nil; + + close(0); + close(1); + dup(p[0], 0); + dup(p[0], 1); + exec("/bin/exportfs", arg); + _exits("whoops: exec failed"); + return -1; +} + +void +slaveopen(Fsrpc *p) +{ + char err[ERRMAX], *path; + Fcall *work, rhdr; + Fid *f; + Dir *d; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + if(f->fid >= 0) { + close(f->fid); + f->fid = -1; + } + + path = makepath(f->f, ""); + DEBUG(DFD, "\topen: %s %d\n", path, work->mode); + + p->canint = 1; + if(p->flushtag != NOTAG){ + free(path); + return; + } + /* There is a race here I ignore because there are no locks */ + f->fid = open(path, work->mode); + free(path); + p->canint = 0; + if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) { + Error: + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + f->f->qid = d->qid; + free(d); + if(f->f->qid.type & QTMOUNT){ /* fork new exportfs for this */ + f->fid = openmount(f->fid); + if(f->fid < 0) + goto Error; + } + + DEBUG(DFD, "\topen: fd %d\n", f->fid); + f->mode = work->mode; + f->offset = 0; + rhdr.iounit = getiounit(f->fid); + rhdr.qid = f->f->qid; + reply(work, &rhdr, 0); +} + +void +slaveread(Fsrpc *p) +{ + Fid *f; + int n, r; + Fcall *work, rhdr; + char *data, err[ERRMAX]; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + + n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; + p->canint = 1; + if(p->flushtag != NOTAG) + return; + data = malloc(n); + if(data == nil) + fatal(Enomem); + + /* can't just call pread, since directories must update the offset */ + if(patternfile != nil && (f->f->qid.type&QTDIR)) + r = preaddir(f, (uchar*)data, n, work->offset); + else + r = pread(f->fid, data, n, work->offset); + p->canint = 0; + if(r < 0) { + free(data); + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + + DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r); + + rhdr.data = data; + rhdr.count = r; + reply(work, &rhdr, 0); + free(data); +} + +void +slavewrite(Fsrpc *p) +{ + char err[ERRMAX]; + Fcall *work, rhdr; + Fid *f; + int n; + + work = &p->work; + + f = getfid(work->fid); + if(f == 0) { + reply(work, &rhdr, Ebadfid); + return; + } + + n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; + p->canint = 1; + if(p->flushtag != NOTAG) + return; + n = pwrite(f->fid, work->data, n, work->offset); + p->canint = 0; + if(n < 0) { + errstr(err, sizeof err); + reply(work, &rhdr, err); + return; + } + + DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid); + + rhdr.count = n; + reply(work, &rhdr, 0); +} + +void +reopen(Fid *f) +{ + USED(f); + fatal("reopen"); +} + +void +flushaction(void *a, char *cause) +{ + USED(a); + if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) { + fprint(2, "exportsrv: note: %s\n", cause); + exits("noted"); + } + if(strncmp(cause, "kill", 4) == 0) + noted(NDFLT); + + noted(NCONT); +} diff --git a/sys/src/cmd/exportfs/mkfile b/sys/src/cmd/exportfs/mkfile new file mode 100755 index 000000000..cf1f51451 --- /dev/null +++ b/sys/src/cmd/exportfs/mkfile @@ -0,0 +1,18 @@ +</$objtype/mkfile + +TARG=exportfs +OFILES=\ + exportfs.$O\ + exportsrv.$O\ + pattern.$O\ + +HFILES=exportfs.h\ + +BIN=/$objtype/bin + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +</sys/src/cmd/mkone diff --git a/sys/src/cmd/exportfs/pattern.c b/sys/src/cmd/exportfs/pattern.c new file mode 100755 index 000000000..0dcbc9380 --- /dev/null +++ b/sys/src/cmd/exportfs/pattern.c @@ -0,0 +1,154 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include <bio.h> +#include <regexp.h> +#define Extern +#include "exportfs.h" + +Reprog **exclude, **include; +char *patternfile; + +void +exclusions(void) +{ + Biobuf *f; + int ni, nmaxi, ne, nmaxe; + char *line; + + if(patternfile == nil) + return; + + f = Bopen(patternfile, OREAD); + if(f == nil) + fatal("cannot open patternfile"); + ni = 0; + nmaxi = 100; + include = malloc(nmaxi*sizeof(*include)); + if(include == nil) + fatal("out of memory"); + include[0] = nil; + ne = 0; + nmaxe = 100; + exclude = malloc(nmaxe*sizeof(*exclude)); + if(exclude == nil) + fatal("out of memory"); + exclude[0] = nil; + while(line = Brdline(f, '\n')){ + line[Blinelen(f) - 1] = 0; + if(strlen(line) < 2 || line[1] != ' ') + continue; + switch(line[0]){ + case '+': + if(ni+1 >= nmaxi){ + nmaxi = 2*nmaxi; + include = realloc(include, nmaxi*sizeof(*include)); + if(include == nil) + fatal("out of memory"); + } + DEBUG(DFD, "\tinclude %s\n", line+2); + include[ni] = regcomp(line+2); + include[++ni] = nil; + break; + case '-': + if(ne+1 >= nmaxe){ + nmaxe = 2*nmaxe; + exclude = realloc(exclude, nmaxe*sizeof(*exclude)); + if(exclude == nil) + fatal("out of memory"); + } + DEBUG(DFD, "\texclude %s\n", line+2); + exclude[ne] = regcomp(line+2); + exclude[++ne] = nil; + break; + default: + DEBUG(DFD, "ignoring pattern %s\n", line); + break; + } + } + Bterm(f); +} + +int +excludefile(char *path) +{ + Reprog **re; + char *p; + + if(*(path+1) == 0) + p = "/"; + else + p = path+1; + + DEBUG(DFD, "checking %s\n", path); + for(re = include; *re != nil; re++){ + if(regexec(*re, p, nil, 0) != 1){ + DEBUG(DFD, "excluded+ %s\n", path); + return -1; + } + } + for(re = exclude; *re != nil; re++){ + if(regexec(*re, p, nil, 0) == 1){ + DEBUG(DFD, "excluded- %s\n", path); + return -1; + } + } + return 0; +} + +int +preaddir(Fid *f, uchar *data, int n, vlong offset) +{ + int r = 0, m; + Dir *d; + + DEBUG(DFD, "\tpreaddir n=%d wo=%lld fo=%lld\n", n, offset, f->offset); + if(offset == 0 && f->offset != 0){ + if(seek(f->fid, 0, 0) != 0) + return -1; + f->offset = f->cdir = f->ndir = 0; + free(f->dir); + f->dir = nil; + }else if(offset != f->offset){ + werrstr("can't seek dir %lld to %lld", f->offset, offset); + return -1; + } + + while(n > 0){ + if(f->dir == nil){ + f->ndir = dirread(f->fid, &f->dir); + if(f->ndir < 0) + return f->ndir; + if(f->ndir == 0) + return r; + } + d = &f->dir[f->cdir++]; + if(exclude){ + char *p = makepath(f->f, d->name); + if(excludefile(p)){ + free(p); + goto skipentry; + } + free(p); + } + m = convD2M(d, data, n); + DEBUG(DFD, "\t\tconvD2M %d\n", m); + if(m <= BIT16SZ){ + DEBUG(DFD, "\t\t\tneeded %d\n", GBIT16(data)); + /* not enough room for full entry; leave for next time */ + f->cdir--; + return r; + }else{ + data += m; + n -= m; + r += m; + f->offset += m; + } +skipentry: if(f->cdir >= f->ndir){ + f->cdir = f->ndir = 0; + free(f->dir); + f->dir = nil; + } + } + return r; +} |