1633 lines
30 KiB
C
1633 lines
30 KiB
C
/* 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);
|
|
}
|