/* * 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 #include #include #include #include <9p.h> #include #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); */ }