Adds kexportfs and ksrvfs used by syscall-exportfs patch. This work was done by rsc in 2005 for 9Netics. Reference: /n/sources/patch/kexportfs Date: Fri Mar 18 22:39:23 CET 2016 Signed-off-by: skip.tavakkolian@gmail.com --- /sys/src/cmd/kexportfs/kexportfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/kexportfs/kexportfs.c Fri Mar 18 22:36:49 2016 @@ -0,0 +1,463 @@ +/* + * exportfs - Export a plan 9 name space across a network + */ +#include +#include +#include +#include +#include + +enum { + Encnone, + Encssl, + Enctls, +}; + +#define DEBUG if(dbg)fprint +#define DFD 2 + +int dbg; +int netfd; +int srvfd = -1; +int nonone = 1; +char *filterp; +char *ealgs = "rc4_256 sha1"; +char *aanfilter = "/bin/aan"; +int encproto = Encnone; + +static void mksecret(char *, uchar *); +static int localread9pmsg(int, void *, uint, ulong *); +static char *anstring = "tcp!*!0"; +int filter(int, char *); +void fatal(char*, ...); +void copylog(int, int, char*); + +int export(int, char*, int); + +void +usage(void) +{ + fprint(2, "usage: %s [-ads] [-f dbgfile] [-m msize] [-r root] [-S srvfile] [-e 'crypt hash'] [-A announce-string]\n", argv0); + fprint(2, " %s -B address\n", argv0); + fatal("usage"); +} + +void +main(int argc, char **argv) +{ + char buf[ERRMAX], ebuf[ERRMAX]; + int n, fd; + char *dbfile, *srv, *file, *na, *nsfile, *keyspec; + AuthInfo *ai; + ulong initial; + int negotiate; + + dbfile = "/tmp/exportdb"; + srv = nil; + srvfd = -1; + na = nil; + nsfile = nil; + keyspec = ""; + + netfd = 0; + dup(2, 1); + + negotiate = 1; + ai = nil; + fmtinstall('F', fcallfmt); + fmtinstall('D', dirmodefmt); + fmtinstall('H', encodefmt); + + ARGBEGIN{ + case 'a': + /* + * 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"); + break; + + case 'k': + keyspec = EARGF(usage()); + break; + + case 'e': + ealgs = ARGF(); + if(ealgs == nil) + usage(); + if(*ealgs == 0 || strcmp(ealgs, "clear") == 0) + ealgs = nil; + break; + + case 'S': + if(srvfd != -1) + usage(); + file = EARGF(usage()); + if((srvfd = open(file, ORDWR)) < 0) + sysfatal("open '%s': %r", file); + break; + + case 'd': + dbg++; + break; + + case 'f': + dbfile = EARGF(usage()); + break; + + case 'n': + nonone = 0; + break; + + case 'N': + nsfile = EARGF(usage()); + break; + + case 'r': + srv = EARGF(usage()); + break; + + case 's': + srv = "/"; + break; + + case 'A': + anstring = EARGF(usage()); + break; + + case 'Z': + negotiate = 0; + break; + + default: + usage(); + }ARGEND + USED(argc, argv); + + 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); + } + + if(dbg){ + n = create(dbfile, OWRITE|OTRUNC, 0666); + dup(n, 2); + dup(2, 1); + if(n > 2) + 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); + + /* + * 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, "exportfs: %s\n", buf); + + if(srv == nil && srvfd == -1 && write(0, "OK", 2) != 2) + fatal("open ack write"); + + if(!negotiate) + goto serve; + + if(readn(netfd, &initial, sizeof(ulong)) < sizeof(ulong)) + fatal("can't read initial string: %r\n"); + + if (strncmp((char *)&initial, "impo", sizeof(ulong)) != 0) + fatal("cannot negotiate"); + + { + 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); + } + +serve: + + if(dbg){ + int p[2]; + + pipe(p); + switch(fork()){ + case -1: + fatal("fork"); + case 0: + close(p[0]); + switch(fork()){ + case -1: + fatal("fork"); + case 0: + fprint(2, "%d: copylog\n", getpid()); + copylog(netfd, p[1], ""); + postnote(PNGROUP, getpid(), "die"); + break; + default: + fprint(2, "%d: copylog\n", getpid()); + copylog(p[1], netfd, " "); + postnote(PNGROUP, getpid(), "die"); + break; + } + _exits(nil); + default: + close(p[1]); + close(netfd); + netfd = p[0]; + } + } + + fprint(2, "%d: export\n", getpid()); + export(netfd, ".", 0); + postnote(PNGROUP, getpid(), "die"); + fprint(2, "%d: export finished\n", getpid()); + fatal(nil); +} + +void +copylog(int r, int w, char *prefix) +{ + int n; + static uchar buf[16384], buf0[16384]; + Fcall f; + vlong t; + Tm *tm; + + for(;;){ + n = read9pmsg(r, buf, sizeof buf); + if(n <= 0) + break; + memmove(buf0, buf, n); + t = nsec(); + tm = localtime(t/1000000000); + if(convM2S(buf0, n, &f) < 0){ + fprint(2, "%s%02d:%02d:%02d.%06d convM2S failed\n", + prefix, tm->hour, tm->min, tm->sec, + (int)((t/1000)%1000000)); + break; + } + fprint(2, "%s%02d:%02d:%02d.%06d %F\n", + prefix, tm->hour, tm->min, tm->sec, + (int)((t/1000)%1000000), &f); + if(write(w, buf, n) < 0) + break; + } +} + + +void +fatal(char *s, ...) +{ + char buf[ERRMAX]; + va_list arg; + + if (s) { + va_start(arg, s); + vsnprint(buf, ERRMAX, s, arg); + va_end(arg); + } + + if (s) + sysfatal(buf); + else + exits(nil); +} + +/* 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]); +} --- /sys/src/cmd/kexportfs/ksrvfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/kexportfs/ksrvfs.c Fri Mar 18 22:36:50 2016 @@ -0,0 +1,100 @@ +#include +#include + +static void +usage(void) +{ + fprint(2, "usage: %s [-dR] [-p perm] [-P patternfile] [-e exportfs] srvname path\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + char *ename, *arglist[16], **argp; + int n, fd, pipefd[2]; + char buf[64]; + int perm = 0600; + + argp = arglist; + ename = "/bin/sns/kexportfs"; + *argp++ = "kexportfs"; + *argp++ = "-Z"; + + ARGBEGIN{ + default: + usage(); + case 'd': + *argp++ = "-d"; + break; + case 'e': + ename = ARGF(); + break; + case 'f': + *argp++ = "-f"; + *argp++ = EARGF(usage()); + break; + case 'p': + perm = strtol(ARGF(), 0, 8); + break; + case 'P': + *argp++ = "-P"; + *argp++ = ARGF(); + break; + case 'R': + *argp++ = "-R"; + break; + }ARGEND + *argp = 0; + if(argc != 2) + usage(); + + if(pipe(pipefd) < 0){ + fprint(2, "can't pipe: %r\n"); + exits("pipe"); + } + + if(argv[0][0] == '/') + strcpy(buf, argv[0]); + else + sprint(buf, "/srv/%s", argv[0]); + fd = create(buf, ORCLOSE|OWRITE, perm); + if(fd < 0){ + fprint(2, "can't create %s: %r\n", buf); + exits("create"); + } + + switch(rfork(RFPROC|RFNOWAIT|RFNOTEG|RFFDG)){ + case -1: + fprint(2, "can't rfork: %r\n"); + exits("rfork"); + case 0: + dup(pipefd[0], 0); + dup(pipefd[0], 1); + close(pipefd[0]); + close(pipefd[1]); + exec(ename, arglist); + fprint(2, "can't exec exportfs: %r\n"); + exits("exec"); + default: + break; + } + close(pipefd[0]); + if(fprint(pipefd[1], "%s", argv[1]) < 0){ + fprint(2, "can't write pipe: %r\n"); + exits("write"); + } + n = read(pipefd[1], buf, sizeof buf-1); + if(n < 0){ + fprint(2, "can't read pipe: %r\n"); + exits("read"); + } + buf[n] = 0; + if(n != 2 || strcmp(buf, "OK") != 0){ + fprint(2, "not OK (%d): %s\n", n, buf); + exits("OK"); + } + fprint(fd, "%d", pipefd[1]); + close(pipefd[1]); + exits(0); +} --- /sys/src/cmd/kexportfs/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/kexportfs/mkfile Fri Mar 18 22:36:52 2016 @@ -0,0 +1,20 @@ +[1=2] + exit usage +} + +@{rfork n; srvfs -df /tmp/a a /} +mount /srv/a /n/a +@{rfork n; $k^srvfs -df /tmp/b b /} +mount /srv/b /n/b +@{rfork n; bind /n/b/n/a /; $* >/dev/null} +grep -c '^([0-9:.]+ )?T' /tmp/[ab] +rm -f /srv/[ab] +