/*
* Copyright (c) 2013, Coraid, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Coraid nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CORAID BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include <ip.h>
#include "dat.h"
/* RPC -- RFC 1057 */
enum {
AUTH_NULL = 0,
AUTH_UNIX,
AUTH_SHORT,
AUTH_DES,
CALL = 0,
REPLY,
MSG_ACCEPTED = 0,
MSG_DENIED,
SUCCESS = 0,
PROG_UNAVAIL,
PROG_MISMATCH,
PROC_UNAVAIL,
GARBAGE_ARGS,
RPC_MISMATCH = 0,
AUTH_ERROR,
AUTH_BADCRED = 1,
AUTH_REJECTEDCRED,
AUTH_BADVERF,
AUTH_REJECTEDVERF,
AUTH_TOOWEAK,
PMAP_PROG = 100000,
PMAP_VERS = 2,
PMAP_PORT = 111,
IPPROTO_TCP = 6,
IPPROTO_UDP = 17,
PMAPPROC_NULL = 0,
PMAPPROC_SET,
PMAPPROC_UNSET,
PMAPPROC_GETPORT,
PMAPPROC_DUMP,
PMAPPROC_CALLIT,
};
/* NFSv3 -- RFC 1813 */
enum {
NFS_PROG = 100003,
NFS_VERS = 3,
NFS_PORT = 2049,
NFS3_FHSIZE = 64,
NFS3_COOKIEVERFSIZE = 8,
NFS3_CREATEVERFSIZE = 8,
NFS3_WRITEVERFSIZE = 8,
NFS3_OK = 0,
NFS3ERR_PERM,
NFS3ERR_NOENT,
NFS3ERR_IO = 5,
NFS3ERR_NXIO,
NFS3ERR_ACCES = 13,
NFS3ERR_EXIST = 17,
NFS3ERR_XDEV,
NFS3ERR_NODEV,
NFS3ERR_NOTDIR,
NFS3ERR_ISDIR,
NFS3ERR_INVAL,
NFS3ERR_FBIG = 27,
NFS3ERR_NOSPC,
NFS3ERR_ROFS = 30,
NFS3ERR_MLINK,
NFS3ERR_NAMETOOLONG = 63,
NFS3ERR_NOTEMPTY = 66,
NFS3ERR_DQUOT = 69,
NFS3ERR_STALE,
NFS3ERR_REMOTE,
NFS3ERR_BADHANDLE = 10001,
NFS3ERR_NOT_SYNC,
NFS3ERR_BAD_COOKIE,
NFS3ERR_NOTSUPP,
NFS3ERR_TOOSMALL,
NFS3ERR_SERVERFAULT,
NFS3ERR_BADTYPE,
NFS3ERR_JUKEBOX,
NF3REG = 1,
NF3DIR,
NF3BLK,
NF3CHR,
NF3LNK,
NF3SOCK,
NF3FIFO,
DONT_CHANGE = 0,
SET_TO_SERVER_TIME,
SET_TO_CLIENT_TIME,
NFSPROC3_NULL = 0,
NFSPROC3_GETATTR,
NFSPROC3_SETATTR,
NFSPROC3_LOOKUP,
NFSPROC3_ACCESS,
NFSPROC3_READLINK,
NFSPROC3_READ,
NFSPROC3_WRITE,
NFSPROC3_CREATE,
NFSPROC3_MKDIR,
NFSPROC3_SYMLINK,
NFSPROC3_MKNOD,
NFSPROC3_REMOVE,
NFSPROC3_RMDIR,
NFSPROC3_RENAME,
NFSPROC3_LINK,
NFSPROC3_READDIR,
NFSPROC3_READDIRPLUS,
NFSPROC3_FSSTAT,
NFSPROC3_FSINFO,
NFSPROC3_PATHCONF,
NFSPROC3_COMMIT,
ACCESS3_READ = 0x0001,
ACCESS3_LOOKUP = 0x0002,
ACCESS3_MODIFY = 0x0004,
ACCESS3_EXTEND = 0x0008,
ACCESS3_DELETE = 0x0010,
ACCESS3_EXECUTE = 0x0020,
UNSTABLE = 0,
DATA_SYNC,
FILE_SYNC,
UNCHECKED = 0,
GUARDED,
EXCLUSIVE,
FSF3_LINK = 0x0001,
FSF3_SYMLINK = 0x0002,
FSF3_HOMOGENEOUS= 0x0008,
FSF3_CANSETTIME = 0x0010,
MNT_PROG = 100005,
MNT_MIN_VERS = 2,
MNT_MAX_VERS = 3,
MNT_PORT = 4003,
MNTPATHLEN = 1024,
MNTNAMELEN = 255,
FHSIZE3 = NFS3_FHSIZE,
MNT3_OK = 0,
MNT3ERR_PERM,
MNT3ERR_NOENT,
MNT3ERR_IO = 5,
MNT3ERR_ACCES = 13,
MNT3ERR_NOTDIR = 20,
MNT3ERR_INVAL = 22,
MNT3ERR_NAMETOOLONG = 63,
MNT3ERR_NOTSUPP = 10004,
MNT3ERR_SERVERFAULT = 10006,
MOUNTPROC3_NULL = 0,
MOUNTPROC3_MNT,
MOUNTPROC3_DUMP,
MOUNTPROC3_UMNT,
MOUNTPROC3_UMNTALL,
MOUNTPROC3_EXPORT,
NLM_PROG = 100021,
NLM_VERS = 4,
NLM_PORT = 4002,
NLM4_GRANTED = 0,
NLM4_DENIED,
NLM4_DENIED_NLOCKS,
NLM4_BLOCKED,
NLM4_DENIED_GRACE_PERIOD,
NLM4_DEADLOCK,
NLM4_ROFS,
NLM4_STALE_FH,
NLM4_FBIG,
NLM4_FAILED,
NLMPROC4_NULL = 0,
NLMPROC4_TEST,
NLMPROC4_LOCK,
NLMPROC4_CANCEL,
NLMPROC4_UNLOCK,
NLMPROC4_GRANTED,
NLMPROC4_TEST_MSG,
NLMPROC4_LOCK_MSG,
NLMPROC4_CANCEL_MSG,
NLMPROC4_UNLOCK_MSG,
NLMPROC4_GRANTED_MSG,
NLMPROC4_TEST_RES,
NLMPROC4_LOCK_RES,
NLMPROC4_CANCEL_RES,
NLMPROC4_UNLOCK_RES,
NLMPROC4_GRANTED_RES,
NLMPROC4_SHARE = 20,
NLMPROC4_UNSHARE,
NLMPROC4_NM_LOCK,
NLMPROC4_FREE_ALL,
};
typedef struct Rcb Rcb;
struct Rcb {
int inuse;
int fd;
Ioproc *io;
ulong myprog;
ulong minver;
ulong maxver;
int (*dispatch)(char *, char *, ulong, char *, char *, ulong);
Rcb *next;
};
static Channel *upchan, *tpchan, *mchan, *nchan;
static Rcb *rcbhd;
static int nfstid, mounttid, tmaptid, umaptid;
int debugnfs;
static int
round4(int x)
{
return (x + 3) & ~3;
}
static char *
rpcputl(char *p, ulong l)
{
hnputl(p, l);
return p + 4;
}
static char *
rpcputv(char *p, uvlong v)
{
hnputv(p, v);
return p + 8;
}
static char *
getauth(char **pp)
{
char *a;
int n;
n = nhgetl(*pp + 4);
a = malloc(n + 8);
memmove(a, *pp, n + 8);
*pp += n + 8;
return a;
}
static char *
putauth(char *p, char *verf)
{
int n;
n = nhgetl(verf + 4);
memmove(p, verf, n + 8);
return p + n + 8;
}
static char *
initreply(char *buf, ulong xid, ulong stat, void *verf, int rstat)
{
char *p;
p = buf;
p = rpcputl(p, xid);
p = rpcputl(p, REPLY);
p = rpcputl(p, stat);
if(stat == MSG_ACCEPTED)
p = putauth(p, verf);
p = rpcputl(p, rstat);
return p;
}
static void
tcprpcreader(void *a)
{
Rcb *r;
char *buf, *p, *auth, *verf;
ulong xid, mtype, rpcvers, prog, vers, proc;
int n;
r = a;
buf = malloc(34004);
while(1) {
n = ioreadn(r->io, r->fd, buf, 4);
if(shutdown || n < 4) {
free(buf);
ioclose(r->io, r->fd);
r->inuse = 0;
threadexits(nil);
}
n = nhgetl(buf) & 0x7fffffff;
if(n > 34000) {
fprint(2, "bogus read size: %d\n", n);
continue;
}
n = ioreadn(r->io, r->fd, buf+4, n);
if(n <= 0) {
if(debugnfs)
fprint(2, "leaving tcpreader for prog %uld\n", r->myprog);
free(buf);
ioclose(r->io, r->fd);
r->inuse = 0;
threadexits(nil);
}
/* if we don't at least have the xid and mtype, ignore */
if(n < 8)
continue;
p = buf+4;
xid = nhgetl(p);
p += 4;
mtype = nhgetl(p);
p += 4;
/* we're only a server - ignore replies */
if(mtype != CALL)
continue;
rpcvers = nhgetl(p);
p += 4;
prog = nhgetl(p);
p += 4;
vers = nhgetl(p);
p += 4;
proc = nhgetl(p);
p += 4;
if(debugnfs)
fprint(2, "got message in prog %uld len=%d xid=%uld(%ulx) mtype=%uld rpcvers=%uld prog=%uld vers=%uld proc=%uld\n", r->myprog, n, xid, xid, mtype, rpcvers, prog, vers, proc);
if(rpcvers != 2) {
p = initreply(buf+4, xid, MSG_DENIED, nil, RPC_MISMATCH);
p = rpcputl(p, 2);
p = rpcputl(p, 2);
hnputl(buf, (p-(buf+4)) | 0x80000000);
iowrite(r->io, r->fd, buf, p-buf);
continue;
}
auth = getauth(&p);
verf = getauth(&p);
if(prog != r->myprog) {
p = initreply(buf+4, xid, MSG_ACCEPTED, verf, PROG_UNAVAIL);
hnputl(buf, (p-(buf+4)) | 0x80000000);
iowrite(r->io, r->fd, buf, p-buf);
free(auth);
free(verf);
continue;
}
if(vers < r->minver || vers > r->maxver) {
p = initreply(buf+4, xid, MSG_ACCEPTED, verf, PROG_MISMATCH);
p = rpcputl(p, r->minver);
p = rpcputl(p, r->maxver);
hnputl(buf, (p-(buf+4)) | 0x80000000);
iowrite(r->io, r->fd, buf, p-buf);
free(auth);
free(verf);
continue;
}
n = r->dispatch(buf+4, p, xid, auth, verf, proc);
if(debugnfs) {
fprint(2, "writing %d bytes in response\n", n);
if(debugnfs > 1) {
int i;
for(i = 0; i < n+4; i += 4) fprint(2, " %ud", nhgetl(buf + i));
fprint(2, "\n");
}
}
hnputl(buf, n | 0x80000000);
iowrite(r->io, r->fd, buf, n+4);
free(auth);
free(verf);
}
}
static void
udprpcreader(void *a)
{
Rcb *r;
char *buf, *p, *auth, *verf;
ulong xid, mtype, rpcvers, prog, vers, proc;
int n;
r = a;
buf = malloc(8500);
n = ioread(r->io, r->fd, buf, 8500);
if(shutdown || n <= 0)
goto done2;
/* if we don't at least have the xid and mtype, ignore */
if(n < 8)
goto done2;
p = buf;
xid = nhgetl(p);
p += 4;
mtype = nhgetl(p);
p += 4;
if(debugnfs)
fprint(2, "got message in prog %uld len=%d xid=%uld(%ulx) mtype=%uld\n", r->myprog, n, xid, xid, mtype);
/* we're only a server - ignore replies */
if(mtype != CALL)
goto done2;
rpcvers = nhgetl(p);
p += 4;
prog = nhgetl(p);
p += 4;
vers = nhgetl(p);
p += 4;
proc = nhgetl(p);
p += 4;
if(debugnfs)
fprint(2, "rpcvers=%uld prog=%uld vers=%uld proc=%uld\n", rpcvers, prog, vers, proc);
if(rpcvers != 2) {
p = initreply(buf, xid, MSG_DENIED, nil, RPC_MISMATCH);
p = rpcputl(p, 2);
p = rpcputl(p, 2);
iowrite(r->io, r->fd, buf, p-buf);
goto done2;
}
auth = getauth(&p);
verf = getauth(&p);
if(prog != r->myprog) {
p = initreply(buf, xid, MSG_ACCEPTED, verf, PROG_UNAVAIL);
iowrite(r->io, r->fd, buf, p-buf);
goto done1;
}
if(vers < r->minver || vers > r->maxver) {
p = initreply(buf, xid, MSG_ACCEPTED, verf, PROG_MISMATCH);
p = rpcputl(p, r->minver);
p = rpcputl(p, r->maxver);
iowrite(r->io, r->fd, buf, p-buf);
goto done1;
}
n = r->dispatch(buf, p, xid, auth, verf, proc);
if(debugnfs) {
fprint(2, "writing %d bytes in response\n", n);
if(debugnfs > 1) {
int i;
for(i = 0; i < n; i += 4) fprint(2, " %ud", nhgetl(buf + i));
fprint(2, "\n");
}
}
iowrite(r->io, r->fd, buf, n);
done1:
free(auth);
free(verf);
done2:
free(buf);
ioclose(r->io, r->fd);
r->inuse = 0;
threadexits(nil);
}
static char *
rpcnull(char *buf, ulong xid, char *verf)
{
char *rp;
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, 0);
return rp;
}
/*
* Fake Port Mapper
*/
static int
pmapdis(char *buf, char *p, ulong xid, char *auth, char *verf, ulong proc)
{
char *rp;
ulong prog, vers, prot, nproc;
switch(proc) {
case PMAPPROC_NULL:
rp = rpcnull(buf, xid, verf);
break;
case PMAPPROC_GETPORT:
prog = nhgetl(p);
p += 4;
vers = nhgetl(p);
p += 4;
prot = nhgetl(p);
if(debugnfs)
fprint(2, "In portmap getport prog=%uld vers=%uld prot=%uld\n", prog, vers, prot);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
hnputl(rp, 0);
switch(prog) {
case NFS_PROG:
if(vers == NFS_VERS && prot == IPPROTO_TCP)
hnputl(rp, NFS_PORT);
break;
case MNT_PROG:
if(vers >= MNT_MIN_VERS && vers <= MNT_MAX_VERS && prot == IPPROTO_TCP)
hnputl(rp, MNT_PORT);
break;
case NLM_PROG:
if(vers == NLM_VERS && prot == IPPROTO_TCP)
hnputl(rp, NLM_PORT);
break;
}
rp += 4;
break;
case PMAPPROC_DUMP:
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, NFS_PROG);
rp = rpcputl(rp, NFS_VERS);
rp = rpcputl(rp, IPPROTO_TCP);
rp = rpcputl(rp, NFS_PORT);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, MNT_PROG);
rp = rpcputl(rp, MNT_MAX_VERS);
rp = rpcputl(rp, IPPROTO_TCP);
rp = rpcputl(rp, MNT_PORT);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, NLM_PROG);
rp = rpcputl(rp, NLM_VERS);
rp = rpcputl(rp, IPPROTO_TCP);
rp = rpcputl(rp, NLM_PORT);
rp = rpcputl(rp, 0);
break;
case PMAPPROC_CALLIT:
SET(nproc);
USED(nproc);
USED(auth);
/*
prog = nhgetl(p);
p += 4;
vers = nhgetl(p);
p += 4;
nproc = nhgetl(p);
p += 4;
switch(prog) {
case NFS_PROG:
return nfsdis(buf, p, xid, auth, verf, nproc);
break;
case MNT_PROG:
return mntdis(buf, p, xid, auth, verf, nproc);
break;
case NLM_PROG:
return nlmdis(buf, p, xid, auth, verf, nproc);
break;
default:
rp = initreply(buf, xid, MSG_ACCEPTED, verf);
break;
}
break;
*/
case PMAPPROC_SET: /* not used here for fake port mapper */
case PMAPPROC_UNSET:
default:
rp = initreply(buf, xid, MSG_ACCEPTED, verf, PROG_UNAVAIL);
rp += 4;
break;
}
return rp - buf;
}
static char *
domnt(char *buf, char *p, ulong xid, char *, char *verf)
{
Qid qid;
char *rp, *path;
uvlong meta, x;
int n;
n = nhgetl(p);
path = malloc(n + 1);
memmove(path, p + 4, n);
path[n] = 0;
if(debugnfs)
fprint(2, "Attempting to mount %s qpath=%ulld\n", path, p2q(-1, path, 0));
meta = q2m(-1, p2q(-1, path, 0), 0);
free(path);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
if(meta == 0) {
rp = rpcputl(rp, MNT3ERR_NOENT);
return rp;
}
if(getmetaint(-1, meta, "qpath", &x) == MTnone) {
rp = rpcputl(rp, MNT3ERR_IO);
return rp;
}
qid.path = x;
getmetaint(-1, meta, "qvers", &x);
qid.vers = x;
getmetaint(-1, meta, "qtype", &x);
qid.type = x;
if(!(qid.type & QTDIR)) {
rp = rpcputl(rp, MNT3ERR_NOTDIR);
return rp;
}
if(debugnfs)
fprint(2, "meta=%ulld qid=(%ulld,%uld,%d)\n", meta, qid.path, qid.vers, qid.type);
rp = rpcputl(rp, MNT3_OK);
rp = rpcputl(rp, sizeof(Qid));
memmove(rp, &qid, sizeof(Qid));
rp += round4(sizeof(Qid));
rp = rpcputl(rp, 1);
rp = rpcputl(rp, AUTH_UNIX);
return rp;
}
static int
mntdis(char *buf, char *p, ulong xid, char *auth, char *verf, ulong proc)
{
char *rp;
switch(proc) {
case MOUNTPROC3_NULL:
rp = rpcnull(buf, xid, verf);
break;
case MOUNTPROC3_MNT:
rp = domnt(buf, p, xid, auth, verf);
break;
case MOUNTPROC3_DUMP:
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
hnputl(rp, 0);
rp += 4;
break;
case MOUNTPROC3_UMNT:
rp = rpcnull(buf, xid, verf);
break;
case MOUNTPROC3_UMNTALL:
rp = rpcnull(buf, xid, verf);
break;
case MOUNTPROC3_EXPORT:
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, 1);
memmove(rp, "/\0\0\0", 4);
rp += 4;
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
break;
default:
rp = initreply(buf, xid, MSG_DENIED, verf, PROC_UNAVAIL);
break;
}
return rp - buf;
}
static char *
fattr3(int fd, char *rp, Qid *qid, char *auth)
{
char *symlink, *ugid, *host;
uvlong meta, len, mtime, x;
int n;
meta = q2m(fd, qid->path, 0);
if(meta == 0)
return nil;
host = nil;
switch(nhgetl(auth)) {
case AUTH_UNIX:
auth += 12;
n = nhgetl(auth);
host = emalloc9p(n + 1);
auth += 4;
memmove(host, auth, n);
break;
default: /* We're going to ignore the others for now */
break;
}
if(qid->type & QTDIR)
rp = rpcputl(rp, NF3DIR);
else {
if((symlink = getmetastr(fd, meta, "symlink")) != nil) {
rp = rpcputl(rp, NF3LNK);
free(symlink);
}
else if(getmetaint(fd, meta, "nodetype", &x) != MTnone)
rp = rpcputl(rp, x);
else
rp = rpcputl(rp, NF3REG);
}
if(getmetaint(fd, meta, "unixmode", &x) != MTnone)
rp = rpcputl(rp, x);
else if(getmetaint(fd, meta, "mode", &x) != MTnone)
rp = rpcputl(rp, x & 0777);
else
rp = rpcputl(rp, 0777);
rp = rpcputl(rp, 1); /* nlink */
if(getmetaint(fd, meta, "nuid", &x) != MTnone) /* uid */
rp = rpcputl(rp, x);
else {
ugid = getmetastr(fd, meta, "uid");
rp = rpcputl(rp, uname2id(host, ugid));
free(ugid);
}
if(getmetaint(fd, meta, "ngid", &x) != MTnone) /* gid */
rp = rpcputl(rp, x);
else {
ugid = getmetastr(fd, meta, "gid");
rp = rpcputl(rp, gname2id(host, ugid));
free(ugid);
}
if(getmetaint(fd, meta, "length", &len) == MTnone)
len = 0;
rp = rpcputv(rp, len); /* size */
if(getmetaint(fd, meta, "used", &x) == MTnone)
rp = rpcputv(rp, len);
else
rp = rpcputv(rp, x);
if(getmetaint(fd, meta, "majordev", &x) == MTnone) /* rdev */
rp = rpcputl(rp, 0);
else
rp = rpcputl(rp, x);
if(getmetaint(fd, meta, "minordev", &x) == MTnone)
rp = rpcputl(rp, 0);
else
rp = rpcputl(rp, x);
rp = rpcputv(rp, 0); /* fsid */
rp = rpcputv(rp, qid->path); /* fileid */
if(getmetaint(fd, meta, "atime", &x) == MTnone)
rp = rpcputv(rp, 0);
else {
rp = rpcputl(rp, x / 1000000000LL);
rp = rpcputl(rp, x % 1000000000LL);
}
if(getmetaint(fd, meta, "mtime", &x) == MTnone)
mtime = 0;
else
mtime = x;
rp = rpcputl(rp, mtime / 1000000000LL);
rp = rpcputl(rp, mtime % 1000000000LL);
if(getmetaint(fd, meta, "ctime", &x) == MTnone) {
rp = rpcputl(rp, mtime / 1000000000LL);
rp = rpcputl(rp, mtime % 1000000000LL);
}
else {
rp = rpcputl(rp, x / 1000000000LL);
rp = rpcputl(rp, x % 1000000000LL);
}
free(host);
return rp;
}
static char *
opattr(int fd, char *rp, Qid *qid, char *auth)
{
char *trp;
rp = rpcputl(rp, 1);
trp = fattr3(fd, rp, qid, auth);
if(trp == nil) {
rp -= 4;
rp = rpcputl(rp, 0);
return rp;
}
return trp;
}
static ulong
getperm(int fd, uvlong meta, char *auth)
{
char *host, *uid, *gid, *s;
uvlong mode, x;
ulong perm;
int n, nuid, ngid;
if(allow)
return 0007;
getmetaint(fd, meta, "mode", &mode);
perm = mode & 0007;
host = nil;
switch(nhgetl(auth)) {
case AUTH_UNIX:
auth += 12;
n = nhgetl(auth);
host = emalloc9p(n + 1);
auth += 4;
memmove(host, auth, n);
auth += round4(n);
nuid = nhgetl(auth);
auth += 4;
ngid = nhgetl(auth);
if(rootallow && nhgetl(auth) == 0) {
perm = 0007;
break;
}
if((uid = getmetastr(fd, meta, "uid")) != nil && (s = id2uname(host, nuid))) {
if(strcmp(s, uid) == 0) {
perm = (mode >> 6) & 0007;
free(uid);
break;
}
}
else if(getmetaint(fd, meta, "nuid", &x) != MTnone && x == nuid) {
perm = (mode >> 6) & 0007;
free(uid);
break;
}
if((gid = getmetastr(fd, meta, "gid")) != nil && (s = id2gname(host, ngid))) {
if(strcmp(s, uid) == 0)
perm = (mode >> 3) & 0007;
}
else if(getmetaint(fd, meta, "ngid", &x) != MTnone && x == ngid)
perm = (mode >> 3) & 0007;
free(uid);
free(gid);
break;
case AUTH_NULL:
case AUTH_SHORT:
case AUTH_DES:
default:
break;
}
free(host);
return perm;
}
static int
prewcc(int fd, uvlong qpath, uvlong *len, uvlong *mtime, uvlong *ctime)
{
uvlong meta, x;
meta = q2m(fd, qpath, 0);
if(meta == 0)
return -1;
if(getmetaint(fd, meta, "length", &x) == MTnone)
x = 0;
*len = x;
getmetaint(fd, meta, "mtime", &x);
*mtime = x;
if(getmetaint(fd, meta, "ctime", &x) == MTnone)
*ctime = *mtime;
else
*ctime = x;
return 0;
}
static char *
dowcc(int fd, char *rp, char *auth, Qid *qid, uvlong prelen, uvlong premtime, uvlong prectime)
{
rp = rpcputl(rp, 1);
rp = rpcputv(rp, prelen);
rp = rpcputl(rp, premtime / 1000000000LL);
rp = rpcputl(rp, premtime % 1000000000LL);
rp = rpcputl(rp, prectime / 1000000000LL);
rp = rpcputl(rp, prectime % 1000000000LL);
rp = opattr(fd, rp, qid, auth);
return rp;
}
static char *
mkpath(int fd, uvlong qpath, int len)
{
char *str, *name, *p;
uvlong meta, parent;
int n;
if(qpath == 1) {
str = malloc(len + 2);
strcpy(str, "/");
return str;
}
meta = q2m(fd, qpath, 0);
if(meta == 0)
return nil;
name = getmetastr(fd, meta, "name");
n = strlen(name);
if(getmetaint(fd, meta, "parent", &parent) == MTnone) {
str = malloc(len + n + 2);
strcpy(str, name);
free(name);
return str;
}
str = mkpath(fd, parent, len + n + 1);
p = str + strlen(str);
*p++ = '/';
strcpy(p, name);
free(name);
return str;
}
static char *
dosattr(uvlong meta, char *p)
{
uvlong now, x;
ulong setit;
now = nsec();
setit = nhgetl(p);
p += 4;
if(setit) {
getmetaint(-1, meta, "mode", &x);
setmetaint(meta, "mode", nil, (nhgetl(p) & 0777) | (x & ~0777));
setmetaint(meta, "unixmode", nil, nhgetl(p));
p += 4;
}
setit = nhgetl(p);
p += 4;
if(setit) {
setmetaint(meta, "nuid", nil, nhgetl(p));
p += 4;
}
setit = nhgetl(p);
p += 4;
if(setit) {
setmetaint(meta, "ngid", nil, nhgetl(p));
p += 4;
}
setit = nhgetl(p);
p += 4;
if(setit) {
setmetaint(meta, "length", nil, nhgetv(p));
p += 8;
setmetaint(meta, "mtime", nil, now);
}
setit = nhgetl(p);
p += 4;
if(setit == SET_TO_CLIENT_TIME) {
setmetaint(meta, "atime", nil, nhgetl(p) * 1000000000LL + nhgetl(p + 4));
p += 8;
}
setit = nhgetl(p);
p += 4;
if(setit == SET_TO_CLIENT_TIME) {
setmetaint(meta, "mtime", nil, nhgetl(p) * 1000000000LL + nhgetl(p + 4));
p += 8;
}
setmetaint(meta, "ctime", nil, now);
return p;
}
static int
opensnap(uvlong qid)
{
char *sname, *spath;
uvlong meta;
int fd;
meta = q2m(-1, qid, 0);
if(meta == 0)
return -1;
sname = getmetastr(-1, meta, "snap");
if(sname == nil)
return -1;
spath = smprint("%s/%s", ddir, sname);
free(sname);
fd = open(spath, OREAD);
free(spath);
return fd;
}
static char *
nfsgetattr(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
int fd;
fd = -1;
if(nhgetl(p) != sizeof(Qid)) {
fd = opensnap(nhgetv(p + sizeof(Qid) + 4));
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
a = fattr3(fd, rp, &qid, auth);
if(a == nil)
hnputl(rp-4, NFS3ERR_BADHANDLE);
else
rp = a;
if(fd != -1)
close(fd);
return rp;
}
static char *
nfssetattr(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp;
uvlong meta, prelen, premeta, prectime;
if(nhgetl(p) != sizeof(Qid)) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
if(prewcc(-1, qid.path, &prelen, &premeta, &prectime) < 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
meta = q2m(-1, qid.path, 0);
dosattr(meta, p);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
rp = dowcc(-1, rp, auth, &qid, prelen, premeta, prectime);
return rp;
}
static char *
nfslookup(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid, qid2;
char *rp, *name, *path, *sname, *spath;
uvlong meta, qp, x, sqid;
ulong perms;
int n, m, fd,pfd;
pfd = fd = -1;
sqid = 0;
if(nhgetl(p) != round4(sizeof(Qid))) {
sqid = nhgetv(p + round4(sizeof(Qid)) + 4);
pfd = fd = opensnap(sqid);
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
n = nhgetl(p);
p += 4;
name = malloc(n + 1);
if(name == nil) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_SERVERFAULT);
return rp;
}
memmove(name, p, n);
name[n] = 0;
meta = q2m(fd, qid.path, 0);
if(debugnfs)
fprint(2, "in nfslookup: qid=(%ulld,%uld,%ud) name=%s\n", qid.path, qid.vers, qid.type, name);
perms = getperm(fd, meta, auth);
if((perms & DMEXEC) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
if(fd != -1)
close(fd);
return rp;
}
if(strcmp(name, ".") == 0) {
/* don't need to do anything */
}
else if(strcmp(name, "..") == 0) {
getmetaint(fd, meta, "parent", &qp);
meta = q2m(fd, qp, 0);
}
else {
path = mkpath(fd, qid.path, n + 1);
m = strlen(path);
path[m] = '/';
strcpy(path + m + 1, name);
x = p2q(fd, path, 0);
meta = q2m(fd, x, 0);
if(meta == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOENT);
rp = rpcputl(rp, 0);
return rp;
}
free(path);
sname = getmetastr(fd, meta, "snap");
if(sname) {
spath = smprint("%s/%s", ddir, sname);
free(sname);
fd = open(spath, OREAD);
free(spath);
sqid = x;
meta = q2m(fd, p2q(fd, "/", 0), 0);
}
}
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
getmetaint(fd, meta, "qpath", &x);
qid2.path = x;
getmetaint(fd, meta, "qvers", &x);
qid2.vers = x;
getmetaint(fd, meta, "qtype", &x);
qid2.type = x;
if(fd == -1)
m = round4(sizeof(Qid));
else
m = round4(sizeof(Qid)) + sizeof(uvlong);
rp = rpcputl(rp, m);
memmove(rp, &qid2, sizeof(Qid));
rp += round4(sizeof(Qid));
if(fd != -1)
rp = rpcputv(rp, sqid);
rp = opattr(fd, rp, &qid2, auth);
rp = opattr(pfd, rp, &qid, auth);
free(name);
if(fd != -1)
close(fd);
if(pfd != -1 && pfd != fd)
close(pfd);
return rp;
}
static char *
nfsaccess(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
uvlong meta;
ulong reqacc, rspacc, perms;
int fd;
fd = -1;
if(nhgetl(p) != round4(sizeof(Qid))) {
fd = opensnap(nhgetv(p + round4(sizeof(Qid)) + 4));
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
reqacc = nhgetl(p);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
if(fd != -1)
close(fd);
return rp;
}
perms = getperm(fd, meta, auth);
rspacc = 0;
if(perms & DMREAD)
rspacc |= ACCESS3_READ;
if(perms & DMWRITE)
rspacc |= ACCESS3_MODIFY | ACCESS3_EXTEND;
if(perms & DMEXEC)
rspacc |= ACCESS3_LOOKUP | ACCESS3_EXECUTE;
rspacc &= reqacc;
rp = rpcputl(rp, NFS3_OK);
a = fattr3(fd, rp + 4, &qid, auth);
if(a == nil) {
hnputl(rp-4, NFS3ERR_BADHANDLE);
rpcputl(rp, 0);
}
else {
rpcputl(rp, 1);
rp = a;
rp = rpcputl(rp, rspacc);
}
if(fd != -1)
close(fd);
return rp;
}
static char *
nfsreadlink(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *pp;
uvlong meta;
int n, fd;
fd = -1;
if(nhgetl(p) != round4(sizeof(Qid))) {
fd = opensnap(nhgetv(p + round4(sizeof(Qid)) + 4));
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
if((pp = getmetastr(fd, meta, "symlink")) == nil) {
rp = rpcputl(rp, NFS3ERR_INVAL);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
n = strlen(pp);
rp = rpcputl(rp, NFS3_OK);
rp = opattr(fd, rp, &qid, auth);
rp = rpcputl(rp, n);
memmove(rp, pp, n);
rp += round4(n);
free(pp);
if(fd != -1)
close(fd);
return rp;
}
static char *
nfsread(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
uvlong offset, meta, len;
ulong perms;
long count1, count2;
int fd;
fd = -1;
if(nhgetl(p) != round4(sizeof(Qid))) {
fd = opensnap(nhgetv(p + round4(sizeof(Qid)) + 4));
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
offset = nhgetv(p);
p += 8;
count1 = nhgetl(p);
if(count1 > 32768)
count1 = 32768;
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
a = rp + 104;
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
perms = getperm(fd, meta, auth);
if((perms & DMREAD) == 0) {
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
if(getmetaint(fd, meta, "length", &len) == MTnone)
len = 0;
count2 = θpread(fd, qid.path, a, count1, offset);
a = fattr3(fd, rp + 8, &qid, auth);
if(a == nil) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
hnputl(rp + 4, 1);
if(count2 < 0) {
hnputl(rp, NFS3ERR_IO);
rp = a;
if(fd != -1)
close(fd);
return rp;
}
hnputl(rp, NFS3_OK);
rp = a;
rp = rpcputl(rp, count2);
if(offset + count2 >= len)
rp = rpcputl(rp, 1);
else
rp = rpcputl(rp, 0);
rp = rpcputl(rp, count2);
rp += round4(count2);
if(fd != -1)
close(fd);
return rp;
}
static char *
nfswrite(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp;
uvlong offset, meta, prelen, premtime, prectime;
ulong perms;
long count1, count2, stable;
if(nhgetl(p) != round4(sizeof(Qid))) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
offset = nhgetv(p);
p += 8;
count1 = nhgetl(p);
p += 4;
stable = nhgetl(p);
p += 8; /* also skip the count at the beginning of the opaque data */
meta = q2m(-1, qid.path, 0);
if(meta == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
return rp;
}
perms = getperm(-1, meta, auth);
if((perms & DMWRITE) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
if(prewcc(-1, qid.path, &prelen, &premtime, &prectime) < 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
count2 = θpwrite(qid.path, p, count1, offset, 1);
if(stable != UNSTABLE) {
resetmeta();
csync();
}
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
if(count2 < 0) {
rp = rpcputl(rp, NFS3ERR_IO);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
rp = rpcputl(rp, NFS3_OK);
rp = dowcc(-1, rp, auth, &qid, prelen, premtime, prectime);
rp = rpcputl(rp, count2);
if(stable == UNSTABLE)
rp = rpcputl(rp, UNSTABLE);
else
rp = rpcputl(rp, FILE_SYNC);
rp = rpcputv(rp, starttime);
return rp;
}
static char *
mkfile(char *buf, char *p, ulong xid, char *auth, char *verf, int ilk)
{
Qid qid, nqid;
char *name, *path, *rp;
uvlong meta, pmeta, dirblk, now, x;
uvlong prelen, premeta, prectime;
ulong perms;
int n, m, how, nodetype;
if(nhgetl(p) != round4(sizeof(Qid))) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
n = nhgetl(p);
p += 4;
name = malloc(n + 1);
if(name == nil) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_SERVERFAULT);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
memmove(name, p, n);
name[n] = 0;
p += round4(n);
if(ilk == NF3REG) {
how = nhgetl(p);
p += 4;
}
else
how = GUARDED;
if(debugnfs)
fprint(2, "in nfscreate: qid=(%ulld,%uld,%ud) name=%s\n", qid.path, qid.vers, qid.type, name);
if((qid.type & QTDIR) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOTDIR);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_EXIST);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
if(how == EXCLUSIVE) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOTSUPP);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
pmeta = q2m(-1, qid.path, 0);
perms = getperm(-1, pmeta, auth);
if((perms & DMWRITE) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
path = mkpath(-1, qid.path, n + 1);
m = strlen(path);
path[m] = '/';
strcpy(path + m + 1, name);
nqid.path = p2q(-1, path, 1);
switch(how) {
case UNCHECKED:
break;
case GUARDED:
meta = q2m(-1, nqid.path, 0);
if(meta != 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_EXIST);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
meta = q2m(-1, nqid.path, 1);
free(path);
nqid.vers = 0;
if(ilk == NF3DIR)
nqid.type = QTDIR;
else
nqid.type = QTFILE;
prewcc(-1, qid.path, &prelen, &premeta, &prectime);
setmetastr(meta, "name", nil, name, 0);
setmetaint(meta, "parent", nil, qid.path);
setmetaint(meta, "qpath", nil, nqid.path);
setmetaint(meta, "qvers", nil, nqid.vers);
setmetaint(meta, "qtype", nil, nqid.type);
getmetaint(-1, pmeta, "mode", &x);
if(ilk == NF3DIR)
setmetaint(meta, "mode", nil, x & 0777 | DMDIR);
else
setmetaint(meta, "mode", nil, x & 0777);
now = nsec();
setmetaint(pmeta, "mtime", nil, now);
getmetaint(-1, pmeta, "child", &x);
setmetaint(meta, "sib", nil, x);
setmetaint(pmeta, "child", nil, nqid.path);
nodetype = 0;
switch(ilk) {
case NF3DIR:
setmetaint(meta, "child", nil, 0);
break;
case NF3REG:
dirblk = allocblock();
cbclean(dirblk);
cbwrite(dirblk);
brelease(dirblk);
setmetaint(meta, "index", nil, dirblk);
break;
case NF3CHR:
nodetype = nhgetl(p);
p += 4;
setmetaint(meta, "nodetype", nil, nodetype);
break;
}
setqhash(nqid.path, meta);
p = dosattr(meta, p);
if(ilk == NF3LNK) {
n = nhgetl(p);
p += 4;
path = malloc(n + 1);
memmove(path, p, n);
path[n] = 0;
setmetastr(meta, "symlink", nil, path, 0);
free(path);
}
else if(ilk == NF3CHR) {
if(nodetype == NF3CHR || nodetype == NF3BLK) {
setmetaint(meta, "majordev", nil, nhgetl(p));
p += 4;
setmetaint(meta, "minordev", nil, nhgetl(p));
}
}
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, round4(sizeof(Qid)));
memmove(rp, &nqid, sizeof(Qid));
rp += round4(sizeof(Qid));
rp = opattr(-1, rp, &nqid, auth);
rp = dowcc(-1, rp, auth, &qid, prelen, premeta, prectime);
savesuper();
return rp;
}
static char *
nfscreate(char *buf, char *p, ulong xid, char *auth, char *verf)
{
return mkfile(buf, p, xid, auth, verf, NF3REG);
}
static char *
nfsmkdir(char *buf, char *p, ulong xid, char *auth, char *verf)
{
return mkfile(buf, p, xid, auth, verf, NF3DIR);
}
static char *
nfssymlink(char *buf, char *p, ulong xid, char *auth, char *verf)
{
return mkfile(buf, p, xid, auth, verf, NF3LNK);
}
static char *
nfsmknod(char *buf, char *p, ulong xid, char *auth, char *verf)
{
return mkfile(buf, p, xid, auth, verf, NF3CHR); /* sort out the exact node type in mkfile */
}
static char *
nfsremove(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *name, *path;
uvlong meta, qpath, pmeta, x;
uvlong prelen, premtime, prectime;
ulong perms;
int n;
if(nhgetl(p) != round4(sizeof(Qid))) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
if((qid.type & QTDIR) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOTDIR);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
pmeta = q2m(-1, qid.path, 0);
if(pmeta == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
perms = getperm(-1, pmeta, auth);
if((perms & DMWRITE) == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
if(prewcc(-1, qid.path, &prelen, &premtime, &prectime) < 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
n = nhgetl(p);
p += 4;
name = malloc(n + 1);
memmove(name, p, n);
name[n] = 0;
path = mkpath(-1, qid.path, n + 1);
n = strlen(path);
path[n] = '/';
strcpy(path + n + 1, name);
qpath = p2q(-1, path, 0);
meta = q2m(-1, qpath, 0);
if(meta == 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOENT);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
free(name);
free(path);
return rp;
}
if(getmetaint(-1, meta, "child", &x) != MTnone && x != 0) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOTEMPTY);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
free(name);
free(path);
return rp;
}
rmq(qpath, meta);
freedata(meta);
rmdlist(meta, qpath);
freeblock(meta);
rmp(path);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
rp = dowcc(-1, rp, auth, &qid, prelen, premtime, prectime);
free(name);
free(path);
return rp;
}
static char *
nfsfsstat(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3_OK);
a = fattr3(-1, rp + 4, &qid, auth);
if(a == nil) {
hnputl(rp - 4, NFS3ERR_BADHANDLE);
hnputl(rp, 0);
return rp;
}
hnputl(rp, 1);
rp = a;
rp = rpcputv(rp, super.nblk * BlkSize); /* tbytes */
rp = rpcputv(rp, super.nfree * BlkSize); /* fbytes */
rp = rpcputv(rp, super.nfree * BlkSize); /* abytes */
rp = rpcputv(rp, super.nht); /* tfiles */
rp = rpcputv(rp, super.nht); /* ffiles */
rp = rpcputv(rp, super.nht); /* afiles */
rp = rpcputl(rp, 0); /* invarsec */
return rp;
}
static char *
nfsfsinfo(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
hnputl(rp, NFS3_OK);
rp += 4;
a = fattr3(-1, rp + 4, &qid, auth);
if(a == nil) {
hnputl(rp - 4, NFS3ERR_BADHANDLE);
hnputl(rp, 0);
return rp;
}
hnputl(rp, 1);
rp = a;
rp = rpcputl(rp, 32768); /* rtmax */
rp = rpcputl(rp, 32768); /* rtpref */
rp = rpcputl(rp, 1); /* rtmult */
rp = rpcputl(rp, 32768); /* wtmax */
rp = rpcputl(rp, 32768); /* wtpref */
rp = rpcputl(rp, 1); /* wtmult */
rp = rpcputl(rp, 8192); /* dtpref */
rp = rpcputv(rp, 1LL << 55); /* maxfilesize */
rp = rpcputl(rp, 0); /* time_delta */
rp = rpcputl(rp, 1);
rp = rpcputl(rp, FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME); /* properties */
return rp;
}
static char *
nfspathconf(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a;
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
hnputl(rp, NFS3_OK);
rp += 4;
a = fattr3(-1, rp + 4, &qid, auth);
if(a == nil) {
hnputl(rp - 4, NFS3ERR_BADHANDLE);
return rp;
}
hnputl(rp, 1);
rp = a;
rp = rpcputl(rp, 1);
rp = rpcputl(rp, MNTNAMELEN);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, 1);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 1);
return rp;
}
static char *
nfsrename(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid fqid, tqid;
char *fname, *tname, *fpath, *tpath, *rp;
uvlong fdmeta, fmeta, tmeta, qpath, now, x;
uvlong fprelen, fpremtime, fprectime, tprelen, tpremtime, tprectime;
ulong perms;
int fn, tn, n;
if(nhgetl(p) != round4(sizeof(Qid))) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
fqid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
fn = nhgetl(p);
p += 4;
fname = malloc(fn + 1);
memmove(fname, p, fn);
fname[fn] = 0;
p += round4(fn);
fpath = mkpath(-1, fqid.path, fn + 1);
n = strlen(fpath);
fpath[n] = '/';
strcpy(fpath + n + 1, fname);
tqid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
tn = nhgetl(p);
p += 4;
tname = malloc(tn + 1);
memmove(tname, p, tn);
tname[tn] = 0;
tpath = mkpath(-1, tqid.path, tn + 1);
n = strlen(tpath);
tpath[n] = '/';
strcpy(tpath + n + 1, tname);
prewcc(-1, fqid.path, &fprelen, &fpremtime, &fprectime);
prewcc(-1, tqid.path, &tprelen, &tpremtime, &tprectime);
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
fdmeta = q2m(-1, fqid.path, 0);
if(fdmeta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
goto done;
}
perms = getperm(-1, fdmeta, auth);
if((perms & DMWRITE) == 0) {
rp = rpcputl(rp, NFS3ERR_ACCES);
goto done;
}
qpath = p2q(-1, fpath, 0);
if(qpath == 0) {
rp = rpcputl(rp, NFS3ERR_NOENT);
goto done;
}
if((tqid.type & QTDIR) == 0) {
rp = rpcputl(rp, NFS3ERR_NOTDIR);
goto done;
}
now = nsec();
fmeta = q2m(-1, qpath, 0);
if(fqid.path != tqid.path) {
tmeta = q2m(-1, tqid.path, 0);
if(tmeta == 0) {
rp = rpcputl(rp, NFS3ERR_NOENT);
goto done;
}
perms = getperm(-1, tmeta, auth);
if((perms & DMWRITE) == 0) {
rp = rpcputl(rp, NFS3ERR_ACCES);
goto done;
}
rmdlist(fmeta, qpath);
setmetaint(fmeta, "parent", nil, tqid.path);
getmetaint(-1, tmeta, "child", &x);
setmetaint(fmeta, "sib", nil, x);
setmetaint(tmeta, "child", nil, qpath);
setmetaint(tmeta, "mtime", nil, now);
setmetaint(tmeta, "atime", nil, now);
}
setmetastr(fmeta, "name", nil, tname, 0);
rehashpath(qpath, fpath, tpath);
setmetaint(fmeta, "ctime", nil, now);
setmetaint(fdmeta, "mtime", nil, now);
setmetaint(fdmeta, "atime", nil, now);
rp = rpcputl(rp, NFS3_OK);
done:
rp = dowcc(-1, rp, auth, &fqid, fprelen, fpremtime, fprectime);
rp = dowcc(-1, rp, auth, &tqid, tprelen, tpremtime, tprectime);
free(fname);
free(fpath);
free(tname);
free(tpath);
return rp;
}
static char *
nfsreaddir(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp, *a, *xs;
uvlong cookie, meta;
ulong perms;
long count1, count2;
int n, fd;
fd = -1;
if(nhgetl(p) != round4(sizeof(Qid))) {
fd = opensnap(nhgetv(p + round4(sizeof(Qid)) + 4));
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
cookie = nhgetv(p);
p += 16;
count1 = nhgetl(p);
if(count1 > 8192)
count1 = 8192;
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
perms = getperm(fd, meta, auth);
if((perms & DMREAD) == 0) {
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
if(cookie == 0) {
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
return rp;
}
getmetaint(fd, meta, "child", &cookie);
}
a = rp + 92;
a = rpcputv(a, 0);
count2 = a - rp;
while(cookie != 0) {
meta = q2m(fd, cookie, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_IO);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
xs = getmetastr(fd, meta, "name");
n = strlen(xs);
if(count2 + round4(n) + 24 + 8 > count1) {
free(xs);
break;
}
a = rpcputl(a, 1);
a = rpcputv(a, cookie);
a = rpcputl(a, n);
memmove(a, xs, n);
a += round4(n);
free(xs);
a = rpcputv(a, cookie);
getmetaint(fd, meta, "sib", &cookie);
count2 += round4(n) + 24;
}
a = fattr3(fd, rp + 8, &qid, auth);
if(a == nil) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
hnputl(rp, NFS3_OK);
hnputl(rp + 4, 1);
rp += count2;
rp = rpcputl(rp, 0);
if(cookie == 0)
rp = rpcputl(rp, 1);
else
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
static char *
nfsreaddirplus(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid, qid2;
char *rp, *a, *xs;
uvlong cookie, meta, x, sqid;
ulong perms;
long count1, count2;
int n, m, fd;
fd = -1;
sqid = 0;
if(nhgetl(p) != round4(sizeof(Qid))) {
sqid = nhgetv(p + round4(sizeof(Qid)) + 4);
fd = opensnap(sqid);
if(fd == -1) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
}
qid = *((Qid *)(p + 4));
p += nhgetl(p) + 4;
cookie = nhgetv(p);
p += 16;
p += 4; /* use maxcount instead of dircount */
count1 = nhgetl(p);
if(count1 > 8192)
count1 = 8192;
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
perms = getperm(fd, meta, auth);
if((perms & DMREAD) == 0) {
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
if(cookie == 0) {
meta = q2m(fd, qid.path, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
getmetaint(fd, meta, "child", &cookie);
}
a = rp + 92;
a = rpcputv(a, 0); /* cookieverf */
count2 = a - rp;
while(cookie != 0) {
meta = q2m(fd, cookie, 0);
if(meta == 0) {
rp = rpcputl(rp, NFS3ERR_IO);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
xs = getmetastr(fd, meta, "name");
n = strlen(xs);
getmetaint(fd, meta, "qpath", &x);
qid2.path = x;
getmetaint(fd, meta, "qvers", &x);
qid2.vers = x;
getmetaint(fd, meta, "qtype", &x);
qid2.type = x;
if(fd == -1)
m = round4(sizeof(Qid));
else
m = round4(sizeof(Qid)) + sizeof(uvlong);
if(count2 + 4 + 8 + 4 + round4(n) + 8 + 88 + 4 + 4 + m + 8 > count1) {
free(xs);
break;
}
a = rpcputl(a, 1);
a = rpcputv(a, cookie); /* fileid */
a = rpcputl(a, n); /* name */
memmove(a, xs, n);
a += round4(n);
free(xs);
a = rpcputv(a, cookie); /* cookie */
a = opattr(fd, a, &qid2, auth); /* name_attributes */
a = rpcputl(a, 1); /* name_handle */
a = rpcputl(a, m);
memmove(a, &qid2, sizeof(Qid));
a += round4(sizeof(Qid));
if(fd != -1)
rp = rpcputv(rp, sqid);
getmetaint(fd, meta, "sib", &cookie);
count2 += 4 + 8 + 4 + round4(n) + 8 + 88 + 4 + 4+ m;
}
a = fattr3(fd, rp + 8, &qid, auth);
if(a == nil) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
hnputl(rp, NFS3_OK);
hnputl(rp + 4, 1);
rp += count2;
rp = rpcputl(rp, 0); /* no more entries */
if(cookie == 0) /* eof? */
rp = rpcputl(rp, 1);
else
rp = rpcputl(rp, 0);
if(fd != -1)
close(fd);
return rp;
}
static char *
nfscommit(char *buf, char *p, ulong xid, char *auth, char *verf)
{
Qid qid;
char *rp;
uvlong prelen, premtime, prectime;
if(*((long *)p) != round4(sizeof(Qid))) {
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_ACCES);
rp = rpcputl(rp, 0);
return rp;
}
qid = *((Qid *)(p + 4));
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
if(prewcc(-1, qid.path, &prelen, &premtime, &prectime) < 0) {
rp = rpcputl(rp, NFS3ERR_BADHANDLE);
rp = rpcputl(rp, 0);
rp = rpcputl(rp, 0);
return rp;
}
resetmeta();
csync();
rp = rpcputl(rp, NFS3_OK);
rp = dowcc(-1, rp, auth, &qid, prelen, premtime, prectime);
rp = rpcputv(rp, starttime);
return rp;
}
static char *
nfsunsupp(char *buf, ulong xid, char *verf)
{
char *rp;
rp = initreply(buf, xid, MSG_ACCEPTED, verf, SUCCESS);
rp = rpcputl(rp, NFS3ERR_NOTSUPP);
rp = rpcputl(rp, 0);
return rp;
}
static int
nfsdis(char *buf, char *p, ulong xid, char *auth, char *verf, ulong proc)
{
char *rp;
switch(proc) {
case NFSPROC3_NULL:
rp = rpcnull(buf, xid, verf);
break;
case NFSPROC3_GETATTR:
rp = nfsgetattr(buf, p, xid, auth, verf);
break;
case NFSPROC3_SETATTR:
rp = nfssetattr(buf, p, xid, auth, verf);
break;
case NFSPROC3_LOOKUP:
rp = nfslookup(buf, p, xid, auth, verf);
break;
case NFSPROC3_ACCESS:
rp = nfsaccess(buf, p, xid, auth, verf);
break;
case NFSPROC3_READLINK:
rp = nfsreadlink(buf, p, xid, auth, verf);
break;
case NFSPROC3_READ:
rp = nfsread(buf, p, xid, auth, verf);
break;
case NFSPROC3_WRITE:
rp = nfswrite(buf, p, xid, auth, verf);
break;
case NFSPROC3_CREATE:
rp = nfscreate(buf, p, xid, auth, verf);
break;
case NFSPROC3_MKDIR:
rp = nfsmkdir(buf, p, xid, auth, verf);
break;
case NFSPROC3_SYMLINK:
rp = nfssymlink(buf, p, xid, auth, verf);
break;
case NFSPROC3_MKNOD:
rp = nfsmknod(buf, p, xid, auth, verf);
break;
case NFSPROC3_REMOVE:
case NFSPROC3_RMDIR:
rp = nfsremove(buf, p, xid, auth, verf);
break;
case NFSPROC3_RENAME:
rp = nfsrename(buf, p, xid, auth, verf);
break;
case NFSPROC3_LINK:
rp = nfsunsupp(buf, xid, verf); /* $ */
break;
case NFSPROC3_READDIR:
rp = nfsreaddir(buf, p, xid, auth, verf);
break;
case NFSPROC3_READDIRPLUS:
rp = nfsreaddirplus(buf, p, xid, auth, verf);
break;
case NFSPROC3_FSSTAT:
rp = nfsfsstat(buf, p, xid, auth, verf);
break;
case NFSPROC3_FSINFO:
rp = nfsfsinfo(buf, p, xid, auth, verf);
break;
case NFSPROC3_PATHCONF:
rp = nfspathconf(buf, p, xid, auth, verf);
break;
case NFSPROC3_COMMIT:
rp = nfscommit(buf, p, xid, auth, verf);
break;
default:
rp = initreply(buf, xid, MSG_DENIED, verf, PROC_UNAVAIL);
break;
}
return rp - buf;
}
static void
tpstarter(void *)
{
Rcb *r;
int fd;
while(recv(tpchan, &fd)) {
for(r = rcbhd; r && r->inuse; r = r->next) ;
if(r == nil) {
r = emalloc9p(sizeof(Rcb));
r->inuse = 1;
r->io = ioproc();
r->next = rcbhd;
rcbhd = r;
}
r->inuse = 1;
r->fd = fd;
r->myprog = PMAP_PROG;
r->minver = PMAP_VERS;
r->maxver = PMAP_VERS;
r->dispatch = pmapdis;
threadcreate(tcprpcreader, r, 8192);
}
threadexits(nil);
}
static void
tportmapper(void *)
{
char *s;
int acfd, lcfd, fd;
char adir[40], ldir[40];
s = smprint("tcp!*!%d", PMAP_PORT);
acfd = announce(s, adir);
if(acfd < 0)
fprint(2, "error in announce: %r\n");
if(debugnfs)
fprint(2, "announce in tcp port mapper got dir: %s:%r\n", adir);
free(s);
if(acfd < 0)
threadexits(nil);
while(1) {
lcfd = listen(adir, ldir);
if(lcfd < 0)
fprint(2, "error in listen: %r\n");
if(shutdown)
threadexits(nil);
if(debugnfs)
fprint(2, "back from listen in tcp port mapper: ldir=%s\n", ldir);
if(lcfd < 0) {
close(acfd);
threadexits(nil);
}
fd = accept(lcfd, ldir);
close(lcfd);
send(tpchan, &fd);
}
}
static void
upstarter(void *)
{
Rcb *r;
int fd;
while(recv(upchan, &fd)) {
if(shutdown)
break;
for(r = rcbhd; r && r->inuse; r = r->next) ;
if(r == nil) {
r = emalloc9p(sizeof(Rcb));
r->inuse = 1;
r->io = ioproc();
r->next = rcbhd;
rcbhd = r;
}
r->inuse = 1;
r->fd = fd;
r->myprog = PMAP_PROG;
r->minver = PMAP_VERS;
r->maxver = PMAP_VERS;
r->dispatch = pmapdis;
threadcreate(udprpcreader, r, 8192);
}
threadexits(nil);
}
static void
uportmapper(void *)
{
char *s;
int acfd, lcfd, fd;
char adir[40], ldir[40];
s = smprint("udp!*!%d", PMAP_PORT);
acfd = announce(s, adir);
if(acfd < 0)
fprint(2, "error in announce: %r\n");
if(debugnfs)
fprint(2, "announce in udp port mapper got dir: %s:%r\n", adir);
free(s);
if(acfd < 0)
threadexits(nil);
while(1) {
lcfd = listen(adir, ldir);
if(lcfd < 0)
fprint(2, "error in listen: %r\n");
if(shutdown)
threadexits(nil);
if(debugnfs)
fprint(2, "back from listen in udp port mapper: ldir=%s\n", ldir);
if(lcfd < 0) {
close(acfd);
threadexits(nil);
}
fd = accept(lcfd, ldir);
close(lcfd);
send(upchan, &fd);
}
}
static void
mountstarter(void *)
{
Rcb *r;
int fd;
while(recv(mchan, &fd)) {
if(shutdown)
break;
for(r = rcbhd; r && r->inuse; r = r->next) ;
if(r == nil) {
r = emalloc9p(sizeof(Rcb));
r->inuse = 1;
r->io = ioproc();
r->next = rcbhd;
rcbhd = r;
}
r->inuse = 1;
r->fd = fd;
r->myprog = MNT_PROG;
r->minver = MNT_MIN_VERS;
r->maxver = MNT_MAX_VERS;
r->dispatch = mntdis;
threadcreate(tcprpcreader, r, 8192);
}
threadexits(nil);
}
static void
mountd(void *)
{
char *s;
int acfd, lcfd, fd;
char adir[40], ldir[40];
s = smprint("tcp!*!%d", MNT_PORT);
acfd = announce(s, adir);
free(s);
if(acfd < 0)
threadexits(nil);
while(1) {
lcfd = listen(adir, ldir);
if(shutdown)
threadexits(nil);
if(debugnfs)
fprint(2, "back from listen in mountd: ldir=%s\n", ldir);
if(lcfd < 0) {
close(acfd);
threadexits(nil);
}
fd = accept(lcfd, ldir);
close(lcfd);
send(mchan, &fd);
}
}
static void
nfsdstarter(void *)
{
Rcb *r;
int fd;
while(recv(nchan, &fd)) {
if(shutdown)
break;
for(r = rcbhd; r && r->inuse; r = r->next) ;
if(r == nil) {
r = emalloc9p(sizeof(Rcb));
r->inuse = 1;
r->io = ioproc();
r->next = rcbhd;
rcbhd = r;
}
r->inuse = 1;
r->fd = fd;
r->myprog = NFS_PROG;
r->minver = NFS_VERS;
r->maxver = NFS_VERS;
r->dispatch = nfsdis;
threadcreate(tcprpcreader, r, 8192);
}
threadexits(nil);
}
static void
nfsd(void *)
{
char *s;
int acfd, lcfd, fd;
char adir[40], ldir[40];
s = smprint("tcp!*!%d", NFS_PORT);
acfd = announce(s, adir);
free(s);
if(acfd < 0)
threadexits(nil);
while(1) {
lcfd = listen(adir, ldir);
if(shutdown)
threadexits(nil);
if(debugnfs)
fprint(2, "back from listen in nfsd: ldir=%s\n", ldir);
if(lcfd < 0) {
close(acfd);
threadexits(nil);
}
fd = accept(lcfd, ldir);
close(lcfd);
send(nchan, &fd);
}
}
static int
regport(void)
{
char *buf, *p;
int fd;
int n, i;
/*
* On Plan 9, don't even bother trying to see if we have
* a local portmap running.
*/
if(access("/net/ipselftab", AREAD) == 0)
return 0;
/*
* Take a crack at using a locks instance of portmap/
* rpcbind. If we succeed, we don't need to bother
* starting out build-in one. If
*/
fd = dial("udp!127.1!111", nil, nil, nil);
if(fd < 0)
return 0;
fprint(2, "Got portmap connection open\n");
buf = malloc(1500);
p = buf;
p = rpcputl(p, 42); /* xid */
p = rpcputl(p, CALL); /* mtype */
p = rpcputl(p, 2); /* rpcvers */
p = rpcputl(p, PMAP_PROG); /* prog */
p = rpcputl(p, PMAP_VERS); /* vers */
p = rpcputl(p, PMAPPROC_SET); /* proc */
p = rpcputl(p, 0); /* auth */
p = rpcputl(p, 0);
p = rpcputl(p, 0); /* verf */
p = rpcputl(p, 0);
p = rpcputl(p, NFS_PROG); /* prog */
p = rpcputl(p, NFS_VERS); /* vers */
p = rpcputl(p, IPPROTO_TCP); /* prot */
p = rpcputl(p, NFS_PORT); /* port */
write(fd, buf, p - buf);
n = read(fd, buf, 1500);
for(i = 0; i < n; ++i) fprint(2, "%02x ", buf[i]);
fprint(2, "\n");
close(fd);
fd = dial("udp!127.1!111", nil, nil, nil);
if(fd < 0) {
free(buf);
return 0;
}
p = buf;
p = rpcputl(p, 42); /* xid */
p = rpcputl(p, CALL); /* mtype */
p = rpcputl(p, 2); /* rpcvers */
p = rpcputl(p, PMAP_PROG); /* prog */
p = rpcputl(p, PMAP_VERS); /* vers */
p = rpcputl(p, PMAPPROC_SET); /* proc */
p = rpcputl(p, 0); /* auth */
p = rpcputl(p, 0);
p = rpcputl(p, 0); /* verf */
p = rpcputl(p, 0);
p = rpcputl(p, MNT_PROG); /* prog */
p = rpcputl(p, MNT_MAX_VERS); /* vers */
p = rpcputl(p, IPPROTO_TCP); /* prot */
p = rpcputl(p, MNT_PORT); /* port */
write(fd, buf, p - buf);
n = read(fd, buf, 1500);
for(i = 0; i < n; ++i) fprint(2, "%02x ", buf[i]);
fprint(2, "\n");
close(fd);
free(buf);
return 1;
}
void
initnfs(void)
{
if(!regport()) {
upchan = chancreate(sizeof(ulong), 1);
threadcreate(upstarter, nil, 1024);
umaptid = proccreate(uportmapper, nil, 8192);
tpchan = chancreate(sizeof(ulong), 1);
threadcreate(tpstarter, nil, 1024);
tmaptid = proccreate(tportmapper, nil, 8192);
}
mchan = chancreate(sizeof(ulong), 1);
threadcreate(mountstarter, nil, 1024);
mounttid = proccreate(mountd, nil, 8192);
nchan = chancreate(sizeof(ulong), 1);
threadcreate(nfsdstarter, nil, 1024);
nfstid = proccreate(nfsd, nil, 8192);
}
void
haltnfs(void)
{
Rcb *r;
if(upchan == nil)
return;
/*
if(upchan) {
chanclose(upchan);
chanclose(tpchan);
}
chanclose(mchan);
chanclose(nchan);
*/
for(r = rcbhd; r; r = r->next) {
if(r->io) {
iointerrupt(r->io);
closeioproc(r->io);
}
}
/*
if(upchan) {
threadkill(umaptid);
threadkill(tmaptid);
}
threadkill(mounttid);
threadkill(nfstid);
*/
}
|