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/nfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/nfs.c')
-rwxr-xr-x | sys/src/cmd/nfs.c | 1632 |
1 files changed, 1632 insertions, 0 deletions
diff --git a/sys/src/cmd/nfs.c b/sys/src/cmd/nfs.c new file mode 100755 index 000000000..03bcc1f47 --- /dev/null +++ b/sys/src/cmd/nfs.c @@ -0,0 +1,1632 @@ +/* Copyright © 2003 Russ Cox, MIT; see /sys/src/libsunrpc/COPYING */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> +#include <sunrpc.h> +#include <nfs3.h> + +SunClient *nfscli; +SunClient *mntcli; +char *defaultpath = "/"; +Channel *fschan; +char *sys; +int verbose; +int readplus = 0; + + +typedef struct Auth Auth; +struct Auth +{ + int ref; + uchar *data; + int ndata; +}; + +typedef struct FidAux FidAux; +struct FidAux +{ + Nfs3Handle handle; + + u64int cookie; /* for continuing directory reads */ + char *name; /* botch: for remove and rename */ + Nfs3Handle parent; /* botch: for remove and rename */ + char err[ERRMAX]; /* for walk1 */ + Auth *auth; +}; + +/* + * various RPCs. here is where we'd insert support for NFS v2 + */ + +void +portCall(SunCall *c, PortCallType type) +{ + c->rpc.prog = PortProgram; + c->rpc.vers = PortVersion; + c->rpc.proc = type>>1; + c->rpc.iscall = !(type&1); + c->type = type; +} + +int +getport(SunClient *client, uint prog, uint vers, uint prot, uint *port) +{ + PortTGetport tx; + PortRGetport rx; + + memset(&tx, 0, sizeof tx); + portCall(&tx.call, PortCallTGetport); + tx.map.prog = prog; + tx.map.vers = vers; + tx.map.prot = prot; + + memset(&rx, 0, sizeof rx); + portCall(&rx.call, PortCallRGetport); + + if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0) + return -1; + *port = rx.port; + return 0; +} + +void +mountCall(Auth *a, SunCall *c, NfsMount3CallType type) +{ + c->rpc.iscall = !(type&1); + c->rpc.proc = type>>1; + c->rpc.prog = NfsMount3Program; + c->rpc.vers = NfsMount3Version; + if(c->rpc.iscall && a){ + c->rpc.cred.flavor = SunAuthSys; + c->rpc.cred.data = a->data; + c->rpc.cred.ndata = a->ndata; + } + c->type = type; +} + +int +mountNull(ulong tag) +{ + NfsMount3TNull tx; + NfsMount3RNull rx; + + memset(&tx, 0, sizeof tx); + mountCall(nil, &tx.call, NfsMount3CallTNull); + + memset(&rx, 0, sizeof rx); + mountCall(nil, &rx.call, NfsMount3CallTNull); + + return sunClientRpc(mntcli, tag, &tx.call, &rx.call, nil); +} + +int +mountMnt(Auth *a, ulong tag, char *path, Nfs3Handle *h) +{ + uchar *freeme; + NfsMount3TMnt tx; + NfsMount3RMnt rx; + + memset(&tx, 0, sizeof tx); + mountCall(a, &tx.call, NfsMount3CallTMnt); + tx.path = path; + + memset(&rx, 0, sizeof rx); + mountCall(a, &rx.call, NfsMount3CallRMnt); + if(sunClientRpc(mntcli, tag, &tx.call, &rx.call, &freeme) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } +if(verbose)print("handle %.*H\n", rx.len, rx.handle); + if(rx.len >= Nfs3MaxHandleSize){ + free(freeme); + werrstr("server-returned handle too long"); + return -1; + } + memmove(h->h, rx.handle, rx.len); + h->len = rx.len; + free(freeme); + return 0; +} + +void +nfs3Call(Auth *a, SunCall *c, Nfs3CallType type) +{ + c->rpc.iscall = !(type&1); + c->rpc.proc = type>>1; + c->rpc.prog = Nfs3Program; + c->rpc.vers = Nfs3Version; + if(c->rpc.iscall && a){ + c->rpc.cred.flavor = SunAuthSys; + c->rpc.cred.data = a->data; + c->rpc.cred.ndata = a->ndata; + } + c->type = type; +} + +int +nfsNull(ulong tag) +{ + Nfs3TNull tx; + Nfs3RNull rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(nil, &tx.call, Nfs3CallTNull); + + memset(&rx, 0, sizeof rx); + nfs3Call(nil, &rx.call, Nfs3CallTNull); + + return sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil); +} + +int +nfsGetattr(Auth *a, ulong tag, Nfs3Handle *h, Nfs3Attr *attr) +{ + Nfs3TGetattr tx; + Nfs3RGetattr rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTGetattr); + tx.handle = *h; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRGetattr); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + + *attr = rx.attr; + return 0; +} + +int +nfsAccess(Auth *a, ulong tag, Nfs3Handle *h, ulong want, ulong *got, u1int *have, Nfs3Attr *attr) +{ + Nfs3TAccess tx; + Nfs3RAccess rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTAccess); + tx.handle = *h; + tx.access = want; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRAccess); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + + *got = rx.access; + + *have = rx.haveAttr; + if(rx.haveAttr) + *attr = rx.attr; + return 0; +} + +int +nfsMkdir(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, ulong mode, uint gid, + u1int *have, Nfs3Attr *attr) +{ + Nfs3TMkdir tx; + Nfs3RMkdir rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTMkdir); + tx.handle = *h; + tx.name = name; + tx.attr.setMode = 1; + tx.attr.mode = mode; + tx.attr.setGid = 1; + tx.attr.gid = gid; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRMkdir); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + + if(!rx.haveHandle){ + werrstr("nfs mkdir did not return handle"); + return -1; + } + *nh = rx.handle; + + *have = rx.haveAttr; + if(rx.haveAttr) + *attr = rx.attr; + return 0; +} + +int +nfsCreate(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, ulong mode, uint gid, + u1int *have, Nfs3Attr *attr) +{ + Nfs3TCreate tx; + Nfs3RCreate rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTCreate); + tx.handle = *h; + tx.name = name; + tx.attr.setMode = 1; + tx.attr.mode = mode; + tx.attr.setGid = 1; + tx.attr.gid = gid; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRCreate); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + + if(!rx.haveHandle){ + werrstr("nfs create did not return handle"); + return -1; + } + *nh = rx.handle; + + *have = rx.haveAttr; + if(rx.haveAttr) + *attr = rx.attr; + return 0; +} + +int +nfsRead(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int offset, + uchar **pp, u32int *pcount, uchar **pfreeme) +{ + uchar *freeme; + Nfs3TRead tx; + Nfs3RRead rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTRead); + tx.handle = *h; + tx.count = count; + tx.offset = offset; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRRead); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, &freeme) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + if(rx.count != rx.ndata){ + werrstr("nfs read returned count=%ud ndata=%ud", (uint)rx.count, (uint)rx.ndata); + free(freeme); + return -1; + } + *pfreeme = freeme; + *pcount = rx.count; + *pp = rx.data; + return 0; +} + +int +nfsWrite(Auth *a, ulong tag, Nfs3Handle *h, uchar *data, u32int count, u64int offset, u32int *pcount) +{ + Nfs3TWrite tx; + Nfs3RWrite rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTWrite); + tx.handle = *h; + tx.count = count; + tx.offset = offset; + tx.data = data; + tx.ndata = count; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRWrite); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + + *pcount = rx.count; + return 0; +} + +int +nfsRmdir(Auth *a, ulong tag, Nfs3Handle *h, char *name) +{ + Nfs3TRmdir tx; + Nfs3RRmdir rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTRmdir); + tx.handle = *h; + tx.name = name; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRRmdir); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + return 0; +} + +int +nfsRemove(Auth *a, ulong tag, Nfs3Handle *h, char *name) +{ + Nfs3TRemove tx; + Nfs3RRemove rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTRemove); + tx.handle = *h; + tx.name = name; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRRemove); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + return 0; +} + +int +nfsRename(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *th, char *tname) +{ + Nfs3TRename tx; + Nfs3RRename rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTRename); + tx.from.handle = *h; + tx.from.name = name; + tx.to.handle = *th; + tx.to.name = tname; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRRename); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + return 0; +} + +int +nfsSetattr(Auth *a, ulong tag, Nfs3Handle *h, Nfs3SetAttr *attr) +{ + Nfs3TSetattr tx; + Nfs3RSetattr rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTSetattr); + tx.handle = *h; + tx.attr = *attr; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRSetattr); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + return 0; +} + +int +nfsCommit(Auth *a, ulong tag, Nfs3Handle *h) +{ + Nfs3TCommit tx; + Nfs3RCommit rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTCommit); + tx.handle = *h; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRCommit); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + return 0; +} + +int +nfsLookup(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, u1int *have, Nfs3Attr *attr) +{ + Nfs3TLookup tx; + Nfs3RLookup rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTLookup); + tx.handle = *h; + tx.name = name; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRLookup); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0) + return -1; + + if(rx.status != Nfs3Ok){ + nfs3Errstr(rx.status); + return -1; + } + *nh = rx.handle; + *have = rx.haveAttr; + if(rx.haveAttr) + *attr = rx.attr; + return 0; +} + +int +nfsReadDirPlus(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int cookie, uchar **pp, + u32int *pcount, int (**unpack)(uchar*, uchar*, uchar**, Nfs3Entry*), uchar **pfreeme) +{ + Nfs3TReadDirPlus tx; + Nfs3RReadDirPlus rx; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTReadDirPlus); + tx.handle = *h; + tx.maxCount = count; + tx.dirCount = 1000; + tx.cookie = cookie; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRReadDirPlus); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, pfreeme) < 0) + return -1; + if(rx.status != Nfs3Ok){ + free(*pfreeme); + *pfreeme = 0; + nfs3Errstr(rx.status); + return -1; + } + + *unpack = nfs3EntryPlusUnpack; + *pcount = rx.count; + *pp = rx.data; + return 0; +} + +int +nfsReadDir(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int cookie, uchar **pp, + u32int *pcount, int (**unpack)(uchar*, uchar*, uchar**, Nfs3Entry*), uchar **pfreeme) +{ + /* BUG: try readdirplus */ + char e[ERRMAX]; + Nfs3TReadDir tx; + Nfs3RReadDir rx; + + if(readplus!=-1){ + if(nfsReadDirPlus(a, tag, h, count, cookie, pp, pcount, unpack, pfreeme) == 0){ + readplus = 1; + return 0; + } + if(readplus == 0){ + rerrstr(e, sizeof e); + if(strstr(e, "procedure unavailable") || strstr(e, "not supported")) + readplus = -1; + } + if(readplus == 0) + fprint(2, "readdirplus: %r\n"); + } + if(readplus == 1) + return -1; + + memset(&tx, 0, sizeof tx); + nfs3Call(a, &tx.call, Nfs3CallTReadDir); + tx.handle = *h; + tx.count = count; + tx.cookie = cookie; + + memset(&rx, 0, sizeof rx); + nfs3Call(a, &rx.call, Nfs3CallRReadDir); + + if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, pfreeme) < 0) + return -1; + if(rx.status != Nfs3Ok){ + free(*pfreeme); + *pfreeme = 0; + nfs3Errstr(rx.status); + return -1; + } + + /* readplus failed but read succeeded */ + readplus = -1; + + *unpack = nfs3EntryUnpack; + *pcount = rx.count; + *pp = rx.data; + return 0; +} + +/* + * name <-> int translation + */ +typedef struct Map Map; +typedef struct User User; +typedef struct Group Group; + +Map *map; +Map emptymap; + +struct User +{ + char *name; + uint uid; + uint gid; + uint g[16]; + uint ng; + uchar *auth; + int nauth; +}; + +struct Group +{ + char *name; /* same pos as in User struct */ + uint gid; /* same pos as in User struct */ +}; + +struct Map +{ + int nuser; + int ngroup; + User *user; + User **ubyname; + User **ubyid; + Group *group; + Group **gbyname; + Group **gbyid; +}; + +User* +finduser(User **u, int nu, char *s) +{ + int lo, hi, mid, n; + + hi = nu; + lo = 0; + while(hi > lo){ + mid = (lo+hi)/2; + n = strcmp(u[mid]->name, s); + if(n == 0) + return u[mid]; + if(n < 0) + lo = mid+1; + else + hi = mid; + } + return nil; +} + +int +strtoid(User **u, int nu, char *s, u32int *id) +{ + u32int x; + char *p; + User *uu; + + x = strtoul(s, &p, 10); + if(*s != 0 && *p == 0){ + *id = x; + return 0; + } + + uu = finduser(u, nu, s); + if(uu == nil) + return -1; + *id = uu->uid; + return 0; +} + +char* +idtostr(User **u, int nu, u32int id) +{ + char buf[32]; + int lo, hi, mid; + + hi = nu; + lo = 0; + while(hi > lo){ + mid = (lo+hi)/2; + if(u[mid]->uid == id) + return estrdup9p(u[mid]->name); + if(u[mid]->uid < id) + lo = mid+1; + else + hi = mid; + } + snprint(buf, sizeof buf, "%ud", id); + return estrdup9p(buf); +} +char* +uidtostr(u32int uid) +{ + return idtostr(map->ubyid, map->nuser, uid); +} + +char* +gidtostr(u32int gid) +{ + return idtostr((User**)map->gbyid, map->ngroup, gid); +} + +int +strtouid(char *s, u32int *id) +{ + return strtoid(map->ubyname, map->nuser, s, id); +} + +int +strtogid(char *s, u32int *id) +{ + return strtoid((User**)map->gbyid, map->ngroup, s, id); +} + + +int +idcmp(const void *va, const void *vb) +{ + User **a, **b; + + a = (User**)va; + b = (User**)vb; + return (*a)->uid - (*b)->uid; +} + +int +namecmp(const void *va, const void *vb) +{ + User **a, **b; + + a = (User**)va; + b = (User**)vb; + return strcmp((*a)->name, (*b)->name); +} + +void +closemap(Map *m) +{ + int i; + + for(i=0; i<m->nuser; i++){ + free(m->user[i].name); + free(m->user[i].auth); + } + for(i=0; i<m->ngroup; i++) + free(m->group[i].name); + free(m->user); + free(m->group); + free(m->ubyid); + free(m->ubyname); + free(m->gbyid); + free(m->gbyname); + free(m); +} + +Map* +readmap(char *passwd, char *group) +{ + char *s, *f[10], *p, *nextp, *name; + uchar *q, *eq; + int i, n, nf, line, uid, gid; + Biobuf *b; + Map *m; + User *u; + Group *g; + SunAuthUnix au; + + m = emalloc(sizeof(Map)); + + if((b = Bopen(passwd, OREAD)) == nil){ + free(m); + return nil; + } + line = 0; + for(; (s = Brdstr(b, '\n', 1)) != nil; free(s)){ + line++; + if(s[0] == '#') + continue; + nf = getfields(s, f, nelem(f), 0, ":"); + if(nf < 4) + continue; + name = f[0]; + uid = strtol(f[2], &p, 10); + if(f[2][0] == 0 || *p != 0){ + fprint(2, "%s:%d: non-numeric id in third field\n", passwd, line); + continue; + } + gid = strtol(f[3], &p, 10); + if(f[3][0] == 0 || *p != 0){ + fprint(2, "%s:%d: non-numeric id in fourth field\n", passwd, line); + continue; + } + if(m->nuser%32 == 0) + m->user = erealloc(m->user, (m->nuser+32)*sizeof(m->user[0])); + u = &m->user[m->nuser++]; + u->name = estrdup9p(name); + u->uid = uid; + u->gid = gid; + u->ng = 0; + u->auth = 0; + u->nauth = 0; + } + Bterm(b); + m->ubyname = emalloc(m->nuser*sizeof(User*)); + m->ubyid = emalloc(m->nuser*sizeof(User*)); + for(i=0; i<m->nuser; i++){ + m->ubyname[i] = &m->user[i]; + m->ubyid[i] = &m->user[i]; + } + qsort(m->ubyname, m->nuser, sizeof(m->ubyname[0]), namecmp); + qsort(m->ubyid, m->nuser, sizeof(m->ubyid[0]), idcmp); + + if((b = Bopen(group, OREAD)) == nil){ + closemap(m); + return nil; + } + line = 0; + for(; (s = Brdstr(b, '\n', 1)) != nil; free(s)){ + line++; + if(s[0] == '#') + continue; + nf = getfields(s, f, nelem(f), 0, ":"); + if(nf < 4) + continue; + name = f[0]; + gid = strtol(f[2], &p, 10); + if(f[2][0] == 0 || *p != 0){ + fprint(2, "%s:%d: non-numeric id in third field\n", group, line); + continue; + } + if(m->ngroup%32 == 0) + m->group = erealloc(m->group, (m->ngroup+32)*sizeof(m->group[0])); + g = &m->group[m->ngroup++]; + g->name = estrdup9p(name); + g->gid = gid; + + for(p=f[3]; *p; p=nextp){ + if((nextp = strchr(p, ',')) != nil) + *nextp++ = 0; + else + nextp = p+strlen(p); + u = finduser(m->ubyname, m->nuser, p); + if(u == nil){ + if(verbose) + fprint(2, "%s:%d: unknown user %s\n", group, line, p); + continue; + } + if(u->ng >= nelem(u->g)){ + fprint(2, "%s:%d: user %s is in too many groups; ignoring %s\n", group, line, p, name); + continue; + } + u->g[u->ng++] = gid; + } + } + Bterm(b); + m->gbyname = emalloc(m->ngroup*sizeof(Group*)); + m->gbyid = emalloc(m->ngroup*sizeof(Group*)); + for(i=0; i<m->ngroup; i++){ + m->gbyname[i] = &m->group[i]; + m->gbyid[i] = &m->group[i]; + } + qsort(m->gbyname, m->ngroup, sizeof(m->gbyname[0]), namecmp); + qsort(m->gbyid, m->ngroup, sizeof(m->gbyid[0]), idcmp); + + for(i=0; i<m->nuser; i++){ + au.stamp = 0; + au.sysname = sys; + au.uid = m->user[i].uid; + au.gid = m->user[i].gid; + memmove(au.g, m->user[i].g, sizeof au.g); + au.ng = m->user[i].ng; + n = sunAuthUnixSize(&au); + q = emalloc(n); + eq = q+n; + m->user[i].auth = q; + m->user[i].nauth = n; + if(sunAuthUnixPack(q, eq, &q, &au) < 0 || q != eq){ + fprint(2, "sunAuthUnixPack failed for %s\n", m->user[i].name); + free(m->user[i].auth); + m->user[i].auth = 0; + m->user[i].nauth = 0; + } + } + + return m; +} + +Auth* +mkauth(char *user) +{ + Auth *a; + uchar *p; + int n; + SunAuthUnix au; + User *u; + + u = finduser(map->ubyname, map->nuser, user); + if(u == nil || u->nauth == 0){ + /* nobody */ + au.stamp = 0; + au.uid = -1; + au.gid = -1; + au.ng = 0; + au.sysname = sys; + n = sunAuthUnixSize(&au); + a = emalloc(sizeof(Auth)+n); + a->data = (uchar*)&a[1]; + a->ndata = n; + if(sunAuthUnixPack(a->data, a->data+a->ndata, &p, &au) < 0 + || p != a->data+a->ndata){ + free(a); + return nil; + } + a->ref = 1; +if(verbose)print("creds for %s: %.*H\n", user, a->ndata, a->data); + return a; + } + + a = emalloc(sizeof(Auth)+u->nauth); + a->data = (uchar*)&a[1]; + a->ndata = u->nauth; + memmove(a->data, u->auth, a->ndata); + a->ref = 1; +if(verbose)print("creds for %s: %.*H\n", user, a->ndata, a->data); + return a; +} + +void +freeauth(Auth *a) +{ + if(--a->ref > 0) + return; + free(a); +} + +/* + * 9P server + */ +void +responderrstr(Req *r) +{ + char e[ERRMAX]; + + rerrstr(e, sizeof e); + respond(r, e); +} + +void +fsdestroyfid(Fid *fid) +{ + FidAux *aux; + + aux = fid->aux; + if(aux == nil) + return; + freeauth(aux->auth); + free(aux->name); + free(aux); +} + +void +attrToQid(Nfs3Attr *attr, Qid *qid) +{ + qid->path = attr->fileid; + qid->vers = attr->mtime.sec; + qid->type = 0; + if(attr->type == Nfs3FileDir) + qid->type |= QTDIR; +} + +void +attrToDir(Nfs3Attr *attr, Dir *d) +{ + d->mode = attr->mode & 0777; + if(attr->type == Nfs3FileDir) + d->mode |= DMDIR; + d->uid = uidtostr(attr->uid); + d->gid = gidtostr(attr->gid); + d->length = attr->size; + attrToQid(attr, &d->qid); + d->mtime = attr->mtime.sec; + d->atime = attr->atime.sec; + d->muid = nil; +} + +void +fsattach(Req *r) +{ + char *path; + Auth *auth; + FidAux *aux; + Nfs3Attr attr; + Nfs3Handle h; + + path = r->ifcall.aname; + if(path==nil || path[0]==0) + path = defaultpath; + + auth = mkauth(r->ifcall.uname); + + if(mountMnt(auth, r->tag, path, &h) < 0 + || nfsGetattr(auth, r->tag, &h, &attr) < 0){ + freeauth(auth); + responderrstr(r); + return; + } + + aux = emalloc(sizeof(FidAux)); + aux->auth = auth; + aux->handle = h; + aux->cookie = 0; + aux->name = nil; + memset(&aux->parent, 0, sizeof aux->parent); + r->fid->aux = aux; + attrToQid(&attr, &r->fid->qid); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +void +fsopen(Req *r) +{ + FidAux *aux; + Nfs3Attr attr; + Nfs3SetAttr sa; + u1int have; + ulong a, b; + + aux = r->fid->aux; + a = 0; + switch(r->ifcall.mode&OMASK){ + case OREAD: + a = 0x0001; + break; + case OWRITE: + a = 0x0004; + break; + case ORDWR: + a = 0x0001|0x0004; + break; + case OEXEC: + a = 0x20; + break; + } + if(r->ifcall.mode&OTRUNC) + a |= 0x0004; + + if(nfsAccess(aux->auth, r->tag, &aux->handle, a, &b, &have, &attr) < 0 + || (!have && nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0)){ + Error: + responderrstr(r); + return; + } + if(a != b){ + respond(r, "permission denied"); + return; + } + if(r->ifcall.mode&OTRUNC){ + memset(&sa, 0, sizeof sa); + sa.setSize = 1; + if(nfsSetattr(aux->auth, r->tag, &aux->handle, &sa) < 0) + goto Error; + } + attrToQid(&attr, &r->fid->qid); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +void +fscreate(Req *r) +{ + FidAux *aux; + u1int have; + Nfs3Attr attr; + Nfs3Handle h; + ulong mode; + uint gid; + int (*mk)(Auth*, ulong, Nfs3Handle*, char*, Nfs3Handle*, ulong, uint, u1int*, Nfs3Attr*); + + aux = r->fid->aux; + + /* + * Plan 9 has no umask, so let's use the + * parent directory bits like Plan 9 does. + * What the heck, let's inherit the group too. + * (Unix will let us set the group to anything + * since we're the owner!) + */ + if(nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){ + responderrstr(r); + return; + } + mode = r->ifcall.perm&0777; + if(r->ifcall.perm&DMDIR) + mode &= (attr.mode&0666) | ~0666; + else + mode &= (attr.mode&0777) | ~0777; + gid = attr.gid; + + if(r->ifcall.perm&DMDIR) + mk = nfsMkdir; + else + mk = nfsCreate; + + if((*mk)(aux->auth, r->tag, &aux->handle, r->ifcall.name, &h, mode, gid, &have, &attr) < 0 + || (!have && nfsGetattr(aux->auth, r->tag, &h, &attr) < 0)){ + responderrstr(r); + return; + } + attrToQid(&attr, &r->fid->qid); + aux->parent = aux->handle; + aux->handle = h; + free(aux->name); + aux->name = estrdup9p(r->ifcall.name); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +void +fsreaddir(Req *r) +{ + FidAux *aux; + uchar *p, *freeme, *ep, *p9, *ep9; + char *s; + uint count; + int n, (*unpack)(uchar*, uchar*, uchar**, Nfs3Entry*); + Nfs3Entry e; + u64int cookie; + Dir d; + + aux = r->fid->aux; + /* + * r->ifcall.count seems a reasonable estimate to + * how much NFS entry data we want. is it? + */ + if(r->ifcall.offset) + cookie = aux->cookie; + else + cookie = 0; + if(nfsReadDir(aux->auth, r->tag, &aux->handle, r->ifcall.count, cookie, + &p, &count, &unpack, &freeme) < 0){ + responderrstr(r); + return; + } + ep = p+count; + + p9 = (uchar*)r->ofcall.data; + ep9 = p9+r->ifcall.count; + + /* + * BUG: Issue all of the stat requests in parallel. + */ + while(p < ep && p9 < ep9){ + if((*unpack)(p, ep, &p, &e) < 0) + break; + aux->cookie = e.cookie; + if(strcmp(e.name, ".") == 0 || strcmp(e.name, "..") == 0) + continue; + for(s=e.name; (uchar)*s >= ' '; s++) + ; + if(*s != 0) /* bad character in name */ + continue; + if(!e.haveAttr && !e.haveHandle) + if(nfsLookup(aux->auth, r->tag, &aux->handle, e.name, &e.handle, &e.haveAttr, &e.attr) < 0) + continue; + if(!e.haveAttr) + if(nfsGetattr(aux->auth, r->tag, &e.handle, &e.attr) < 0) + continue; + memset(&d, 0, sizeof d); + attrToDir(&e.attr, &d); + d.name = e.name; + if((n = convD2M(&d, p9, ep9-p9)) <= BIT16SZ) + break; + p9 += n; + } + free(freeme); + r->ofcall.count = p9 - (uchar*)r->ofcall.data; + respond(r, nil); +} + +void +fsread(Req *r) +{ + uchar *p, *freeme; + uint count; + FidAux *aux; + + if(r->fid->qid.type&QTDIR){ + fsreaddir(r); + return; + } + + aux = r->fid->aux; + if(nfsRead(aux->auth, r->tag, &aux->handle, r->ifcall.count, r->ifcall.offset, &p, &count, &freeme) < 0){ + responderrstr(r); + return; + } + r->ofcall.data = (char*)p; + r->ofcall.count = count; + respond(r, nil); + free(freeme); +} + +void +fswrite(Req *r) +{ + uint count; + FidAux *aux; + + aux = r->fid->aux; + if(nfsWrite(aux->auth, r->tag, &aux->handle, (uchar*)r->ifcall.data, r->ifcall.count, r->ifcall.offset, &count) < 0){ + responderrstr(r); + return; + } + r->ofcall.count = count; + respond(r, nil); +} + +void +fsremove(Req *r) +{ + int n; + FidAux *aux; + + aux = r->fid->aux; + if(aux->name == nil){ + respond(r, "nfs3client botch -- don't know parent handle in remove"); + return; + } + if(r->fid->qid.type&QTDIR) + n = nfsRmdir(aux->auth, r->tag, &aux->parent, aux->name); + else + n = nfsRemove(aux->auth, r->tag, &aux->parent, aux->name); + if(n < 0){ + responderrstr(r); + return; + } + respond(r, nil); +} + +void +fsstat(Req *r) +{ + FidAux *aux; + Nfs3Attr attr; + + aux = r->fid->aux; + if(nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){ + responderrstr(r); + return; + } + memset(&r->d, 0, sizeof r->d); + attrToDir(&attr, &r->d); + r->d.name = estrdup9p(aux->name ? aux->name : "???"); + respond(r, nil); +} + +void +fswstat(Req *r) +{ + int op, sync; + FidAux *aux; + Nfs3SetAttr attr; + + memset(&attr, 0, sizeof attr); + aux = r->fid->aux; + + /* Fill out stat first to catch errors */ + op = 0; + sync = 1; + if(~r->d.mode){ + if(r->d.mode&(DMAPPEND|DMEXCL)){ + respond(r, "wstat -- DMAPPEND and DMEXCL bits not supported"); + return; + } + op = 1; + sync = 0; + attr.setMode = 1; + attr.mode = r->d.mode & 0777; + } + if(r->d.uid && r->d.uid[0]){ + attr.setUid = 1; + if(strtouid(r->d.uid, &attr.uid) < 0){ + respond(r, "wstat -- unknown uid"); + return; + } + op = 1; + sync = 0; + } + if(r->d.gid && r->d.gid[0]){ + attr.setGid = 1; + if(strtogid(r->d.gid, &attr.gid) < 0){ + respond(r, "wstat -- unknown gid"); + return; + } + op = 1; + sync = 0; + } + if(~r->d.length){ + attr.setSize = 1; + attr.size = r->d.length; + op = 1; + sync = 0; + } + if(~r->d.mtime){ + attr.setMtime = Nfs3SetTimeClient; + attr.mtime.sec = r->d.mtime; + op = 1; + sync = 0; + } + if(~r->d.atime){ + attr.setAtime = Nfs3SetTimeClient; + attr.atime.sec = r->d.atime; + op = 1; + sync = 0; + } + + /* Try rename first because it's more likely to fail (?) */ + if(r->d.name && r->d.name[0]){ + if(aux->name == nil){ + respond(r, "nfsclient botch -- don't know parent handle in rename"); + return; + } + if(nfsRename(aux->auth, r->tag, &aux->parent, aux->name, &aux->parent, r->d.name) < 0){ + responderrstr(r); + return; + } + free(aux->name); + aux->name = estrdup9p(r->d.name); + sync = 0; + } + + /* + * Now we have a problem. The rename succeeded + * but the setattr could fail. Sic transit atomicity. + */ + if(op){ + if(nfsSetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){ + responderrstr(r); + return; + } + } + + if(sync){ + /* NFS commit */ + if(nfsCommit(aux->auth, r->tag, &aux->handle) < 0){ + responderrstr(r); + return; + } + } + + respond(r, nil); +} + +char* +fswalk1(Fid *fid, char *name, void *v) +{ + u1int have; + ulong tag; + FidAux *aux; + Nfs3Attr attr; + Nfs3Handle h; + + tag = *(ulong*)v; + aux = fid->aux; + + if(nfsLookup(aux->auth, tag, &aux->handle, name, &h, &have, &attr) < 0 + || (!have && nfsGetattr(aux->auth, tag, &h, &attr) < 0)){ + rerrstr(aux->err, sizeof aux->err); + return aux->err; + } + + aux->parent = aux->handle; + aux->handle = h; + free(aux->name); + if(strcmp(name, "..") == 0) + aux->name = nil; + else + aux->name = estrdup9p(name); + attrToQid(&attr, &fid->qid); + return nil; +} + +char* +fsclone(Fid *fid, Fid *newfid, void*) +{ + FidAux *a, *na; + + a = fid->aux; + na = emalloc9p(sizeof(FidAux)); + *na = *a; + if(na->name) + na->name = estrdup9p(na->name); + newfid->aux = na; + if(na->auth) + na->auth->ref++; + return nil; +} + +void +fswalk(Req *r) +{ + walkandclone(r, fswalk1, fsclone, &r->tag); +} + +void +fsflush(Req *r) +{ + Req *or; + + /* + * Send on the flush channel(s). + * The library will make sure the response + * is delayed as necessary. + */ + or = r->oldreq; + if(nfscli) + sendul(nfscli->flushchan, (ulong)or->tag); + if(mntcli) + sendul(mntcli->flushchan, (ulong)or->tag); + respond(r, nil); +} + +void +fsdispatch(void *v) +{ + Req *r; + + r = v; + switch(r->ifcall.type){ + default: respond(r, "unknown type"); break; + case Tattach: fsattach(r); break; + case Topen: fsopen(r); break; + case Tcreate: fscreate(r); break; + case Tread: fsread(r); break; + case Twrite: fswrite(r); break; + case Tremove: fsremove(r); break; + case Tflush: fsflush(r); break; + case Tstat: fsstat(r); break; + case Twstat: fswstat(r); break; + case Twalk: fswalk(r); break; + } +} + +void +fsthread(void*) +{ + Req *r; + + while((r = recvp(fschan)) != nil) + threadcreate(fsdispatch, r, SunStackSize); +} + +void +fssend(Req *r) +{ + sendp(fschan, r); +} + +void +fsdie(Srv*) +{ + threadexitsall(nil); +} + +Srv fs = +{ +.destroyfid = fsdestroyfid, +.attach= fssend, +.open= fssend, +.create= fssend, +.read= fssend, +.write= fssend, +.remove= fssend, +.flush= fssend, +.stat= fssend, +.wstat= fssend, +.walk= fssend, +.end= fsdie +}; + +void +usage(void) +{ + fprint(2, "usage: nfs [-DRv] [-p perm] [-s srvname] [-u passwd group] addr [addr]\n"); + fprint(2, "\taddr - address of portmapper server\n"); + fprint(2, "\taddr addr - addresses of mount server and nfs server\n"); + exits("usage"); +} + +char* +netchangeport(char *addr, uint port, char *buf, uint nbuf) +{ + char *r; + + strecpy(buf, buf+nbuf, addr); + r = strrchr(buf, '!'); + if(r == nil) + return nil; + r++; + seprint(r, buf+nbuf, "%ud", port); + return buf; +} + +char mbuf[256], nbuf[256]; +char *mountaddr, *nfsaddr; +Channel *csync; +int chattyrpc; +void dialproc(void*); + +void +threadmain(int argc, char **argv) +{ + char *srvname, *passwd, *group, *addr, *p; + SunClient *cli; + int proto; + uint mport, nport; + ulong perm; + Dir d; + + perm = 0600; + passwd = nil; + group = nil; + srvname = nil; + sys = sysname(); + if(sys == nil) + sys = "plan9"; + ARGBEGIN{ + default: + usage(); + case 'D': + chatty9p++; + break; + case 'R': + chattyrpc++; + break; + case 'p': + perm = strtol(EARGF(usage()), &p, 8); + if(perm==0 || *p != 0) + usage(); + break; + case 's': + srvname = EARGF(usage()); + break; + case 'u': + passwd = EARGF(usage()); + group = EARGF(usage()); + break; + case 'v': + verbose++; + break; + }ARGEND + + if(argc != 1 && argc != 2) + usage(); + + if(srvname == nil) + srvname = argv[0]; + + fmtinstall('B', sunRpcFmt); + fmtinstall('C', sunCallFmt); + fmtinstall('H', encodefmt); + sunFmtInstall(&portProg); + sunFmtInstall(&nfs3Prog); + sunFmtInstall(&nfsMount3Prog); + + if(passwd && (map = readmap(passwd, group)) == nil) + fprint(2, "warning: reading %s and %s: %r\n", passwd, group); + + if(map == nil) + map = &emptymap; + + if(argc == 1){ + addr = netmkaddr(argv[0], "udp", "portmap"); + if((cli = sunDial(addr)) == nil) + sysfatal("dial %s: %r", addr); + cli->chatty = chattyrpc; + sunClientProg(cli, &portProg); + if(strstr(addr, "udp!")) + proto = PortProtoUdp; + else + proto = PortProtoTcp; + if(getport(cli, NfsMount3Program, NfsMount3Version, proto, &mport) < 0) + sysfatal("lookup mount program port: %r"); + if(getport(cli, Nfs3Program, Nfs3Version, proto, &nport) < 0) + sysfatal("lookup nfs program port: %r"); + sunClientClose(cli); + mountaddr = netchangeport(addr, mport, mbuf, sizeof mbuf); + nfsaddr = netchangeport(addr, nport, nbuf, sizeof nbuf); + strcat(mountaddr, "!r"); + strcat(nfsaddr, "!r"); + if(verbose) + fprint(2, "nfs %s %s\n", mountaddr, nfsaddr); + }else{ + mountaddr = argv[0]; + nfsaddr = argv[1]; + } + + /* have to dial in another proc because it creates threads */ + csync = chancreate(sizeof(void*), 0); + proccreate(dialproc, nil, SunStackSize); + recvp(csync); + + threadpostmountsrv(&fs, srvname, nil, 0); + if(perm != 0600){ + p = smprint("/srv/%s", srvname); + if(p){ + nulldir(&d); + d.mode = perm; + dirwstat(p, &d); + } + } + threadexits(nil); +} + +void +dialproc(void*) +{ + rfork(RFNAMEG); + rfork(RFNOTEG); + if((mntcli = sunDial(mountaddr)) == nil) + sysfatal("dial mount program at %s: %r", mountaddr); + mntcli->chatty = chattyrpc; + sunClientProg(mntcli, &nfsMount3Prog); + if(mountNull(0) < 0) + sysfatal("execute nop with mnt server at %s: %r", mountaddr); + + if((nfscli = sunDial(nfsaddr)) == nil) + sysfatal("dial nfs program at %s: %r", nfsaddr); + nfscli->chatty = chattyrpc; + sunClientProg(nfscli, &nfs3Prog); + if(nfsNull(0) < 0) + sysfatal("execute nop with nfs server at %s: %r", nfsaddr); + + fschan = chancreate(sizeof(Req*), 0); + threadcreate(fsthread, nil, SunStackSize); + sendp(csync, 0); +} |