/* Copyright © 2003 Russ Cox, MIT; see /sys/src/libsunrpc/COPYING */ #include #include #include #include #include #include <9p.h> #include #include 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; inuser; i++){ free(m->user[i].name); free(m->user[i].auth); } for(i=0; ingroup; 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; inuser; 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; ingroup; 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; inuser; 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); }