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/9nfs |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/9nfs')
27 files changed, 4861 insertions, 0 deletions
diff --git a/sys/src/cmd/9nfs/9auth.c b/sys/src/cmd/9nfs/9auth.c new file mode 100755 index 000000000..588aca4cb --- /dev/null +++ b/sys/src/cmd/9nfs/9auth.c @@ -0,0 +1,88 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> + +#define NETCHLEN 16 /* max network challenge length */ + +char * argv0; +int debug; +int delete; + +char * root = "/n/emelie"; +char * user; +char file[64]; +char challenge[NETCHLEN]; +char response[NETCHLEN]; + +void +usage(void) +{ + printf("usage: %s [-d] username\n", argv0); + exit(1); +} + +void +main(int argc, char **argv) +{ + int fd, n; + + for(argv0=*argv++,--argc; argc>0; ++argv,--argc){ + if(argv[0][0] != '-' || argv[0][1] == '-') + break; + switch(argv[0][1]){ + case 'D': + ++debug; + break; + case 'd': + ++delete; + break; + case 'r': + root = argv[0][2] ? &argv[0][2] : (--argc, *++argv); + break; + default: + usage(); + break; + } + } + if(argc != 1) + usage(); + user = argv[0]; + snprintf(file, sizeof file, "%s/#%s", root, user); + if(debug) + printf("debug=%d, file=%s\n", debug, file); + if(delete){ + fd = creat(file, 0600); + if(fd < 0){ + perror(file); + exit(1); + } + exit(0); + } + fd = open(file, 2); + if(fd < 0){ + perror(file); + exit(1); + } + n = read(fd, challenge, NETCHLEN); + if(debug) + printf("read %d\n", n); + if(n <= 0){ + printf("read %d: ", n); + perror(""); + exit(1); + } + printf("challenge: %s\n", challenge); + write(1, "response: ", 10); + read(0, response, NETCHLEN-1); + lseek(fd, 0, 0); + n = write(fd, response, NETCHLEN); + if(debug) + printf("write %d\n", n); + if(n <= 0){ + printf("write %d: ", n); + perror(""); + exit(1); + } + exit(0); +} diff --git a/sys/src/cmd/9nfs/9p.c b/sys/src/cmd/9nfs/9p.c new file mode 100755 index 000000000..27fc2b5a8 --- /dev/null +++ b/sys/src/cmd/9nfs/9p.c @@ -0,0 +1,184 @@ +#include "all.h" + +static char *tnames[] = { + [Tversion] "version", + [Tauth] "auth", + [Tattach] "attach", + [Tflush] "flush", + [Twalk] "walk", + [Topen] "open", + [Tcreate] "create", + [Tread] "read", + [Twrite] "write", + [Tclunk] "clunk", + [Tremove] "remove", + [Tstat] "stat", + [Twstat] "wstat", +}; + +int messagesize = IOHDRSZ+Maxfdata; + +int +xmesg(Session *s, int t) +{ + int n; + + if(chatty){ + if(0 <= t && t < nelem(tnames) && tnames[t]) + chat("T%s...", tnames[t]); + else + chat("T%d...", t); + } + s->f.type = t; + s->f.tag = ++s->tag; + if(p9debug) + fprint(2, "xmseg\tsend %F\n", &s->f); + n = convS2M(&s->f, s->data, messagesize); + if(niwrite(s->fd, s->data, n) != n){ + clog("xmesg write error on %d: %r\n", s->fd); + return -1; + } +again: + n = read9pmsg(s->fd, s->data, messagesize); + if(n < 0){ + clog("xmesg read error: %r\n"); + return -1; + } + if(convM2S(s->data, n, &s->f) <= 0){ + clog("xmesg bad convM2S %d %.2x %.2x %.2x %.2x\n", + n, ((uchar*)s->data)[0], ((uchar*)s->data)[1], + ((uchar*)s->data)[2], ((uchar*)s->data)[3]); + return -1; + } + if(p9debug) + fprint(2, "\trecv %F\n", &s->f); + if(s->f.tag != s->tag){ + clog("xmesg tag %d for %d\n", s->f.tag, s->tag); + goto again; + } + if(s->f.type == Rerror){ + if(t == Tclunk) + clog("xmesg clunk: %s", s->f.ename); + chat("xmesg %d error %s...", t, s->f.ename); + return -1; + } + if(s->f.type != t+1){ + clog("xmesg type mismatch: %d, expected %d\n", s->f.type, t+1); + return -1; + } + return 0; +} + +int +clunkfid(Session *s, Fid *f) +{ + putfid(s, f); + if(s == 0 || f == 0) + return 0; + s->f.fid = f - s->fids; + return xmesg(s, Tclunk); +} + +#define UNLINK(p) ((p)->prev->next = (p)->next, (p)->next->prev = (p)->prev) + +#define LINK(h, p) ((p)->next = (h)->next, (p)->prev = (h), \ + (h)->next->prev = (p), (h)->next = (p)) + +#define TOFRONT(h, p) ((h)->next != (p) ? (UNLINK(p), LINK(h,p)) : 0) + +Fid * +newfid(Session *s) +{ + Fid *f, *fN; + + chat("newfid.."); + if(s->list.prev == 0){ + chat("init.."); + s->list.prev = &s->list; + s->list.next = &s->list; + s->free = s->fids; + if(0 && chatty) + fN = &s->fids[25]; + else + fN = &s->fids[nelem(s->fids)]; + for(f=s->fids; f<fN; f++){ + f->owner = 0; + f->prev = 0; + f->next = f+1; + } + (f-1)->next = 0; + } + if(s->free){ + f = s->free; + s->free = f->next; + LINK(&s->list, f); + }else{ + for(f=s->list.prev; f!=&s->list; f=f->prev) + if(f->owner) + break; + if(f == &s->list){ + clog("fid leak"); + return 0; + } + setfid(s, f); + if(xmesg(s, Tclunk) < 0){ + clog("clunk failed, no fids?"); + /*return 0;*/ + } + *(f->owner) = 0; + f->owner = 0; + } + chat("%ld...", f - s->fids); + f->tstale = nfstime + staletime; + return f; +} + +void +setfid(Session *s, Fid *f) +{ + /* + * TOFRONT(&s->list, f); + */ + if(s->list.next != f){ + UNLINK(f); + LINK(&s->list, f); + } + + f->tstale = nfstime + staletime; + s->f.fid = f - s->fids; +} + +void +putfid(Session *s, Fid *f) +{ + chat("putfid %ld...", f-s->fids); + if(s == 0 || f == 0){ + clog("putfid(0x%p, 0x%p) %s", s, f, (s ? s->service : "?")); + return; + } + UNLINK(f); + if(f->owner) + *(f->owner) = 0; + f->owner = 0; + f->prev = 0; + f->next = s->free; + s->free = f; +} + +void +fidtimer(Session *s, long now) +{ + Fid *f, *t; + int n; + + f = s->list.next; + n = 0; + while(f != &s->list){ + t = f; + f = f->next; + if(t->owner && now >= t->tstale) + ++n, clunkfid(s, t); + } + if(n > 0) + chat("fidtimer %s\n", s->service); +} diff --git a/sys/src/cmd/9nfs/all.h b/sys/src/cmd/9nfs/all.h new file mode 100755 index 000000000..a4d720ef7 --- /dev/null +++ b/sys/src/cmd/9nfs/all.h @@ -0,0 +1,16 @@ +#include <u.h> +#include <libc.h> +#include <ip.h> +#include <bio.h> +#include <auth.h> +#include <authsrv.h> +#include <fcall.h> +#include <regexp.h> +#include "dat.h" +#include "fns.h" +#include "rpc.h" +#include "nfs.h" +#pragma varargck type "I" ulong +#pragma varargck argpos chat 1 +#pragma varargck argpos clog 1 +#pragma varargck argpos panic 1 diff --git a/sys/src/cmd/9nfs/auth.c b/sys/src/cmd/9nfs/auth.c new file mode 100755 index 000000000..672308452 --- /dev/null +++ b/sys/src/cmd/9nfs/auth.c @@ -0,0 +1,31 @@ +#include "all.h" + +/* this is all stubbed out; the NFS authentication stuff is now disabled - rob */ + +Xfid * +xfauth(Xfile *, String *) +{ + return 0; +} + +long +xfauthread(Xfid *xf, long, uchar *, long) +{ + + chat("xfauthread %s...", xf->uid); + return 0; +} + +long +xfauthwrite(Xfid *xf, long, uchar *, long) +{ + chat("xfauthwrite %s...", xf->uid); + return 0; +} + +int +xfauthremove(Xfid *, char *) +{ + chat("authremove..."); + return -1; +} diff --git a/sys/src/cmd/9nfs/authhostowner.c b/sys/src/cmd/9nfs/authhostowner.c new file mode 100755 index 000000000..e91f0030b --- /dev/null +++ b/sys/src/cmd/9nfs/authhostowner.c @@ -0,0 +1,239 @@ +#include "all.h" + +enum { + ARgiveup = 100, +}; + +static uchar* +gstring(uchar *p, uchar *ep, char **s) +{ + uint n; + + if(p == nil) + return nil; + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ; + if(p+n > ep) + return nil; + *s = malloc(n+1); + memmove((*s), p, n); + (*s)[n] = '\0'; + p += n; + return p; +} + +static uchar* +gcarray(uchar *p, uchar *ep, uchar **s, int *np) +{ + uint n; + + if(p == nil) + return nil; + if(p+BIT16SZ > ep) + return nil; + n = GBIT16(p); + p += BIT16SZ; + if(p+n > ep) + return nil; + *s = malloc(n); + if(*s == nil) + return nil; + memmove((*s), p, n); + *np = n; + p += n; + return p; +} + +static uchar* +convM2AI(uchar *p, int n, AuthInfo **aip) +{ + uchar *e = p+n; + AuthInfo *ai; + + ai = mallocz(sizeof(*ai), 1); + if(ai == nil) + return nil; + + p = gstring(p, e, &ai->cuid); + p = gstring(p, e, &ai->suid); + p = gstring(p, e, &ai->cap); + p = gcarray(p, e, &ai->secret, &ai->nsecret); + if(p == nil) + auth_freeAI(ai); + else + *aip = ai; + return p; +} + +static int +dorpc(AuthRpc *rpc, char *verb, char *val, int len, AuthGetkey *getkey) +{ + int ret; + + for(;;){ + if((ret = auth_rpc(rpc, verb, val, len)) != ARneedkey && ret != ARbadkey) + return ret; + if(getkey == nil) + return ARgiveup; /* don't know how */ + if((*getkey)(rpc->arg) < 0) + return ARgiveup; /* user punted */ + } +} + +static int +doread(Session *s, Fid *f, void *buf, int n) +{ + s->f.fid = f - s->fids; + s->f.offset = 0; + s->f.count = n; + if(xmesg(s, Tread) < 0) + return -1; + n = s->f.count; + memmove(buf, s->f.data, n); + return n; +} + +static int +dowrite(Session *s, Fid *f, void *buf, int n) +{ + s->f.fid = f - s->fids; + s->f.offset = 0; + s->f.count = n; + s->f.data = (char *)buf; + if(xmesg(s, Twrite) < 0) + return -1; + return n; +} + +/* + * this just proxies what the factotum tells it to. + */ +AuthInfo* +authproto(Session *s, Fid *f, AuthRpc *rpc, AuthGetkey *getkey, char *params) +{ + char *buf; + int m, n, ret; + AuthInfo *a; + char oerr[ERRMAX]; + + rerrstr(oerr, sizeof oerr); + werrstr("UNKNOWN AUTH ERROR"); + + if(dorpc(rpc, "start", params, strlen(params), getkey) != ARok){ + werrstr("fauth_proxy start: %r"); + return nil; + } + + buf = malloc(AuthRpcMax); + if(buf == nil) + return nil; + for(;;){ + switch(dorpc(rpc, "read", nil, 0, getkey)){ + case ARdone: + free(buf); + a = auth_getinfo(rpc); + errstr(oerr, sizeof oerr); /* no error, restore whatever was there */ + return a; + case ARok: + if(dowrite(s, f, rpc->arg, rpc->narg) != rpc->narg){ + werrstr("auth_proxy write fd: %r"); + goto Error; + } + break; + case ARphase: + n = 0; + memset(buf, 0, AuthRpcMax); + while((ret = dorpc(rpc, "write", buf, n, getkey)) == ARtoosmall){ + if(atoi(rpc->arg) > AuthRpcMax) + break; + m = doread(s, f, buf+n, atoi(rpc->arg)-n); + if(m <= 0){ + if(m == 0) + werrstr("auth_proxy short read: %s", buf); + goto Error; + } + n += m; + } + if(ret != ARok){ + werrstr("auth_proxy rpc write: %s: %r", buf); + goto Error; + } + break; + default: + werrstr("auth_proxy rpc: %r"); + goto Error; + } + } +Error: + free(buf); + return nil; +} + +/* returns 0 if auth succeeded (or unneeded), -1 otherwise */ +int +authhostowner(Session *s) +{ + Fid *af, *f; + int rv = -1; + int afd; + AuthInfo *ai; + AuthRpc *rpc; + + /* get a fid to authenticate over */ + f = nil; + af = newfid(s); + s->f.afid = af - s->fids; + s->f.uname = getuser(); + s->f.aname = s->spec; + if(xmesg(s, Tauth)){ + /* not needed */ + rv = 0; + goto out; + } + + quotefmtinstall(); /* just in case */ + + afd = open("/mnt/factotum/rpc", ORDWR); + if(afd < 0){ + werrstr("opening /mnt/factotum/rpc: %r"); + goto out; + } + + rpc = auth_allocrpc(afd); + if(rpc == nil) + goto out; + + ai = authproto(s, af, rpc, auth_getkey, "proto=p9any role=client"); + if(ai != nil){ + rv = 0; + auth_freeAI(ai); + } + auth_freerpc(rpc); + close(afd); + + /* try attaching with the afid */ + chat("attaching as hostowner..."); + f = newfid(s); + s->f.fid = f - s->fids; + s->f.afid = af - s->fids;; + s->f.uname = getuser(); + s->f.aname = s->spec; + if(xmesg(s, Tattach) == 0) + rv = 0; +out: + if(af != nil){ + putfid(s, af); + s->f.fid = af - s->fids; + xmesg(s, Tclunk); + } + if(f != nil){ + putfid(s, f); + s->f.fid = f - s->fids; + xmesg(s, Tclunk); + } + + return rv; +} + diff --git a/sys/src/cmd/9nfs/chat.c b/sys/src/cmd/9nfs/chat.c new file mode 100755 index 000000000..79b5315b1 --- /dev/null +++ b/sys/src/cmd/9nfs/chat.c @@ -0,0 +1,131 @@ +#include "all.h" + +#define SIZE 1024 + +int chatty; +int conftime; + +#define NSIZE 128 + +static char nbuf[NSIZE]; +static int chatpid; + +static void +killchat(void) +{ + char buf[NSIZE]; + int fd; + + remove(nbuf); + snprint(buf, sizeof buf, "/proc/%d/note", chatpid); + fd = open(buf, OWRITE); + write(fd, "kill\n", 5); + close(fd); +} + +void +chatsrv(char *name) +{ + int n, sfd, pfd[2]; + char *p, buf[256]; + + if(name && *name) + snprint(nbuf, sizeof nbuf, "/srv/%s", name); + else{ + if(p = strrchr(argv0, '/')) /* assign = */ + name = p+1; + else + name = argv0; + snprint(nbuf, sizeof nbuf, "/srv/%s.chat", name); + } + remove(nbuf); + if(pipe(pfd) < 0) + panic("chatsrv pipe"); + sfd = create(nbuf, OWRITE, 0600); + if(sfd < 0) + panic("chatsrv create %s", nbuf); + chatpid = rfork(RFPROC|RFMEM); + switch(chatpid){ + case -1: + panic("chatsrv fork"); + case 0: + break; + default: + atexit(killchat); + return; + } + fprint(sfd, "%d", pfd[1]); + close(sfd); + close(pfd[1]); + for(;;){ + n = read(pfd[0], buf, sizeof(buf)-1); + if(n < 0) + break; + if(n == 0) + continue; + buf[n] = 0; + if(buf[0] == 'c') + conftime = 999; + chatty = strtol(buf, 0, 0); + if(abs(chatty) < 2) + rpcdebug = 0; + else + rpcdebug = abs(chatty) - 1; + fprint(2, "%s: chatty=%d, rpcdebug=%d, conftime=%d\n", + nbuf, chatty, rpcdebug, conftime); + } + _exits(0); +} + +void +chat(char *fmt, ...) +{ + char buf[SIZE]; + va_list arg; + Fmt f; + + if(!chatty) + return; + + fmtfdinit(&f, 2, buf, sizeof buf); + va_start(arg, fmt); + fmtvprint(&f, fmt, arg); + va_end(arg); + fmtfdflush(&f); +} + +void +clog(char *fmt, ...) +{ + char buf[SIZE]; + va_list arg; + int n; + + va_start(arg, fmt); + vseprint(buf, buf+SIZE, fmt, arg); + va_end(arg); + n = strlen(buf); + if(chatty || rpcdebug) + write(2, buf, n); + if(chatty <= 0){ + if(n>0 && buf[n-1] == '\n') + buf[n-1] = 0; + syslog(0, "nfs", buf); + } +} + +void +panic(char *fmt, ...) +{ + char buf[SIZE]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+SIZE, fmt, arg); + va_end(arg); + if(chatty || rpcdebug) + fprint(2, "%s %d: %s: %r\n", argv0, getpid(), buf); + if(chatty <= 0) + syslog(0, "nfs", buf); + exits("panic"); +} diff --git a/sys/src/cmd/9nfs/dat.h b/sys/src/cmd/9nfs/dat.h new file mode 100755 index 000000000..c9f6a8664 --- /dev/null +++ b/sys/src/cmd/9nfs/dat.h @@ -0,0 +1,273 @@ +enum +{ + FHSIZE = 32 +}; + +typedef struct Accept Accept; +typedef struct Auth Auth; +typedef struct Authunix Authunix; +typedef struct Chalstuff Chalstuff; +typedef uchar Fhandle[FHSIZE]; +typedef struct Fid Fid; +typedef struct Procmap Procmap; +typedef struct Progmap Progmap; +typedef struct Reject Reject; +typedef struct Rpccall Rpccall; +typedef struct Rpccache Rpccache; +typedef struct Sattr Sattr; +typedef struct Session Session; +typedef struct String String; +typedef struct Strnode Strnode; +typedef struct Unixid Unixid; +typedef struct Unixidmap Unixidmap; +typedef struct Unixmap Unixmap; +typedef struct Unixscmap Unixscmap; +typedef struct Xfid Xfid; +typedef struct Xfile Xfile; + +struct String +{ + ulong n; + char * s; +}; + +struct Progmap +{ + int progno; + int vers; + void (*init)(int, char**); + Procmap *pmap; +}; + +struct Procmap +{ + int procno; + int (*procp)(int, Rpccall*, Rpccall*); +}; + +struct Auth +{ + ulong flavor; + ulong count; + void * data; +}; + +struct Authunix +{ + ulong stamp; + String mach; + ulong uid; + ulong gid; + int gidlen; + ulong gids[10]; +}; + +struct Accept +{ + Auth averf; + ulong astat; + union{ + void * results; /* SUCCESS */ + struct{ /* PROG_MISMATCH */ + ulong plow; /* acceptable version numbers */ + ulong phigh; + }; + }; +}; + +struct Reject +{ + ulong rstat; + union{ + struct{ /* RPC_MISMATCH */ + ulong rlow; /* acceptable rpc version numbers */ + ulong rhigh; + }; + ulong authstat; /* AUTH_ERROR */ + }; +}; + +struct Rpccall +{ + /* corresponds to Udphdr */ + uchar prefix0[12]; + ulong host; /* ipv4 subset: prefixed to RPC message */ + uchar prefix1[12]; + ulong lhost; /* ipv4 subset: prefixed to RPC message */ + /* ignore ifcaddr */ + ulong port; /* prefixed to RPC message */ + ulong lport; /* prefixed to RPC message */ + + ulong xid; /* transaction id */ + ulong mtype; /* CALL or REPLY */ + union{ + struct{ /* CALL */ + ulong rpcvers; /* must be equal to two (2) */ + ulong prog; /* program number */ + ulong vers; /* program version */ + ulong proc; /* procedure number */ + Auth cred; /* authentication credentials */ + Auth verf; /* authentication verifier */ + Unixidmap *up; + char * user; + void * args; /* procedure-specific */ + }; + struct{ /* REPLY */ + ulong stat; /* MSG_ACCEPTED or MSG_DENIED */ + union{ + Accept; + Reject; + }; + }; + }; +}; + +struct Rpccache +{ + Rpccache *prev; + Rpccache *next; + ulong host; + ulong port; + ulong xid; + int n; + uchar data[4]; +}; + +struct Sattr +{ + ulong mode; + ulong uid; + ulong gid; + ulong size; + ulong atime; /* sec's */ + ulong ausec; /* microsec's */ + ulong mtime; + ulong musec; +}; + +struct Strnode +{ + Strnode *next; /* in hash bucket */ + char str[4]; +}; + +struct Unixid +{ + Unixid *next; + char * name; + int id; +}; + +struct Unixmap +{ + char * file; + int style; + long timestamp; + Unixid *ids; +}; + +struct Unixidmap +{ + Unixidmap *next; + int flag; + char * server; + char * client; + Reprog *sexp; + Reprog *cexp; + Unixmap u; + Unixmap g; +}; + +struct Unixscmap +{ + Unixscmap *next; + char * server; + ulong clientip; + Unixidmap *map; +}; + +struct Xfile +{ + Xfile * next; /* hash chain */ + Session *s; + Qid qid; /* from stat */ + Xfile * parent; + Xfile * child; /* if directory */ + Xfile * sib; /* siblings */ + char * name; /* path element */ + Xfid * users; +}; + +enum +{ + Oread = 1, + Owrite = 2, + Open = 3, + Trunc = 4 +}; + +struct Xfid +{ + Xfid * next; /* Xfile's user list */ + Xfile * xp; + char * uid; + Fid * urfid; + Fid * opfid; + ulong mode; /* open mode, if opfid is non-zero */ + ulong offset; +}; + +struct Fid +{ + Fid ** owner; /* null for root fids */ + Fid * prev; + Fid * next; + long tstale; /* auto-clunk */ +}; + +enum +{ + Maxfdata = 8192, + Maxstatdata = 2048, +}; + +struct Session +{ + Session *next; + char * service; /* for dial */ + int fd; +#define CHALLEN 1 + char cchal[CHALLEN]; /* client challenge */ + char schal[CHALLEN]; /* server challenge */ + char authid[ANAMELEN]; /* server encryption uid */ + char authdom[DOMLEN]; /* server encryption domain */ + char * spec; /* for attach */ + Xfile * root; /* to answer mount rpc */ + ushort tag; + Fcall f; + uchar data[IOHDRSZ+Maxfdata]; + uchar statbuf[Maxstatdata]; + Fid * free; /* available */ + Fid list; /* active, most-recently-used order */ + Fid fids[1000]; + int noauth; +}; + +struct Chalstuff +{ + Chalstuff *next; + Xfid * xf; + long tstale; + Chalstate; +}; + +extern int rpcdebug; +extern int p9debug; +extern int chatty; +extern void (*rpcalarm)(void); +extern long starttime; +extern long nfstime; +extern char * config; +extern int staletime; +extern int messagesize; +extern char * commonopts; diff --git a/sys/src/cmd/9nfs/fns.h b/sys/src/cmd/9nfs/fns.h new file mode 100755 index 000000000..784e18a27 --- /dev/null +++ b/sys/src/cmd/9nfs/fns.h @@ -0,0 +1,66 @@ +int argopt(int c); +int auth2unix(Auth*, Authunix*); +int authhostowner(Session*); +int canlock(Lock*); +void chat(char*, ...); +void chatsrv(char*); +int checkreply(Session*, char*); +int checkunixmap(Unixmap*); +void clog(char*, ...); +int clunkfid(Session*, Fid*); +int convM2sattr(void*, Sattr*); +int dir2fattr(Unixidmap*, Dir*, void*); +int error(Rpccall*, int); +void fidtimer(Session*, long); +int garbage(Rpccall*, char*); +int getdom(ulong, char*, int); +int getticket(Session*, char*); +char* id2name(Unixid**, int); +void idprint(int, Unixid*); +void* listalloc(long, long); +void lock(Lock*); +void mnttimer(long); +int name2id(Unixid**, char*); +Fid* newfid(Session*); +long niwrite(int, void*, long); +Unixidmap* pair2idmap(char*, ulong); +void panic(char*, ...); +void putfid(Session*, Fid*); +int readunixidmaps(char*); +Unixid* readunixids(char*, int); +Xfid* rpc2xfid(Rpccall*, Dir*); +int rpcM2S(void*, Rpccall*, int); +int rpcS2M(Rpccall*, int, void*); +void rpcprint(int, Rpccall*); +void server(int argc, char *argv[], int, Progmap*); +void setfid(Session*, Fid*); +Xfid* setuser(Xfile*, char*); +void showauth(Auth*); +void srvinit(int, char*, char*); +char* strfind(char*); +int string2S(void*, String*); +int strparse(void*, int, char**); +void strprint(int); +char* strstore(char*); +Waitmsg *system(char*, char**); +Waitmsg *systeml(char*, ...); +void unlock(Lock*); +int xfattach(Session*, char*, int); +Xfid* xfauth(Xfile*, String*); +void xfauthclose(Xfid*); +long xfauthread(Xfid*, long, uchar*, long); +int xfauthremove(Xfid*, char*); +long xfauthwrite(Xfid*, long, uchar*, long); +void xfclear(Xfid*); +void xfclose(Xfid*); +Xfid* xfid(char*, Xfile*, int); +Xfile* xfile(Qid*, void*, int); +int xfopen(Xfid*, int); +int xfpurgeuid(Session*, char*); +Xfile* xfroot(char*, int); +int xfstat(Xfid*, Dir*); +Xfid* xfwalkcr(int, Xfid*, String*, long); +int xfwstat(Xfid*, Dir*); +int xmesg(Session*, int); +int xp2fhandle(Xfile*, Fhandle); +void xpclear(Xfile*); diff --git a/sys/src/cmd/9nfs/listalloc.c b/sys/src/cmd/9nfs/listalloc.c new file mode 100755 index 000000000..605b289bf --- /dev/null +++ b/sys/src/cmd/9nfs/listalloc.c @@ -0,0 +1,19 @@ +#include <u.h> +#include <libc.h> + +void* listalloc(long, long); + +void * +listalloc(long n, long size) +{ + char *p, *base; + + size = (size+sizeof(ulong)-1)/sizeof(ulong)*sizeof(ulong); + p = base = malloc(n*size); + while(--n > 0){ + *(char**)p = p+size; + p += size; + } + *(char**)p = 0; + return base; +} diff --git a/sys/src/cmd/9nfs/mkfile b/sys/src/cmd/9nfs/mkfile new file mode 100755 index 000000000..7a6fc720d --- /dev/null +++ b/sys/src/cmd/9nfs/mkfile @@ -0,0 +1,52 @@ +</$objtype/mkfile +CFLAGS=-FVw + +TARG=nfsserver\ + portmapper\ + pcnfsd\ + +HFILES=rpc.h\ + nfs.h\ + dat.h\ + fns.h\ + +OFILES=chat.$O\ + rpc.$O\ + string.$O\ + strparse.$O\ + system.$O\ + listalloc.$O\ + server.$O\ + unixnames.$O\ + +NFSSERVER=9p.$O\ + auth.$O\ + authhostowner.$O\ + nfs.$O\ + nfsmount.$O\ + xfile.$O + +BIN=/$objtype/bin/aux + +all:V: + + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${NFSSERVER:%.$O=%.c}\ + ${TARG:%=%.c}\ + +</sys/src/cmd/mkmany + +$O.nfsserver: $NFSSERVER + +nametest: $O.nametest + cp $O.nametest $target + +mport: $O.mport + cp $O.mport $target + +clean nuke:V: + rm -f *.[$OS] y.tab.? y.debug y.output [$OS].* $TARG mport nametest pcnfsd diff --git a/sys/src/cmd/9nfs/mport.c b/sys/src/cmd/9nfs/mport.c new file mode 100755 index 000000000..553c2bede --- /dev/null +++ b/sys/src/cmd/9nfs/mport.c @@ -0,0 +1,185 @@ +#include "all.h" + +typedef struct Rpcconn Rpcconn; + +struct Rpcconn +{ + int data; + int ctl; + Rpccall cmd; + Rpccall reply; + uchar rpcbuf[8192]; + uchar argbuf[8192]; +}; + +void putauth(char*, Auth*); +int rpccall(Rpcconn*, int); + +int rpcdebug; + +Rpcconn r; +char * mach; + +void +main(int argc, char **argv) +{ + char addr[64], dir[64], name[128]; + char buf[33], *p; + uchar *dataptr, *argptr; + int i, fd, n, remport; + + ARGBEGIN{ + case 'm': + mach = ARGF(); + break; + case 'D': + ++rpcdebug; + break; + }ARGEND + if(argc != 1) + exits("usage"); + + snprint(addr, sizeof addr, "udp!%s!111", argv[0]); + r.data = dial(addr, 0, dir, &r.ctl); + if(r.data < 0){ + fprint(2, "dial %s: %r\n", addr); + exits("dial"); + } + if(rpcdebug) + fprint(2, "dial %s: dir=%s\n", addr, dir); + if(fprint(r.ctl, "headers") < 0){ + fprint(2, "can't set header mode: %r\n"); + exits("headers"); + } + sprint(name, "%s/remote", dir); + fd = open(name, OREAD); + if(fd < 0){ + fprint(2, "can't open %s: %r\n", name); + exits("remote"); + } + n = read(fd, buf, sizeof buf-1); + if(n < 0){ + fprint(2, "can't read %s: %r\n", name); + exits("remote"); + } + close(fd); + buf[n] = 0; + p = buf; + r.cmd.host = 0; + for(i=0; i<4; i++, p++) + r.cmd.host = (r.cmd.host<<8)|strtol(p, &p, 10); + r.cmd.port = strtol(p, 0, 10); + fprint(2, "host=%ld.%ld.%ld.%ld, port=%lud\n", + (r.cmd.host>>24)&0xff, (r.cmd.host>>16)&0xff, + (r.cmd.host>>8)&0xff, r.cmd.host&0xff, r.cmd.port); + fprint(r.ctl, "disconnect"); + + r.cmd.xid = time(0); + r.cmd.mtype = CALL; + r.cmd.rpcvers = 2; + r.cmd.args = r.argbuf; + if(mach) + putauth(mach, &r.cmd.cred); + + r.cmd.prog = 100000; /* portmapper */ + r.cmd.vers = 2; /* vers */ + r.cmd.proc = 3; /* getport */ + dataptr = r.cmd.args; + + PLONG(100005); /* mount */ + PLONG(1); /* vers */ + PLONG(IPPROTO_UDP); + PLONG(0); + + i = rpccall(&r, dataptr-(uchar*)r.cmd.args); + if(i != 4) + exits("trouble"); + argptr = r.reply.results; + remport = GLONG(); + fprint(2, "remote port = %d\n", remport); + + r.cmd.port = remport; + r.cmd.prog = 100005; /* mount */ + r.cmd.vers = 1; /* vers */ + r.cmd.proc = 0; /* null */ + dataptr = r.cmd.args; + + i = rpccall(&r, dataptr-(uchar*)r.cmd.args); + if(i != 0) + exits("trouble"); + fprint(2, "OK ping mount\n"); + + r.cmd.prog = 100005; /* mount */ + r.cmd.vers = 1; /* vers */ + r.cmd.proc = 5; /* export */ + dataptr = r.cmd.args; + + i = rpccall(&r, dataptr-(uchar*)r.cmd.args); + fprint(2, "export: %d bytes returned\n", i); + argptr = r.reply.results; + while (GLONG() != 0) { + n = GLONG(); + p = GPTR(n); + print("%.*s\n", utfnlen(p, n), p); + while (GLONG() != 0) { + n = GLONG(); + p = GPTR(n); + print("\t%.*s\n", utfnlen(p, n), p); + } + } + + exits(0); +} + +void +putauth(char *mach, Auth *a) +{ + uchar *dataptr; + long stamp = time(0); + int n = strlen(mach); + + dataptr = realloc(a->data, 2*4+ROUNDUP(n)+4*4); + a->data = dataptr; + a->flavor = AUTH_UNIX; + PLONG(stamp); + PLONG(n); + PPTR(mach, n); + PLONG(0); + PLONG(1); + PLONG(1); + PLONG(0); + a->count = dataptr - (uchar*)a->data; +} + +int +rpccall(Rpcconn *r, int narg) +{ + int n; + + r->cmd.xid++; + n = rpcS2M(&r->cmd, narg, r->rpcbuf); + if(rpcdebug) + rpcprint(2, &r->cmd); + if(write(r->data, r->rpcbuf, n) < 0){ + fprint(2, "rpc write: %r\n"); + exits("rpc"); + } + n = read(r->data, r->rpcbuf, sizeof r->rpcbuf); + if(n < 0){ + fprint(2, "rpc read: %r\n"); + exits("rpc"); + } + if(rpcM2S(r->rpcbuf, &r->reply, n) != 0){ + fprint(2, "rpc reply format error\n"); + exits("rpc"); + } + if(rpcdebug) + rpcprint(2, &r->reply); + if(r->reply.mtype != REPLY || r->reply.stat != MSG_ACCEPTED || + r->reply.astat != SUCCESS){ + fprint(2, "rpc mtype, stat, astat = %ld, %ld, %ld\n", + r->reply.mtype, r->reply.stat, r->reply.astat); + exits("rpc"); + } + return n - (((uchar *)r->reply.results) - r->rpcbuf); +} diff --git a/sys/src/cmd/9nfs/nametest.c b/sys/src/cmd/9nfs/nametest.c new file mode 100755 index 000000000..cb605720c --- /dev/null +++ b/sys/src/cmd/9nfs/nametest.c @@ -0,0 +1,99 @@ +#include "all.h" + +void mapinit(char*, char*); + +int debug; +int rpcdebug; +int style = 'u'; +Biobuf *in; +Unixid *ids; +Unixid **pids; +Unixidmap *mp; + +void +main(int argc, char **argv) +{ + int id, arc; char *arv[4]; + char *l, *name; + + chatty = 1; + ARGBEGIN{ + case '9': + case 'u': + style = ARGC(); + break; + case 'D': + ++debug; + break; + }ARGEND + if(argc <= 0){ + ids = readunixids("/fd/0", style); + if(ids) + idprint(1, ids); + exits(ids ? 0 : "readunixids"); + } + mapinit(argv[0], 0); + in = Bopen("/fd/0", OREAD); + while(l = Brdline(in, '\n')){ /* assign = */ + l[Blinelen(in)-1] = 0; + arc = strparse(l, nelem(arv), arv); + if(arc <= 0) + continue; + switch(arv[0][0]){ + case 'r': + if(arc < 2) + continue; + mapinit(arv[1], arv[2]); + break; + case 'i': + if(arc < 2) + continue; + id = strtol(arv[1], 0, 10); + name = id2name(pids, id); + print("%d -> %s\n", id, name); + break; + case 'n': + if(arc < 2) + continue; + name = arv[1]; + id = name2id(pids, name); + print("%s -> %d\n", name, id); + break; + case 'p': + print("server=%s, client=%s\n", mp->server, mp->client); + break; + case 'P': + idprint(1, *pids); + break; + case 'u': + pids = &mp->u.ids; + print("users...\n"); + break; + case 'g': + pids = &mp->g.ids; + print("groups...\n"); + break; + } + } + exits(0); +} + +void +mapinit(char *file, char *client) +{ + if(file){ + print("reading %s...\n", file); + if(readunixidmaps(file) < 0) + exits("readunixidmaps"); + if(!client) + client = "nslocum.research.bell-labs.com"; + } + print("client = %s...\n", client); + mp = pair2idmap("bootes", client, 0); + if(mp == 0){ + fprint(2, "%s: pair2idmap failed\n", argv0); + exits("pair2idmap"); + } + pids = &mp->u.ids; + print("[users...]\n"); +} diff --git a/sys/src/cmd/9nfs/nfs.c b/sys/src/cmd/9nfs/nfs.c new file mode 100755 index 000000000..c01536742 --- /dev/null +++ b/sys/src/cmd/9nfs/nfs.c @@ -0,0 +1,471 @@ +#include "all.h" + +extern uchar buf[]; + +Xfid * +rpc2xfid(Rpccall *cmd, Dir *dp) +{ + char *argptr = cmd->args; + Xfile *xp; + Xfid *xf; + Session *s; + char *service; + Authunix au; + Qid qid; + char client[256], *user; + Unixidmap *m; + int i; + uvlong x1, x2; + + chat("rpc2xfid %.8lux %.8lux %p %p\n", *((ulong*)argptr), *((ulong*)argptr+1), buf, argptr); + if(argptr[0] == 0 && argptr[1] == 0){ /* root */ + chat("root..."); + xp = xfroot(&argptr[2], 0); + s = xp ? xp->s : 0; + }else{ + ulong ul; + chat("noroot %.8lux...", *((ulong*)argptr)); + if((ul=GLONG()) != starttime){ + chat("bad tag %lux %lux...", ul, starttime); + return 0; + } + s = (Session *)GLONG(); + x1 = GLONG(); + x2 = GLONG(); + qid.path = x1 | (x2<<32); + qid.vers = 0; + qid.type = GBYTE(); + xp = xfile(&qid, s, 0); + } + if(xp == 0){ + chat("no xfile..."); + return 0; + } + if(auth2unix(&cmd->cred, &au) != 0){ + chat("auth flavor=%ld, count=%ld\n", + cmd->cred.flavor, cmd->cred.count); + for(i=0; i<cmd->cred.count; i++) + chat(" %.2ux", ((uchar *)cmd->cred.data)[i]); + chat("..."); + return 0; + }else{ +/* chat("auth: %d %.*s u=%d g=%d", + * au.stamp, utfnlen(au.mach.s, au.mach.n), au.mach.s, au.uid, au.gid); + * for(i=0; i<au.gidlen; i++) + * chat(", %d", au.gids[i]); + * chat("..."); + */ + char *p = memchr(au.mach.s, '.', au.mach.n); + chat("%ld@%.*s...", au.uid, utfnlen(au.mach.s, (p ? p-au.mach.s : au.mach.n)), au.mach.s); + } + if(au.mach.n >= sizeof client){ + chat("client name too long..."); + return 0; + } + memcpy(client, au.mach.s, au.mach.n); + client[au.mach.n] = 0; + service = xp->parent->s->service; + cmd->up = m = pair2idmap(service, cmd->host); + if(m == 0){ + chat("no map for pair (%s,%s)...", service, client); + /*chat("getdom %d.%d.%d.%d", cmd->host&0xFF, (cmd->host>>8)&0xFF, + (cmd->host>>16)&0xFF, (cmd->host>>24)&0xFF);/**/ + /*if(getdom(cmd->host, client, sizeof(client))<0) + return 0;/**/ + return 0; + } + /*chat("map=(%s,%s)...", m->server, m->client);/**/ + cmd->user = user = id2name(&m->u.ids, au.uid); + if(user == 0){ + chat("no user for id %ld...", au.uid); + return 0; + } + chat("user=%s...", user);/**/ + xf = 0; + if(s == xp->parent->s){ + if(!s->noauth) + xf = setuser(xp, user); + if(xf == 0) + xf = setuser(xp, "none"); + if(xf == 0) + chat("can't set user none..."); + }else + xf = xp->users; + if(xf) + chat("uid=%s...", xf->uid); + if(xf && dp && xfstat(xf, dp) < 0){ + chat("can't stat %s...", xp->name); + return 0; + } + return xf; +} + +Xfid * +setuser(Xfile *xp, char *user) +{ + Xfid *xf, *xpf; + Session *s; + + xf = xfid(user, xp, 1); + if(xf->urfid) + return xf; + if(xp->parent==xp || !(xpf = setuser(xp->parent, user))) /* assign = */ + return xfid(user, xp, -1); + s = xp->s; + xf->urfid = newfid(s); + xf->urfid->owner = &xf->urfid; + setfid(s, xpf->urfid); + s->f.newfid = xf->urfid - s->fids; + s->f.nwname = 1; + s->f.wname[0] = xp->name; + if(xmesg(s, Twalk) || s->f.nwqid != 1) + return xfid(user, xp, -1); + return xf; +} + +int +xfstat(Xfid *xf, Dir *dp) +{ + Xfile *xp; + Session *s; + char buf[128]; + + xp = xf->xp; + s = xp->s; + if(s != xp->parent->s){ + seprint(buf, buf+sizeof buf, "#%s", xf->uid); + dp->name = strstore(buf); + dp->uid = xf->uid; + dp->gid = xf->uid; + dp->muid = xf->uid; + dp->qid.path = (uvlong)xf->uid; + dp->qid.type = QTFILE; + dp->qid.vers = 0; + dp->mode = 0666; + dp->atime = time(0); + dp->mtime = dp->atime; + dp->length = NETCHLEN; + dp->type = 0; + dp->type = 0; + return 0; + } + setfid(s, xf->urfid); + if(xmesg(s, Tstat) == 0){ + convM2D(s->f.stat, s->f.nstat, dp, (char*)s->statbuf); + if(xp->qid.path == dp->qid.path){ + xp->name = strstore(dp->name); + return 0; + } + /* not reached ? */ + chat("xp->qid.path=0x%.16llux, dp->qid.path=0x%.16llux name=%s...", + xp->qid.path, dp->qid.path, dp->name); + } + if(xp != xp->parent) + xpclear(xp); + else + clog("can't stat root: %s", + s->f.type == Rerror ? s->f.ename : "??"); + return -1; +} + +int +xfwstat(Xfid *xf, Dir *dp) +{ + Xfile *xp; + Session *s; + + xp = xf->xp; + s = xp->s; + + /* + * xf->urfid can be zero because some DOS NFS clients + * try to do wstat on the #user authentication files on close. + */ + if(s == 0 || xf->urfid == 0) + return -1; + setfid(s, xf->urfid); + s->f.stat = s->statbuf; + convD2M(dp, s->f.stat, Maxstatdata); + if(xmesg(s, Twstat)) + return -1; + xp->name = strstore(dp->name); + return 0; +} + +int +xfopen(Xfid *xf, int flag) +{ + static int modes[] = { + [Oread] OREAD, [Owrite] OWRITE, [Oread|Owrite] ORDWR, + }; + Xfile *xp; + Session *s; + Fid *opfid; + int omode; + + if(xf->opfid && (xf->mode & flag & Open) == flag) + return 0; + omode = modes[(xf->mode|flag) & Open]; + if(flag & Trunc) + omode |= OTRUNC; + xp = xf->xp; + chat("open(\"%s\", %d)...", xp->name, omode); + s = xp->s; + opfid = newfid(s); + setfid(s, xf->urfid); + s->f.newfid = opfid - s->fids; + s->f.nwname = 0; + if(xmesg(s, Twalk)){ + putfid(s, opfid); + return -1; + } + setfid(s, opfid); + s->f.mode = omode; + if(xmesg(s, Topen)){ + clunkfid(s, opfid); + return -1; + } + if(xf->opfid) + clunkfid(s, xf->opfid); + xf->mode |= flag & Open; + xf->opfid = opfid; + opfid->owner = &xf->opfid; + xf->offset = 0; + return 0; +} + +void +xfclose(Xfid *xf) +{ + Xfile *xp; + + if(xf->mode & Open){ + xp = xf->xp; + chat("close(\"%s\")...", xp->name); + if(xf->opfid) + clunkfid(xp->s, xf->opfid); + xf->mode &= ~Open; + xf->opfid = 0; + } +} + +void +xfclear(Xfid *xf) +{ + Xfile *xp = xf->xp; + + if(xf->opfid){ + clunkfid(xp->s, xf->opfid); + xf->opfid = 0; + } + if(xf->urfid){ + clunkfid(xp->s, xf->urfid); + xf->urfid = 0; + } + xfid(xf->uid, xp, -1); +} + +Xfid * +xfwalkcr(int type, Xfid *xf, String *elem, long perm) +{ + Session *s; + Xfile *xp, *newxp; + Xfid *newxf; + Fid *nfid; + + chat("xf%s(\"%s\")...", type==Tcreate ? "create" : "walk", elem->s); + xp = xf->xp; + s = xp->s; + nfid = newfid(s); + setfid(s, xf->urfid); + s->f.newfid = nfid - s->fids; + if(type == Tcreate){ + s->f.nwname = 0; + if(xmesg(s, Twalk)){ + putfid(s, nfid); + return 0; + } + s->f.fid = nfid - s->fids; + } + if(type == Tcreate){ + s->f.name = elem->s; + s->f.perm = perm; + s->f.mode = (perm&DMDIR) ? OREAD : ORDWR; + if(xmesg(s, type)){ + clunkfid(s, nfid); + return 0; + } + }else{ /* Twalk */ + s->f.nwname = 1; + s->f.wname[0] = elem->s; + if(xmesg(s, type) || s->f.nwqid!=1){ + putfid(s, nfid); + return 0; + } + s->f.qid = s->f.wqid[0]; /* only one element */ + } + chat("fid=%d,qid=0x%llux,%ld,%.2ux...", s->f.fid, s->f.qid.path, s->f.qid.vers, s->f.qid.type); + newxp = xfile(&s->f.qid, s, 1); + if(newxp->parent == 0){ + chat("new xfile..."); + newxp->parent = xp; + newxp->sib = xp->child; + xp->child = newxp; + } + newxf = xfid(xf->uid, newxp, 1); + if(type == Tcreate){ + newxf->mode = (perm&DMDIR) ? Oread : (Oread|Owrite); + newxf->opfid = nfid; + nfid->owner = &newxf->opfid; + nfid = newfid(s); + setfid(s, xf->urfid); + s->f.newfid = nfid - s->fids; + s->f.nwname = 1; + s->f.wname[0] = elem->s; + if(xmesg(s, Twalk) || s->f.nwqid!=1){ + putfid(s, nfid); + xpclear(newxp); + return 0; + } + newxf->urfid = nfid; + nfid->owner = &newxf->urfid; + }else if(newxf->urfid){ + chat("old xfid %ld...", newxf->urfid-s->fids); + clunkfid(s, nfid); + }else{ + newxf->urfid = nfid; + nfid->owner = &newxf->urfid; + } + newxp->name = strstore(elem->s); + return newxf; +} + +void +xpclear(Xfile *xp) +{ + Session *s; + Xfid *xf; + Xfile *xnp; + + s = xp->s; + while(xf = xp->users) /* assign = */ + xfclear(xf); + while(xnp = xp->child){ /* assign = */ + xp->child = xnp->sib; + xnp->parent = 0; + xpclear(xnp); + xfile(&xnp->qid, s, -1); + } + if(xnp = xp->parent){ /* assign = */ + if(xnp->child == xp) + xnp->child = xp->sib; + else{ + xnp = xnp->child; + while(xnp->sib != xp) + xnp = xnp->sib; + xnp->sib = xp->sib; + } + xfile(&xp->qid, s, -1); + } +} + +int +xp2fhandle(Xfile *xp, Fhandle fh) +{ + uchar *dataptr = fh; + ulong x; + int n; + + memset(fh, 0, FHSIZE); + if(xp == xp->parent){ /* root */ + dataptr[0] = 0; + dataptr[1] = 0; + n = strlen(xp->s->service); + if(n > FHSIZE-3) + n = FHSIZE-3; + memmove(&dataptr[2], xp->s->service, n); + dataptr[2+n] = 0; + }else{ + PLONG(starttime); + PLONG((u32int)(uintptr)xp->s); + x = xp->qid.path; + PLONG(x); + x = xp->qid.path>>32; + PLONG(x); + PBYTE(xp->qid.type); + USED(dataptr); + } + return FHSIZE; +} + +int +dir2fattr(Unixidmap *up, Dir *dp, void *mp) +{ + uchar *dataptr = mp; + long length; + int r; + + r = dp->mode & 0777; + if (dp->mode & DMDIR) + length = 1024; + else + length = dp->length; + if((dp->mode & DMDIR) && dp->type == '/' && dp->dev == 0) + r |= 0555; + if(dp->mode & DMDIR){ + PLONG(NFDIR); /* type */ + r |= S_IFDIR; + PLONG(r); /* mode */ + PLONG(3); /* nlink */ + }else{ + PLONG(NFREG); /* type */ + r |= S_IFREG; + PLONG(r); /* mode */ + PLONG(1); /* nlink */ + } + r = name2id(&up->u.ids, dp->uid); + if(r < 0){ + r = name2id(&up->u.ids, "daemon"); + if(r < 0) + r = 1; + } + PLONG(r); /* uid */ + r = name2id(&up->g.ids, dp->gid); + if(r < 0){ + r = name2id(&up->g.ids, "user"); + if(r < 0) + r = 1; + } + PLONG(r); /* gid */ + PLONG(length); /* size */ + PLONG(2048); /* blocksize */ + PLONG(0); /* rdev */ + r = (length+2047)/2048; + PLONG(r); /* blocks */ + r = (dp->type<<16) | dp->dev; + PLONG(r); /* fsid */ + PLONG(dp->qid.path); /* fileid */ + PLONG(dp->atime); /* atime */ + PLONG(0); + PLONG(dp->mtime); /* mtime */ + PLONG(0); + PLONG(dp->mtime); /* ctime */ + PLONG(0); + return dataptr - (uchar *)mp; +} + +int +convM2sattr(void *mp, Sattr *sp) +{ + uchar *argptr = mp; + + sp->mode = GLONG(); + sp->uid = GLONG(); + sp->gid = GLONG(); + sp->size = GLONG(); + sp->atime = GLONG(); + sp->ausec = GLONG(); + sp->mtime = GLONG(); + sp->musec = GLONG(); + return argptr - (uchar *)mp; +} diff --git a/sys/src/cmd/9nfs/nfs.h b/sys/src/cmd/9nfs/nfs.h new file mode 100755 index 000000000..869906e4a --- /dev/null +++ b/sys/src/cmd/9nfs/nfs.h @@ -0,0 +1,44 @@ +/* + * Cf. /lib/rfc/rfc1094 + */ + +enum NFS_stat +{ + NFS_OK = 0, + NFSERR_PERM = 1, + NFSERR_NOENT = 2, + NFSERR_IO = 5, + NFSERR_NXIO = 6, + NFSERR_ACCES = 13, + NFSERR_EXIST = 17, + NFSERR_NODEV = 19, + NFSERR_NOTDIR = 20, + NFSERR_ISDIR = 21, + NFSERR_FBIG = 27, + NFSERR_NOSPC = 28, + NFSERR_ROFS = 30, + NFSERR_NAMETOOLONG = 63, + NFSERR_NOTEMPTY = 66, + NFSERR_DQUOT = 69, + NFSERR_STALE = 70, + NFSERR_WFLUSH = 99 +}; + +enum NFS_ftype +{ + NFNON = 0, + NFREG = 1, + NFDIR = 2, + NFBLK = 3, + NFCHR = 4, + NFLNK = 5 +}; + +enum NFS_mode +{ + S_IFMT = 0170000, /* mask */ + S_IFDIR = 0040000, /* directory */ + S_IFREG = 0100000 /* regular */ +}; + +#define NOATTR 0xffffffff diff --git a/sys/src/cmd/9nfs/nfsmount.c b/sys/src/cmd/9nfs/nfsmount.c new file mode 100755 index 000000000..583669273 --- /dev/null +++ b/sys/src/cmd/9nfs/nfsmount.c @@ -0,0 +1,333 @@ +#include "all.h" + +/* + * Cf. /lib/rfc/rfc1094 + */ + +static int mntnull(int, Rpccall*, Rpccall*); +static int mntmnt(int, Rpccall*, Rpccall*); +static int mntdump(int, Rpccall*, Rpccall*); +static int mntumnt(int, Rpccall*, Rpccall*); +static int mntumntall(int, Rpccall*, Rpccall*); +static int mntexport(int, Rpccall*, Rpccall*); + +Procmap mntproc[] = { + 0, mntnull, + 1, mntmnt, + 2, mntdump, + 3, mntumnt, + 4, mntumntall, + 5, mntexport, + 0, 0 +}; + +long starttime; +static int noauth; +char * config; +Session * head; +Session * tail; +int staletime = 10*60; + +void +mnttimer(long now) +{ + Session *s; + + for(s=head; s; s=s->next) + fidtimer(s, now); +} + +static void +usage(void) +{ + sysfatal("usage: %s %s [-ns] [-a dialstring] [-c uidmap] [-f srvfile] " + "[-T staletime]", argv0, commonopts); +} + +void +mntinit(int argc, char **argv) +{ + int tries; + + config = "config"; + starttime = time(0); + clog("nfs mount server init, starttime = %lud\n", starttime); + tries = 0; + ARGBEGIN{ + case 'a': + ++tries; + srvinit(-1, 0, EARGF(usage())); + break; + case 'c': + config = EARGF(usage()); + break; + case 'f': + ++tries; + srvinit(-1, EARGF(usage()), 0); + break; + case 'n': + ++noauth; + break; + case 's': + ++tries; + srvinit(1, 0, 0); + break; + case 'T': + staletime = atoi(EARGF(usage())); + break; + default: + if(argopt(ARGC()) < 0) + sysfatal("usage: %s %s [-ns] [-a dialstring] " + "[-c uidmap] [-f srvfile] [-T staletime]", + argv0, commonopts); + break; + }ARGEND +noauth=1; /* ZZZ */ + if(tries == 0 && head == 0) + srvinit(-1, 0, "tcp!fs"); + if(head == 0) + panic("can't initialize services"); + readunixidmaps(config); +} + +void +srvinit(int fd, char *file, char *addr) +{ + char fdservice[16], *naddr; + Session *s; + Xfile *xp; + Xfid *xf; + Fid *f; + + s = calloc(1, sizeof(Session)); + s->spec = ""; + s->fd = -1; + if(fd >= 0){ + s->fd = fd; + sprint(fdservice, "/fd/%d", s->fd); + s->service = strstore(fdservice); + chat("fd = %d\n", s->fd); + }else if(file){ + chat("file = \"%s\"\n", file); + s->service = file; + s->fd = open(file, ORDWR); + if(s->fd < 0){ + clog("can't open %s: %r\n", file); + goto error; + } + }else if(addr){ + chat("addr = \"%s\"\n", addr); + naddr = netmkaddr(addr, 0, "9fs"); + s->service = addr; + s->fd = dial(naddr, 0, 0, 0); + if(s->fd < 0){ + clog("can't dial %s: %r\n", naddr); + goto error; + } + } + + chat("version..."); + s->tag = NOTAG-1; + s->f.msize = Maxfdata+IOHDRSZ; + s->f.version = "9P2000"; + xmesg(s, Tversion); + messagesize = IOHDRSZ+s->f.msize; + chat("version spec %s size %d\n", s->f.version, s->f.msize); + + s->tag = 0; + + chat("authenticate..."); + if(authhostowner(s) < 0){ + clog("auth failed %r\n"); + goto error; + } + + chat("attach as none..."); + f = newfid(s); + s->f.fid = f - s->fids; + s->f.afid = ~0x0UL; + s->f.uname = "none"; + s->f.aname = s->spec; + if(xmesg(s, Tattach)){ + clog("attach failed\n"); + goto error; + } + + xp = xfile(&s->f.qid, s, 1); + s->root = xp; + xp->parent = xp; + xp->name = "/"; + xf = xfid("none", xp, 1); + xf->urfid = f; + clog("service=%s uid=%s fid=%ld\n", + s->service, xf->uid, xf->urfid - s->fids); + if(tail) + tail->next = s; + else + head = s; + tail = s; + return; + +error: + if(s->fd >= 0) + close(s->fd); + free(s); +} + +static int +mntnull(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, cmd, reply); + chat("mntnull\n"); + return 0; +} + +static char* +Str2str(String s, char *buf, int nbuf) +{ + int i; + i = s.n; + if(i >= nbuf) + i = nbuf-1; + memmove(buf, s.s, i); + buf[i] = 0; + return buf; +} + +static int +mntmnt(int n, Rpccall *cmd, Rpccall *reply) +{ + int i; + char dom[64]; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + Authunix au; + Xfile *xp; + String root; + + chat("mntmnt...\n"); + if(n < 8) + return garbage(reply, "n too small"); + argptr += string2S(argptr, &root); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + clog("host=%I, port=%ld, root=\"%.*s\"...", + cmd->host, cmd->port, utfnlen(root.s, root.n), root.s); + if(auth2unix(&cmd->cred, &au) != 0){ + chat("auth flavor=%ld, count=%ld\n", + cmd->cred.flavor, cmd->cred.count); + for(i=0; i<cmd->cred.count; i++) + chat(" %.2ux", ((uchar *)cmd->cred.data)[i]); + chat("\n"); + clog("auth: bad credentials"); + return error(reply, 1); + } + clog("auth: %ld %.*s u=%ld g=%ld", + au.stamp, utfnlen(au.mach.s, au.mach.n), au.mach.s, au.uid, au.gid); + for(i=0; i<au.gidlen; i++) + chat(", %ld", au.gids[i]); + chat("..."); + if(getdom(cmd->host, dom, sizeof(dom))<0){ + clog("auth: unknown ip address"); + return error(reply, 1); + } + chat("dom=%s...", dom); + xp = xfroot(root.s, root.n); + if(xp == 0){ + chat("xp=0..."); + clog("mntmnt: no fs"); + return error(reply, 3); + } + + PLONG(0); + dataptr += xp2fhandle(xp, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +mntdump(int n, Rpccall *cmd, Rpccall *reply) +{ + if(n != 0) + return garbage(reply, "mntdump"); + USED(cmd); + chat("mntdump..."); + return error(reply, FALSE); +} + +static int +mntumnt(int n, Rpccall *cmd, Rpccall *reply) +{ + if(n <= 0) + return garbage(reply, "mntumnt"); + USED(cmd); + chat("mntumnt\n"); + return 0; +} + +static int +mntumntall(int n, Rpccall *cmd, Rpccall *reply) +{ + if(n != 0) + return garbage(reply, "mntumntall"); + USED(cmd); + chat("mntumntall\n"); + return 0; +} + +static int +mntexport(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *dataptr = reply->results; + Authunix au; + int i; + + chat("mntexport..."); + if(n != 0) + return garbage(reply, "mntexport"); + if(auth2unix(&cmd->cred, &au) != 0){ + chat("auth flavor=%ld, count=%ld\n", + cmd->cred.flavor, cmd->cred.count); + for(i=0; i<cmd->cred.count; i++) + chat(" %.2ux", ((uchar *)cmd->cred.data)[i]); + chat("..."); + au.mach.n = 0; + }else + chat("%ld@%.*s...", au.uid, utfnlen(au.mach.s, au.mach.n), au.mach.s); + PLONG(TRUE); + PLONG(1); + PPTR("/", 1); + if(au.mach.n > 0){ + PLONG(TRUE); + PLONG(au.mach.n); + PPTR(au.mach.s, au.mach.n); + } + PLONG(FALSE); + PLONG(FALSE); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +Xfile * +xfroot(char *name, int n) +{ + Session *s; + char *p; + + if(n <= 0) + n = strlen(name); + chat("xfroot: %.*s...", utfnlen(name, n), name); + if(n == 1 && name[0] == '/') + return head->root; + for(s=head; s; s=s->next){ + if(strncmp(name, s->service, n) == 0) + return s->root; + p = strrchr(s->service, '!'); /* for -a tcp!foo */ + if(p && strncmp(name, p+1, n) == 0) + return s->root; + p = strrchr(s->service, '/'); /* for -f /srv/foo */ + if(p && strncmp(name, p+1, n) == 0) + return s->root; + } + return 0; +} diff --git a/sys/src/cmd/9nfs/nfsserver.c b/sys/src/cmd/9nfs/nfsserver.c new file mode 100755 index 000000000..6951dfeb1 --- /dev/null +++ b/sys/src/cmd/9nfs/nfsserver.c @@ -0,0 +1,655 @@ +#include "all.h" + +/* + * Cf. /lib/rfc/rfc1094 + */ + +static int nfsnull(int, Rpccall*, Rpccall*); +static int nfsgetattr(int, Rpccall*, Rpccall*); +static int nfssetattr(int, Rpccall*, Rpccall*); +static int nfsroot(int, Rpccall*, Rpccall*); +static int nfslookup(int, Rpccall*, Rpccall*); +static int nfsreadlink(int, Rpccall*, Rpccall*); +static int nfsread(int, Rpccall*, Rpccall*); +static int nfswritecache(int, Rpccall*, Rpccall*); +static int nfswrite(int, Rpccall*, Rpccall*); +static int nfscreate(int, Rpccall*, Rpccall*); +static int nfsremove(int, Rpccall*, Rpccall*); +static int nfsrename(int, Rpccall*, Rpccall*); +static int nfslink(int, Rpccall*, Rpccall*); +static int nfssymlink(int, Rpccall*, Rpccall*); +static int nfsmkdir(int, Rpccall*, Rpccall*); +static int nfsrmdir(int, Rpccall*, Rpccall*); +static int nfsreaddir(int, Rpccall*, Rpccall*); +static int nfsstatfs(int, Rpccall*, Rpccall*); + +Procmap nfsproc[] = { + 0, nfsnull, /* void */ + 1, nfsgetattr, /* Fhandle */ + 2, nfssetattr, /* Fhandle, Sattr */ + 3, nfsroot, /* void */ + 4, nfslookup, /* Fhandle, String */ + 5, nfsreadlink, /* Fhandle */ + 6, nfsread, /* Fhandle, long, long, long */ + 7, nfswritecache,/* void */ + 8, nfswrite, /* Fhandle, long, long, long, String */ + 9, nfscreate, /* Fhandle, String, Sattr */ + 10, nfsremove, /* Fhandle, String */ + 11, nfsrename, /* Fhandle, String, Fhandle, String */ + 12, nfslink, /* Fhandle, Fhandle, String */ + 13, nfssymlink, /* Fhandle, String, String, Sattr */ + 14, nfsmkdir, /* Fhandle, String, Sattr */ + 15, nfsrmdir, /* Fhandle, String */ + 16, nfsreaddir, /* Fhandle, long, long */ + 17, nfsstatfs, /* Fhandle */ + 0, 0 +}; + +void nfsinit(int, char**); +extern void mntinit(int, char**); +extern Procmap mntproc[]; + +Progmap progmap[] = { + 100005, 1, mntinit, mntproc, + 100003, 2, nfsinit, nfsproc, + 0, 0, 0, +}; + +int myport = 2049; +long nfstime; +int conftime; + +void +main(int argc, char *argv[]) +{ + server(argc, argv, myport, progmap); +} + +static void +doalarm(void) +{ + nfstime = time(0); + mnttimer(nfstime); + if(conftime+5*60 < nfstime){ + conftime = nfstime; + readunixidmaps(config); + } +} + +void +nfsinit(int argc, char **argv) +{ + /* + * mntinit will have already parsed our options. + */ + USED(argc, argv); + clog("nfs file server init\n"); + rpcalarm = doalarm; + nfstime = time(0); +} + +static int +nfsnull(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("nfsnull..."); + showauth(&cmd->cred); + chat("OK\n"); + return 0; +} + +static int +nfsgetattr(int n, Rpccall *cmd, Rpccall *reply) +{ + Xfid *xf; + Dir dir; + uchar *dataptr = reply->results; + + chat("getattr..."); + if(n != FHSIZE) + return garbage(reply, "bad count"); + xf = rpc2xfid(cmd, &dir); + if(xf == 0) + return error(reply, NFSERR_STALE); + chat("%s...", xf->xp->name); + PLONG(NFS_OK); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfssetattr(int n, Rpccall *cmd, Rpccall *reply) +{ + Xfid *xf; + Dir dir, nd; + Sattr sattr; + int r; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + chat("setattr..."); + if(n <= FHSIZE) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, &dir); + argptr += FHSIZE; + argptr += convM2sattr(argptr, &sattr); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + chat("mode=0%lo,u=%ld,g=%ld,size=%ld,atime=%ld,mtime=%ld...", + sattr.mode, sattr.uid, sattr.gid, sattr.size, + sattr.atime, sattr.mtime); + if(xf == 0) + return error(reply, NFSERR_STALE); + if(sattr.uid != NOATTR || sattr.gid != NOATTR) + return error(reply, NFSERR_PERM); + if(sattr.size == 0){ + if(xf->xp->s != xf->xp->parent->s){ + if(xfauthremove(xf, cmd->user) < 0) + return error(reply, NFSERR_PERM); + }else if(dir.length && xfopen(xf, Trunc|Oread|Owrite) < 0) + return error(reply, NFSERR_PERM); + }else if(sattr.size != NOATTR) + return error(reply, NFSERR_PERM); + r = 0; + nulldir(&nd); + if(sattr.mode != NOATTR) + ++r, nd.mode = (dir.mode & ~0777) | (sattr.mode & 0777); + if(sattr.atime != NOATTR) + ++r, nd.atime = sattr.atime; + if(sattr.mtime != NOATTR) + ++r, nd.mtime = sattr.mtime; + chat("sattr.mode=%luo dir.mode=%luo nd.mode=%luo...", sattr.mode, dir.mode, nd.mode); + if(r){ + r = xfwstat(xf, &nd); + if(r < 0) + return error(reply, NFSERR_PERM); + } + if(xfstat(xf, &dir) < 0) + return error(reply, NFSERR_STALE); + PLONG(NFS_OK); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfsroot(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("nfsroot..."); + showauth(&cmd->cred); + chat("OK\n"); + return 0; +} + +static int +nfslookup(int n, Rpccall *cmd, Rpccall *reply) +{ + Xfile *xp; + Xfid *xf, *newxf; + String elem; + Dir dir; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + chat("lookup..."); + if(n <= FHSIZE) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE; + argptr += string2S(argptr, &elem); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + if(xf == 0) + return error(reply, NFSERR_STALE); + xp = xf->xp; + if(!(xp->qid.type & QTDIR)) + return error(reply, NFSERR_NOTDIR); + chat("%s -> \"%.*s\"...", xp->name, utfnlen(elem.s, elem.n), elem.s); + if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#') + newxf = xfauth(xp, &elem); + else + newxf = xfwalkcr(Twalk, xf, &elem, 0); + if(newxf == 0) + return error(reply, NFSERR_NOENT); + if(xfstat(newxf, &dir) < 0) + return error(reply, NFSERR_IO); + PLONG(NFS_OK); + dataptr += xp2fhandle(newxf->xp, dataptr); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfsreadlink(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("readlink..."); + showauth(&cmd->cred); + return error(reply, NFSERR_NOENT); +} + +static int +nfsread(int n, Rpccall *cmd, Rpccall *reply) +{ + Session *s; + Xfid *xf; + Dir dir; + int offset, count; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + uchar *readptr = dataptr + 4 + 17*4 + 4; + + chat("read..."); + if(n != FHSIZE+12) + return garbage(reply, "bad count"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE; + offset = GLONG(); + count = GLONG(); + if(xf == 0) + return error(reply, NFSERR_STALE); + chat("%s %d %d...", xf->xp->name, offset, count); + if(xf->xp->s != xf->xp->parent->s){ + count = xfauthread(xf, offset, readptr, count); + }else{ + if(xfopen(xf, Oread) < 0) + return error(reply, NFSERR_PERM); + if(count > 8192) + count = 8192; + s = xf->xp->s; + setfid(s, xf->opfid); + xf->opfid->tstale = nfstime + 60; + s->f.offset = offset; + s->f.count = count; + if(xmesg(s, Tread) < 0) + return error(reply, NFSERR_IO); + count = s->f.count; + memmove(readptr, s->f.data, count); + } + if(xfstat(xf, &dir) < 0) + return error(reply, NFSERR_IO); + PLONG(NFS_OK); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + PLONG(count); + dataptr += ROUNDUP(count); + chat("%d OK\n", count); + return dataptr - (uchar *)reply->results; +} + +static int +nfswritecache(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("writecache..."); + showauth(&cmd->cred); + chat("OK\n"); + return 0; +} + +static int +nfswrite(int n, Rpccall *cmd, Rpccall *reply) +{ + Session *s; + Xfid *xf; + Dir dir; + int offset, count; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + chat("write..."); + if(n < FHSIZE+16) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE + 4; + offset = GLONG(); + argptr += 4; + count = GLONG(); + if(xf == 0) + return error(reply, NFSERR_STALE); + chat("%s %d %d...", xf->xp->name, offset, count); + if(xf->xp->s != xf->xp->parent->s){ + if(xfauthwrite(xf, offset, argptr, count) < 0) + return error(reply, NFSERR_IO); + }else{ + if(xfopen(xf, Owrite) < 0) + return error(reply, NFSERR_PERM); + s = xf->xp->s; + setfid(s, xf->opfid); + xf->opfid->tstale = nfstime + 60; + s->f.offset = offset; + s->f.count = count; + s->f.data = (char *)argptr; + if(xmesg(s, Twrite) < 0) + return error(reply, NFSERR_IO); + } + if(xfstat(xf, &dir) < 0) + return error(reply, NFSERR_IO); + PLONG(NFS_OK); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +creat(int n, Rpccall *cmd, Rpccall *reply, int chdir) +{ + Xfid *xf, *newxf; + Xfile *xp; + String elem; + Dir dir; Sattr sattr; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + int trunced; + + if(n <= FHSIZE) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE; + argptr += string2S(argptr, &elem); + argptr += convM2sattr(argptr, &sattr); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + if(xf == 0) + return error(reply, NFSERR_STALE); + xp = xf->xp; + if(!(xp->qid.type & QTDIR)) + return error(reply, NFSERR_NOTDIR); + chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s); + trunced = 0; + if(xp->parent == xp && elem.s[0] == '#'){ + newxf = xfauth(xp, &elem); + if(newxf == 0) + return error(reply, NFSERR_PERM); + if(xfauthremove(newxf, cmd->user) < 0) + return error(reply, NFSERR_PERM); + trunced = 1; + }else + newxf = xfwalkcr(Twalk, xf, &elem, 0); + if(newxf == 0){ + newxf = xfwalkcr(Tcreate, xf, &elem, chdir|(sattr.mode&0777)); + if(newxf) + trunced = 1; + else + newxf = xfwalkcr(Twalk, xf, &elem, 0); + } + if(newxf == 0) + return error(reply, NFSERR_PERM); + if(!trunced && chdir) + return error(reply, NFSERR_EXIST); + if(!trunced && xfopen(newxf, Trunc|Oread|Owrite) < 0) + return error(reply, NFSERR_PERM); + if(xfstat(newxf, &dir) < 0) + return error(reply, NFSERR_IO); + + PLONG(NFS_OK); + dataptr += xp2fhandle(newxf->xp, dataptr); + dataptr += dir2fattr(cmd->up, &dir, dataptr); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfscreate(int n, Rpccall *cmd, Rpccall *reply) +{ + chat("create..."); + return creat(n, cmd, reply, 0); +} + +static int +remov(int n, Rpccall *cmd, Rpccall *reply) +{ + Session *s; + Xfile *xp; + Xfid *xf, *newxf; + String elem; + Fid *nfid; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + if(n <= FHSIZE) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE; + argptr += string2S(argptr, &elem); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + if(xf == 0) + return error(reply, NFSERR_STALE); + xp = xf->xp; + if(!(xp->qid.type & QTDIR)) + return error(reply, NFSERR_NOTDIR); + chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s); + if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#') + return error(reply, NFSERR_PERM); + newxf = xfwalkcr(Twalk, xf, &elem, 0); + if(newxf == 0) + return error(reply, NFSERR_NOENT); + s = xp->s; + nfid = newfid(s); + setfid(s, newxf->urfid); + s->f.newfid = nfid - s->fids; + s->f.nwname = 0; + if(xmesg(s, Twalk) < 0){ + putfid(s, nfid); + return error(reply, NFSERR_IO); + } + s->f.fid = nfid - s->fids; + if(xmesg(s, Tremove) < 0){ + putfid(s, nfid); + return error(reply, NFSERR_PERM); + } + putfid(s, nfid); + xpclear(newxf->xp); + PLONG(NFS_OK); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfsremove(int n, Rpccall *cmd, Rpccall *reply) +{ + chat("remove..."); + return remov(n, cmd, reply); +} + +static int +nfsrename(int n, Rpccall *cmd, Rpccall *reply) +{ + Xfid *xf, *newxf; + Xfile *xp; + uchar *fromdir, *todir; + String fromelem, toelem; + Dir dir; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + chat("rename..."); + if(n <= FHSIZE) + return garbage(reply, "count too small"); + xf = rpc2xfid(cmd, 0); + fromdir = argptr; + argptr += FHSIZE; + argptr += string2S(argptr, &fromelem); + todir = argptr; + argptr += FHSIZE; + argptr += string2S(argptr, &toelem); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + if(xf == 0) + return error(reply, NFSERR_STALE); + xp = xf->xp; + if(!(xp->qid.type & QTDIR)) + return error(reply, NFSERR_NOTDIR); + if(memcmp(fromdir, todir, FHSIZE) != 0) + return error(reply, NFSERR_NXIO); + newxf = xfwalkcr(Twalk, xf, &fromelem, 0); + if(newxf == 0) + return error(reply, NFSERR_NOENT); + if(xfstat(newxf, &dir) < 0) + return error(reply, NFSERR_IO); + + if(xp->parent == xp && toelem.s[0] == '#') + return error(reply, NFSERR_PERM); + nulldir(&dir); + dir.name = toelem.s; + if(xfwstat(newxf, &dir) < 0) + return error(reply, NFSERR_PERM); + PLONG(NFS_OK); + chat("OK\n"); + return dataptr - (uchar *)reply->results; +} + +static int +nfslink(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("link..."); + showauth(&cmd->cred); + return error(reply, NFSERR_NOENT); +} + +static int +nfssymlink(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, reply); + chat("symlink..."); + showauth(&cmd->cred); + return error(reply, NFSERR_NOENT); +} + +static int +nfsmkdir(int n, Rpccall *cmd, Rpccall *reply) +{ + chat("mkdir..."); + return creat(n, cmd, reply, DMDIR); +} + +static int +nfsrmdir(int n, Rpccall *cmd, Rpccall *reply) +{ + chat("rmdir..."); + return remov(n, cmd, reply); +} + +static int +nfsreaddir(int n, Rpccall *cmd, Rpccall *reply) +{ + Session *s; + Xfid *xf; + Dir dir; + char *rdata; + int k, offset, count, sfcount, entries, dsize; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + + chat("readdir..."); + if(n != FHSIZE+8) + return garbage(reply, "bad count"); + xf = rpc2xfid(cmd, 0); + argptr += FHSIZE; + offset = GLONG(); + count = GLONG(); + if(xf == 0) + return error(reply, NFSERR_STALE); + chat("%s (%ld) %d %d...", xf->xp->name, xf->offset, offset, count); + s = xf->xp->s; + if((xf->mode & Open) && xf->offset > offset) + xfclose(xf); + if(xfopen(xf, Oread) < 0) + return error(reply, NFSERR_PERM); + while(xf->offset < offset){ /* if we reopened, xf->offset will be zero */ + sfcount = offset - xf->offset; + if(sfcount > messagesize-IOHDRSZ) + sfcount = messagesize-IOHDRSZ; + setfid(s, xf->opfid); + s->f.offset = xf->offset; + s->f.count = sfcount; + if(xmesg(s, Tread) < 0){ + xfclose(xf); + return error(reply, NFSERR_IO); + } + if(s->f.count <= BIT16SZ) + break; + xf->offset += s->f.count; + } + if(count > messagesize-IOHDRSZ) + count = messagesize-IOHDRSZ; + PLONG(NFS_OK); + entries = 0; + while(count > 16){ /* at least 16 bytes required; we don't know size of name */ +chat("top of loop\n"); + setfid(s, xf->opfid); + s->f.offset = xf->offset; + s->f.count = count; /* as good a guess as any */ + if(xmesg(s, Tread) < 0){ + xfclose(xf); + return error(reply, NFSERR_IO); + } + sfcount = s->f.count; + if(sfcount <= BIT16SZ) + break; + xf->offset += sfcount; +chat("count %d data 0x%p\n", s->f.count, s->f.data); + rdata = s->f.data; + /* now have a buffer of Plan 9 directories; unpack into NFS thingies */ + while(sfcount >= 0){ + dsize = convM2D((uchar*)rdata, sfcount, &dir, (char*)s->statbuf); + if(dsize <= BIT16SZ){ + count = 0; /* force break from outer loop */ + break; + } + offset += dsize; + k = strlen(dir.name); + if(count < 16+ROUNDUP(k)){ + count = 0; /* force break from outer loop */ + break; + } + PLONG(TRUE); + PLONG(dir.qid.path); + PLONG(k); + PPTR(dir.name, k); + PLONG(offset); + count -= 16+ROUNDUP(k); + rdata += dsize; + sfcount -= dsize; + } + } + PLONG(FALSE); + if(s->f.count <= 0){ + xfclose(xf); + chat("eof..."); + PLONG(TRUE); + }else + PLONG(FALSE); + chat("%d OK\n", entries); + return dataptr - (uchar *)reply->results; +} + +static int +nfsstatfs(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *dataptr = reply->results; + enum { + Xfersize = 2048, + Maxlong = (long)((1ULL<<31) - 1), + Maxfreeblks = Maxlong / Xfersize, + }; + + chat("statfs..."); + showauth(&cmd->cred); + if(n != FHSIZE) + return garbage(reply, "bad count"); + PLONG(NFS_OK); + PLONG(4096); /* tsize (fs block size) */ + PLONG(Xfersize); /* bsize (optimal transfer size) */ + PLONG(Maxfreeblks); /* blocks in fs */ + PLONG(Maxfreeblks); /* bfree to root*/ + PLONG(Maxfreeblks); /* bavail (free to mortals) */ + chat("OK\n"); + /*conftime = 0; + readunixidmaps(config);*/ + return dataptr - (uchar *)reply->results; +} diff --git a/sys/src/cmd/9nfs/pcnfsd.c b/sys/src/cmd/9nfs/pcnfsd.c new file mode 100755 index 000000000..cfb947dd6 --- /dev/null +++ b/sys/src/cmd/9nfs/pcnfsd.c @@ -0,0 +1,180 @@ +#include "all.h" + +static void pcinit(int, char**); +static int pcnull(int, Rpccall*, Rpccall*); +static int pcinfo(int, Rpccall*, Rpccall*); +static int pcauth(int, Rpccall*, Rpccall*); +static int pc1auth(int, Rpccall*, Rpccall*); +static int pcfacilities[15]; +static char no_comment[] = "Trust me."; +static char pc_vers[] = "@(#)pcnfsd_v2.c 1.6 - rpc.pcnfsd V2.0 (c) 1994 P9, GmbH"; +static char pc_home[] = "merrimack:/"; + +static Procmap pcproc[] = { /* pcnfsd v2 */ + 0, pcnull, + 1, pcinfo, + 13, pcauth, + 0, 0 +}; + +static Procmap pc1proc[] = { /* pc-nfsd v1 */ + 0, pcnull, + 1, pc1auth, + 0, 0 +}; + +int myport = 1111; + +Progmap progmap[] = { + 150001, 2, pcinit, pcproc, + 150001, 1, 0, pc1proc, + 0, 0, 0, +}; + +void +main(int argc, char *argv[]) +{ + server(argc, argv, myport, progmap); +} + +static void +pcinit(int argc, char **argv) +{ + Procmap *p; + int i; + char *config = "config"; + + ARGBEGIN{ + case 'c': + config = ARGF(); + break; + default: + if(argopt(ARGC()) < 0) + sysfatal("usage: %s %s [-c config]", argv0, commonopts); + break; + }ARGEND; + clog("pc init\n"); + + for(i=0; i<nelem(pcfacilities); i++) + pcfacilities[i] = -1; + for(p=pcproc; p->procp; p++) + pcfacilities[p->procno] = 100; + readunixidmaps(config); +} + +static int +pcnull(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, cmd, reply); + return 0; +} + +static void +scramble(String *x) +{ + int i; + + for(i=0; i<x->n; i++) + x->s[i] = (x->s[i] ^ 0x5b) & 0x7f; +} + +static int +pcinfo(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + String vers, cm; + int i; + + chat("host=%I, port=%ld: pcinfo...", + cmd->host, cmd->port); + if(n <= 16) + return garbage(reply, "count too small"); + argptr += string2S(argptr, &vers); + argptr += string2S(argptr, &cm); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + chat("\"%.*s\",\"%.*s\"\n", utfnlen(vers.s, vers.n), vers.s, utfnlen(cm.s, cm.n), cm.s); + PLONG(sizeof(pc_vers)-1); + PPTR(pc_vers, sizeof(pc_vers)-1); + PLONG(sizeof(no_comment)-1); + PPTR(no_comment, sizeof(no_comment)-1); + PLONG(nelem(pcfacilities)); + for(i=0; i<nelem(pcfacilities); i++) + PLONG(pcfacilities[i]); + return dataptr - (uchar *)reply->results; +} + +static int +pc1auth(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + String id, pw; + Unixidmap *m; + int uid; + + chat("host=%I, port=%ld: pcauth...", + cmd->host, cmd->port); + if(n <= 8) + return garbage(reply, "count too small"); + argptr += string2S(argptr, &id); + argptr += string2S(argptr, &pw); + if(argptr != &((uchar*)cmd->args)[n]) + return garbage(reply, "bad count"); + scramble(&id); + scramble(&pw); + m = pair2idmap("pcnfsd", cmd->host); + uid = -1; + if(m) + uid = name2id(&m->u.ids, id.s); + if(uid < 0) + uid = 1; + chat("\"%.*s\",\"%.*s\" uid=%d\n", utfnlen(id.s, id.n), id.s, utfnlen(pw.s, pw.n), pw.s, uid); + PLONG(0); /* status */ + PLONG(uid); /* uid */ + PLONG(uid); /* gid */ + return dataptr - (uchar*)reply->results; +} + +static int +pcauth(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + String sys, id, pw, cm; + Unixidmap *m; + int uid; + + chat("host=%I, port=%ld: pcauth...", + cmd->host, cmd->port); + if(n <= 16) + return garbage(reply, "count too small"); + argptr += string2S(argptr, &sys); + argptr += string2S(argptr, &id); + argptr += string2S(argptr, &pw); + argptr += string2S(argptr, &cm); + if(argptr != &((uchar *)cmd->args)[n]) + return garbage(reply, "bad count"); + scramble(&id); + scramble(&pw); + + m = pair2idmap("pcnfsd", cmd->host); + uid = -1; + if(m) + uid = name2id(&m->u.ids, id.s); + if(uid < 0) + uid = 1; + chat("\"%.*s\",\"%.*s\",\"%.*s\",\"%.*s\"\n", utfnlen(sys.s, sys.n), sys.s, + utfnlen(id.s, id.n), id.s, utfnlen(pw.s, pw.n), pw.s, utfnlen(cm.s, cm.n), cm.s); + PLONG(0); /* status - OK */ + PLONG(uid); + PLONG(uid); /* gid */ + PLONG(0); /* ngids */ + PLONG(sizeof(pc_home)-1); + PPTR(pc_home, sizeof(pc_home)-1); + PLONG(0); /* umask */ + PLONG(sizeof(no_comment)-1); + PPTR(no_comment, sizeof(no_comment)-1); + return dataptr - (uchar *)reply->results; +} diff --git a/sys/src/cmd/9nfs/portmapper.c b/sys/src/cmd/9nfs/portmapper.c new file mode 100755 index 000000000..ef3d7cfdd --- /dev/null +++ b/sys/src/cmd/9nfs/portmapper.c @@ -0,0 +1,172 @@ +/* + * sunrpc portmapper + */ +#include "all.h" + +typedef struct Portmap Portmap; +struct Portmap +{ + int prog; + int vers; + int protocol; + int port; +}; + +Portmap map[] = { + 100003, 2, IPPROTO_UDP, 2049, /* nfs v2 */ +// 100003, 3, IPPROTO_UDP, 2049, /* nfs v3 */ + 100005, 1, IPPROTO_UDP, 2049, /* mount */ + 150001, 2, IPPROTO_UDP, 1111, /* pcnfsd v2 */ + 150001, 1, IPPROTO_UDP, 1111, /* pcnfsd v1 */ + 0, 0, 0, 0, +}; + +static void pmapinit(int, char**); +static int pmapnull(int, Rpccall*, Rpccall*); +static int pmapset(int, Rpccall*, Rpccall*); +static int pmapunset(int, Rpccall*, Rpccall*); +static int pmapgetport(int, Rpccall*, Rpccall*); +static int pmapdump(int, Rpccall*, Rpccall*); +static int pmapcallit(int, Rpccall*, Rpccall*); + +static Procmap pmapproc[] = { + 0, pmapnull, + 1, pmapset, + 2, pmapunset, + 3, pmapgetport, + 4, pmapdump, + 5, pmapcallit, + 0, 0 +}; + +int myport = 111; + +Progmap progmap[] = { + 100000, 2, pmapinit, pmapproc, + 0, 0, 0, +}; + +void +main(int argc, char *argv[]) +{ + server(argc, argv, myport, progmap); +} + +static +void +pmapinit(int argc, char **argv) +{ + ARGBEGIN{ + default: + if(argopt(ARGC()) < 0) + sysfatal("usage: %s %s", argv0, commonopts); + break; + }ARGEND; + clog("portmapper init\n"); +} + +static int +pmapnull(int n, Rpccall *cmd, Rpccall *reply) +{ + USED(n, cmd, reply); + return 0; +} + +static int +pmapset(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *dataptr = reply->results; + + if(n != 16) + return garbage(reply, "bad count"); + USED(cmd); + PLONG(FALSE); + return dataptr - (uchar *)reply->results; +} + +static int +pmapunset(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *dataptr = reply->results; + + if(n != 16) + return garbage(reply, "bad count"); + USED(cmd); + PLONG(TRUE); + return dataptr - (uchar *)reply->results; +} + +static int +pmapgetport(int n, Rpccall *cmd, Rpccall *reply) +{ + int prog, vers, prot; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + Portmap *mp; + + clog("get port\n"); + + if(n != 16) + return garbage(reply, "bad count"); + prog = GLONG(); + vers = GLONG(); + prot = GLONG(); + chat("host=%I, port=%ld: ", cmd->host, cmd->port); + chat("getport: %d, %d, %d...", prog, vers, prot); + for(mp=map; mp->prog>0; mp++) + if(prog == mp->prog && vers == mp->vers && + prot == mp->protocol) + break; + chat("%d\n", mp->port); + PLONG(mp->port); + return dataptr - (uchar *)reply->results; +} + +static int +pmapdump(int n, Rpccall *cmd, Rpccall *reply) +{ + uchar *dataptr = reply->results; + Portmap *mp; + + if(n != 0) + return garbage(reply, "bad count"); + USED(cmd); + for(mp=map; mp->prog>0; mp++){ + PLONG(1); + PLONG(mp->prog); + PLONG(mp->vers); + PLONG(mp->protocol); + PLONG(mp->port); + } + PLONG(0); + return dataptr - (uchar *)reply->results; +} + +static int +pmapcallit(int n, Rpccall *cmd, Rpccall *reply) +{ + int prog, vers, proc; + uchar *argptr = cmd->args; + uchar *dataptr = reply->results; + Portmap *mp; + + if(n < 12) + return garbage(reply, "bad count"); + prog = GLONG(); + vers = GLONG(); + proc = GLONG(); + chat("host=%I, port=%ld: ", cmd->host, cmd->port); + chat("callit: %d, %d, %d...", prog, vers, proc); + for(mp=map; mp->prog>0; mp++) + if(prog == mp->prog && vers == mp->vers && + proc == 0) + break; + if(mp->port == 0){ + chat("ignored\n"); + return -1; + } + chat("%d\n", mp->port); + PLONG(mp->port); + PLONG(0); + return dataptr - (uchar *)reply->results; +} diff --git a/sys/src/cmd/9nfs/rpc.c b/sys/src/cmd/9nfs/rpc.c new file mode 100755 index 000000000..91086c91a --- /dev/null +++ b/sys/src/cmd/9nfs/rpc.c @@ -0,0 +1,314 @@ +#include "all.h" + +#define SHORT(x) r->x = (p[1] | (p[0]<<8)); p += 2 +#define LONG(x) r->x = (p[3] | (p[2]<<8) |\ + (p[1]<<16) | (p[0]<<24)); p += 4 +#define SKIPLONG p += 4 +#define PTR(x, n) r->x = (void *)(p); p += ROUNDUP(n) + +int +rpcM2S(void *ap, Rpccall *r, int n) +{ + int k; + uchar *p; + Udphdr *up; + + /* copy IPv4 header fields from Udphdr */ + up = ap; + p = &up->raddr[IPaddrlen - IPv4addrlen]; + LONG(host); + USED(p); + p = &up->laddr[IPaddrlen - IPv4addrlen]; + LONG(lhost); + USED(p); + /* ignore up->ifcaddr */ + p = up->rport; + SHORT(port); + SHORT(lport); + + LONG(xid); + LONG(mtype); + switch(r->mtype){ + case CALL: + LONG(rpcvers); + if(r->rpcvers != 2) + break; + LONG(prog); + LONG(vers); + LONG(proc); + LONG(cred.flavor); + LONG(cred.count); + PTR(cred.data, r->cred.count); + LONG(verf.flavor); + LONG(verf.count); + PTR(verf.data, r->verf.count); + r->up = 0; + k = n - (p - (uchar *)ap); + if(k < 0) + break; + PTR(args, k); + break; + case REPLY: + LONG(stat); + switch(r->stat){ + case MSG_ACCEPTED: + LONG(averf.flavor); + LONG(averf.count); + PTR(averf.data, r->averf.count); + LONG(astat); + switch(r->astat){ + case SUCCESS: + k = n - (p - (uchar *)ap); + if(k < 0) + break; + PTR(results, k); + break; + case PROG_MISMATCH: + LONG(plow); + LONG(phigh); + break; + } + break; + case MSG_DENIED: + LONG(rstat); + switch(r->rstat){ + case RPC_MISMATCH: + LONG(rlow); + LONG(rhigh); + break; + case AUTH_ERROR: + LONG(authstat); + break; + } + break; + } + break; + } + n -= p - (uchar *)ap; + return n; +} + +int +auth2unix(Auth *arg, Authunix *r) +{ + int i, n; + uchar *p; + + if(arg->flavor != AUTH_UNIX) + return -1; + p = arg->data; + LONG(stamp); + LONG(mach.n); + PTR(mach.s, r->mach.n); + LONG(uid); + LONG(gid); + LONG(gidlen); + n = r->gidlen; + for(i=0; i<n && i < nelem(r->gids); i++){ + LONG(gids[i]); + } + for(; i<n; i++){ + SKIPLONG; + } + return arg->count - (p - (uchar *)arg->data); +} + +int +string2S(void *arg, String *r) +{ + uchar *p; + char *s; + + p = arg; + LONG(n); + PTR(s, r->n); + /* must NUL terminate */ + s = malloc(r->n+1); + if(s == nil) + panic("malloc(%ld) failed in string2S\n", r->n+1); + memmove(s, r->s, r->n); + s[r->n] = '\0'; + r->s = strstore(s); + free(s); + return p - (uchar *)arg; +} + +#undef SHORT +#undef LONG +#undef PTR + +#define SHORT(x) p[1] = r->x; p[0] = r->x>>8; p += 2 +#define LONG(x) p[3] = r->x; p[2] = r->x>>8; p[1] = r->x>>16; p[0] = r->x>>24; p += 4 + +#define PTR(x,n) memmove(p, r->x, n); p += ROUNDUP(n) + +int +rpcS2M(Rpccall *r, int ndata, void *ap) +{ + uchar *p; + Udphdr *up; + + /* copy header fields to Udphdr */ + up = ap; + memmove(up->raddr, v4prefix, IPaddrlen); + p = &up->raddr[IPaddrlen - IPv4addrlen]; + LONG(host); + USED(p); + memmove(up->laddr, v4prefix, IPaddrlen); + p = &up->laddr[IPaddrlen - IPv4addrlen]; + LONG(lhost); + USED(p); + memmove(up->ifcaddr, IPnoaddr, sizeof up->ifcaddr); + p = up->rport; + SHORT(port); + SHORT(lport); + + LONG(xid); + LONG(mtype); + switch(r->mtype){ + case CALL: + LONG(rpcvers); + LONG(prog); + LONG(vers); + LONG(proc); + LONG(cred.flavor); + LONG(cred.count); + PTR(cred.data, r->cred.count); + LONG(verf.flavor); + LONG(verf.count); + PTR(verf.data, r->verf.count); + PTR(args, ndata); + break; + case REPLY: + LONG(stat); + switch(r->stat){ + case MSG_ACCEPTED: + LONG(averf.flavor); + LONG(averf.count); + PTR(averf.data, r->averf.count); + LONG(astat); + switch(r->astat){ + case SUCCESS: + PTR(results, ndata); + break; + case PROG_MISMATCH: + LONG(plow); + LONG(phigh); + break; + } + break; + case MSG_DENIED: + LONG(rstat); + switch(r->rstat){ + case RPC_MISMATCH: + LONG(rlow); + LONG(rhigh); + break; + case AUTH_ERROR: + LONG(authstat); + break; + } + break; + } + break; + } + return p - (uchar *)ap; +} + +#undef SHORT +#undef LONG +#undef PTR + +#define LONG(m, x) fprint(fd, "%s = %ld\n", m, r->x) + +#define PTR(m, count) fprint(fd, "%s [%ld]\n", m, count) + +void +rpcprint(int fd, Rpccall *r) +{ + fprint(fd, "%s: host = %I, port = %ld\n", + argv0, r->host, r->port); + LONG("xid", xid); + LONG("mtype", mtype); + switch(r->mtype){ + case CALL: + LONG("rpcvers", rpcvers); + LONG("prog", prog); + LONG("vers", vers); + LONG("proc", proc); + LONG("cred.flavor", cred.flavor); + PTR("cred.data", r->cred.count); + LONG("verf.flavor", verf.flavor); + PTR("verf.data", r->verf.count); + fprint(fd, "args...\n"); + break; + case REPLY: + LONG("stat", stat); + switch(r->stat){ + case MSG_ACCEPTED: + LONG("averf.flavor", averf.flavor); + PTR("averf.data", r->averf.count); + LONG("astat", astat); + switch(r->astat){ + case SUCCESS: + fprint(fd, "results...\n"); + break; + case PROG_MISMATCH: + LONG("plow", plow); + LONG("phigh", phigh); + break; + } + break; + case MSG_DENIED: + LONG("rstat", rstat); + switch(r->rstat){ + case RPC_MISMATCH: + LONG("rlow", rlow); + LONG("rhigh", rhigh); + break; + case AUTH_ERROR: + LONG("authstat", authstat); + break; + } + break; + } + } +} + +void +showauth(Auth *ap) +{ + Authunix au; + int i; + + if(auth2unix(ap, &au) != 0){ + chat("auth flavor=%ld, count=%ld", + ap->flavor, ap->count); + for(i=0; i<ap->count; i++) + chat(" %.2ux", ((uchar *)ap->data)[i]); + }else{ + chat("auth: %ld %.*s u=%ld g=%ld", + au.stamp, utfnlen(au.mach.s, au.mach.n), au.mach.s, au.uid, au.gid); + for(i=0; i<au.gidlen; i++) + chat(", %ld", au.gids[i]); + } + chat("..."); +} + +int +garbage(Rpccall *reply, char *msg) +{ + chat("%s\n", msg ? msg : "garbage"); + reply->astat = GARBAGE_ARGS; + return 0; +} + +int +error(Rpccall *reply, int errno) +{ + uchar *dataptr = reply->results; + + chat("error %d\n", errno); + PLONG(errno); + return dataptr - (uchar *)reply->results; +} diff --git a/sys/src/cmd/9nfs/rpc.h b/sys/src/cmd/9nfs/rpc.h new file mode 100755 index 000000000..fda304864 --- /dev/null +++ b/sys/src/cmd/9nfs/rpc.h @@ -0,0 +1,84 @@ +/* + * Cf. /lib/rfc/rfc1014, /lib/rfc/rfc1050 + */ + +enum Bool +{ + FALSE = 0, + TRUE = 1 +}; + +enum Auth_flavor +{ + AUTH_NULL = 0, + AUTH_UNIX = 1, + AUTH_SHORT = 2, + AUTH_DES = 3 +}; + +enum Msg_type +{ + CALL = 0, + REPLY = 1 +}; + +/* + * A reply to a call message can take on two forms: + * The message was either accepted or rejected. + */ + +enum Reply_stat +{ + MSG_ACCEPTED = 0, + MSG_DENIED = 1 +}; + +/* + * Given that a call message was accepted, the following is the + * status of an attempt to call a remote procedure. + */ +enum Accept_stat +{ + SUCCESS = 0, /* RPC executed successfully */ + PROG_UNAVAIL = 1, /* remote hasn't exported program */ + PROG_MISMATCH = 2, /* remote can't support version # */ + PROC_UNAVAIL = 3, /* program can't support procedure */ + GARBAGE_ARGS = 4 /* procedure can't decode params */ +}; + +/* + * Reasons why a call message was rejected: + */ +enum Reject_stat +{ + RPC_MISMATCH = 0, /* RPC version number != 2 */ + AUTH_ERROR = 1 /* remote can't authenticate caller */ +}; + +/* + * Why authentication failed: + */ +enum Auth_stat +{ + AUTH_BADCRED = 1, /* bad credentials (seal broken) */ + AUTH_REJECTEDCRED = 2, /* client must begin new session */ + AUTH_BADVERF = 3, /* bad verifier (seal broken) */ + AUTH_REJECTEDVERF = 4, /* verifier expired or replayed */ + AUTH_TOOWEAK = 5 /* rejected for security reasons */ +}; + +enum +{ + IPPROTO_TCP = 6, /* protocol number for TCP/IP */ + IPPROTO_UDP = 17 /* protocol number for UDP/IP */ +}; + +#define ROUNDUP(n) ((n) + ((-(n))&3)) + +#define PLONG(x) (dataptr[3] = ((ulong)(x)), dataptr[2] = ((ulong)(x))>>8, dataptr[1] = ((ulong)(x))>>16, dataptr[0] = ((ulong)(x))>>24, dataptr += 4) +#define PPTR(x, n) (memmove(dataptr, (x), n), dataptr += ROUNDUP(n)) +#define PBYTE(x) (*dataptr++ = (x)) + +#define GLONG() (argptr += 4, (((uchar*)argptr)[-1] | (((uchar*)argptr)[-2]<<8) | (((uchar*)argptr)[-3]<<16) | (((uchar*)argptr)[-4]<<24))) +#define GPTR(n) (void *)(argptr); argptr += ROUNDUP(n) +#define GBYTE() (argptr++, ((uchar*)argptr)[-1]) diff --git a/sys/src/cmd/9nfs/server.c b/sys/src/cmd/9nfs/server.c new file mode 100755 index 000000000..fe70f1036 --- /dev/null +++ b/sys/src/cmd/9nfs/server.c @@ -0,0 +1,618 @@ +#include "all.h" +#include <ndb.h> + +static int alarmflag; + +static int Iconv(Fmt*); +static void openudp(int); +static void cachereply(Rpccall*, void*, int); +static int replycache(int, Rpccall*, long (*)(int, void*, long)); +static void udpserver(int, Progmap*); +static void tcpserver(int, Progmap*); +static void getendpoints(Udphdr*, char*); +static long readtcp(int, void*, long); +static long writetcp(int, void*, long); +static int servemsg(int, long (*)(int, void*, long), long (*)(int, void*, long), + int, Progmap*); +void (*rpcalarm)(void); +int rpcdebug; +int rejectall; +int p9debug; + +int nocache; + +uchar buf[9000]; +uchar rbuf[9000]; +uchar resultbuf[9000]; + +static int tcp; + +char *commonopts = "[-9CDrtv]"; /* for usage() messages */ + +/* + * this recognises common, nominally rcp-related options. + * they may not take arguments. + */ +int +argopt(int c) +{ + switch(c){ + case '9': + ++p9debug; + return 0; + case 'C': + ++nocache; + return 0; + case 'D': + ++rpcdebug; + return 0; + case 'r': + ++rejectall; + return 0; + case 't': + tcp = 1; + return 0; + case 'v': + ++chatty; + return 0; + default: + return -1; + } +} + +/* + * all option parsing is now done in (*pg->init)(), which can call back + * here to argopt for common options. + */ +void +server(int argc, char **argv, int myport, Progmap *progmap) +{ + Progmap *pg; + + fmtinstall('I', Iconv); + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); + + switch(rfork(RFNOWAIT|RFENVG|RFNAMEG|RFNOTEG|RFFDG|RFPROC)){ + case -1: + panic("fork"); + default: + _exits(0); + case 0: + break; + } + + switch(rfork(RFMEM|RFPROC)){ + case 0: + for(;;){ + sleep(30*1000); + alarmflag = 1; + } + case -1: + sysfatal("rfork: %r"); + } + + for(pg=progmap; pg->init; pg++) + (*pg->init)(argc, argv); + if(tcp) + tcpserver(myport, progmap); + else + udpserver(myport, progmap); +} + +static void +udpserver(int myport, Progmap *progmap) +{ + char service[128]; + char data[128]; + char devdir[40]; + int ctlfd, datafd; + + snprint(service, sizeof service, "udp!*!%d", myport); + ctlfd = announce(service, devdir); + if(ctlfd < 0) + panic("can't announce %s: %r\n", service); + if(fprint(ctlfd, "headers") < 0) + panic("can't set header mode: %r\n"); + + snprint(data, sizeof data, "%s/data", devdir); + datafd = open(data, ORDWR); + if(datafd < 0) + panic("can't open udp data: %r\n"); + close(ctlfd); + + chatsrv(0); + clog("%s: listening to port %d\n", argv0, myport); + while (servemsg(datafd, read, write, myport, progmap) >= 0) + continue; + exits(0); +} + +static void +tcpserver(int myport, Progmap *progmap) +{ + char adir[40]; + char ldir[40]; + char ds[40]; + int actl, lctl, data; + + snprint(ds, sizeof ds, "tcp!*!%d", myport); + chatsrv(0); + actl = -1; + for(;;){ + if(actl < 0){ + actl = announce(ds, adir); + if(actl < 0){ + clog("%s: listening to tcp port %d\n", + argv0, myport); + clog("announcing: %r"); + break; + } + } + lctl = listen(adir, ldir); + if(lctl < 0){ + close(actl); + actl = -1; + continue; + } + switch(fork()){ + case -1: + clog("%s!%d: %r\n", argv0, myport); + /* fall through */ + default: + close(lctl); + continue; + case 0: + close(actl); + data = accept(lctl, ldir); + close(lctl); + if(data < 0) + exits(0); + + /* pretend it's udp; fill in Udphdr */ + getendpoints((Udphdr*)buf, ldir); + + while (servemsg(data, readtcp, writetcp, myport, + progmap) >= 0) + continue; + close(data); + exits(0); + } + } + exits(0); +} + +static int +servemsg(int fd, long (*readmsg)(int, void*, long), long (*writemsg)(int, void*, long), + int myport, Progmap * progmap) +{ + int i, n, nreply; + Rpccall rcall, rreply; + int vlo, vhi; + Progmap *pg; + Procmap *pp; + char errbuf[ERRMAX]; + + if(alarmflag){ + alarmflag = 0; + if(rpcalarm) + (*rpcalarm)(); + } + n = (*readmsg)(fd, buf, sizeof buf); + if(n < 0){ + errstr(errbuf, sizeof errbuf); + if(strcmp(errbuf, "interrupted") == 0) + return 0; + clog("port %d: error: %s\n", myport, errbuf); + return -1; + } + if(n == 0){ + clog("port %d: EOF\n", myport); + return -1; + } + if(rpcdebug == 1) + fprint(2, "%s: rpc from %d.%d.%d.%d/%d\n", + argv0, buf[12], buf[13], buf[14], buf[15], + (buf[32]<<8)|buf[33]); + i = rpcM2S(buf, &rcall, n); + if(i != 0){ + clog("udp port %d: message format error %d\n", + myport, i); + return 0; + } + if(rpcdebug > 1) + rpcprint(2, &rcall); + if(rcall.mtype != CALL) + return 0; + if(replycache(fd, &rcall, writemsg)) + return 0; + nreply = 0; + rreply.host = rcall.host; + rreply.port = rcall.port; + rreply.lhost = rcall.lhost; + rreply.lport = rcall.lport; + rreply.xid = rcall.xid; + rreply.mtype = REPLY; + if(rcall.rpcvers != 2){ + rreply.stat = MSG_DENIED; + rreply.rstat = RPC_MISMATCH; + rreply.rlow = 2; + rreply.rhigh = 2; + goto send_reply; + } + if(rejectall){ + rreply.stat = MSG_DENIED; + rreply.rstat = AUTH_ERROR; + rreply.authstat = AUTH_TOOWEAK; + goto send_reply; + } + i = n - (((uchar *)rcall.args) - buf); + if(rpcdebug > 1) + fprint(2, "arg size = %d\n", i); + rreply.stat = MSG_ACCEPTED; + rreply.averf.flavor = 0; + rreply.averf.count = 0; + rreply.results = resultbuf; + vlo = 0x7fffffff; + vhi = -1; + for(pg=progmap; pg->pmap; pg++){ + if(pg->progno != rcall.prog) + continue; + if(pg->vers == rcall.vers) + break; + if(pg->vers < vlo) + vlo = pg->vers; + if(pg->vers > vhi) + vhi = pg->vers; + } + if(pg->pmap == 0){ + if(vhi < 0) + rreply.astat = PROG_UNAVAIL; + else{ + rreply.astat = PROG_MISMATCH; + rreply.plow = vlo; + rreply.phigh = vhi; + } + goto send_reply; + } + for(pp = pg->pmap; pp->procp; pp++) + if(rcall.proc == pp->procno){ + if(rpcdebug > 1) + fprint(2, "process %d\n", pp->procno); + rreply.astat = SUCCESS; + nreply = (*pp->procp)(i, &rcall, &rreply); + goto send_reply; + } + rreply.astat = PROC_UNAVAIL; +send_reply: + if(nreply >= 0){ + i = rpcS2M(&rreply, nreply, rbuf); + if(rpcdebug > 1) + rpcprint(2, &rreply); + (*writemsg)(fd, rbuf, i); + cachereply(&rreply, rbuf, i); + } + return 0; +} + +static void +getendpoint(char *dir, char *file, uchar *addr, uchar *port) +{ + int fd, n; + char buf[128]; + char *sys, *serv; + + sys = serv = 0; + + snprint(buf, sizeof buf, "%s/%s", dir, file); + fd = open(buf, OREAD); + if(fd >= 0){ + n = read(fd, buf, sizeof(buf)-1); + if(n>0){ + buf[n-1] = 0; + serv = strchr(buf, '!'); + if(serv){ + *serv++ = 0; + serv = strdup(serv); + } + sys = strdup(buf); + } + close(fd); + } + if(serv == 0) + serv = strdup("unknown"); + if(sys == 0) + sys = strdup("unknown"); + parseip(addr, sys); + n = atoi(serv); + hnputs(port, n); +} + +/* set Udphdr values from protocol dir local & remote files */ +static void +getendpoints(Udphdr *ep, char *dir) +{ + getendpoint(dir, "local", ep->laddr, ep->lport); + getendpoint(dir, "remote", ep->raddr, ep->rport); +} + +static long +readtcp(int fd, void *vbuf, long blen) +{ + uchar mk[4]; + int n, m, sofar; + ulong done; + char *buf; + + buf = vbuf; + buf += Udphdrsize; + blen -= Udphdrsize; + + done = 0; + for(sofar = 0; !done; sofar += n){ + m = readn(fd, mk, 4); + if(m < 4) + return 0; + done = (mk[0]<<24)|(mk[1]<<16)|(mk[2]<<8)|mk[3]; + m = done & 0x7fffffff; + done &= 0x80000000; + if(m > blen-sofar) + return -1; + n = readn(fd, buf+sofar, m); + if(m != n) + return 0; + } + return sofar + Udphdrsize; +} + +static long +writetcp(int fd, void *vbuf, long len) +{ + char *buf; + + buf = vbuf; + buf += Udphdrsize; + len -= Udphdrsize; + + buf -= 4; + buf[0] = 0x80 | (len>>24); + buf[1] = len>>16; + buf[2] = len>>8; + buf[3] = len; + len += 4; + return write(fd, buf, len); +} +/* + *long + *niwrite(int fd, void *buf, long count) + *{ + * char errbuf[ERRLEN]; + * long n; + * + * for(;;){ + * n = write(fd, buf, count); + * if(n < 0){ + * errstr(errbuf); + * if(strcmp(errbuf, "interrupted") == 0) + * continue; + * clog("niwrite error: %s\n", errbuf); + * werrstr(errbuf); + * } + * break; + * } + * return n; + *} + */ +long +niwrite(int fd, void *buf, long n) +{ +// int savalarm; + +// savalarm = alarm(0); + n = write(fd, buf, n); +// if(savalarm > 0) +// alarm(savalarm); + return n; +} + +typedef struct Namecache Namecache; +struct Namecache { + char dom[256]; + ulong ipaddr; + Namecache *next; +}; + +Namecache *dnscache; + +static Namecache* +domlookupl(void *name, int len) +{ + Namecache *n, **ln; + + if(len >= sizeof(n->dom)) + return nil; + + for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) { + if(strncmp(n->dom, name, len) == 0 && n->dom[len] == 0) { + *ln = n->next; + n->next = dnscache; + dnscache = n; + return n; + } + } + return nil; +} + +static Namecache* +domlookup(void *name) +{ + return domlookupl(name, strlen(name)); +} + +static Namecache* +iplookup(ulong ip) +{ + Namecache *n, **ln; + + for(ln=&dnscache, n=*ln; n; ln=&(*ln)->next, n=*ln) { + if(n->ipaddr == ip) { + *ln = n->next; + n->next = dnscache; + dnscache = n; + return n; + } + } + return nil; +} + +static Namecache* +addcacheentry(void *name, int len, ulong ip) +{ + Namecache *n; + + if(len >= sizeof(n->dom)) + return nil; + + n = malloc(sizeof(*n)); + if(n == nil) + return nil; + strncpy(n->dom, name, len); + n->dom[len] = 0; + n->ipaddr = ip; + n->next = dnscache; + dnscache = n; + return nil; +} + +int +getdnsdom(ulong ip, char *name, int len) +{ + char buf[128]; + Namecache *nc; + char *p; + + if(nc=iplookup(ip)) { + strncpy(name, nc->dom, len); + name[len-1] = 0; + return 0; + } + clog("getdnsdom: %I\n", ip); + snprint(buf, sizeof buf, "%I", ip); + p = csgetvalue("/net", "ip", buf, "dom", nil); + if(p == nil) + return -1; + strncpy(name, p, len-1); + name[len] = 0; + free(p); + addcacheentry(name, strlen(name), ip); + return 0; +} + +int +getdom(ulong ip, char *dom, int len) +{ + int i; + static char *prefix[] = { "", "gate-", "fddi-", "u-", 0 }; + char **pr; + + if(getdnsdom(ip, dom, len)<0) + return -1; + + for(pr=prefix; *pr; pr++){ + i = strlen(*pr); + if(strncmp(dom, *pr, i) == 0) { + memmove(dom, dom+i, len-i); + break; + } + } + return 0; +} + +#define MAXCACHE 64 + +static Rpccache *head, *tail; +static int ncache; + +static void +cachereply(Rpccall *rp, void *buf, int len) +{ + Rpccache *cp; + + if(nocache) + return; + + if(ncache >= MAXCACHE){ + if(rpcdebug) + fprint(2, "%s: drop %I/%ld, xid %uld, len %d\n", + argv0, tail->host, + tail->port, tail->xid, tail->n); + tail = tail->prev; + free(tail->next); + tail->next = 0; + --ncache; + } + cp = malloc(sizeof(Rpccache)+len-4); + if(cp == 0){ + clog("cachereply: malloc %d failed\n", len); + return; + } + ++ncache; + cp->prev = 0; + cp->next = head; + if(head) + head->prev = cp; + else + tail = cp; + head = cp; + cp->host = rp->host; + cp->port = rp->port; + cp->xid = rp->xid; + cp->n = len; + memmove(cp->data, buf, len); + if(rpcdebug) + fprint(2, "%s: cache %I/%ld, xid %uld, len %d\n", + argv0, cp->host, cp->port, cp->xid, cp->n); +} + +static int +replycache(int fd, Rpccall *rp, long (*writemsg)(int, void*, long)) +{ + Rpccache *cp; + + for(cp=head; cp; cp=cp->next) + if(cp->host == rp->host && + cp->port == rp->port && + cp->xid == rp->xid) + break; + if(cp == 0) + return 0; + if(cp->prev){ /* move to front */ + cp->prev->next = cp->next; + if(cp->next) + cp->next->prev = cp->prev; + else + tail = cp->prev; + cp->prev = 0; + cp->next = head; + head->prev = cp; + head = cp; + } + (*writemsg)(fd, cp->data, cp->n); + if(rpcdebug) + fprint(2, "%s: reply %I/%ld, xid %uld, len %d\n", + argv0, cp->host, cp->port, cp->xid, cp->n); + return 1; +} + +static int +Iconv(Fmt *f) +{ + char buf[16]; + ulong h; + + h = va_arg(f->args, ulong); + snprint(buf, sizeof buf, "%ld.%ld.%ld.%ld", + (h>>24)&0xff, (h>>16)&0xff, + (h>>8)&0xff, h&0xff); + return fmtstrcpy(f, buf); +} diff --git a/sys/src/cmd/9nfs/string.c b/sys/src/cmd/9nfs/string.c new file mode 100755 index 000000000..e71bb2e20 --- /dev/null +++ b/sys/src/cmd/9nfs/string.c @@ -0,0 +1,105 @@ +#include "all.h" + +#define STRHASH 509 /* prime */ + +static Strnode * stab[STRHASH]; + +static long hashfun(void*); +static Strnode* nalloc(int); + +char * +strfind(char *a) +{ + Strnode **bin, *x, *xp; + + bin = &stab[hashfun(a) % STRHASH]; + for(xp=0, x=*bin; x; xp=x, x=x->next) + if(x->str[0] == a[0] && strcmp(x->str, a) == 0) + break; + if(x == 0) + return 0; + if(xp){ + xp->next = x->next; + x->next = *bin; + *bin = x; + } + return x->str; +} + +char * +strstore(char *a) +{ + Strnode **bin, *x, *xp; + int n; + + bin = &stab[hashfun(a) % STRHASH]; + for(xp=0, x=*bin; x; xp=x, x=x->next) + if(x->str[0] == a[0] && strcmp(x->str, a) == 0) + break; + if(x == 0){ + n = strlen(a)+1; + x = nalloc(n); + memmove(x->str, a, n); + x->next = *bin; + *bin = x; + }else if(xp){ + xp->next = x->next; + x->next = *bin; + *bin = x; + } + return x->str; +} + +void +strprint(int fd) +{ + Strnode **bin, *x; + + for(bin = stab; bin < stab+STRHASH; bin++) + for(x=*bin; x; x=x->next) + fprint(fd, "%ld %s\n", bin-stab, x->str); +} + +static long +hashfun(void *v) +{ + ulong a = 0, b; + uchar *s = v; + + while(*s){ + a = (a << 4) + *s++; + if(b = a&0xf0000000){ /* assign = */ + a ^= b >> 24; + a ^= b; + } + } + return a; +} + +#define STRSIZE 1000 + +static Strnode * +nalloc(int n) /* get permanent storage for Strnode */ +{ + static char *curstp; + static int nchleft; + int k; + char *p; + + if(n < 4) + n = 4; + else if(k = n&3) /* assign = */ + n += 4-k; + n += sizeof(Strnode)-4; + if(n > nchleft){ + nchleft = STRSIZE; + while(nchleft < n) + nchleft *= 2; + if((curstp = malloc(nchleft)) == 0) + panic("malloc(%d) failed in nalloc\n", nchleft); + } + p = curstp; + curstp += n; + nchleft -= n; + return (Strnode*)p; +} diff --git a/sys/src/cmd/9nfs/strparse.c b/sys/src/cmd/9nfs/strparse.c new file mode 100755 index 000000000..d89fae307 --- /dev/null +++ b/sys/src/cmd/9nfs/strparse.c @@ -0,0 +1,32 @@ +#include <u.h> +#include <libc.h> + +int strcomment = '#'; + +int +strparse(char *p, int arsize, char **arv) +{ + int arc = 0; + + /*print("parse: 0x%lux = \"%s\"\n", p, p);/**/ + while(p){ + while(*p == ' ' || *p == '\t') + p++; + if(*p == 0 || *p == strcomment) + break; + if(arc >= arsize-1) + break; + arv[arc++] = p; + while(*p && *p != ' ' && *p != '\t') + p++; + if(*p == 0) + break; + *p++ = 0; + } + arv[arc] = 0; + /*while(*arv){ + print("\t0x%lux = \"%s\"\n", *arv, *arv); + ++arv; + }/**/ + return arc; +} diff --git a/sys/src/cmd/9nfs/system.c b/sys/src/cmd/9nfs/system.c new file mode 100755 index 000000000..5c1d654ff --- /dev/null +++ b/sys/src/cmd/9nfs/system.c @@ -0,0 +1,34 @@ +#include <u.h> +#include <libc.h> + +Waitmsg* +system(char *name, char **argv) +{ + char err[ERRMAX]; + Waitmsg *w; + int pid; + + switch(pid = fork()){ /* assign = */ + case -1: + return nil; + case 0: + exec(name, argv); + errstr(err, sizeof err); + _exits(err); + } + for(;;){ + w = wait(); + if(w == nil) + break; + if(w->pid == pid) + return w; + free(w); + } + return nil; +} + +Waitmsg* +systeml(char *name, ...) +{ + return system(name, &name+1); +} diff --git a/sys/src/cmd/9nfs/testit b/sys/src/cmd/9nfs/testit new file mode 100755 index 000000000..c08d74093 --- /dev/null +++ b/sys/src/cmd/9nfs/testit @@ -0,0 +1,8 @@ +#!/bin/rc +9fs nslocum +Kill 8.portmapper|rc +Kill 8.nfsserver|rc +rm -f /srv/nfsserver.chat /srv/portmapper.chat +8.nfsserver -a il!emelie -c /lib/ndb/nfs >[2] /tmp/nfsserver +8.portmapper >[2] /tmp/portmapper +# on unix: mount -t nfs -o nfsv2 edith:/ /mnt diff --git a/sys/src/cmd/9nfs/unixnames.c b/sys/src/cmd/9nfs/unixnames.c new file mode 100755 index 000000000..7b12fbb30 --- /dev/null +++ b/sys/src/cmd/9nfs/unixnames.c @@ -0,0 +1,322 @@ +#include "all.h" + +static void uxfree(Unixid*); + +static Unixid * xfree; + +Unixidmap *idhead, *idtail; + +Unixscmap *scmap; + +#define UNUSED 0x7FFFFFFF + +/* + * Sadly we have to use the IP address, since some systems (FreeBSD in particular) + * do not believe it to be safe to depend on the hostname and so refuse to send it. + * I dislike making this IP-centric, but so be it. + * We keep a cache of host names in getdom. + */ +Unixidmap * +pair2idmap(char *server, ulong clientip) +{ + Resub match; + Unixscmap *m, *mp; + Unixidmap *r; + char dom[256]; + + for(mp=0,m=scmap; m; mp=m,m=m->next){ + if(m->server[0] != server[0]) + continue; + if(strcmp(m->server, server)) + continue; + if(m->clientip != clientip) + continue; + if(mp){ + mp->next = m->next; + m->next = scmap; + scmap = m; + } + r = m->map; + if(r->u.timestamp != 0 && r->g.timestamp != 0) + return r; + scmap = m->next; + free(m); + break; + } + if(rpcdebug) + fprint(2, "looking for %lux\n", clientip); + if(getdom(clientip, dom, sizeof dom)<0){ + clog("auth: unknown ip address"); + return nil; + } + if(rpcdebug) + fprint(2, "dom is %s\n", dom); + for(r=idhead; r; r=r->next){ + if(r->u.timestamp == 0 || r->g.timestamp == 0) + continue; + match.sp = match.ep = 0; + if(regexec(r->sexp, server, &match, 1) == 0) + continue; + if(match.sp != server || match.ep <= match.sp || *match.ep) + continue; + match.sp = match.ep = 0; + if(regexec(r->cexp, dom, &match, 1) == 0) + continue; + if(match.sp != dom || match.ep <= match.sp || *match.ep) + continue; + m = malloc(sizeof(Unixscmap)); + m->next = scmap; + scmap = m; + m->server = strstore(server); + m->clientip = clientip; + m->map = r; + break; + } + return r; +} + +int +readunixidmaps(char *file) +{ + Waitmsg *w; + Biobuf *in; + Unixidmap *m; + int i, arc; char *arv[16], buf[256]; + char *l; +// long savalarm; + +// savalarm = alarm(0); + in = Bopen(file, OREAD); + if(in == 0){ + clog("readunixidmaps can't open %s: %r\n", file); +// alarm(savalarm); + return -1; + } + for(m=idhead; m; m=m->next) + m->flag = 0; + while(l = Brdline(in, '\n')){ /* assign = */ + l[Blinelen(in)-1] = 0; + arc = strparse(l, nelem(arv), arv); + if(arc > 0 && arv[0][0] == '!'){ + ++arv[0]; + snprint(buf, sizeof buf, "/bin/%s", arv[0]); + if(chatty){ + chat("!"); + for(i=0; i<arc; i++) + chat(" %s", arv[i]); + chat("..."); + } + w = system(buf, arv); + if(w == nil) + chat("err: %r\n"); + else if(w->msg && w->msg[0]) + chat("status: %s\n", w->msg); + else + chat("OK\n"); + free(w); + continue; + } + if(arc != 4) + continue; + for(m=idhead; m; m=m->next) + if(strcmp(arv[0], m->server) == 0 && + strcmp(arv[1], m->client) == 0) + break; + if(m == 0){ + m = malloc(sizeof(Unixidmap)); + if(idtail) + idtail->next = m; + else + idhead = m; + idtail = m; + m->next = 0; + m->server = strstore(arv[0]); + m->client = strstore(arv[1]); + m->sexp = regcomp(m->server); + m->cexp = regcomp(m->client); + m->u.file = strstore(arv[2]); + m->u.style = 'u'; + m->u.timestamp = 0; + m->u.ids = 0; + m->g.file = strstore(arv[3]); + m->g.style = 'u'; + m->g.timestamp = 0; + m->g.ids = 0; + }else{ + if(!m->u.file || strcmp(m->u.file, arv[2]) != 0){ + m->u.file = strstore(arv[2]); + m->u.timestamp = 0; + } + if(!m->g.file || strcmp(m->g.file, arv[3]) != 0){ + m->g.file = strstore(arv[3]); + m->g.timestamp = 0; + } + } + m->flag = 1; + checkunixmap(&m->u); + checkunixmap(&m->g); + } + Bterm(in); + for(m=idhead; m; m=m->next) + if(m->flag == 0){ + m->u.file = 0; + m->u.timestamp = 0; + uxfree(m->u.ids); + m->u.ids = 0; + m->g.file = 0; + m->g.timestamp = 0; + uxfree(m->g.ids); + m->g.ids = 0; + } +// alarm(savalarm); + return 0; +} + +static void +uxfree(Unixid *x) +{ + Unixid *tail; + int count=0; + + if(x){ + tail = x; + if(tail->id < 0) + abort(); + tail->id = UNUSED; + while(tail->next){ + tail = tail->next; + ++count; + if(tail->id == UNUSED) + abort(); + tail->id = UNUSED; + } + tail->next = xfree; + xfree = x; + } +} + +int +checkunixmap(Unixmap *u) +{ + Dir *dir; + + dir = dirstat(u->file); + if(dir == nil){ + clog("checkunixmap can't stat %s: %r\n", u->file); + return -1; + } + if(u->timestamp > dir->mtime){ + free(dir); + return 0; + } + uxfree(u->ids); + u->ids = readunixids(u->file, u->style); + u->timestamp = time(0); + free(dir); + return 1; +} + +int +name2id(Unixid **list, char *name) +{ + Unixid *x, *xp; + + for(xp=0,x=*list; x; xp=x,x=x->next){ + if(x->name[0] == name[0] && strcmp(x->name, name) == 0){ + if(xp){ + xp->next = x->next; + x->next = *list; + *list = x; + } + return x->id; + } + } + return -1; +} + +char * +id2name(Unixid **list, int id) +{ + Unixid *x, *xp; + + for(xp=0,x=*list; x; xp=x,x=x->next){ + if(x->id == id){ + if(xp){ + xp->next = x->next; + x->next = *list; + *list = x; + } + return x->name; + } + } + return "none"; +} + +void +idprint(int fd, Unixid *xp) +{ + while(xp){ + fprint(fd, "%d\t%s\n", xp->id, xp->name); + xp = xp->next; + } +} + +/* + * style '9': 3:tom:tom: + * style 'u': sysadm:*:0:0:System-Administrator:/usr/admin:/bin/sh + */ + +Unixid * +readunixids(char *file, int style) +{ + Biobuf *in; + char *l, *name = 0; + Unixid *x, *xp = 0; + int id = 0; + + in = Bopen(file, OREAD); + if(in == 0){ + clog("readunixids can't open %s: %r\n", file); + return 0; + } + while(l = Brdline(in, '\n')){ /* assign = */ + l[Blinelen(in)-1] = 0; + switch(style){ + case '9': + id = strtol(l, &l, 10); + if(*l != ':') + continue; + name = ++l; + l = strchr(l, ':'); + if(l == 0) + continue; + *l = 0; + break; + case 'u': + name = l; + l = strchr(l, ':'); + if(l == 0) + continue; + *l++ = 0; + /* skip password */ + l = strchr(l, ':'); + if(l == 0) + continue; + id = strtol(l+1, 0, 10); + break; + default: + panic("unknown unixid style %d\n", style); + } + if(id == UNUSED) + id = -1; /* any value will do */ + if(!(x = xfree)) /* assign = */ + x = listalloc(1024/sizeof(Unixid), sizeof(Unixid)); + xfree = x->next; + x->id = id; + x->name = strstore(name); + x->next = xp; + xp = x; + } + Bterm(in); + return xp; +} diff --git a/sys/src/cmd/9nfs/xfile.c b/sys/src/cmd/9nfs/xfile.c new file mode 100755 index 000000000..6f294d6d9 --- /dev/null +++ b/sys/src/cmd/9nfs/xfile.c @@ -0,0 +1,106 @@ +#include "all.h" + +static Xfile * xflfree; +static Xfid * xfdfree; + +#define FIDMOD 127 /* prime */ + +static Xfile *xfiles[FIDMOD]; +static Lock xlocks[FIDMOD]; + +Xfile * +xfile(Qid *qid, void *s, int new) +{ + int k; + Xfile **hp, *f, *pf; + + k = ((ulong)qid->path ^ (((u32int)(uintptr)s)<<24))%FIDMOD; + hp = &xfiles[k]; + + lock(&xlocks[k]); + for(f=*hp, pf=0; f; pf=f, f=f->next) + if(f->qid.path == qid->path + && (u32int)(uintptr)f->s == (u32int)(uintptr)s) + break; + if(f && pf){ + pf->next = f->next; + f->next = *hp; + *hp = f; + } + if(new < 0 && f){ + *hp = f->next; + f->next = xflfree; + xflfree = f; + f = 0; + } + if(new > 0 && !f){ + if(!(f = xflfree)) /* assign = */ + f = listalloc(1024/sizeof(Xfile), sizeof(Xfile)); + xflfree = f->next; + memset(f, 0, sizeof(Xfile)); + f->next = *hp; + *hp = f; + f->qid = *qid; + f->s = s; + } + unlock(&xlocks[k]); + return f; +} + +Xfid * +xfid(char *uid, Xfile *xp, int new) +{ + Xfid *f, *pf; + + for(f=xp->users, pf=0; f; pf=f, f=f->next) + if(f->uid[0] == uid[0] && strcmp(f->uid, uid) == 0) + break; + if(f && pf){ + pf->next = f->next; + f->next = xp->users; + xp->users = f; + } + if(new < 0 && f){ + if(f->urfid) + clunkfid(xp->s, f->urfid); + if(f->opfid) + clunkfid(xp->s, f->opfid); + xp->users = f->next; + f->next = xfdfree; + xfdfree = f; + f = 0; + } + if(new > 0 && !f){ + if(!(f = xfdfree)) /* assign = */ + f = listalloc(1024/sizeof(Xfid), sizeof(Xfid)); + xfdfree = f->next; + memset(f, 0, sizeof(Xfid)); + f->next = xp->users; + xp->users = f; + f->xp = xp; + f->uid = strstore(uid); + f->urfid = 0; + f->opfid = 0; + } + return f; +} + +int +xfpurgeuid(Session *s, char *uid) +{ + Xfile **hp, *f; + Xfid *xf; + int k, count=0; + + for(k=0; k<FIDMOD; k++){ + lock(&xlocks[k]); + hp=&xfiles[k]; + for(f=*hp; f; f=f->next) + if(f->s == s && (xf = xfid(uid, f, 0))){ /* assign = */ + xfclear(xf); + ++count; + } + unlock(&xlocks[k]); + } + return count; +} |