summaryrefslogtreecommitdiffstats
path: root/sys/nfsserver
diff options
context:
space:
mode:
Diffstat (limited to 'sys/nfsserver')
-rw-r--r--sys/nfsserver/nfs.h297
-rw-r--r--sys/nfsserver/nfs_serv.c1908
-rw-r--r--sys/nfsserver/nfs_srvcache.c348
-rw-r--r--sys/nfsserver/nfs_srvsock.c1990
-rw-r--r--sys/nfsserver/nfs_srvsubs.c1130
-rw-r--r--sys/nfsserver/nfs_syscalls.c874
-rw-r--r--sys/nfsserver/nfsm_subs.h269
-rw-r--r--sys/nfsserver/nfsrvcache.h84
-rw-r--r--sys/nfsserver/nfsrvstats.h297
9 files changed, 7197 insertions, 0 deletions
diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsserver/nfs.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c
new file mode 100644
index 0000000..f31b96e
--- /dev/null
+++ b/sys/nfsserver/nfs_serv.c
@@ -0,0 +1,1908 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * nfs version 2 server calls to vnode ops
+ * - these routines generally have 3 phases
+ * 1 - break down and validate rpc request in mbuf list
+ * 2 - do the vnode ops for the request
+ * (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c)
+ * 3 - build the rpc reply in an mbuf list
+ * nb:
+ * - do not mix the phases, since the nfsm_?? macros can return failures
+ * on a bad rpc or similar and do not do any vrele() or vput()'s
+ *
+ * - the nfsm_reply() macro generates an nfs rpc reply with the nfs
+ * error number iff error != 0 whereas
+ * returning an error from the server function implies a fatal error
+ * such as a badly constructed rpc request that should be dropped without
+ * a reply.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/mbuf.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <vm/vm.h>
+
+#include <nfs/nfsv2.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nqnfs.h>
+
+/* Defs */
+#define TRUE 1
+#define FALSE 0
+
+/* Global vars */
+extern u_long nfs_procids[NFS_NPROCS];
+extern u_long nfs_xdrneg1;
+extern u_long nfs_false, nfs_true;
+nfstype nfs_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
+ NFCHR, NFNON };
+
+/*
+ * nqnfs access service
+ */
+nqnfsrv_access(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, mode = 0;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (*tl++ == nfs_true)
+ mode |= VREAD;
+ if (*tl++ == nfs_true)
+ mode |= VWRITE;
+ if (*tl == nfs_true)
+ mode |= VEXEC;
+ error = nfsrv_access(vp, mode, cred, rdonly, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs getattr service
+ */
+nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs setattr service
+ */
+nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register struct nfsv2_fattr *fp;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_WRITE);
+ VATTR_NULL(vap);
+ /*
+ * Nah nah nah nah na nah
+ * There is a bug in the Sun client that puts 0xffff in the mode
+ * field of sattr when it should put in 0xffffffff. The u_short
+ * doesn't sign extend.
+ * --> check the low order 2 bytes for 0xffff
+ */
+ if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (sp->sa_uid != nfs_xdrneg1)
+ vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid);
+ if (sp->sa_gid != nfs_xdrneg1)
+ vap->va_gid = fxdr_unsigned(gid_t, sp->sa_gid);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ if (sp->sa_nfssize != nfs_xdrneg1)
+ vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_nfssize);
+ if (sp->sa_nfsatime.nfs_sec != nfs_xdrneg1) {
+#ifdef notyet
+ fxdr_nfstime(&sp->sa_nfsatime, &vap->va_atime);
+#else
+ vap->va_atime.ts_sec =
+ fxdr_unsigned(long, sp->sa_nfsatime.nfs_sec);
+ vap->va_atime.ts_nsec = 0;
+#endif
+ }
+ if (sp->sa_nfsmtime.nfs_sec != nfs_xdrneg1)
+ fxdr_nfstime(&sp->sa_nfsmtime, &vap->va_mtime);
+ } else {
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ fxdr_nqtime(&sp->sa_nqatime, &vap->va_atime);
+ fxdr_nqtime(&sp->sa_nqmtime, &vap->va_mtime);
+ vap->va_flags = fxdr_unsigned(u_long, sp->sa_nqflags);
+ }
+
+ /*
+ * If the size is being changed write acces is required, otherwise
+ * just check for a read only file system.
+ */
+ if (vap->va_size == ((u_quad_t)((quad_t) -1))) {
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ error = EROFS;
+ goto out;
+ }
+ } else {
+ if (vp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly,
+ nfsd->nd_procp))
+ goto out;
+ }
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+out:
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 2*NFSX_UNSIGNED);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&frev2, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs lookup rpc
+ */
+nfsrv_lookup(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, duration2, cache2, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vattr va, *vap = &va;
+ u_quad_t frev, frev2;
+
+ fhp = &nfh.fh_generic;
+ duration2 = 0;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ duration2 = fxdr_unsigned(int, *tl);
+ }
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nqsrv_getl(nd.ni_startdir, NQL_READ);
+ vrele(nd.ni_startdir);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (duration2)
+ (void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd,
+ nam, &cache2, &frev2, cred);
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH + NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL) + 5*NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ if (duration2) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(NQL_READ);
+ *tl++ = txdr_unsigned(cache2);
+ *tl++ = txdr_unsigned(duration2);
+ txdr_hyper(&frev2, tl);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readlink service
+ */
+nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN];
+ register struct iovec *ivp = iv;
+ register struct mbuf *mp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, tlen, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mp2, *mp3, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ len = 0;
+ i = 0;
+ while (len < NFS_MAXPATHLEN) {
+ MGET(mp, M_WAIT, MT_DATA);
+ MCLGET(mp, M_WAIT);
+ mp->m_len = NFSMSIZ(mp);
+ if (len == 0)
+ mp3 = mp2 = mp;
+ else {
+ mp2->m_next = mp;
+ mp2 = mp;
+ }
+ if ((len+mp->m_len) > NFS_MAXPATHLEN) {
+ mp->m_len = NFS_MAXPATHLEN-len;
+ len = NFS_MAXPATHLEN;
+ } else
+ len += mp->m_len;
+ ivp->iov_base = mtod(mp, caddr_t);
+ ivp->iov_len = mp->m_len;
+ i++;
+ ivp++;
+ }
+ uiop->uio_iov = iv;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = 0;
+ uiop->uio_resid = len;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly)) {
+ m_freem(mp3);
+ nfsm_reply(0);
+ }
+ if (vp->v_type != VLNK) {
+ error = EINVAL;
+ goto out;
+ }
+ nqsrv_getl(vp, NQL_READ);
+ error = VOP_READLINK(vp, uiop, cred);
+out:
+ vput(vp);
+ if (error)
+ m_freem(mp3);
+ nfsm_reply(NFSX_UNSIGNED);
+ if (uiop->uio_resid > 0) {
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len);
+ }
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(len);
+ mb->m_next = mp3;
+ nfsm_srvdone;
+}
+
+/*
+ * nfs read service
+ */
+nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *iv;
+ struct iovec *iv2;
+ register struct mbuf *m;
+ register struct nfsv2_fattr *fp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, i, cnt, len, left, siz, tlen;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct mbuf *m2;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ struct vattr va, *vap = &va;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ off = (off_t)fxdr_unsigned(u_long, *tl);
+ } else {
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ fxdr_hyper(tl, &off);
+ }
+ nfsm_srvstrsiz(cnt, NFS_MAXDATA);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_READ);
+ if ((error = nfsrv_access(vp, VREAD, cred, rdonly, nfsd->nd_procp)) &&
+ (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp))) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ if (off >= vap->va_size)
+ cnt = 0;
+ else if ((off + cnt) > vap->va_size)
+ cnt = nfsm_rndup(vap->va_size - off);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL)+NFSX_UNSIGNED+nfsm_rndup(cnt));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ len = left = cnt;
+ if (cnt > 0) {
+ /*
+ * Generate the mbuf list with the uio_iov ref. to it.
+ */
+ i = 0;
+ m = m2 = mb;
+ MALLOC(iv, struct iovec *,
+ ((NFS_MAXDATA+MLEN-1)/MLEN) * sizeof (struct iovec),
+ M_TEMP, M_WAITOK);
+ iv2 = iv;
+ while (left > 0) {
+ siz = min(M_TRAILINGSPACE(m), left);
+ if (siz > 0) {
+ m->m_len += siz;
+ iv->iov_base = bpos;
+ iv->iov_len = siz;
+ iv++;
+ i++;
+ left -= siz;
+ }
+ if (left > 0) {
+ MGET(m, M_WAIT, MT_DATA);
+ MCLGET(m, M_WAIT);
+ m->m_len = 0;
+ m2->m_next = m;
+ m2 = m;
+ bpos = mtod(m, caddr_t);
+ }
+ }
+ uiop->uio_iov = iv2;
+ uiop->uio_iovcnt = i;
+ uiop->uio_offset = off;
+ uiop->uio_resid = cnt;
+ uiop->uio_rw = UIO_READ;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ error = VOP_READ(vp, uiop, IO_NODELOCKED, cred);
+ off = uiop->uio_offset;
+ FREE((caddr_t)iv2, M_TEMP);
+ if (error || (error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp))) {
+ m_freem(mreq);
+ vput(vp);
+ nfsm_reply(0);
+ }
+ } else
+ uiop->uio_resid = 0;
+ vput(vp);
+ nfsm_srvfillattr;
+ len -= uiop->uio_resid;
+ tlen = nfsm_rndup(len);
+ if (cnt != tlen || tlen != len)
+ nfsm_adj(mb, cnt-tlen, tlen-len);
+ *tl = txdr_unsigned(len);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs write service
+ */
+nfsrv_write(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct iovec *ivp;
+ register struct mbuf *mp;
+ register struct nfsv2_fattr *fp;
+ struct iovec iv[NFS_MAXIOVEC];
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, siz, len, xfer;
+ int ioflags = IO_SYNC | IO_NODELOCKED;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io, *uiop = &io;
+ off_t off;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ off = (off_t)fxdr_unsigned(u_long, *++tl);
+ tl += 2;
+ } else {
+ fxdr_hyper(tl, &off);
+ tl += 2;
+ if (fxdr_unsigned(u_long, *tl++))
+ ioflags |= IO_APPEND;
+ }
+ len = fxdr_unsigned(long, *tl);
+ if (len > NFS_MAXDATA || len <= 0) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ if (dpos == (mtod(md, caddr_t)+md->m_len)) {
+ mp = md->m_next;
+ if (mp == NULL) {
+ error = EBADRPC;
+ nfsm_reply(0);
+ }
+ } else {
+ mp = md;
+ siz = dpos-mtod(mp, caddr_t);
+ mp->m_len -= siz;
+ NFSMADV(mp, siz);
+ }
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type != VREG) {
+ error = (vp->v_type == VDIR) ? EISDIR : EACCES;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = 0;
+ uiop->uio_rw = UIO_WRITE;
+ uiop->uio_segflg = UIO_SYSSPACE;
+ uiop->uio_procp = (struct proc *)0;
+ /*
+ * Do up to NFS_MAXIOVEC mbufs of write each iteration of the
+ * loop until done.
+ */
+ while (len > 0 && uiop->uio_resid == 0) {
+ ivp = iv;
+ siz = 0;
+ uiop->uio_iov = ivp;
+ uiop->uio_iovcnt = 0;
+ uiop->uio_offset = off;
+ while (len > 0 && uiop->uio_iovcnt < NFS_MAXIOVEC && mp != NULL) {
+ ivp->iov_base = mtod(mp, caddr_t);
+ if (len < mp->m_len)
+ ivp->iov_len = xfer = len;
+ else
+ ivp->iov_len = xfer = mp->m_len;
+#ifdef notdef
+ /* Not Yet .. */
+ if (M_HASCL(mp) && (((u_long)ivp->iov_base) & CLOFSET) == 0)
+ ivp->iov_op = NULL; /* what should it be ?? */
+ else
+ ivp->iov_op = NULL;
+#endif
+ uiop->uio_iovcnt++;
+ ivp++;
+ len -= xfer;
+ siz += xfer;
+ mp = mp->m_next;
+ }
+ if (len > 0 && mp == NULL) {
+ error = EBADRPC;
+ vput(vp);
+ nfsm_reply(0);
+ }
+ uiop->uio_resid = siz;
+ if (error = VOP_WRITE(vp, uiop, ioflags, cred)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ off = uiop->uio_offset;
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ if (nfsd->nd_nqlflag != NQL_NOVAL) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ txdr_hyper(&vap->va_filerev, tl);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * nfs create service
+ * now does a truncate to 0 length via. setattr if it already exists
+ */
+nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct nfsv2_fattr *fp;
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_sattr *sp;
+ register u_long *tl;
+ struct nameidata nd;
+ register caddr_t cp;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdev, cache, len, tsize;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ nd.ni_cnd.cn_nameiop = 0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ VATTR_NULL(vap);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ /*
+ * Iff doesn't exist, create it
+ * otherwise just truncate to 0 length
+ * should I set the mode too ??
+ */
+ if (nd.ni_vp == NULL) {
+ vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
+ if (vap->va_type == VNON)
+ vap->va_type = VREG;
+ vap->va_mode = nfstov_mode(sp->sa_mode);
+ if (nfsd->nd_nqlflag == NQL_NOVAL)
+ rdev = fxdr_unsigned(long, sp->sa_nfssize);
+ else
+ rdev = fxdr_unsigned(long, sp->sa_nqrdev);
+ if (vap->va_type == VREG || vap->va_type == VSOCK) {
+ vrele(nd.ni_startdir);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ } else if (vap->va_type == VCHR || vap->va_type == VBLK ||
+ vap->va_type == VFIFO) {
+ if (vap->va_type == VCHR && rdev == 0xffffffff)
+ vap->va_type = VFIFO;
+ if (vap->va_type == VFIFO) {
+#ifndef FIFO
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+#endif /* FIFO */
+ } else if (error = suser(cred, (u_short *)0)) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ goto out;
+ } else
+ vap->va_rdev = (dev_t)rdev;
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) {
+ vrele(nd.ni_startdir);
+ nfsm_reply(0);
+ }
+ nd.ni_cnd.cn_nameiop = LOOKUP;
+ nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART);
+ nd.ni_cnd.cn_proc = nfsd->nd_procp;
+ nd.ni_cnd.cn_cred = nfsd->nd_procp->p_ucred;
+ if (error = lookup(&nd)) {
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ }
+ FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ if (nd.ni_cnd.cn_flags & ISSYMLINK) {
+ vrele(nd.ni_dvp);
+ vput(nd.ni_vp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ error = EINVAL;
+ nfsm_reply(0);
+ }
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ vput(nd.ni_dvp);
+ error = ENXIO;
+ goto out;
+ }
+ vp = nd.ni_vp;
+ } else {
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ vp = nd.ni_vp;
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nfsd->nd_nqlflag == NQL_NOVAL) {
+ tsize = fxdr_unsigned(long, sp->sa_nfssize);
+ if (tsize != -1)
+ vap->va_size = (u_quad_t)tsize;
+ else
+ vap->va_size = -1;
+ } else
+ fxdr_hyper(&sp->sa_nqsize, &vap->va_size);
+ if (vap->va_size != -1) {
+ if (error = nfsrv_access(vp, VWRITE, cred,
+ (nd.ni_cnd.cn_flags & RDONLY), nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ nqsrv_getl(vp, NQL_WRITE);
+ if (error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ }
+ }
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags)
+ vrele(nd.ni_startdir);
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vput(nd.ni_vp);
+ return (error);
+
+out:
+ vrele(nd.ni_startdir);
+ free(nd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+}
+
+/*
+ * nfs remove service
+ */
+nfsrv_remove(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type == VDIR &&
+ (error = suser(cred, (u_short *)0)))
+ goto out;
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT) {
+ error = EBUSY;
+ goto out;
+ }
+ if (vp->v_flag & VTEXT)
+ (void) vnode_pager_uncache(vp);
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs rename service
+ */
+nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len, len2;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct nameidata fromnd, tond;
+ struct vnode *fvp, *tvp, *tdvp;
+ nfsv2fh_t fnfh, tnfh;
+ fhandle_t *ffhp, *tfhp;
+ u_quad_t frev;
+ uid_t saved_uid;
+
+ ffhp = &fnfh.fh_generic;
+ tfhp = &tnfh.fh_generic;
+ fromnd.ni_cnd.cn_nameiop = 0;
+ tond.ni_cnd.cn_nameiop = 0;
+ nfsm_srvmtofh(ffhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ /*
+ * Remember our original uid so that we can reset cr_uid before
+ * the second nfs_namei() call, in case it is remapped.
+ */
+ saved_uid = cred->cr_uid;
+ fromnd.ni_cnd.cn_cred = cred;
+ fromnd.ni_cnd.cn_nameiop = DELETE;
+ fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART;
+ if (error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp))
+ nfsm_reply(0);
+ fvp = fromnd.ni_vp;
+ nfsm_srvmtofh(tfhp);
+ nfsm_strsiz(len2, NFS_MAXNAMLEN);
+ cred->cr_uid = saved_uid;
+ tond.ni_cnd.cn_cred = cred;
+ tond.ni_cnd.cn_nameiop = RENAME;
+ tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART;
+ if (error = nfs_namei(&tond, tfhp, len2, nfsd->nd_slp, nam, &md,
+ &dpos, nfsd->nd_procp)) {
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ goto out1;
+ }
+ tdvp = tond.ni_dvp;
+ tvp = tond.ni_vp;
+ if (tvp != NULL) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = EISDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ if (tvp->v_type == VDIR && tvp->v_mountedhere) {
+ error = EXDEV;
+ goto out;
+ }
+ }
+ if (fvp->v_type == VDIR && fvp->v_mountedhere) {
+ error = EBUSY;
+ goto out;
+ }
+ if (fvp->v_mount != tdvp->v_mount) {
+ error = EXDEV;
+ goto out;
+ }
+ if (fvp == tdvp)
+ error = EINVAL;
+ /*
+ * If source is the same as the destination (that is the
+ * same vnode with the same name in the same directory),
+ * then there is nothing to do.
+ */
+ if (fvp == tvp && fromnd.ni_dvp == tdvp &&
+ fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen &&
+ !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr,
+ fromnd.ni_cnd.cn_namelen))
+ error = -1;
+out:
+ if (!error) {
+ nqsrv_getl(fromnd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(tdvp, NQL_WRITE);
+ if (tvp)
+ nqsrv_getl(tvp, NQL_WRITE);
+ error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
+ tond.ni_dvp, tond.ni_vp, &tond.ni_cnd);
+ } else {
+ VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd);
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+out1:
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ nfsm_reply(0);
+ return (error);
+
+nfsmout:
+ if (tond.ni_cnd.cn_nameiop || tond.ni_cnd.cn_flags) {
+ vrele(tond.ni_startdir);
+ FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI);
+ }
+ if (fromnd.ni_cnd.cn_nameiop || fromnd.ni_cnd.cn_flags) {
+ vrele(fromnd.ni_startdir);
+ FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI);
+ VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ }
+ return (error);
+}
+
+/*
+ * nfs link service
+ */
+nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct nameidata nd;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp, *xp;
+ nfsv2fh_t nfh, dnfh;
+ fhandle_t *fhp, *dfhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ dfhp = &dnfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvmtofh(dfhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ if (vp->v_type == VDIR && (error = suser(cred, (u_short *)0)))
+ goto out1;
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, dfhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out1;
+ xp = nd.ni_vp;
+ if (xp != NULL) {
+ error = EEXIST;
+ goto out;
+ }
+ xp = nd.ni_dvp;
+ if (vp->v_mount != xp->v_mount)
+ error = EXDEV;
+out:
+ if (!error) {
+ nqsrv_getl(vp, NQL_WRITE);
+ nqsrv_getl(xp, NQL_WRITE);
+ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ }
+out1:
+ vrele(vp);
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs symbolic link service
+ */
+nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ struct nameidata nd;
+ register struct vattr *vap = &va;
+ register u_long *tl;
+ register long t1;
+ struct nfsv2_sattr *sp;
+ caddr_t bpos;
+ struct uio io;
+ struct iovec iv;
+ int error = 0, cache, len, len2;
+ char *pathcp, *cp2;
+ struct mbuf *mb, *mreq;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ pathcp = (char *)0;
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ goto out;
+ nfsm_strsiz(len2, NFS_MAXPATHLEN);
+ MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK);
+ iv.iov_base = pathcp;
+ iv.iov_len = len2;
+ io.uio_resid = len2;
+ io.uio_offset = 0;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ nfsm_mtouio(&io, len2);
+ nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ *(pathcp + len2) = '\0';
+ if (nd.ni_vp) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(nd.ni_vp);
+ error = EEXIST;
+ goto out;
+ }
+ VATTR_NULL(vap);
+ vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode);
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp);
+out:
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ nfsm_reply(0);
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+}
+
+/*
+ * nfs mkdir service
+ */
+nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ struct vattr va;
+ register struct vattr *vap = &va;
+ register struct nfsv2_fattr *fp;
+ struct nameidata nd;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = CREATE;
+ nd.ni_cnd.cn_flags = LOCKPARENT;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ VATTR_NULL(vap);
+ vap->va_type = VDIR;
+ vap->va_mode = nfstov_mode(*tl++);
+ vp = nd.ni_vp;
+ if (vp != NULL) {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vrele(vp);
+ error = EEXIST;
+ nfsm_reply(0);
+ }
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ if (error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ bzero((caddr_t)fhp, sizeof(nfh));
+ fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ if (error = VFS_VPTOFH(vp, &fhp->fh_fid)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_FH+NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfhtom(fhp);
+ nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL));
+ nfsm_srvfillattr;
+ return (error);
+nfsmout:
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ if (nd.ni_vp)
+ vrele(nd.ni_vp);
+ return (error);
+}
+
+/*
+ * nfs rmdir service
+ */
+nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, cache, len;
+ char *cp2;
+ struct mbuf *mb, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct nameidata nd;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_srvstrsiz(len, NFS_MAXNAMLEN);
+ nd.ni_cnd.cn_cred = cred;
+ nd.ni_cnd.cn_nameiop = DELETE;
+ nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
+ if (error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos,
+ nfsd->nd_procp))
+ nfsm_reply(0);
+ vp = nd.ni_vp;
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
+ /*
+ * No rmdir "." please.
+ */
+ if (nd.ni_dvp == vp) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * The root of a mounted filesystem cannot be deleted.
+ */
+ if (vp->v_flag & VROOT)
+ error = EBUSY;
+out:
+ if (!error) {
+ nqsrv_getl(nd.ni_dvp, NQL_WRITE);
+ nqsrv_getl(vp, NQL_WRITE);
+ error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd);
+ } else {
+ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd);
+ if (nd.ni_dvp == nd.ni_vp)
+ vrele(nd.ni_dvp);
+ else
+ vput(nd.ni_dvp);
+ vput(vp);
+ }
+ nfsm_reply(0);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs readdir service
+ * - mallocs what it thinks is enough to read
+ * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
+ * - calls VOP_READDIR()
+ * - loops around building the reply
+ * if the output generated exceeds count break out of loop
+ * The nfsm_clget macro is used here so that the reply will be packed
+ * tightly in mbuf clusters.
+ * - it only knows that it has encountered eof when the VOP_READDIR()
+ * reads nothing
+ * - as such one readdir rpc will return eof false although you are there
+ * and then the next will return eof
+ * - it trims out records with d_fileno == 0
+ * this doesn't matter for Unix clients, but they might confuse clients
+ * for other os'.
+ * NB: It is tempting to set eof to true if the VOP_READDIR() reads less
+ * than requested, but this may not apply to all filesystems. For
+ * example, client NFS does not { although it is never remote mounted
+ * anyhow }
+ * The alternate call nqnfsrv_readdirlook() does lookups as well.
+ * PS: The NFS protocol spec. does not clarify what the "count" byte
+ * argument is a count of.. just name strings and file id's or the
+ * entire reply rpc or ...
+ * I tried just file name and id sizes and it confused the Sun client,
+ * so I am using the full rpc size now. The "paranoia.." comment refers
+ * to including the status longwords that are not a part of the dir.
+ * "entry" structures, but are in the rpc.
+ */
+struct flrep {
+ u_long fl_cachable;
+ u_long fl_duration;
+ u_long fl_frev[2];
+ nfsv2fh_t fl_nfh;
+ u_long fl_fattr[NFSX_NQFATTR / sizeof (u_long)];
+};
+
+nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ int len, nlen, rem, xfer, tsiz, i, error = 0;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (off_t)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2*NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3*NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+ len += (4*NFSX_UNSIGNED + nlen + rem);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register char *bp, *be;
+ register struct mbuf *mp;
+ register struct dirent *dp;
+ register caddr_t cp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2, *mreq, *mp2;
+ char *cpos, *cend, *cp2, *rbuf;
+ struct vnode *vp, *nvp;
+ struct flrep fl;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct uio io;
+ struct iovec iv;
+ struct vattr va, *vap = &va;
+ struct nfsv2_fattr *fp;
+ int len, nlen, rem, xfer, tsiz, i, error = 0, duration2, cache2;
+ int siz, cnt, fullsiz, eofflag, rdonly, cache;
+ u_quad_t frev, frev2;
+ u_long on, off, toff;
+
+ fhp = &nfh.fh_generic;
+ nfsm_srvmtofh(fhp);
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ toff = fxdr_unsigned(u_long, *tl++);
+ off = (toff & ~(NFS_DIRBLKSIZ-1));
+ on = (toff & (NFS_DIRBLKSIZ-1));
+ cnt = fxdr_unsigned(int, *tl++);
+ duration2 = fxdr_unsigned(int, *tl);
+ siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1));
+ if (cnt > NFS_MAXREADDIR)
+ siz = NFS_MAXREADDIR;
+ fullsiz = siz;
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ nqsrv_getl(vp, NQL_READ);
+ if (error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp)) {
+ vput(vp);
+ nfsm_reply(0);
+ }
+ VOP_UNLOCK(vp);
+ MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
+again:
+ iv.iov_base = rbuf;
+ iv.iov_len = fullsiz;
+ io.uio_iov = &iv;
+ io.uio_iovcnt = 1;
+ io.uio_offset = (off_t)off;
+ io.uio_resid = fullsiz;
+ io.uio_segflg = UIO_SYSSPACE;
+ io.uio_rw = UIO_READ;
+ io.uio_procp = (struct proc *)0;
+ error = VOP_READDIR(vp, &io, cred);
+ off = (u_long)io.uio_offset;
+ if (error) {
+ vrele(vp);
+ free((caddr_t)rbuf, M_TEMP);
+ nfsm_reply(0);
+ }
+ if (io.uio_resid < fullsiz)
+ eofflag = 0;
+ else
+ eofflag = 1;
+ if (io.uio_resid) {
+ siz -= io.uio_resid;
+
+ /*
+ * If nothing read, return eof
+ * rpc reply
+ */
+ if (siz == 0) {
+ vrele(vp);
+ nfsm_reply(2 * NFSX_UNSIGNED);
+ nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
+ *tl++ = nfs_false;
+ *tl = nfs_true;
+ FREE((caddr_t)rbuf, M_TEMP);
+ return (0);
+ }
+ }
+
+ /*
+ * Check for degenerate cases of nothing useful read.
+ * If so go try again
+ */
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ while (cpos < cend && dp->d_fileno == 0) {
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ if (cpos >= cend) {
+ toff = off;
+ siz = fullsiz;
+ on = 0;
+ goto again;
+ }
+
+ cpos = rbuf + on;
+ cend = rbuf + siz;
+ dp = (struct dirent *)cpos;
+ len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */
+ nfsm_reply(siz);
+ mp = mp2 = mb;
+ bp = bpos;
+ be = bp + M_TRAILINGSPACE(mp);
+
+ /* Loop through the records and build reply */
+ while (cpos < cend) {
+ if (dp->d_fileno != 0) {
+ nlen = dp->d_namlen;
+ rem = nfsm_rndup(nlen)-nlen;
+
+ /*
+ * For readdir_and_lookup get the vnode using
+ * the file number.
+ */
+ if (VFS_VGET(vp->v_mount, dp->d_fileno, &nvp))
+ goto invalid;
+ bzero((caddr_t)&fl.fl_nfh, sizeof (nfsv2fh_t));
+ fl.fl_nfh.fh_generic.fh_fsid =
+ nvp->v_mount->mnt_stat.f_fsid;
+ if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) {
+ vput(nvp);
+ goto invalid;
+ }
+ if (duration2) {
+ (void) nqsrv_getlease(nvp, &duration2, NQL_READ,
+ nfsd, nam, &cache2, &frev2, cred);
+ fl.fl_duration = txdr_unsigned(duration2);
+ fl.fl_cachable = txdr_unsigned(cache2);
+ txdr_hyper(&frev2, fl.fl_frev);
+ } else
+ fl.fl_duration = 0;
+ if (VOP_GETATTR(nvp, vap, cred, nfsd->nd_procp)) {
+ vput(nvp);
+ goto invalid;
+ }
+ vput(nvp);
+ fp = (struct nfsv2_fattr *)&fl.fl_fattr;
+ nfsm_srvfillattr;
+ len += (4*NFSX_UNSIGNED + nlen + rem + NFSX_FH
+ + NFSX_NQFATTR);
+ if (len > cnt) {
+ eofflag = 0;
+ break;
+ }
+ /*
+ * Build the directory record xdr from
+ * the dirent entry.
+ */
+ nfsm_clget;
+ *tl = nfs_true;
+ bp += NFSX_UNSIGNED;
+
+ /*
+ * For readdir_and_lookup copy the stuff out.
+ */
+ xfer = sizeof (struct flrep);
+ cp = (caddr_t)&fl;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ nfsm_clget;
+ *tl = txdr_unsigned(dp->d_fileno);
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ *tl = txdr_unsigned(nlen);
+ bp += NFSX_UNSIGNED;
+
+ /* And loop around copying the name */
+ xfer = nlen;
+ cp = dp->d_name;
+ while (xfer > 0) {
+ nfsm_clget;
+ if ((bp+xfer) > be)
+ tsiz = be-bp;
+ else
+ tsiz = xfer;
+ bcopy(cp, bp, tsiz);
+ bp += tsiz;
+ xfer -= tsiz;
+ if (xfer > 0)
+ cp += tsiz;
+ }
+ /* And null pad to a long boundary */
+ for (i = 0; i < rem; i++)
+ *bp++ = '\0';
+ nfsm_clget;
+
+ /* Finish off the record */
+ toff += dp->d_reclen;
+ *tl = txdr_unsigned(toff);
+ bp += NFSX_UNSIGNED;
+ } else
+invalid:
+ toff += dp->d_reclen;
+ cpos += dp->d_reclen;
+ dp = (struct dirent *)cpos;
+ }
+ vrele(vp);
+ nfsm_clget;
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ nfsm_clget;
+ if (eofflag)
+ *tl = nfs_true;
+ else
+ *tl = nfs_false;
+ bp += NFSX_UNSIGNED;
+ if (mp != mb) {
+ if (bp < be)
+ mp->m_len = bp - mtod(mp, caddr_t);
+ } else
+ mp->m_len += bp - bpos;
+ FREE(rbuf, M_TEMP);
+ nfsm_srvdone;
+}
+
+/*
+ * nfs statfs service
+ */
+nfsrv_statfs(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ register struct statfs *sf;
+ register struct nfsv2_statfs *sfp;
+ register u_long *tl;
+ register long t1;
+ caddr_t bpos;
+ int error = 0, rdonly, cache, isnq;
+ char *cp2;
+ struct mbuf *mb, *mb2, *mreq;
+ struct vnode *vp;
+ nfsv2fh_t nfh;
+ fhandle_t *fhp;
+ struct statfs statfs;
+ u_quad_t frev;
+
+ fhp = &nfh.fh_generic;
+ isnq = (nfsd->nd_nqlflag != NQL_NOVAL);
+ nfsm_srvmtofh(fhp);
+ if (error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly))
+ nfsm_reply(0);
+ sf = &statfs;
+ error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp);
+ vput(vp);
+ nfsm_reply(NFSX_STATFS(isnq));
+ nfsm_build(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq));
+ sfp->sf_tsize = txdr_unsigned(NFS_MAXDGRAMDATA);
+ sfp->sf_bsize = txdr_unsigned(sf->f_bsize);
+ sfp->sf_blocks = txdr_unsigned(sf->f_blocks);
+ sfp->sf_bfree = txdr_unsigned(sf->f_bfree);
+ sfp->sf_bavail = txdr_unsigned(sf->f_bavail);
+ if (isnq) {
+ sfp->sf_files = txdr_unsigned(sf->f_files);
+ sfp->sf_ffree = txdr_unsigned(sf->f_ffree);
+ }
+ nfsm_srvdone;
+}
+
+/*
+ * Null operation, used by clients to ping server
+ */
+/* ARGSUSED */
+nfsrv_null(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error = VNOVAL, cache;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * No operation, used for obsolete procedures
+ */
+/* ARGSUSED */
+nfsrv_noop(nfsd, mrep, md, dpos, cred, nam, mrq)
+ struct nfsd *nfsd;
+ struct mbuf *mrep, *md;
+ caddr_t dpos;
+ struct ucred *cred;
+ struct mbuf *nam, **mrq;
+{
+ caddr_t bpos;
+ int error, cache;
+ struct mbuf *mb, *mreq;
+ u_quad_t frev;
+
+ if (nfsd->nd_repstat)
+ error = nfsd->nd_repstat;
+ else
+ error = EPROCUNAVAIL;
+ nfsm_reply(0);
+ return (error);
+}
+
+/*
+ * Perform access checking for vnodes obtained from file handles that would
+ * refer to files already opened by a Unix client. You cannot just use
+ * vn_writechk() and VOP_ACCESS() for two reasons.
+ * 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
+ * 2 - The owner is to be given access irrespective of mode bits so that
+ * processes that chmod after opening a file don't break. I don't like
+ * this because it opens a security hole, but since the nfs server opens
+ * a security hole the size of a barn door anyhow, what the heck.
+ */
+nfsrv_access(vp, flags, cred, rdonly, p)
+ register struct vnode *vp;
+ int flags;
+ register struct ucred *cred;
+ int rdonly;
+ struct proc *p;
+{
+ struct vattr vattr;
+ int error;
+ if (flags & VWRITE) {
+ /* Just vn_writechk() changed to check rdonly */
+ /*
+ * Disallow write attempts on read-only file systems;
+ * unless the file is a socket or a block or character
+ * device resident on the file system.
+ */
+ if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) {
+ switch (vp->v_type) {
+ case VREG: case VDIR: case VLNK:
+ return (EROFS);
+ }
+ }
+ /*
+ * If there's shared text associated with
+ * the inode, try to free it up once. If
+ * we fail, we can't allow writing.
+ */
+ if ((vp->v_flag & VTEXT) && !vnode_pager_uncache(vp))
+ return (ETXTBSY);
+ }
+ if (error = VOP_GETATTR(vp, &vattr, cred, p))
+ return (error);
+ if ((error = VOP_ACCESS(vp, flags, cred, p)) &&
+ cred->cr_uid != vattr.va_uid)
+ return (error);
+ return (0);
+}
diff --git a/sys/nfsserver/nfs_srvcache.c b/sys/nfsserver/nfs_srvcache.c
new file mode 100644
index 0000000..63d8bb7
--- /dev/null
+++ b/sys/nfsserver/nfs_srvcache.c
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs_srvcache.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Reference: Chet Juszczak, "Improving the Performance and Correctness
+ * of an NFS Server", in Proc. Winter 1989 USENIX Conference,
+ * pages 53-63. San Diego, February 1989.
+ */
+#include <sys/param.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/nfsm_subs.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nqnfs.h>
+
+long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ;
+
+#define NFSRCHASH(xid) (((xid) + ((xid) >> 24)) & rheadhash)
+static struct nfsrvcache *nfsrvlruhead, **nfsrvlrutail = &nfsrvlruhead;
+static struct nfsrvcache **rheadhtbl;
+static u_long rheadhash;
+
+#define TRUE 1
+#define FALSE 0
+
+#define NETFAMILY(rp) \
+ (((rp)->rc_flag & RC_INETADDR) ? AF_INET : AF_ISO)
+
+/*
+ * Static array that defines which nfs rpc's are nonidempotent
+ */
+int nonidempotent[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+};
+
+/* True iff the rpc reply is an nfs status ONLY! */
+static int repliesstatus[NFS_NPROCS] = {
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+ TRUE,
+ TRUE,
+ TRUE,
+ FALSE,
+ TRUE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ TRUE,
+};
+
+/*
+ * Initialize the server request cache list
+ */
+nfsrv_initcache()
+{
+
+ rheadhtbl = hashinit(desirednfsrvcache, M_NFSD, &rheadhash);
+}
+
+/*
+ * Look for the request in the cache
+ * If found then
+ * return action and optionally reply
+ * else
+ * insert it in the cache
+ *
+ * The rules are as follows:
+ * - if in progress, return DROP request
+ * - if completed within DELAY of the current time, return DROP it
+ * - if completed a longer time ago return REPLY if the reply was cached or
+ * return DOIT
+ * Update/add new request at end of lru list
+ */
+nfsrv_getcache(nam, nd, repp)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ struct mbuf **repp;
+{
+ register struct nfsrvcache *rp, *rq, **rpp;
+ struct mbuf *mb;
+ struct sockaddr_in *saddr;
+ caddr_t bpos;
+ int ret;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return (RC_DOIT);
+ rpp = &rheadhtbl[NFSRCHASH(nd->nd_retxid)];
+loop:
+ for (rp = *rpp; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* If not at end of LRU chain, move it there */
+ if (rp->rc_next) {
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ /* and replace at end of it */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ }
+ if (rp->rc_state == RC_UNUSED)
+ panic("nfsrv cache");
+ if (rp->rc_state == RC_INPROG) {
+ nfsstats.srvcache_inproghits++;
+ ret = RC_DROPIT;
+ } else if (rp->rc_flag & RC_REPSTATUS) {
+ nfsstats.srvcache_nonidemdonehits++;
+ nfs_rephead(0, nd, rp->rc_status,
+ 0, (u_quad_t *)0, repp, &mb, &bpos);
+ ret = RC_REPLY;
+ } else if (rp->rc_flag & RC_REPMBUF) {
+ nfsstats.srvcache_nonidemdonehits++;
+ *repp = m_copym(rp->rc_reply, 0, M_COPYALL,
+ M_WAIT);
+ ret = RC_REPLY;
+ } else {
+ nfsstats.srvcache_idemdonehits++;
+ rp->rc_state = RC_INPROG;
+ ret = RC_DOIT;
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (ret);
+ }
+ }
+ nfsstats.srvcache_misses++;
+ if (numnfsrvcache < desirednfsrvcache) {
+ rp = (struct nfsrvcache *)malloc((u_long)sizeof *rp,
+ M_NFSD, M_WAITOK);
+ bzero((char *)rp, sizeof *rp);
+ numnfsrvcache++;
+ rp->rc_flag = RC_LOCKED;
+ } else {
+ rp = nfsrvlruhead;
+ while ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ rp = nfsrvlruhead;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ /* remove from hash chain */
+ if (rq = rp->rc_forw)
+ rq->rc_back = rp->rc_back;
+ *rp->rc_back = rq;
+ /* remove from LRU chain */
+ *rp->rc_prev = rp->rc_next;
+ rp->rc_next->rc_prev = rp->rc_prev;
+ if (rp->rc_flag & RC_REPMBUF)
+ m_freem(rp->rc_reply);
+ if (rp->rc_flag & RC_NAM)
+ MFREE(rp->rc_nam, mb);
+ rp->rc_flag &= (RC_LOCKED | RC_WANTED);
+ }
+ /* place at end of LRU list */
+ rp->rc_next = NULL;
+ rp->rc_prev = nfsrvlrutail;
+ *nfsrvlrutail = rp;
+ nfsrvlrutail = &rp->rc_next;
+ rp->rc_state = RC_INPROG;
+ rp->rc_xid = nd->nd_retxid;
+ saddr = mtod(nam, struct sockaddr_in *);
+ switch (saddr->sin_family) {
+ case AF_INET:
+ rp->rc_flag |= RC_INETADDR;
+ rp->rc_inetaddr = saddr->sin_addr.s_addr;
+ break;
+ case AF_ISO:
+ default:
+ rp->rc_flag |= RC_NAM;
+ rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT);
+ break;
+ };
+ rp->rc_proc = nd->nd_procnum;
+ /* insert into hash chain */
+ if (rq = *rpp)
+ rq->rc_back = &rp->rc_forw;
+ rp->rc_forw = rq;
+ rp->rc_back = rpp;
+ *rpp = rp;
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return (RC_DOIT);
+}
+
+/*
+ * Update a request cache entry after the rpc has been done
+ */
+void
+nfsrv_updatecache(nam, nd, repvalid, repmbuf)
+ struct mbuf *nam;
+ register struct nfsd *nd;
+ int repvalid;
+ struct mbuf *repmbuf;
+{
+ register struct nfsrvcache *rp;
+
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ return;
+loop:
+ for (rp = rheadhtbl[NFSRCHASH(nd->nd_retxid)]; rp; rp = rp->rc_forw) {
+ if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc &&
+ netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) {
+ if ((rp->rc_flag & RC_LOCKED) != 0) {
+ rp->rc_flag |= RC_WANTED;
+ (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0);
+ goto loop;
+ }
+ rp->rc_flag |= RC_LOCKED;
+ rp->rc_state = RC_DONE;
+ /*
+ * If we have a valid reply update status and save
+ * the reply for non-idempotent rpc's.
+ */
+ if (repvalid && nonidempotent[nd->nd_procnum]) {
+ if (repliesstatus[nd->nd_procnum]) {
+ rp->rc_status = nd->nd_repstat;
+ rp->rc_flag |= RC_REPSTATUS;
+ } else {
+ rp->rc_reply = m_copym(repmbuf,
+ 0, M_COPYALL, M_WAIT);
+ rp->rc_flag |= RC_REPMBUF;
+ }
+ }
+ rp->rc_flag &= ~RC_LOCKED;
+ if (rp->rc_flag & RC_WANTED) {
+ rp->rc_flag &= ~RC_WANTED;
+ wakeup((caddr_t)rp);
+ }
+ return;
+ }
+ }
+}
+
+/*
+ * Clean out the cache. Called when the last nfsd terminates.
+ */
+void
+nfsrv_cleancache()
+{
+ register struct nfsrvcache *rp, *nextrp;
+
+ for (rp = nfsrvlruhead; rp; rp = nextrp) {
+ nextrp = rp->rc_next;
+ free(rp, M_NFSD);
+ }
+ bzero((char *)rheadhtbl, (rheadhash + 1) * sizeof(void *));
+ nfsrvlruhead = NULL;
+ nfsrvlrutail = &nfsrvlruhead;
+ numnfsrvcache = 0;
+}
diff --git a/sys/nfsserver/nfs_srvsock.c b/sys/nfsserver/nfs_srvsock.c
new file mode 100644
index 0000000..cf88ed3
--- /dev/null
+++ b/sys/nfsserver/nfs_srvsock.c
@@ -0,0 +1,1990 @@
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs_socket.c 8.3 (Berkeley) 1/12/94
+ */
+
+/*
+ * Socket operations for use by nfs
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/vnode.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+#include <sys/tprintf.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfsrtt.h>
+#include <nfs/nqnfs.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Estimate rto for an nfs rpc sent via. an unreliable datagram.
+ * Use the mean and mean deviation of rtt for the appropriate type of rpc
+ * for the frequent rpcs and a default for the others.
+ * The justification for doing "other" this way is that these rpcs
+ * happen so infrequently that timer est. would probably be stale.
+ * Also, since many of these rpcs are
+ * non-idempotent, a conservative timeout is desired.
+ * getattr, lookup - A+2D
+ * read, write - A+4D
+ * other - nm_timeo
+ */
+#define NFS_RTO(n, t) \
+ ((t) == 0 ? (n)->nm_timeo : \
+ ((t) < 3 ? \
+ (((((n)->nm_srtt[t-1] + 3) >> 2) + (n)->nm_sdrtt[t-1] + 1) >> 1) : \
+ ((((n)->nm_srtt[t-1] + 7) >> 3) + (n)->nm_sdrtt[t-1] + 1)))
+#define NFS_SRTT(r) (r)->r_nmp->nm_srtt[proct[(r)->r_procnum] - 1]
+#define NFS_SDRTT(r) (r)->r_nmp->nm_sdrtt[proct[(r)->r_procnum] - 1]
+/*
+ * External data, mostly RPC constants in XDR form
+ */
+extern u_long rpc_reply, rpc_msgdenied, rpc_mismatch, rpc_vers, rpc_auth_unix,
+ rpc_msgaccepted, rpc_call, rpc_autherr, rpc_rejectedcred,
+ rpc_auth_kerb;
+extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers;
+extern time_t nqnfsstarttime;
+extern int nonidempotent[NFS_NPROCS];
+
+/*
+ * Maps errno values to nfs error numbers.
+ * Use NFSERR_IO as the catch all for ones not specifically defined in
+ * RFC 1094.
+ */
+static int nfsrv_errmap[ELAST] = {
+ NFSERR_PERM, NFSERR_NOENT, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_NXIO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_ACCES, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_EXIST, NFSERR_IO, NFSERR_NODEV, NFSERR_NOTDIR,
+ NFSERR_ISDIR, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_FBIG, NFSERR_NOSPC, NFSERR_IO, NFSERR_ROFS,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_NAMETOL, NFSERR_IO, NFSERR_IO,
+ NFSERR_NOTEMPTY, NFSERR_IO, NFSERR_IO, NFSERR_DQUOT, NFSERR_STALE,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO, NFSERR_IO,
+ NFSERR_IO,
+};
+
+/*
+ * Defines which timer to use for the procnum.
+ * 0 - default
+ * 1 - getattr
+ * 2 - lookup
+ * 3 - read
+ * 4 - write
+ */
+static int proct[NFS_NPROCS] = {
+ 0, 1, 0, 0, 2, 3, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0,
+};
+
+/*
+ * There is a congestion window for outstanding rpcs maintained per mount
+ * point. The cwnd size is adjusted in roughly the way that:
+ * Van Jacobson, Congestion avoidance and Control, In "Proceedings of
+ * SIGCOMM '88". ACM, August 1988.
+ * describes for TCP. The cwnd size is chopped in half on a retransmit timeout
+ * and incremented by 1/cwnd when each rpc reply is received and a full cwnd
+ * of rpcs is in progress.
+ * (The sent count and cwnd are scaled for integer arith.)
+ * Variants of "slow start" were tried and were found to be too much of a
+ * performance hit (ave. rtt 3 times larger),
+ * I suspect due to the large rtt that nfs rpcs have.
+ */
+#define NFS_CWNDSCALE 256
+#define NFS_MAXCWND (NFS_CWNDSCALE * 32)
+static int nfs_backoff[8] = { 2, 4, 8, 16, 32, 64, 128, 256, };
+int nfs_sbwait();
+void nfs_disconnect(), nfs_realign(), nfsrv_wakenfsd(), nfs_sndunlock();
+void nfs_rcvunlock(), nqnfs_serverd(), nqnfs_clientlease();
+struct mbuf *nfsm_rpchead();
+int nfsrtton = 0;
+struct nfsrtt nfsrtt;
+struct nfsd nfsd_head;
+
+int nfsrv_null(),
+ nfsrv_getattr(),
+ nfsrv_setattr(),
+ nfsrv_lookup(),
+ nfsrv_readlink(),
+ nfsrv_read(),
+ nfsrv_write(),
+ nfsrv_create(),
+ nfsrv_remove(),
+ nfsrv_rename(),
+ nfsrv_link(),
+ nfsrv_symlink(),
+ nfsrv_mkdir(),
+ nfsrv_rmdir(),
+ nfsrv_readdir(),
+ nfsrv_statfs(),
+ nfsrv_noop(),
+ nqnfsrv_readdirlook(),
+ nqnfsrv_getlease(),
+ nqnfsrv_vacated(),
+ nqnfsrv_access();
+
+int (*nfsrv_procs[NFS_NPROCS])() = {
+ nfsrv_null,
+ nfsrv_getattr,
+ nfsrv_setattr,
+ nfsrv_noop,
+ nfsrv_lookup,
+ nfsrv_readlink,
+ nfsrv_read,
+ nfsrv_noop,
+ nfsrv_write,
+ nfsrv_create,
+ nfsrv_remove,
+ nfsrv_rename,
+ nfsrv_link,
+ nfsrv_symlink,
+ nfsrv_mkdir,
+ nfsrv_rmdir,
+ nfsrv_readdir,
+ nfsrv_statfs,
+ nqnfsrv_readdirlook,
+ nqnfsrv_getlease,
+ nqnfsrv_vacated,
+ nfsrv_noop,
+ nqnfsrv_access,
+};
+
+struct nfsreq nfsreqh;
+
+/*
+ * Initialize sockets and congestion for a new NFS connection.
+ * We do not free the sockaddr if error.
+ */
+nfs_connect(nmp, rep)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+{
+ register struct socket *so;
+ int s, error, rcvreserve, sndreserve;
+ struct sockaddr *saddr;
+ struct sockaddr_in *sin;
+ struct mbuf *m;
+ u_short tport;
+
+ nmp->nm_so = (struct socket *)0;
+ saddr = mtod(nmp->nm_nam, struct sockaddr *);
+ if (error = socreate(saddr->sa_family,
+ &nmp->nm_so, nmp->nm_sotype, nmp->nm_soproto))
+ goto bad;
+ so = nmp->nm_so;
+ nmp->nm_soflags = so->so_proto->pr_flags;
+
+ /*
+ * Some servers require that the client port be a reserved port number.
+ */
+ if (saddr->sa_family == AF_INET && (nmp->nm_flag & NFSMNT_RESVPORT)) {
+ MGET(m, M_WAIT, MT_SONAME);
+ sin = mtod(m, struct sockaddr_in *);
+ sin->sin_len = m->m_len = sizeof (struct sockaddr_in);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ tport = IPPORT_RESERVED - 1;
+ sin->sin_port = htons(tport);
+ while ((error = sobind(so, m)) == EADDRINUSE &&
+ --tport > IPPORT_RESERVED / 2)
+ sin->sin_port = htons(tport);
+ m_freem(m);
+ if (error)
+ goto bad;
+ }
+
+ /*
+ * Protocols that do not require connections may be optionally left
+ * unconnected for servers that reply from a port other than NFS_PORT.
+ */
+ if (nmp->nm_flag & NFSMNT_NOCONN) {
+ if (nmp->nm_soflags & PR_CONNREQUIRED) {
+ error = ENOTCONN;
+ goto bad;
+ }
+ } else {
+ if (error = soconnect(so, nmp->nm_nam))
+ goto bad;
+
+ /*
+ * Wait for the connection to complete. Cribbed from the
+ * connect system call but with the wait timing out so
+ * that interruptible mounts don't hang here for a long time.
+ */
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ (void) tsleep((caddr_t)&so->so_timeo, PSOCK,
+ "nfscon", 2 * hz);
+ if ((so->so_state & SS_ISCONNECTING) &&
+ so->so_error == 0 && rep &&
+ (error = nfs_sigintr(nmp, rep, rep->r_procp))) {
+ so->so_state &= ~SS_ISCONNECTING;
+ splx(s);
+ goto bad;
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ goto bad;
+ }
+ splx(s);
+ }
+ if (nmp->nm_flag & (NFSMNT_SOFT | NFSMNT_INT)) {
+ so->so_rcv.sb_timeo = (5 * hz);
+ so->so_snd.sb_timeo = (5 * hz);
+ } else {
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ }
+ if (nmp->nm_sotype == SOCK_DGRAM) {
+ sndreserve = nmp->nm_wsize + NFS_MAXPKTHDR;
+ rcvreserve = nmp->nm_rsize + NFS_MAXPKTHDR;
+ } else if (nmp->nm_sotype == SOCK_SEQPACKET) {
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR) * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR) * 2;
+ } else {
+ if (nmp->nm_sotype != SOCK_STREAM)
+ panic("nfscon sotype");
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ sndreserve = (nmp->nm_wsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ rcvreserve = (nmp->nm_rsize + NFS_MAXPKTHDR + sizeof (u_long))
+ * 2;
+ }
+ if (error = soreserve(so, sndreserve, rcvreserve))
+ goto bad;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+ /* Initialize other non-zero congestion variables */
+ nmp->nm_srtt[0] = nmp->nm_srtt[1] = nmp->nm_srtt[2] = nmp->nm_srtt[3] =
+ nmp->nm_srtt[4] = (NFS_TIMEO << 3);
+ nmp->nm_sdrtt[0] = nmp->nm_sdrtt[1] = nmp->nm_sdrtt[2] =
+ nmp->nm_sdrtt[3] = nmp->nm_sdrtt[4] = 0;
+ nmp->nm_cwnd = NFS_MAXCWND / 2; /* Initial send window */
+ nmp->nm_sent = 0;
+ nmp->nm_timeouts = 0;
+ return (0);
+
+bad:
+ nfs_disconnect(nmp);
+ return (error);
+}
+
+/*
+ * Reconnect routine:
+ * Called when a connection is broken on a reliable protocol.
+ * - clean up the old socket
+ * - nfs_connect() again
+ * - set R_MUSTRESEND for all outstanding requests on mount point
+ * If this fails the mount point is DEAD!
+ * nb: Must be called with the nfs_sndlock() set on the mount point.
+ */
+nfs_reconnect(rep)
+ register struct nfsreq *rep;
+{
+ register struct nfsreq *rp;
+ register struct nfsmount *nmp = rep->r_nmp;
+ int error;
+
+ nfs_disconnect(nmp);
+ while (error = nfs_connect(nmp, rep)) {
+ if (error == EINTR || error == ERESTART)
+ return (EINTR);
+ (void) tsleep((caddr_t)&lbolt, PSOCK, "nfscon", 0);
+ }
+
+ /*
+ * Loop through outstanding request list and fix up all requests
+ * on old socket.
+ */
+ rp = nfsreqh.r_next;
+ while (rp != &nfsreqh) {
+ if (rp->r_nmp == nmp)
+ rp->r_flags |= R_MUSTRESEND;
+ rp = rp->r_next;
+ }
+ return (0);
+}
+
+/*
+ * NFS disconnect. Clean up and unlink.
+ */
+void
+nfs_disconnect(nmp)
+ register struct nfsmount *nmp;
+{
+ register struct socket *so;
+
+ if (nmp->nm_so) {
+ so = nmp->nm_so;
+ nmp->nm_so = (struct socket *)0;
+ soshutdown(so, 2);
+ soclose(so);
+ }
+}
+
+/*
+ * This is the nfs send routine. For connection based socket types, it
+ * must be called with an nfs_sndlock() on the socket.
+ * "rep == NULL" indicates that it has been called from a server.
+ * For the client side:
+ * - return EINTR if the RPC is terminated, 0 otherwise
+ * - set R_MUSTRESEND if the send fails for any reason
+ * - do any cleanup required by recoverable socket errors (???)
+ * For the server side:
+ * - return EINTR or ERESTART if interrupted by a signal
+ * - return EPIPE if a connection is lost for connection based sockets (TCP...)
+ * - do any cleanup required by recoverable socket errors (???)
+ */
+nfs_send(so, nam, top, rep)
+ register struct socket *so;
+ struct mbuf *nam;
+ register struct mbuf *top;
+ struct nfsreq *rep;
+{
+ struct mbuf *sendnam;
+ int error, soflags, flags;
+
+ if (rep) {
+ if (rep->r_flags & R_SOFTTERM) {
+ m_freem(top);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ rep->r_flags |= R_MUSTRESEND;
+ m_freem(top);
+ return (0);
+ }
+ rep->r_flags &= ~R_MUSTRESEND;
+ soflags = rep->r_nmp->nm_soflags;
+ } else
+ soflags = so->so_proto->pr_flags;
+ if ((soflags & PR_CONNREQUIRED) || (so->so_state & SS_ISCONNECTED))
+ sendnam = (struct mbuf *)0;
+ else
+ sendnam = nam;
+ if (so->so_type == SOCK_SEQPACKET)
+ flags = MSG_EOR;
+ else
+ flags = 0;
+
+ error = sosend(so, sendnam, (struct uio *)0, top,
+ (struct mbuf *)0, flags);
+ if (error) {
+ if (rep) {
+ log(LOG_INFO, "nfs send error %d for server %s\n",error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ /*
+ * Deal with errors for the client side.
+ */
+ if (rep->r_flags & R_SOFTTERM)
+ error = EINTR;
+ else
+ rep->r_flags |= R_MUSTRESEND;
+ } else
+ log(LOG_INFO, "nfsd send error %d\n", error);
+
+ /*
+ * Handle any recoverable (soft) socket errors here. (???)
+ */
+ if (error != EINTR && error != ERESTART &&
+ error != EWOULDBLOCK && error != EPIPE)
+ error = 0;
+ }
+ return (error);
+}
+
+/*
+ * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all
+ * done by soreceive(), but for SOCK_STREAM we must deal with the Record
+ * Mark and consolidate the data into a new mbuf list.
+ * nb: Sometimes TCP passes the data up to soreceive() in long lists of
+ * small mbufs.
+ * For SOCK_STREAM we must be very careful to read an entire record once
+ * we have read any of it, even if the system call has been interrupted.
+ */
+nfs_receive(rep, aname, mp)
+ register struct nfsreq *rep;
+ struct mbuf **aname;
+ struct mbuf **mp;
+{
+ register struct socket *so;
+ struct uio auio;
+ struct iovec aio;
+ register struct mbuf *m;
+ struct mbuf *control;
+ u_long len;
+ struct mbuf **getnam;
+ int error, sotype, rcvflg;
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * Set up arguments for soreceive()
+ */
+ *mp = (struct mbuf *)0;
+ *aname = (struct mbuf *)0;
+ sotype = rep->r_nmp->nm_sotype;
+
+ /*
+ * For reliable protocols, lock against other senders/receivers
+ * in case a reconnect is necessary.
+ * For SOCK_STREAM, first get the Record Mark to find out how much
+ * more there is to get.
+ * We must lock the socket against other receivers
+ * until we have an entire rpc request/reply.
+ */
+ if (sotype != SOCK_DGRAM) {
+ if (error = nfs_sndlock(&rep->r_nmp->nm_flag, rep))
+ return (error);
+tryagain:
+ /*
+ * Check for fatal errors and resending request.
+ */
+ /*
+ * Ugh: If a reconnect attempt just happened, nm_so
+ * would have changed. NULL indicates a failed
+ * attempt that has essentially shut down this
+ * mount point.
+ */
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (EINTR);
+ }
+ if ((so = rep->r_nmp->nm_so) == NULL) {
+ if (error = nfs_reconnect(rep)) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ while (rep->r_flags & R_MUSTRESEND) {
+ m = m_copym(rep->r_mreq, 0, M_COPYALL, M_WAIT);
+ nfsstats.rpcretries++;
+ if (error = nfs_send(so, rep->r_nmp->nm_nam, m, rep)) {
+ if (error == EINTR || error == ERESTART ||
+ (error = nfs_reconnect(rep))) {
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ return (error);
+ }
+ goto tryagain;
+ }
+ }
+ nfs_sndunlock(&rep->r_nmp->nm_flag);
+ if (sotype == SOCK_STREAM) {
+ aio.iov_base = (caddr_t) &len;
+ aio.iov_len = sizeof(u_long);
+ auio.uio_iov = &aio;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof(u_long);
+ auio.uio_procp = p;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0, &auio,
+ (struct mbuf **)0, (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ sizeof(u_long) - auio.uio_resid,
+ sizeof(u_long),
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ if (error)
+ goto errout;
+ len = ntohl(len) & ~0x80000000;
+ /*
+ * This is SERIOUS! We are out of sync with the sender
+ * and forcing a disconnect/reconnect is all I can do.
+ */
+ if (len > NFS_MAXPACKET) {
+ log(LOG_ERR, "%s (%d) from nfs server %s\n",
+ "impossible packet length",
+ len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EFBIG;
+ goto errout;
+ }
+ auio.uio_resid = len;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, (struct mbuf **)0, &rcvflg);
+ } while (error == EWOULDBLOCK || error == EINTR ||
+ error == ERESTART);
+ if (!error && auio.uio_resid > 0) {
+ log(LOG_INFO,
+ "short receive (%d/%d) from nfs server %s\n",
+ len - auio.uio_resid, len,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = EPIPE;
+ }
+ } else {
+ /*
+ * NB: Since uio_resid is big, MSG_WAITALL is ignored
+ * and soreceive() will return when it has either a
+ * control msg or a data msg.
+ * We have no use for control msg., but must grab them
+ * and then throw them away so we know what is going
+ * on.
+ */
+ auio.uio_resid = len = 100000000; /* Anything Big */
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, (struct mbuf **)0,
+ &auio, mp, &control, &rcvflg);
+ if (control)
+ m_freem(control);
+ if (error == EWOULDBLOCK && rep) {
+ if (rep->r_flags & R_SOFTTERM)
+ return (EINTR);
+ }
+ } while (error == EWOULDBLOCK ||
+ (!error && *mp == NULL && control));
+ if ((rcvflg & MSG_EOR) == 0)
+ printf("Egad!!\n");
+ if (!error && *mp == NULL)
+ error = EPIPE;
+ len -= auio.uio_resid;
+ }
+errout:
+ if (error && error != EINTR && error != ERESTART) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ if (error != EPIPE)
+ log(LOG_INFO,
+ "receive error %d from nfs server %s\n",
+ error,
+ rep->r_nmp->nm_mountp->mnt_stat.f_mntfromname);
+ error = nfs_sndlock(&rep->r_nmp->nm_flag, rep);
+ if (!error)
+ error = nfs_reconnect(rep);
+ if (!error)
+ goto tryagain;
+ }
+ } else {
+ if ((so = rep->r_nmp->nm_so) == NULL)
+ return (EACCES);
+ if (so->so_state & SS_ISCONNECTED)
+ getnam = (struct mbuf **)0;
+ else
+ getnam = aname;
+ auio.uio_resid = len = 1000000;
+ auio.uio_procp = p;
+ do {
+ rcvflg = 0;
+ error = soreceive(so, getnam, &auio, mp,
+ (struct mbuf **)0, &rcvflg);
+ if (error == EWOULDBLOCK &&
+ (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ } while (error == EWOULDBLOCK);
+ len -= auio.uio_resid;
+ }
+ if (error) {
+ m_freem(*mp);
+ *mp = (struct mbuf *)0;
+ }
+ /*
+ * Search for any mbufs that are not a multiple of 4 bytes long
+ * or with m_data not longword aligned.
+ * These could cause pointer alignment problems, so copy them to
+ * well aligned mbufs.
+ */
+ nfs_realign(*mp, 5 * NFSX_UNSIGNED);
+ return (error);
+}
+
+/*
+ * Implement receipt of reply on a socket.
+ * We must search through the list of received datagrams matching them
+ * with outstanding requests using the xid, until ours is found.
+ */
+/* ARGSUSED */
+nfs_reply(myrep)
+ struct nfsreq *myrep;
+{
+ register struct nfsreq *rep;
+ register struct nfsmount *nmp = myrep->r_nmp;
+ register long t1;
+ struct mbuf *mrep, *nam, *md;
+ u_long rxid, *tl;
+ caddr_t dpos, cp2;
+ int error;
+
+ /*
+ * Loop around until we get our own reply
+ */
+ for (;;) {
+ /*
+ * Lock against other receivers so that I don't get stuck in
+ * sbwait() after someone else has received my reply for me.
+ * Also necessary for connection based protocols to avoid
+ * race conditions during a reconnect.
+ */
+ if (error = nfs_rcvlock(myrep))
+ return (error);
+ /* Already received, bye bye */
+ if (myrep->r_mrep != NULL) {
+ nfs_rcvunlock(&nmp->nm_flag);
+ return (0);
+ }
+ /*
+ * Get the next Rpc reply off the socket
+ */
+ error = nfs_receive(myrep, &nam, &mrep);
+ nfs_rcvunlock(&nmp->nm_flag);
+ if (error) {
+
+ /*
+ * Ignore routing errors on connectionless protocols??
+ */
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error)) {
+ nmp->nm_so->so_error = 0;
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+ return (error);
+ }
+ if (nam)
+ m_freem(nam);
+
+ /*
+ * Get the xid and check that it is an rpc reply
+ */
+ md = mrep;
+ dpos = mtod(md, caddr_t);
+ nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED);
+ rxid = *tl++;
+ if (*tl != rpc_reply) {
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ if (nqnfs_callback(nmp, mrep, md, dpos))
+ nfsstats.rpcinvalid++;
+ } else {
+ nfsstats.rpcinvalid++;
+ m_freem(mrep);
+ }
+nfsmout:
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ continue;
+ }
+
+ /*
+ * Loop through the request list to match up the reply
+ * Iff no match, just drop the datagram
+ */
+ rep = nfsreqh.r_next;
+ while (rep != &nfsreqh) {
+ if (rep->r_mrep == NULL && rxid == rep->r_xid) {
+ /* Found it.. */
+ rep->r_mrep = mrep;
+ rep->r_md = md;
+ rep->r_dpos = dpos;
+ if (nfsrtton) {
+ struct rttl *rt;
+
+ rt = &nfsrtt.rttl[nfsrtt.pos];
+ rt->proc = rep->r_procnum;
+ rt->rto = NFS_RTO(nmp, proct[rep->r_procnum]);
+ rt->sent = nmp->nm_sent;
+ rt->cwnd = nmp->nm_cwnd;
+ rt->srtt = nmp->nm_srtt[proct[rep->r_procnum] - 1];
+ rt->sdrtt = nmp->nm_sdrtt[proct[rep->r_procnum] - 1];
+ rt->fsid = nmp->nm_mountp->mnt_stat.f_fsid;
+ rt->tstamp = time;
+ if (rep->r_flags & R_TIMING)
+ rt->rtt = rep->r_rtt;
+ else
+ rt->rtt = 1000000;
+ nfsrtt.pos = (nfsrtt.pos + 1) % NFSRTTLOGSIZ;
+ }
+ /*
+ * Update congestion window.
+ * Do the additive increase of
+ * one rpc/rtt.
+ */
+ if (nmp->nm_cwnd <= nmp->nm_sent) {
+ nmp->nm_cwnd +=
+ (NFS_CWNDSCALE * NFS_CWNDSCALE +
+ (nmp->nm_cwnd >> 1)) / nmp->nm_cwnd;
+ if (nmp->nm_cwnd > NFS_MAXCWND)
+ nmp->nm_cwnd = NFS_MAXCWND;
+ }
+ rep->r_flags &= ~R_SENT;
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ /*
+ * Update rtt using a gain of 0.125 on the mean
+ * and a gain of 0.25 on the deviation.
+ */
+ if (rep->r_flags & R_TIMING) {
+ /*
+ * Since the timer resolution of
+ * NFS_HZ is so course, it can often
+ * result in r_rtt == 0. Since
+ * r_rtt == N means that the actual
+ * rtt is between N+dt and N+2-dt ticks,
+ * add 1.
+ */
+ t1 = rep->r_rtt + 1;
+ t1 -= (NFS_SRTT(rep) >> 3);
+ NFS_SRTT(rep) += t1;
+ if (t1 < 0)
+ t1 = -t1;
+ t1 -= (NFS_SDRTT(rep) >> 2);
+ NFS_SDRTT(rep) += t1;
+ }
+ nmp->nm_timeouts = 0;
+ break;
+ }
+ rep = rep->r_next;
+ }
+ /*
+ * If not matched to a request, drop it.
+ * If it's mine, get out.
+ */
+ if (rep == &nfsreqh) {
+ nfsstats.rpcunexpected++;
+ m_freem(mrep);
+ } else if (rep == myrep) {
+ if (rep->r_mrep == NULL)
+ panic("nfsreply nil");
+ return (0);
+ }
+ if (myrep->r_flags & R_GETONEREP)
+ return (0);
+ }
+}
+
+/*
+ * nfs_request - goes something like this
+ * - fill in request struct
+ * - links it into list
+ * - calls nfs_send() for first transmit
+ * - calls nfs_receive() to get reply
+ * - break down rpc header and return with nfs reply pointed to
+ * by mrep or error
+ * nb: always frees up mreq mbuf list
+ */
+nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp)
+ struct vnode *vp;
+ struct mbuf *mrest;
+ int procnum;
+ struct proc *procp;
+ struct ucred *cred;
+ struct mbuf **mrp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+{
+ register struct mbuf *m, *mrep;
+ register struct nfsreq *rep;
+ register u_long *tl;
+ register int i;
+ struct nfsmount *nmp;
+ struct mbuf *md, *mheadend;
+ struct nfsreq *reph;
+ struct nfsnode *np;
+ time_t reqtime, waituntil;
+ caddr_t dpos, cp2;
+ int t1, nqlflag, cachable, s, error = 0, mrest_len, auth_len, auth_type;
+ int trylater_delay = NQ_TRYLATERDEL, trylater_cnt = 0, failed_auth = 0;
+ u_long xid;
+ u_quad_t frev;
+ char *auth_str;
+
+ nmp = VFSTONFS(vp->v_mount);
+ MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK);
+ rep->r_nmp = nmp;
+ rep->r_vp = vp;
+ rep->r_procp = procp;
+ rep->r_procnum = procnum;
+ i = 0;
+ m = mrest;
+ while (m) {
+ i += m->m_len;
+ m = m->m_next;
+ }
+ mrest_len = i;
+
+ /*
+ * Get the RPC header with authorization.
+ */
+kerbauth:
+ auth_str = (char *)0;
+ if (nmp->nm_flag & NFSMNT_KERB) {
+ if (failed_auth) {
+ error = nfs_getauth(nmp, rep, cred, &auth_type,
+ &auth_str, &auth_len);
+ if (error) {
+ free((caddr_t)rep, M_NFSREQ);
+ m_freem(mrest);
+ return (error);
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ auth_len = 5 * NFSX_UNSIGNED;
+ }
+ } else {
+ auth_type = RPCAUTH_UNIX;
+ if (cred->cr_ngroups < 1)
+ panic("nfsreq nogrps");
+ auth_len = ((((cred->cr_ngroups - 1) > nmp->nm_numgrps) ?
+ nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
+ 5 * NFSX_UNSIGNED;
+ }
+ m = nfsm_rpchead(cred, (nmp->nm_flag & NFSMNT_NQNFS), procnum,
+ auth_type, auth_len, auth_str, mrest, mrest_len, &mheadend, &xid);
+ if (auth_str)
+ free(auth_str, M_TEMP);
+
+ /*
+ * For stream protocols, insert a Sun RPC Record Mark.
+ */
+ if (nmp->nm_sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 |
+ (m->m_pkthdr.len - NFSX_UNSIGNED));
+ }
+ rep->r_mreq = m;
+ rep->r_xid = xid;
+tryagain:
+ if (nmp->nm_flag & NFSMNT_SOFT)
+ rep->r_retry = nmp->nm_retry;
+ else
+ rep->r_retry = NFS_MAXREXMIT + 1; /* past clip limit */
+ rep->r_rtt = rep->r_rexmit = 0;
+ if (proct[procnum] > 0)
+ rep->r_flags = R_TIMING;
+ else
+ rep->r_flags = 0;
+ rep->r_mrep = NULL;
+
+ /*
+ * Do the client side RPC.
+ */
+ nfsstats.rpcrequests++;
+ /*
+ * Chain request into list of outstanding requests. Be sure
+ * to put it LAST so timer finds oldest requests first.
+ */
+ s = splsoftclock();
+ reph = &nfsreqh;
+ reph->r_prev->r_next = rep;
+ rep->r_prev = reph->r_prev;
+ reph->r_prev = rep;
+ rep->r_next = reph;
+
+ /* Get send time for nqnfs */
+ reqtime = time.tv_sec;
+
+ /*
+ * If backing off another request or avoiding congestion, don't
+ * send this one now but let timer do it. If not timing a request,
+ * do it now.
+ */
+ if (nmp->nm_so && (nmp->nm_sotype != SOCK_DGRAM ||
+ (nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ nmp->nm_sent < nmp->nm_cwnd)) {
+ splx(s);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ error = nfs_sndlock(&nmp->nm_flag, rep);
+ if (!error) {
+ m = m_copym(m, 0, M_COPYALL, M_WAIT);
+ error = nfs_send(nmp->nm_so, nmp->nm_nam, m, rep);
+ if (nmp->nm_soflags & PR_CONNREQUIRED)
+ nfs_sndunlock(&nmp->nm_flag);
+ }
+ if (!error && (rep->r_flags & R_MUSTRESEND) == 0) {
+ nmp->nm_sent += NFS_CWNDSCALE;
+ rep->r_flags |= R_SENT;
+ }
+ } else {
+ splx(s);
+ rep->r_rtt = -1;
+ }
+
+ /*
+ * Wait for the reply from our send or the timer's.
+ */
+ if (!error || error == EPIPE)
+ error = nfs_reply(rep);
+
+ /*
+ * RPC done, unlink the request.
+ */
+ s = splsoftclock();
+ rep->r_prev->r_next = rep->r_next;
+ rep->r_next->r_prev = rep->r_prev;
+ splx(s);
+
+ /*
+ * Decrement the outstanding request count.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_SENT; /* paranoia */
+ nmp->nm_sent -= NFS_CWNDSCALE;
+ }
+
+ /*
+ * If there was a successful reply and a tprintf msg.
+ * tprintf a response.
+ */
+ if (!error && (rep->r_flags & R_TPRINTFMSG))
+ nfs_msg(rep->r_procp, nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "is alive again");
+ mrep = rep->r_mrep;
+ md = rep->r_md;
+ dpos = rep->r_dpos;
+ if (error) {
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * break down the rpc header and check if ok
+ */
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ if (*tl++ == rpc_msgdenied) {
+ if (*tl == rpc_mismatch)
+ error = EOPNOTSUPP;
+ else if ((nmp->nm_flag & NFSMNT_KERB) && *tl++ == rpc_autherr) {
+ if (*tl == rpc_rejectedcred && failed_auth == 0) {
+ failed_auth++;
+ mheadend->m_next = (struct mbuf *)0;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ goto kerbauth;
+ } else
+ error = EAUTH;
+ } else
+ error = EACCES;
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * skip over the auth_verf, someday we may want to cache auth_short's
+ * for nfs_reqhead(), but for now just dump it
+ */
+ if (*++tl != 0) {
+ i = nfsm_rndup(fxdr_unsigned(long, *tl));
+ nfsm_adv(i);
+ }
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ /* 0 == ok */
+ if (*tl == 0) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl != 0) {
+ error = fxdr_unsigned(int, *tl);
+ m_freem(mrep);
+ if ((nmp->nm_flag & NFSMNT_NQNFS) &&
+ error == NQNFS_TRYLATER) {
+ error = 0;
+ waituntil = time.tv_sec + trylater_delay;
+ while (time.tv_sec < waituntil)
+ (void) tsleep((caddr_t)&lbolt,
+ PSOCK, "nqnfstry", 0);
+ trylater_delay *= nfs_backoff[trylater_cnt];
+ if (trylater_cnt < 7)
+ trylater_cnt++;
+ goto tryagain;
+ }
+
+ /*
+ * If the File Handle was stale, invalidate the
+ * lookup cache, just in case.
+ */
+ if (error == ESTALE)
+ cache_purge(vp);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ return (error);
+ }
+
+ /*
+ * For nqnfs, get any lease in reply
+ */
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ if (*tl) {
+ np = VTONFS(vp);
+ nqlflag = fxdr_unsigned(int, *tl);
+ nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED);
+ cachable = fxdr_unsigned(int, *tl++);
+ reqtime += fxdr_unsigned(int, *tl++);
+ if (reqtime > time.tv_sec) {
+ fxdr_hyper(tl, &frev);
+ nqnfs_clientlease(nmp, np, nqlflag,
+ cachable, reqtime, frev);
+ }
+ }
+ }
+ *mrp = mrep;
+ *mdp = md;
+ *dposp = dpos;
+ m_freem(rep->r_mreq);
+ FREE((caddr_t)rep, M_NFSREQ);
+ return (0);
+ }
+ m_freem(mrep);
+ m_freem(rep->r_mreq);
+ free((caddr_t)rep, M_NFSREQ);
+ error = EPROTONOSUPPORT;
+nfsmout:
+ return (error);
+}
+
+/*
+ * Generate the rpc reply header
+ * siz arg. is used to decide if adding a cluster is worthwhile
+ */
+nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp)
+ int siz;
+ struct nfsd *nd;
+ int err;
+ int cache;
+ u_quad_t *frev;
+ struct mbuf **mrq;
+ struct mbuf **mbp;
+ caddr_t *bposp;
+{
+ register u_long *tl;
+ register struct mbuf *mreq;
+ caddr_t bpos;
+ struct mbuf *mb, *mb2;
+
+ MGETHDR(mreq, M_WAIT, MT_DATA);
+ mb = mreq;
+ /*
+ * If this is a big reply, use a cluster else
+ * try and leave leading space for the lower level headers.
+ */
+ siz += RPC_REPLYSIZ;
+ if (siz >= MINCLSIZE) {
+ MCLGET(mreq, M_WAIT);
+ } else
+ mreq->m_data += max_hdr;
+ tl = mtod(mreq, u_long *);
+ mreq->m_len = 6*NFSX_UNSIGNED;
+ bpos = ((caddr_t)tl)+mreq->m_len;
+ *tl++ = nd->nd_retxid;
+ *tl++ = rpc_reply;
+ if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) {
+ *tl++ = rpc_msgdenied;
+ if (err == NQNFS_AUTHERR) {
+ *tl++ = rpc_autherr;
+ *tl = rpc_rejectedcred;
+ mreq->m_len -= NFSX_UNSIGNED;
+ bpos -= NFSX_UNSIGNED;
+ } else {
+ *tl++ = rpc_mismatch;
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2);
+ }
+ } else {
+ *tl++ = rpc_msgaccepted;
+ *tl++ = 0;
+ *tl++ = 0;
+ switch (err) {
+ case EPROGUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROGUNAVAIL);
+ break;
+ case EPROGMISMATCH:
+ *tl = txdr_unsigned(RPC_PROGMISMATCH);
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(2);
+ *tl = txdr_unsigned(2); /* someday 3 */
+ break;
+ case EPROCUNAVAIL:
+ *tl = txdr_unsigned(RPC_PROCUNAVAIL);
+ break;
+ default:
+ *tl = 0;
+ if (err != VNOVAL) {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ if (err)
+ *tl = txdr_unsigned(nfsrv_errmap[err - 1]);
+ else
+ *tl = 0;
+ }
+ break;
+ };
+ }
+
+ /*
+ * For nqnfs, piggyback lease as requested.
+ */
+ if (nd->nd_nqlflag != NQL_NOVAL && err == 0) {
+ if (nd->nd_nqlflag) {
+ nfsm_build(tl, u_long *, 5*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nd->nd_nqlflag);
+ *tl++ = txdr_unsigned(cache);
+ *tl++ = txdr_unsigned(nd->nd_duration);
+ txdr_hyper(frev, tl);
+ } else {
+ if (nd->nd_nqlflag != 0)
+ panic("nqreph");
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ *mrq = mreq;
+ *mbp = mb;
+ *bposp = bpos;
+ if (err != 0 && err != VNOVAL)
+ nfsstats.srvrpc_errs++;
+ return (0);
+}
+
+/*
+ * Nfs timer routine
+ * Scan the nfsreq list and retranmit any requests that have timed out
+ * To avoid retransmission attempts on STREAM sockets (in the future) make
+ * sure to set the r_retry field to 0 (implies nm_retry == 0).
+ */
+void
+nfs_timer(arg)
+ void *arg;
+{
+ register struct nfsreq *rep;
+ register struct mbuf *m;
+ register struct socket *so;
+ register struct nfsmount *nmp;
+ register int timeo;
+ static long lasttime = 0;
+ int s, error;
+
+ s = splnet();
+ for (rep = nfsreqh.r_next; rep != &nfsreqh; rep = rep->r_next) {
+ nmp = rep->r_nmp;
+ if (rep->r_mrep || (rep->r_flags & R_SOFTTERM))
+ continue;
+ if (nfs_sigintr(nmp, rep, rep->r_procp)) {
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (rep->r_rtt >= 0) {
+ rep->r_rtt++;
+ if (nmp->nm_flag & NFSMNT_DUMBTIMR)
+ timeo = nmp->nm_timeo;
+ else
+ timeo = NFS_RTO(nmp, proct[rep->r_procnum]);
+ if (nmp->nm_timeouts > 0)
+ timeo *= nfs_backoff[nmp->nm_timeouts - 1];
+ if (rep->r_rtt <= timeo)
+ continue;
+ if (nmp->nm_timeouts < 8)
+ nmp->nm_timeouts++;
+ }
+ /*
+ * Check for server not responding
+ */
+ if ((rep->r_flags & R_TPRINTFMSG) == 0 &&
+ rep->r_rexmit > nmp->nm_deadthresh) {
+ nfs_msg(rep->r_procp,
+ nmp->nm_mountp->mnt_stat.f_mntfromname,
+ "not responding");
+ rep->r_flags |= R_TPRINTFMSG;
+ }
+ if (rep->r_rexmit >= rep->r_retry) { /* too many */
+ nfsstats.rpctimeouts++;
+ rep->r_flags |= R_SOFTTERM;
+ continue;
+ }
+ if (nmp->nm_sotype != SOCK_DGRAM) {
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ continue;
+ }
+ if ((so = nmp->nm_so) == NULL)
+ continue;
+
+ /*
+ * If there is enough space and the window allows..
+ * Resend it
+ * Set r_rtt to -1 in case we fail to send it now.
+ */
+ rep->r_rtt = -1;
+ if (sbspace(&so->so_snd) >= rep->r_mreq->m_pkthdr.len &&
+ ((nmp->nm_flag & NFSMNT_DUMBTIMR) ||
+ (rep->r_flags & R_SENT) ||
+ nmp->nm_sent < nmp->nm_cwnd) &&
+ (m = m_copym(rep->r_mreq, 0, M_COPYALL, M_DONTWAIT))){
+ if ((nmp->nm_flag & NFSMNT_NOCONN) == 0)
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ (struct mbuf *)0, (struct mbuf *)0);
+ else
+ error = (*so->so_proto->pr_usrreq)(so, PRU_SEND, m,
+ nmp->nm_nam, (struct mbuf *)0);
+ if (error) {
+ if (NFSIGNORE_SOERROR(nmp->nm_soflags, error))
+ so->so_error = 0;
+ } else {
+ /*
+ * Iff first send, start timing
+ * else turn timing off, backoff timer
+ * and divide congestion window by 2.
+ */
+ if (rep->r_flags & R_SENT) {
+ rep->r_flags &= ~R_TIMING;
+ if (++rep->r_rexmit > NFS_MAXREXMIT)
+ rep->r_rexmit = NFS_MAXREXMIT;
+ nmp->nm_cwnd >>= 1;
+ if (nmp->nm_cwnd < NFS_CWNDSCALE)
+ nmp->nm_cwnd = NFS_CWNDSCALE;
+ nfsstats.rpcretries++;
+ } else {
+ rep->r_flags |= R_SENT;
+ nmp->nm_sent += NFS_CWNDSCALE;
+ }
+ rep->r_rtt = 0;
+ }
+ }
+ }
+
+ /*
+ * Call the nqnfs server timer once a second to handle leases.
+ */
+ if (lasttime != time.tv_sec) {
+ lasttime = time.tv_sec;
+ nqnfs_serverd();
+ }
+ splx(s);
+ timeout(nfs_timer, (void *)0, hz / NFS_HZ);
+}
+
+/*
+ * Test for a termination condition pending on the process.
+ * This is used for NFSMNT_INT mounts.
+ */
+nfs_sigintr(nmp, rep, p)
+ struct nfsmount *nmp;
+ struct nfsreq *rep;
+ register struct proc *p;
+{
+
+ if (rep && (rep->r_flags & R_SOFTTERM))
+ return (EINTR);
+ if (!(nmp->nm_flag & NFSMNT_INT))
+ return (0);
+ if (p && p->p_siglist &&
+ (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) &
+ NFSINT_SIGMASK))
+ return (EINTR);
+ return (0);
+}
+
+/*
+ * Lock a socket against others.
+ * Necessary for STREAM sockets to ensure you get an entire rpc request/reply
+ * and also to avoid race conditions between the processes with nfs requests
+ * in progress when a reconnect is necessary.
+ */
+nfs_sndlock(flagp, rep)
+ register int *flagp;
+ struct nfsreq *rep;
+{
+ struct proc *p;
+ int slpflag = 0, slptimeo = 0;
+
+ if (rep) {
+ p = rep->r_procp;
+ if (rep->r_nmp->nm_flag & NFSMNT_INT)
+ slpflag = PCATCH;
+ } else
+ p = (struct proc *)0;
+ while (*flagp & NFSMNT_SNDLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, p))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTSND;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsndlck",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_SNDLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_sndunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_SNDLOCK) == 0)
+ panic("nfs sndunlock");
+ *flagp &= ~NFSMNT_SNDLOCK;
+ if (*flagp & NFSMNT_WANTSND) {
+ *flagp &= ~NFSMNT_WANTSND;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+nfs_rcvlock(rep)
+ register struct nfsreq *rep;
+{
+ register int *flagp = &rep->r_nmp->nm_flag;
+ int slpflag, slptimeo = 0;
+
+ if (*flagp & NFSMNT_INT)
+ slpflag = PCATCH;
+ else
+ slpflag = 0;
+ while (*flagp & NFSMNT_RCVLOCK) {
+ if (nfs_sigintr(rep->r_nmp, rep, rep->r_procp))
+ return (EINTR);
+ *flagp |= NFSMNT_WANTRCV;
+ (void) tsleep((caddr_t)flagp, slpflag | (PZERO - 1), "nfsrcvlk",
+ slptimeo);
+ if (slpflag == PCATCH) {
+ slpflag = 0;
+ slptimeo = 2 * hz;
+ }
+ }
+ *flagp |= NFSMNT_RCVLOCK;
+ return (0);
+}
+
+/*
+ * Unlock the stream socket for others.
+ */
+void
+nfs_rcvunlock(flagp)
+ register int *flagp;
+{
+
+ if ((*flagp & NFSMNT_RCVLOCK) == 0)
+ panic("nfs rcvunlock");
+ *flagp &= ~NFSMNT_RCVLOCK;
+ if (*flagp & NFSMNT_WANTRCV) {
+ *flagp &= ~NFSMNT_WANTRCV;
+ wakeup((caddr_t)flagp);
+ }
+}
+
+/*
+ * Check for badly aligned mbuf data areas and
+ * realign data in an mbuf list by copying the data areas up, as required.
+ */
+void
+nfs_realign(m, hsiz)
+ register struct mbuf *m;
+ int hsiz;
+{
+ register struct mbuf *m2;
+ register int siz, mlen, olen;
+ register caddr_t tcp, fcp;
+ struct mbuf *mnew;
+
+ while (m) {
+ /*
+ * This never happens for UDP, rarely happens for TCP
+ * but frequently happens for iso transport.
+ */
+ if ((m->m_len & 0x3) || (mtod(m, int) & 0x3)) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ if ((int)fcp & 0x3) {
+ m->m_flags &= ~M_PKTHDR;
+ if (m->m_flags & M_EXT)
+ m->m_data = m->m_ext.ext_buf +
+ ((m->m_ext.ext_size - olen) & ~0x3);
+ else
+ m->m_data = m->m_dat;
+ }
+ m->m_len = 0;
+ tcp = mtod(m, caddr_t);
+ mnew = m;
+ m2 = m->m_next;
+
+ /*
+ * If possible, only put the first invariant part
+ * of the RPC header in the first mbuf.
+ */
+ mlen = M_TRAILINGSPACE(m);
+ if (olen <= hsiz && mlen > hsiz)
+ mlen = hsiz;
+
+ /*
+ * Loop through the mbuf list consolidating data.
+ */
+ while (m) {
+ while (olen > 0) {
+ if (mlen == 0) {
+ m2->m_flags &= ~M_PKTHDR;
+ if (m2->m_flags & M_EXT)
+ m2->m_data = m2->m_ext.ext_buf;
+ else
+ m2->m_data = m2->m_dat;
+ m2->m_len = 0;
+ mlen = M_TRAILINGSPACE(m2);
+ tcp = mtod(m2, caddr_t);
+ mnew = m2;
+ m2 = m2->m_next;
+ }
+ siz = min(mlen, olen);
+ if (tcp != fcp)
+ bcopy(fcp, tcp, siz);
+ mnew->m_len += siz;
+ mlen -= siz;
+ olen -= siz;
+ tcp += siz;
+ fcp += siz;
+ }
+ m = m->m_next;
+ if (m) {
+ olen = m->m_len;
+ fcp = mtod(m, caddr_t);
+ }
+ }
+
+ /*
+ * Finally, set m_len == 0 for any trailing mbufs that have
+ * been copied out of.
+ */
+ while (m2) {
+ m2->m_len = 0;
+ m2 = m2->m_next;
+ }
+ return;
+ }
+ m = m->m_next;
+ }
+}
+
+/*
+ * Socket upcall routine for the nfsd sockets.
+ * The caddr_t arg is a pointer to the "struct nfssvc_sock".
+ * Essentially do as much as possible non-blocking, else punt and it will
+ * be called with M_WAIT from an nfsd.
+ */
+void
+nfsrv_rcv(so, arg, waitflag)
+ struct socket *so;
+ caddr_t arg;
+ int waitflag;
+{
+ register struct nfssvc_sock *slp = (struct nfssvc_sock *)arg;
+ register struct mbuf *m;
+ struct mbuf *mp, *nam;
+ struct uio auio;
+ int flags, error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+#ifdef notdef
+ /*
+ * Define this to test for nfsds handling this under heavy load.
+ */
+ if (waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ; goto dorecs;
+ }
+#endif
+ auio.uio_procp = NULL;
+ if (so->so_type == SOCK_STREAM) {
+ /*
+ * If there are already records on the queue, defer soreceive()
+ * to an nfsd so that there is feedback to the TCP layer that
+ * the nfs servers are heavily loaded.
+ */
+ if (slp->ns_rec && waitflag == M_DONTWAIT) {
+ slp->ns_flag |= SLP_NEEDQ;
+ goto dorecs;
+ }
+
+ /*
+ * Do soreceive().
+ */
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp, (struct mbuf **)0, &flags);
+ if (error || mp == (struct mbuf *)0) {
+ if (error == EWOULDBLOCK)
+ slp->ns_flag |= SLP_NEEDQ;
+ else
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ m = mp;
+ if (slp->ns_rawend) {
+ slp->ns_rawend->m_next = m;
+ slp->ns_cc += 1000000000 - auio.uio_resid;
+ } else {
+ slp->ns_raw = m;
+ slp->ns_cc = 1000000000 - auio.uio_resid;
+ }
+ while (m->m_next)
+ m = m->m_next;
+ slp->ns_rawend = m;
+
+ /*
+ * Now try and parse record(s) out of the raw stream data.
+ */
+ if (error = nfsrv_getstream(slp, waitflag)) {
+ if (error == EPERM)
+ slp->ns_flag |= SLP_DISCONN;
+ else
+ slp->ns_flag |= SLP_NEEDQ;
+ }
+ } else {
+ do {
+ auio.uio_resid = 1000000000;
+ flags = MSG_DONTWAIT;
+ error = soreceive(so, &nam, &auio, &mp,
+ (struct mbuf **)0, &flags);
+ if (mp) {
+ nfs_realign(mp, 10 * NFSX_UNSIGNED);
+ if (nam) {
+ m = nam;
+ m->m_next = mp;
+ } else
+ m = mp;
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = m;
+ else
+ slp->ns_rec = m;
+ slp->ns_recend = m;
+ m->m_nextpkt = (struct mbuf *)0;
+ }
+ if (error) {
+ if ((so->so_proto->pr_flags & PR_CONNREQUIRED)
+ && error != EWOULDBLOCK) {
+ slp->ns_flag |= SLP_DISCONN;
+ goto dorecs;
+ }
+ }
+ } while (mp);
+ }
+
+ /*
+ * Now try and process the request records, non-blocking.
+ */
+dorecs:
+ if (waitflag == M_DONTWAIT &&
+ (slp->ns_rec || (slp->ns_flag & (SLP_NEEDQ | SLP_DISCONN))))
+ nfsrv_wakenfsd(slp);
+}
+
+/*
+ * Try and extract an RPC request from the mbuf data list received on a
+ * stream socket. The "waitflag" argument indicates whether or not it
+ * can sleep.
+ */
+nfsrv_getstream(slp, waitflag)
+ register struct nfssvc_sock *slp;
+ int waitflag;
+{
+ register struct mbuf *m;
+ register char *cp1, *cp2;
+ register int len;
+ struct mbuf *om, *m2, *recm;
+ u_long recmark;
+
+ if (slp->ns_flag & SLP_GETSTREAM)
+ panic("nfs getstream");
+ slp->ns_flag |= SLP_GETSTREAM;
+ for (;;) {
+ if (slp->ns_reclen == 0) {
+ if (slp->ns_cc < NFSX_UNSIGNED) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ m = slp->ns_raw;
+ if (m->m_len >= NFSX_UNSIGNED) {
+ bcopy(mtod(m, caddr_t), (caddr_t)&recmark, NFSX_UNSIGNED);
+ m->m_data += NFSX_UNSIGNED;
+ m->m_len -= NFSX_UNSIGNED;
+ } else {
+ cp1 = (caddr_t)&recmark;
+ cp2 = mtod(m, caddr_t);
+ while (cp1 < ((caddr_t)&recmark) + NFSX_UNSIGNED) {
+ while (m->m_len == 0) {
+ m = m->m_next;
+ cp2 = mtod(m, caddr_t);
+ }
+ *cp1++ = *cp2++;
+ m->m_data++;
+ m->m_len--;
+ }
+ }
+ slp->ns_cc -= NFSX_UNSIGNED;
+ slp->ns_reclen = ntohl(recmark) & ~0x80000000;
+ if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EPERM);
+ }
+ }
+
+ /*
+ * Now get the record part.
+ */
+ if (slp->ns_cc == slp->ns_reclen) {
+ recm = slp->ns_raw;
+ slp->ns_raw = slp->ns_rawend = (struct mbuf *)0;
+ slp->ns_cc = slp->ns_reclen = 0;
+ } else if (slp->ns_cc > slp->ns_reclen) {
+ len = 0;
+ m = slp->ns_raw;
+ om = (struct mbuf *)0;
+ while (len < slp->ns_reclen) {
+ if ((len + m->m_len) > slp->ns_reclen) {
+ m2 = m_copym(m, 0, slp->ns_reclen - len,
+ waitflag);
+ if (m2) {
+ if (om) {
+ om->m_next = m2;
+ recm = slp->ns_raw;
+ } else
+ recm = m2;
+ m->m_data += slp->ns_reclen - len;
+ m->m_len -= slp->ns_reclen - len;
+ len = slp->ns_reclen;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (EWOULDBLOCK);
+ }
+ } else if ((len + m->m_len) == slp->ns_reclen) {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ recm = slp->ns_raw;
+ om->m_next = (struct mbuf *)0;
+ } else {
+ om = m;
+ len += m->m_len;
+ m = m->m_next;
+ }
+ }
+ slp->ns_raw = m;
+ slp->ns_cc -= len;
+ slp->ns_reclen = 0;
+ } else {
+ slp->ns_flag &= ~SLP_GETSTREAM;
+ return (0);
+ }
+ nfs_realign(recm, 10 * NFSX_UNSIGNED);
+ if (slp->ns_recend)
+ slp->ns_recend->m_nextpkt = recm;
+ else
+ slp->ns_rec = recm;
+ slp->ns_recend = recm;
+ }
+}
+
+/*
+ * Parse an RPC header.
+ */
+nfsrv_dorec(slp, nd)
+ register struct nfssvc_sock *slp;
+ register struct nfsd *nd;
+{
+ register struct mbuf *m;
+ int error;
+
+ if ((slp->ns_flag & SLP_VALID) == 0 ||
+ (m = slp->ns_rec) == (struct mbuf *)0)
+ return (ENOBUFS);
+ if (slp->ns_rec = m->m_nextpkt)
+ m->m_nextpkt = (struct mbuf *)0;
+ else
+ slp->ns_recend = (struct mbuf *)0;
+ if (m->m_type == MT_SONAME) {
+ nd->nd_nam = m;
+ nd->nd_md = nd->nd_mrep = m->m_next;
+ m->m_next = (struct mbuf *)0;
+ } else {
+ nd->nd_nam = (struct mbuf *)0;
+ nd->nd_md = nd->nd_mrep = m;
+ }
+ nd->nd_dpos = mtod(nd->nd_md, caddr_t);
+ if (error = nfs_getreq(nd, TRUE)) {
+ m_freem(nd->nd_nam);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Parse an RPC request
+ * - verify it
+ * - fill in the cred struct.
+ */
+nfs_getreq(nd, has_header)
+ register struct nfsd *nd;
+ int has_header;
+{
+ register int len, i;
+ register u_long *tl;
+ register long t1;
+ struct uio uio;
+ struct iovec iov;
+ caddr_t dpos, cp2;
+ u_long nfsvers, auth_type;
+ int error = 0, nqnfs = 0;
+ struct mbuf *mrep, *md;
+
+ mrep = nd->nd_mrep;
+ md = nd->nd_md;
+ dpos = nd->nd_dpos;
+ if (has_header) {
+ nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED);
+ nd->nd_retxid = *tl++;
+ if (*tl++ != rpc_call) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ } else {
+ nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED);
+ }
+ nd->nd_repstat = 0;
+ if (*tl++ != rpc_vers) {
+ nd->nd_repstat = ERPCMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nfsvers = nfs_vers;
+ if (*tl != nfs_prog) {
+ if (*tl == nqnfs_prog) {
+ nqnfs++;
+ nfsvers = nqnfs_vers;
+ } else {
+ nd->nd_repstat = EPROGUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ }
+ tl++;
+ if (*tl++ != nfsvers) {
+ nd->nd_repstat = EPROGMISMATCH;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ nd->nd_procnum = fxdr_unsigned(u_long, *tl++);
+ if (nd->nd_procnum == NFSPROC_NULL)
+ return (0);
+ if (nd->nd_procnum >= NFS_NPROCS ||
+ (!nqnfs && nd->nd_procnum > NFSPROC_STATFS) ||
+ (*tl != rpc_auth_unix && *tl != rpc_auth_kerb)) {
+ nd->nd_repstat = EPROCUNAVAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ return (0);
+ }
+ auth_type = *tl++;
+ len = fxdr_unsigned(int, *tl++);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+
+ /*
+ * Handle auth_unix or auth_kerb.
+ */
+ if (auth_type == rpc_auth_unix) {
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > NFS_MAXNAMLEN) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_adv(nfsm_rndup(len));
+ nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED);
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_cr.cr_gid = fxdr_unsigned(gid_t, *tl++);
+ len = fxdr_unsigned(int, *tl);
+ if (len < 0 || len > RPCAUTH_UNIXGIDS) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED);
+ for (i = 1; i <= len; i++)
+ if (i < NGROUPS)
+ nd->nd_cr.cr_groups[i] = fxdr_unsigned(gid_t, *tl++);
+ else
+ tl++;
+ nd->nd_cr.cr_ngroups = (len >= NGROUPS) ? NGROUPS : (len + 1);
+ } else if (auth_type == rpc_auth_kerb) {
+ nd->nd_cr.cr_uid = fxdr_unsigned(uid_t, *tl++);
+ nd->nd_authlen = fxdr_unsigned(int, *tl);
+ uio.uio_resid = nfsm_rndup(nd->nd_authlen);
+ if (uio.uio_resid > (len - 2 * NFSX_UNSIGNED)) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ uio.uio_offset = 0;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ iov.iov_base = (caddr_t)nd->nd_authstr;
+ iov.iov_len = RPCAUTH_MAXSIZ;
+ nfsm_mtouio(&uio, uio.uio_resid);
+ nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
+ nd->nd_flag |= NFSD_NEEDAUTH;
+ }
+
+ /*
+ * Do we have any use for the verifier.
+ * According to the "Remote Procedure Call Protocol Spec." it
+ * should be AUTH_NULL, but some clients make it AUTH_UNIX?
+ * For now, just skip over it
+ */
+ len = fxdr_unsigned(int, *++tl);
+ if (len < 0 || len > RPCAUTH_MAXSIZ) {
+ m_freem(mrep);
+ return (EBADRPC);
+ }
+ if (len > 0) {
+ nfsm_adv(nfsm_rndup(len));
+ }
+
+ /*
+ * For nqnfs, get piggybacked lease request.
+ */
+ if (nqnfs && nd->nd_procnum != NQNFSPROC_EVICTED) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_nqlflag = fxdr_unsigned(int, *tl);
+ if (nd->nd_nqlflag) {
+ nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
+ nd->nd_duration = fxdr_unsigned(int, *tl);
+ } else
+ nd->nd_duration = NQ_MINLEASE;
+ } else {
+ nd->nd_nqlflag = NQL_NOVAL;
+ nd->nd_duration = NQ_MINLEASE;
+ }
+ nd->nd_md = md;
+ nd->nd_dpos = dpos;
+ return (0);
+nfsmout:
+ return (error);
+}
+
+/*
+ * Search for a sleeping nfsd and wake it up.
+ * SIDE EFFECT: If none found, set NFSD_CHECKSLP flag, so that one of the
+ * running nfsds will go look for the work in the nfssvc_sock list.
+ */
+void
+nfsrv_wakenfsd(slp)
+ struct nfssvc_sock *slp;
+{
+ register struct nfsd *nd = nfsd_head.nd_next;
+
+ if ((slp->ns_flag & SLP_VALID) == 0)
+ return;
+ while (nd != (struct nfsd *)&nfsd_head) {
+ if (nd->nd_flag & NFSD_WAITING) {
+ nd->nd_flag &= ~NFSD_WAITING;
+ if (nd->nd_slp)
+ panic("nfsd wakeup");
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ wakeup((caddr_t)nd);
+ return;
+ }
+ nd = nd->nd_next;
+ }
+ slp->ns_flag |= SLP_DOREC;
+ nfsd_head.nd_flag |= NFSD_CHECKSLP;
+}
+
+nfs_msg(p, server, msg)
+ struct proc *p;
+ char *server, *msg;
+{
+ tpr_t tpr;
+
+ if (p)
+ tpr = tprintf_open(p);
+ else
+ tpr = NULL;
+ tprintf(tpr, "nfs server %s: %s\n", server, msg);
+ tprintf_close(tpr);
+}
diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c
new file mode 100644
index 0000000..5778f7d
--- /dev/null
+++ b/sys/nfsserver/nfs_srvsubs.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94
+ */
+
+/*
+ * These functions support the macros and help fiddle mbuf chains for
+ * the nfs op functions. They do things like create the rpc header and
+ * copy data between mbuf chains and uio lists.
+ */
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nfs.h>
+#include <nfs/xdr_subs.h>
+#include <nfs/nfsm_subs.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <netinet/in.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ * Data items converted to xdr at startup, since they are constant
+ * This is kinda hokey, but may save a little time doing byte swaps
+ */
+u_long nfs_procids[NFS_NPROCS];
+u_long nfs_xdrneg1;
+u_long rpc_call, rpc_vers, rpc_reply, rpc_msgdenied, rpc_autherr,
+ rpc_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_rejectedcred,
+ rpc_auth_kerb;
+u_long nfs_vers, nfs_prog, nfs_true, nfs_false;
+
+/* And other global data */
+static u_long nfs_xid = 0;
+enum vtype ntov_type[7] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON };
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern struct nfsreq nfsreqh;
+extern int nqnfs_piggy[NFS_NPROCS];
+extern struct nfsrtt nfsrtt;
+extern time_t nqnfsstarttime;
+extern u_long nqnfs_prog, nqnfs_vers;
+extern int nqsrv_clockskew;
+extern int nqsrv_writeslack;
+extern int nqsrv_maxlease;
+
+/*
+ * Create the header for an rpc request packet
+ * The hsiz is the size of the rest of the nfs request header.
+ * (just used to decide if a cluster is a good idea)
+ */
+struct mbuf *
+nfsm_reqh(vp, procid, hsiz, bposp)
+ struct vnode *vp;
+ u_long procid;
+ int hsiz;
+ caddr_t *bposp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ struct mbuf *mb2;
+ struct nfsmount *nmp;
+ int nqflag;
+
+ MGET(mb, M_WAIT, MT_DATA);
+ if (hsiz >= MINCLSIZE)
+ MCLGET(mb, M_WAIT);
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * For NQNFS, add lease request.
+ */
+ if (vp) {
+ nmp = VFSTONFS(vp->v_mount);
+ if (nmp->nm_flag & NFSMNT_NQNFS) {
+ nqflag = NQNFS_NEEDLEASE(vp, procid);
+ if (nqflag) {
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(nqflag);
+ *tl = txdr_unsigned(nmp->nm_leaseterm);
+ } else {
+ nfsm_build(tl, u_long *, NFSX_UNSIGNED);
+ *tl = 0;
+ }
+ }
+ }
+ /* Finally, return values */
+ *bposp = bpos;
+ return (mb);
+}
+
+/*
+ * Build the RPC header and fill in the authorization info.
+ * The authorization string argument is only used when the credentials
+ * come from outside of the kernel.
+ * Returns the head of the mbuf list.
+ */
+struct mbuf *
+nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest,
+ mrest_len, mbp, xidp)
+ register struct ucred *cr;
+ int nqnfs;
+ int procid;
+ int auth_type;
+ int auth_len;
+ char *auth_str;
+ struct mbuf *mrest;
+ int mrest_len;
+ struct mbuf **mbp;
+ u_long *xidp;
+{
+ register struct mbuf *mb;
+ register u_long *tl;
+ register caddr_t bpos;
+ register int i;
+ struct mbuf *mreq, *mb2;
+ int siz, grpsiz, authsiz;
+
+ authsiz = nfsm_rndup(auth_len);
+ if (auth_type == RPCAUTH_NQNFS)
+ authsiz += 2 * NFSX_UNSIGNED;
+ MGETHDR(mb, M_WAIT, MT_DATA);
+ if ((authsiz + 10*NFSX_UNSIGNED) >= MINCLSIZE) {
+ MCLGET(mb, M_WAIT);
+ } else if ((authsiz + 10*NFSX_UNSIGNED) < MHLEN) {
+ MH_ALIGN(mb, authsiz + 10*NFSX_UNSIGNED);
+ } else {
+ MH_ALIGN(mb, 8*NFSX_UNSIGNED);
+ }
+ mb->m_len = 0;
+ mreq = mb;
+ bpos = mtod(mb, caddr_t);
+
+ /*
+ * First the RPC header.
+ */
+ nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED);
+ if (++nfs_xid == 0)
+ nfs_xid++;
+ *tl++ = *xidp = txdr_unsigned(nfs_xid);
+ *tl++ = rpc_call;
+ *tl++ = rpc_vers;
+ if (nqnfs) {
+ *tl++ = txdr_unsigned(NQNFS_PROG);
+ *tl++ = txdr_unsigned(NQNFS_VER1);
+ } else {
+ *tl++ = txdr_unsigned(NFS_PROG);
+ *tl++ = txdr_unsigned(NFS_VER2);
+ }
+ *tl++ = txdr_unsigned(procid);
+
+ /*
+ * And then the authorization cred.
+ */
+ *tl++ = txdr_unsigned(auth_type);
+ *tl = txdr_unsigned(authsiz);
+ switch (auth_type) {
+ case RPCAUTH_UNIX:
+ nfsm_build(tl, u_long *, auth_len);
+ *tl++ = 0; /* stamp ?? */
+ *tl++ = 0; /* NULL hostname */
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl++ = txdr_unsigned(cr->cr_groups[0]);
+ grpsiz = (auth_len >> 2) - 5;
+ *tl++ = txdr_unsigned(grpsiz);
+ for (i = 1; i <= grpsiz; i++)
+ *tl++ = txdr_unsigned(cr->cr_groups[i]);
+ break;
+ case RPCAUTH_NQNFS:
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cr->cr_uid);
+ *tl = txdr_unsigned(auth_len);
+ siz = auth_len;
+ while (siz > 0) {
+ if (M_TRAILINGSPACE(mb) == 0) {
+ MGET(mb2, M_WAIT, MT_DATA);
+ if (siz >= MINCLSIZE)
+ MCLGET(mb2, M_WAIT);
+ mb->m_next = mb2;
+ mb = mb2;
+ mb->m_len = 0;
+ bpos = mtod(mb, caddr_t);
+ }
+ i = min(siz, M_TRAILINGSPACE(mb));
+ bcopy(auth_str, bpos, i);
+ mb->m_len += i;
+ auth_str += i;
+ bpos += i;
+ siz -= i;
+ }
+ if ((siz = (nfsm_rndup(auth_len) - auth_len)) > 0) {
+ for (i = 0; i < siz; i++)
+ *bpos++ = '\0';
+ mb->m_len += siz;
+ }
+ break;
+ };
+ nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_NULL);
+ *tl = 0;
+ mb->m_next = mrest;
+ mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len;
+ mreq->m_pkthdr.rcvif = (struct ifnet *)0;
+ *mbp = mb;
+ return (mreq);
+}
+
+/*
+ * copies mbuf chain to the uio scatter/gather list
+ */
+nfsm_mbuftouio(mrep, uiop, siz, dpos)
+ struct mbuf **mrep;
+ register struct uio *uiop;
+ int siz;
+ caddr_t *dpos;
+{
+ register char *mbufcp, *uiocp;
+ register int xfer, left, len;
+ register struct mbuf *mp;
+ long uiosiz, rem;
+ int error = 0;
+
+ mp = *mrep;
+ mbufcp = *dpos;
+ len = mtod(mp, caddr_t)+mp->m_len-mbufcp;
+ rem = nfsm_rndup(siz)-siz;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EFBIG);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ while (len == 0) {
+ mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ mbufcp = mtod(mp, caddr_t);
+ len = mp->m_len;
+ }
+ xfer = (left > len) ? len : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (mbufcp, uiocp, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(mbufcp, uiocp, xfer);
+ else
+ copyout(mbufcp, uiocp, xfer);
+ left -= xfer;
+ len -= xfer;
+ mbufcp += xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ *dpos = mbufcp;
+ *mrep = mp;
+ if (rem > 0) {
+ if (len < rem)
+ error = nfs_adv(mrep, dpos, rem, len);
+ else
+ *dpos += rem;
+ }
+ return (error);
+}
+
+/*
+ * copies a uio scatter/gather list to an mbuf chain...
+ */
+nfsm_uiotombuf(uiop, mq, siz, bpos)
+ register struct uio *uiop;
+ struct mbuf **mq;
+ int siz;
+ caddr_t *bpos;
+{
+ register char *uiocp;
+ register struct mbuf *mp, *mp2;
+ register int xfer, left, mlen;
+ int uiosiz, clflg, rem;
+ char *cp;
+
+ if (siz > MLEN) /* or should it >= MCLBYTES ?? */
+ clflg = 1;
+ else
+ clflg = 0;
+ rem = nfsm_rndup(siz)-siz;
+ mp = mp2 = *mq;
+ while (siz > 0) {
+ if (uiop->uio_iovcnt <= 0 || uiop->uio_iov == NULL)
+ return (EINVAL);
+ left = uiop->uio_iov->iov_len;
+ uiocp = uiop->uio_iov->iov_base;
+ if (left > siz)
+ left = siz;
+ uiosiz = left;
+ while (left > 0) {
+ mlen = M_TRAILINGSPACE(mp);
+ if (mlen == 0) {
+ MGET(mp, M_WAIT, MT_DATA);
+ if (clflg)
+ MCLGET(mp, M_WAIT);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ mp2 = mp;
+ mlen = M_TRAILINGSPACE(mp);
+ }
+ xfer = (left > mlen) ? mlen : left;
+#ifdef notdef
+ /* Not Yet.. */
+ if (uiop->uio_iov->iov_op != NULL)
+ (*(uiop->uio_iov->iov_op))
+ (uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+#endif
+ if (uiop->uio_segflg == UIO_SYSSPACE)
+ bcopy(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ else
+ copyin(uiocp, mtod(mp, caddr_t)+mp->m_len, xfer);
+ mp->m_len += xfer;
+ left -= xfer;
+ uiocp += xfer;
+ uiop->uio_offset += xfer;
+ uiop->uio_resid -= xfer;
+ }
+ if (uiop->uio_iov->iov_len <= siz) {
+ uiop->uio_iovcnt--;
+ uiop->uio_iov++;
+ } else {
+ uiop->uio_iov->iov_base += uiosiz;
+ uiop->uio_iov->iov_len -= uiosiz;
+ }
+ siz -= uiosiz;
+ }
+ if (rem > 0) {
+ if (rem > M_TRAILINGSPACE(mp)) {
+ MGET(mp, M_WAIT, MT_DATA);
+ mp->m_len = 0;
+ mp2->m_next = mp;
+ }
+ cp = mtod(mp, caddr_t)+mp->m_len;
+ for (left = 0; left < rem; left++)
+ *cp++ = '\0';
+ mp->m_len += rem;
+ *bpos = cp;
+ } else
+ *bpos = mtod(mp, caddr_t)+mp->m_len;
+ *mq = mp;
+ return (0);
+}
+
+/*
+ * Help break down an mbuf chain by setting the first siz bytes contiguous
+ * pointed to by returned val.
+ * This is used by the macros nfsm_dissect and nfsm_dissecton for tough
+ * cases. (The macros use the vars. dpos and dpos2)
+ */
+nfsm_disct(mdp, dposp, siz, left, cp2)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int siz;
+ int left;
+ caddr_t *cp2;
+{
+ register struct mbuf *mp, *mp2;
+ register int siz2, xfer;
+ register caddr_t p;
+
+ mp = *mdp;
+ while (left == 0) {
+ *mdp = mp = mp->m_next;
+ if (mp == NULL)
+ return (EBADRPC);
+ left = mp->m_len;
+ *dposp = mtod(mp, caddr_t);
+ }
+ if (left >= siz) {
+ *cp2 = *dposp;
+ *dposp += siz;
+ } else if (mp->m_next == NULL) {
+ return (EBADRPC);
+ } else if (siz > MHLEN) {
+ panic("nfs S too big");
+ } else {
+ MGET(mp2, M_WAIT, MT_DATA);
+ mp2->m_next = mp->m_next;
+ mp->m_next = mp2;
+ mp->m_len -= left;
+ mp = mp2;
+ *cp2 = p = mtod(mp, caddr_t);
+ bcopy(*dposp, p, left); /* Copy what was left */
+ siz2 = siz-left;
+ p += left;
+ mp2 = mp->m_next;
+ /* Loop around copying up the siz2 bytes */
+ while (siz2 > 0) {
+ if (mp2 == NULL)
+ return (EBADRPC);
+ xfer = (siz2 > mp2->m_len) ? mp2->m_len : siz2;
+ if (xfer > 0) {
+ bcopy(mtod(mp2, caddr_t), p, xfer);
+ NFSMADV(mp2, xfer);
+ mp2->m_len -= xfer;
+ p += xfer;
+ siz2 -= xfer;
+ }
+ if (siz2 > 0)
+ mp2 = mp2->m_next;
+ }
+ mp->m_len = siz;
+ *mdp = mp2;
+ *dposp = mtod(mp2, caddr_t);
+ }
+ return (0);
+}
+
+/*
+ * Advance the position in the mbuf chain.
+ */
+nfs_adv(mdp, dposp, offs, left)
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ int offs;
+ int left;
+{
+ register struct mbuf *m;
+ register int s;
+
+ m = *mdp;
+ s = left;
+ while (s < offs) {
+ offs -= s;
+ m = m->m_next;
+ if (m == NULL)
+ return (EBADRPC);
+ s = m->m_len;
+ }
+ *mdp = m;
+ *dposp = mtod(m, caddr_t)+offs;
+ return (0);
+}
+
+/*
+ * Copy a string into mbufs for the hard cases...
+ */
+nfsm_strtmbuf(mb, bpos, cp, siz)
+ struct mbuf **mb;
+ char **bpos;
+ char *cp;
+ long siz;
+{
+ register struct mbuf *m1, *m2;
+ long left, xfer, len, tlen;
+ u_long *tl;
+ int putsize;
+
+ putsize = 1;
+ m2 = *mb;
+ left = M_TRAILINGSPACE(m2);
+ if (left > 0) {
+ tl = ((u_long *)(*bpos));
+ *tl++ = txdr_unsigned(siz);
+ putsize = 0;
+ left -= NFSX_UNSIGNED;
+ m2->m_len += NFSX_UNSIGNED;
+ if (left > 0) {
+ bcopy(cp, (caddr_t) tl, left);
+ siz -= left;
+ cp += left;
+ m2->m_len += left;
+ left = 0;
+ }
+ }
+ /* Loop around adding mbufs */
+ while (siz > 0) {
+ MGET(m1, M_WAIT, MT_DATA);
+ if (siz > MLEN)
+ MCLGET(m1, M_WAIT);
+ m1->m_len = NFSMSIZ(m1);
+ m2->m_next = m1;
+ m2 = m1;
+ tl = mtod(m1, u_long *);
+ tlen = 0;
+ if (putsize) {
+ *tl++ = txdr_unsigned(siz);
+ m1->m_len -= NFSX_UNSIGNED;
+ tlen = NFSX_UNSIGNED;
+ putsize = 0;
+ }
+ if (siz < m1->m_len) {
+ len = nfsm_rndup(siz);
+ xfer = siz;
+ if (xfer < len)
+ *(tl+(xfer>>2)) = 0;
+ } else {
+ xfer = len = m1->m_len;
+ }
+ bcopy(cp, (caddr_t) tl, xfer);
+ m1->m_len = len+tlen;
+ siz -= xfer;
+ cp += xfer;
+ }
+ *mb = m1;
+ *bpos = mtod(m1, caddr_t)+m1->m_len;
+ return (0);
+}
+
+/*
+ * Called once to initialize data structures...
+ */
+nfs_init()
+{
+ register int i;
+
+ nfsrtt.pos = 0;
+ rpc_vers = txdr_unsigned(RPC_VER2);
+ rpc_call = txdr_unsigned(RPC_CALL);
+ rpc_reply = txdr_unsigned(RPC_REPLY);
+ rpc_msgdenied = txdr_unsigned(RPC_MSGDENIED);
+ rpc_msgaccepted = txdr_unsigned(RPC_MSGACCEPTED);
+ rpc_mismatch = txdr_unsigned(RPC_MISMATCH);
+ rpc_autherr = txdr_unsigned(RPC_AUTHERR);
+ rpc_rejectedcred = txdr_unsigned(AUTH_REJECTCRED);
+ rpc_auth_unix = txdr_unsigned(RPCAUTH_UNIX);
+ rpc_auth_kerb = txdr_unsigned(RPCAUTH_NQNFS);
+ nfs_vers = txdr_unsigned(NFS_VER2);
+ nfs_prog = txdr_unsigned(NFS_PROG);
+ nfs_true = txdr_unsigned(TRUE);
+ nfs_false = txdr_unsigned(FALSE);
+ /* Loop thru nfs procids */
+ for (i = 0; i < NFS_NPROCS; i++)
+ nfs_procids[i] = txdr_unsigned(i);
+ /* Ensure async daemons disabled */
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ nfs_iodwant[i] = (struct proc *)0;
+ TAILQ_INIT(&nfs_bufq);
+ nfs_xdrneg1 = txdr_unsigned(-1);
+ nfs_nhinit(); /* Init the nfsnode table */
+ nfsrv_init(0); /* Init server data structures */
+ nfsrv_initcache(); /* Init the server request cache */
+
+ /*
+ * Initialize the nqnfs server stuff.
+ */
+ if (nqnfsstarttime == 0) {
+ nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease
+ + nqsrv_clockskew + nqsrv_writeslack;
+ NQLOADNOVRAM(nqnfsstarttime);
+ nqnfs_prog = txdr_unsigned(NQNFS_PROG);
+ nqnfs_vers = txdr_unsigned(NQNFS_VER1);
+ nqthead.th_head[0] = &nqthead;
+ nqthead.th_head[1] = &nqthead;
+ nqfhead = hashinit(NQLCHSZ, M_NQLEASE, &nqfheadhash);
+ }
+
+ /*
+ * Initialize reply list and start timer
+ */
+ nfsreqh.r_prev = nfsreqh.r_next = &nfsreqh;
+ nfs_timer();
+}
+
+/*
+ * Attribute cache routines.
+ * nfs_loadattrcache() - loads or updates the cache contents from attributes
+ * that are on the mbuf list
+ * nfs_getattrcache() - returns valid attributes if found in cache, returns
+ * error otherwise
+ */
+
+/*
+ * Load the attribute cache (that lives in the nfsnode entry) with
+ * the values on the mbuf list and
+ * Iff vap not NULL
+ * copy the attributes to *vaper
+ */
+nfs_loadattrcache(vpp, mdp, dposp, vaper)
+ struct vnode **vpp;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct vattr *vaper;
+{
+ register struct vnode *vp = *vpp;
+ register struct vattr *vap;
+ register struct nfsv2_fattr *fp;
+ extern int (**spec_nfsv2nodeop_p)();
+ register struct nfsnode *np, *nq, **nhpp;
+ register long t1;
+ caddr_t dpos, cp2;
+ int error = 0, isnq;
+ struct mbuf *md;
+ enum vtype vtyp;
+ u_short vmode;
+ long rdev;
+ struct timespec mtime;
+ struct vnode *nvp;
+
+ md = *mdp;
+ dpos = *dposp;
+ t1 = (mtod(md, caddr_t) + md->m_len) - dpos;
+ isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS);
+ if (error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2))
+ return (error);
+ fp = (struct nfsv2_fattr *)cp2;
+ vtyp = nfstov_type(fp->fa_type);
+ vmode = fxdr_unsigned(u_short, fp->fa_mode);
+ if (vtyp == VNON || vtyp == VREG)
+ vtyp = IFTOVT(vmode);
+ if (isnq) {
+ rdev = fxdr_unsigned(long, fp->fa_nqrdev);
+ fxdr_nqtime(&fp->fa_nqmtime, &mtime);
+ } else {
+ rdev = fxdr_unsigned(long, fp->fa_nfsrdev);
+ fxdr_nfstime(&fp->fa_nfsmtime, &mtime);
+ }
+ /*
+ * If v_type == VNON it is a new node, so fill in the v_type,
+ * n_mtime fields. Check to see if it represents a special
+ * device, and if so, check for a possible alias. Once the
+ * correct vnode has been obtained, fill in the rest of the
+ * information.
+ */
+ np = VTONFS(vp);
+ if (vp->v_type == VNON) {
+ if (vtyp == VCHR && rdev == 0xffffffff)
+ vp->v_type = vtyp = VFIFO;
+ else
+ vp->v_type = vtyp;
+ if (vp->v_type == VFIFO) {
+#ifdef FIFO
+ extern int (**fifo_nfsv2nodeop_p)();
+ vp->v_op = fifo_nfsv2nodeop_p;
+#else
+ return (EOPNOTSUPP);
+#endif /* FIFO */
+ }
+ if (vp->v_type == VCHR || vp->v_type == VBLK) {
+ vp->v_op = spec_nfsv2nodeop_p;
+ if (nvp = checkalias(vp, (dev_t)rdev, vp->v_mount)) {
+ /*
+ * Discard unneeded vnode, but save its nfsnode.
+ */
+ if (nq = np->n_forw)
+ nq->n_back = np->n_back;
+ *np->n_back = nq;
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased node.
+ */
+ np->n_vnode = nvp;
+ nhpp = (struct nfsnode **)nfs_hash(&np->n_fh);
+ if (nq = *nhpp)
+ nq->n_back = &np->n_forw;
+ np->n_forw = nq;
+ np->n_back = nhpp;
+ *nhpp = np;
+ *vpp = vp = nvp;
+ }
+ }
+ np->n_mtime = mtime.ts_sec;
+ }
+ vap = &np->n_vattr;
+ vap->va_type = vtyp;
+ vap->va_mode = (vmode & 07777);
+ vap->va_nlink = fxdr_unsigned(u_short, fp->fa_nlink);
+ vap->va_uid = fxdr_unsigned(uid_t, fp->fa_uid);
+ vap->va_gid = fxdr_unsigned(gid_t, fp->fa_gid);
+ vap->va_rdev = (dev_t)rdev;
+ vap->va_mtime = mtime;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ if (isnq) {
+ fxdr_hyper(&fp->fa_nqsize, &vap->va_size);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nqblocksize);
+ fxdr_hyper(&fp->fa_nqbytes, &vap->va_bytes);
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nqfileid);
+ fxdr_nqtime(&fp->fa_nqatime, &vap->va_atime);
+ vap->va_flags = fxdr_unsigned(u_long, fp->fa_nqflags);
+ fxdr_nqtime(&fp->fa_nqctime, &vap->va_ctime);
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nqgen);
+ fxdr_hyper(&fp->fa_nqfilerev, &vap->va_filerev);
+ } else {
+ vap->va_size = fxdr_unsigned(u_long, fp->fa_nfssize);
+ vap->va_blocksize = fxdr_unsigned(long, fp->fa_nfsblocksize);
+ vap->va_bytes = fxdr_unsigned(long, fp->fa_nfsblocks) * NFS_FABLKSIZE;
+ vap->va_fileid = fxdr_unsigned(long, fp->fa_nfsfileid);
+ fxdr_nfstime(&fp->fa_nfsatime, &vap->va_atime);
+ vap->va_flags = 0;
+ vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa_nfsctime.nfs_sec);
+ vap->va_ctime.ts_nsec = 0;
+ vap->va_gen = fxdr_unsigned(u_long, fp->fa_nfsctime.nfs_usec);
+ vap->va_filerev = 0;
+ }
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ np->n_attrstamp = time.tv_sec;
+ *dposp = dpos;
+ *mdp = md;
+ if (vaper != NULL) {
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(*vap));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) && np->n_size > vap->va_size)
+ if (np->n_size > vap->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec =
+ np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec =
+ np->n_mtim.tv_usec * 1000;
+ }
+ }
+ }
+ return (0);
+}
+
+/*
+ * Check the time stamp
+ * If the cache is valid, copy contents to *vap and return 0
+ * otherwise return an error
+ */
+nfs_getattrcache(vp, vaper)
+ register struct vnode *vp;
+ struct vattr *vaper;
+{
+ register struct nfsnode *np = VTONFS(vp);
+ register struct vattr *vap;
+
+ if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQLOOKLEASE) {
+ if (!NQNFS_CKCACHABLE(vp, NQL_READ) || np->n_attrstamp == 0) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ } else if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) {
+ nfsstats.attrcache_misses++;
+ return (ENOENT);
+ }
+ nfsstats.attrcache_hits++;
+ vap = &np->n_vattr;
+ if (vap->va_size != np->n_size) {
+ if (vap->va_type == VREG) {
+ if (np->n_flag & NMODIFIED) {
+ if (vap->va_size < np->n_size)
+ vap->va_size = np->n_size;
+ else
+ np->n_size = vap->va_size;
+ } else
+ np->n_size = vap->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else
+ np->n_size = vap->va_size;
+ }
+ bcopy((caddr_t)vap, (caddr_t)vaper, sizeof(struct vattr));
+#ifdef notdef
+ if ((np->n_flag & NMODIFIED) == 0) {
+ np->n_size = vaper->va_size;
+ vnode_pager_setsize(vp, (u_long)np->n_size);
+ } else if (np->n_size > vaper->va_size)
+ if (np->n_size > vaper->va_size)
+ vaper->va_size = np->n_size;
+#endif
+ if (np->n_flag & NCHG) {
+ if (np->n_flag & NACC) {
+ vaper->va_atime.ts_sec = np->n_atim.tv_sec;
+ vaper->va_atime.ts_nsec = np->n_atim.tv_usec * 1000;
+ }
+ if (np->n_flag & NUPD) {
+ vaper->va_mtime.ts_sec = np->n_mtim.tv_sec;
+ vaper->va_mtime.ts_nsec = np->n_mtim.tv_usec * 1000;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Set up nameidata for a lookup() call and do it
+ */
+nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p)
+ register struct nameidata *ndp;
+ fhandle_t *fhp;
+ int len;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ struct mbuf **mdp;
+ caddr_t *dposp;
+ struct proc *p;
+{
+ register int i, rem;
+ register struct mbuf *md;
+ register char *fromcp, *tocp;
+ struct vnode *dp;
+ int error, rdonly;
+ struct componentname *cnp = &ndp->ni_cnd;
+
+ MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK);
+ /*
+ * Copy the name from the mbuf list to ndp->ni_pnbuf
+ * and set the various ndp fields appropriately.
+ */
+ fromcp = *dposp;
+ tocp = cnp->cn_pnbuf;
+ md = *mdp;
+ rem = mtod(md, caddr_t) + md->m_len - fromcp;
+ cnp->cn_hash = 0;
+ for (i = 0; i < len; i++) {
+ while (rem == 0) {
+ md = md->m_next;
+ if (md == NULL) {
+ error = EBADRPC;
+ goto out;
+ }
+ fromcp = mtod(md, caddr_t);
+ rem = md->m_len;
+ }
+ if (*fromcp == '\0' || *fromcp == '/') {
+ error = EINVAL;
+ goto out;
+ }
+ cnp->cn_hash += (unsigned char)*fromcp;
+ *tocp++ = *fromcp++;
+ rem--;
+ }
+ *tocp = '\0';
+ *mdp = md;
+ *dposp = fromcp;
+ len = nfsm_rndup(len)-len;
+ if (len > 0) {
+ if (rem >= len)
+ *dposp += len;
+ else if (error = nfs_adv(mdp, dposp, len, rem))
+ goto out;
+ }
+ ndp->ni_pathlen = tocp - cnp->cn_pnbuf;
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+ /*
+ * Extract and set starting directory.
+ */
+ if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp,
+ nam, &rdonly))
+ goto out;
+ if (dp->v_type != VDIR) {
+ vrele(dp);
+ error = ENOTDIR;
+ goto out;
+ }
+ ndp->ni_startdir = dp;
+ if (rdonly)
+ cnp->cn_flags |= (NOCROSSMOUNT | RDONLY);
+ else
+ cnp->cn_flags |= NOCROSSMOUNT;
+ /*
+ * And call lookup() to do the real work
+ */
+ cnp->cn_proc = p;
+ if (error = lookup(ndp))
+ goto out;
+ /*
+ * Check for encountering a symbolic link
+ */
+ if (cnp->cn_flags & ISSYMLINK) {
+ if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
+ vput(ndp->ni_dvp);
+ else
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Check for saved name request
+ */
+ if (cnp->cn_flags & (SAVENAME | SAVESTART)) {
+ cnp->cn_flags |= HASBUF;
+ return (0);
+ }
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ return (error);
+}
+
+/*
+ * A fiddled version of m_adj() that ensures null fill to a long
+ * boundary and only trims off the back end
+ */
+void
+nfsm_adj(mp, len, nul)
+ struct mbuf *mp;
+ register int len;
+ int nul;
+{
+ register struct mbuf *m;
+ register int count, i;
+ register char *cp;
+
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ count = 0;
+ m = mp;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len) {
+ m->m_len -= len;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ for (m = mp; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (nul > 0) {
+ cp = mtod(m, caddr_t)+m->m_len-nul;
+ for (i = 0; i < nul; i++)
+ *cp++ = '\0';
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ while (m = m->m_next)
+ m->m_len = 0;
+}
+
+/*
+ * nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
+ * - look up fsid in mount list (if not found ret error)
+ * - get vp and export rights by calling VFS_FHTOVP()
+ * - if cred->cr_uid == 0 or MNT_EXPORTANON set it to credanon
+ * - if not lockflag unlock it with VOP_UNLOCK()
+ */
+nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp)
+ fhandle_t *fhp;
+ int lockflag;
+ struct vnode **vpp;
+ struct ucred *cred;
+ struct nfssvc_sock *slp;
+ struct mbuf *nam;
+ int *rdonlyp;
+{
+ register struct mount *mp;
+ register struct nfsuid *uidp;
+ register int i;
+ struct ucred *credanon;
+ int error, exflags;
+
+ *vpp = (struct vnode *)0;
+ if ((mp = getvfs(&fhp->fh_fsid)) == NULL)
+ return (ESTALE);
+ if (error = VFS_FHTOVP(mp, &fhp->fh_fid, nam, vpp, &exflags, &credanon))
+ return (error);
+ /*
+ * Check/setup credentials.
+ */
+ if (exflags & MNT_EXKERB) {
+ uidp = slp->ns_uidh[NUIDHASH(cred->cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == cred->cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (uidp) {
+ cred->cr_uid = uidp->nu_cr.cr_uid;
+ for (i = 0; i < uidp->nu_cr.cr_ngroups; i++)
+ cred->cr_groups[i] = uidp->nu_cr.cr_groups[i];
+ } else {
+ vput(*vpp);
+ return (NQNFS_AUTHERR);
+ }
+ } else if (cred->cr_uid == 0 || (exflags & MNT_EXPORTANON)) {
+ cred->cr_uid = credanon->cr_uid;
+ for (i = 0; i < credanon->cr_ngroups && i < NGROUPS; i++)
+ cred->cr_groups[i] = credanon->cr_groups[i];
+ }
+ if (exflags & MNT_EXRDONLY)
+ *rdonlyp = 1;
+ else
+ *rdonlyp = 0;
+ if (!lockflag)
+ VOP_UNLOCK(*vpp);
+ return (0);
+}
+
+/*
+ * This function compares two net addresses by family and returns TRUE
+ * if they are the same host.
+ * If there is any doubt, return FALSE.
+ * The AF_INET family is handled as a special case so that address mbufs
+ * don't need to be saved to store "struct in_addr", which is only 4 bytes.
+ */
+netaddr_match(family, haddr, nam)
+ int family;
+ union nethostaddr *haddr;
+ struct mbuf *nam;
+{
+ register struct sockaddr_in *inetaddr;
+
+ switch (family) {
+ case AF_INET:
+ inetaddr = mtod(nam, struct sockaddr_in *);
+ if (inetaddr->sin_family == AF_INET &&
+ inetaddr->sin_addr.s_addr == haddr->had_inetaddr)
+ return (1);
+ break;
+#ifdef ISO
+ case AF_ISO:
+ {
+ register struct sockaddr_iso *isoaddr1, *isoaddr2;
+
+ isoaddr1 = mtod(nam, struct sockaddr_iso *);
+ isoaddr2 = mtod(haddr->had_nam, struct sockaddr_iso *);
+ if (isoaddr1->siso_family == AF_ISO &&
+ isoaddr1->siso_nlen > 0 &&
+ isoaddr1->siso_nlen == isoaddr2->siso_nlen &&
+ SAME_ISOADDR(isoaddr1, isoaddr2))
+ return (1);
+ break;
+ }
+#endif /* ISO */
+ default:
+ break;
+ };
+ return (0);
+}
diff --git a/sys/nfsserver/nfs_syscalls.c b/sys/nfsserver/nfs_syscalls.c
new file mode 100644
index 0000000..5d86b42
--- /dev/null
+++ b/sys/nfsserver/nfs_syscalls.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs_syscalls.c 8.3 (Berkeley) 1/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/namei.h>
+#include <sys/syslog.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsv2.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsrvcache.h>
+#include <nfs/nfsmount.h>
+#include <nfs/nfsnode.h>
+#include <nfs/nqnfs.h>
+#include <nfs/nfsrtt.h>
+
+/* Global defs. */
+extern u_long nfs_prog, nfs_vers;
+extern int (*nfsrv_procs[NFS_NPROCS])();
+extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON];
+extern int nfs_numasync;
+extern time_t nqnfsstarttime;
+extern struct nfsrv_req nsrvq_head;
+extern struct nfsd nfsd_head;
+extern int nqsrv_writeslack;
+extern int nfsrtton;
+struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock;
+int nuidhash_max = NFS_MAXUIDHASH;
+static int nfs_numnfsd = 0;
+int nfsd_waiting = 0;
+static int notstarted = 1;
+static int modify_flag = 0;
+static struct nfsdrt nfsdrt;
+void nfsrv_cleancache(), nfsrv_rcv(), nfsrv_wakenfsd(), nfs_sndunlock();
+static void nfsd_rt();
+void nfsrv_slpderef(), nfsrv_init();
+
+#define TRUE 1
+#define FALSE 0
+
+static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
+/*
+ * NFS server system calls
+ * getfh() lives here too, but maybe should move to kern/vfs_syscalls.c
+ */
+
+/*
+ * Get file handle system call
+ */
+struct getfh_args {
+ char *fname;
+ fhandle_t *fhp;
+};
+getfh(p, uap, retval)
+ struct proc *p;
+ register struct getfh_args *uap;
+ int *retval;
+{
+ register struct vnode *vp;
+ fhandle_t fh;
+ int error;
+ struct nameidata nd;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, uap->fname, p);
+ if (error = namei(&nd))
+ return (error);
+ vp = nd.ni_vp;
+ bzero((caddr_t)&fh, sizeof(fh));
+ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid;
+ error = VFS_VPTOFH(vp, &fh.fh_fid);
+ vput(vp);
+ if (error)
+ return (error);
+ error = copyout((caddr_t)&fh, (caddr_t)uap->fhp, sizeof (fh));
+ return (error);
+}
+
+static struct nfssvc_sock nfssvc_sockhead;
+
+/*
+ * Nfs server psuedo system call for the nfsd's
+ * Based on the flag value it either:
+ * - adds a socket to the selection list
+ * - remains in the kernel as an nfsd
+ * - remains in the kernel as an nfsiod
+ */
+struct nfssvc_args {
+ int flag;
+ caddr_t argp;
+};
+nfssvc(p, uap, retval)
+ struct proc *p;
+ register struct nfssvc_args *uap;
+ int *retval;
+{
+ struct nameidata nd;
+ struct file *fp;
+ struct mbuf *nam;
+ struct nfsd_args nfsdarg;
+ struct nfsd_srvargs nfsd_srvargs, *nsd = &nfsd_srvargs;
+ struct nfsd_cargs ncd;
+ struct nfsd *nfsd;
+ struct nfssvc_sock *slp;
+ struct nfsuid *nuidp, **nuh;
+ struct nfsmount *nmp;
+ int error;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(p->p_ucred, &p->p_acflag))
+ return (error);
+ while (nfssvc_sockhead.ns_flag & SLP_INIT) {
+ nfssvc_sockhead.ns_flag |= SLP_WANTINIT;
+ (void) tsleep((caddr_t)&nfssvc_sockhead, PSOCK, "nfsd init", 0);
+ }
+ if (uap->flag & NFSSVC_BIOD)
+ error = nfssvc_iod(p);
+ else if (uap->flag & NFSSVC_MNTD) {
+ if (error = copyin(uap->argp, (caddr_t)&ncd, sizeof (ncd)))
+ return (error);
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
+ ncd.ncd_dirp, p);
+ if (error = namei(&nd))
+ return (error);
+ if ((nd.ni_vp->v_flag & VROOT) == 0)
+ error = EINVAL;
+ nmp = VFSTONFS(nd.ni_vp->v_mount);
+ vput(nd.ni_vp);
+ if (error)
+ return (error);
+ if ((nmp->nm_flag & NFSMNT_MNTD) &&
+ (uap->flag & NFSSVC_GOTAUTH) == 0)
+ return (0);
+ nmp->nm_flag |= NFSMNT_MNTD;
+ error = nqnfs_clientd(nmp, p->p_ucred, &ncd, uap->flag,
+ uap->argp, p);
+ } else if (uap->flag & NFSSVC_ADDSOCK) {
+ if (error = copyin(uap->argp, (caddr_t)&nfsdarg,
+ sizeof(nfsdarg)))
+ return (error);
+ if (error = getsock(p->p_fd, nfsdarg.sock, &fp))
+ return (error);
+ /*
+ * Get the client address for connected sockets.
+ */
+ if (nfsdarg.name == NULL || nfsdarg.namelen == 0)
+ nam = (struct mbuf *)0;
+ else if (error = sockargs(&nam, nfsdarg.name, nfsdarg.namelen,
+ MT_SONAME))
+ return (error);
+ error = nfssvc_addsock(fp, nam);
+ } else {
+ if (error = copyin(uap->argp, (caddr_t)nsd, sizeof (*nsd)))
+ return (error);
+ if ((uap->flag & NFSSVC_AUTHIN) && (nfsd = nsd->nsd_nfsd) &&
+ (nfsd->nd_slp->ns_flag & SLP_VALID)) {
+ slp = nfsd->nd_slp;
+
+ /*
+ * First check to see if another nfsd has already
+ * added this credential.
+ */
+ nuidp = slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ while (nuidp) {
+ if (nuidp->nu_uid == nsd->nsd_uid)
+ break;
+ nuidp = nuidp->nu_hnext;
+ }
+ if (!nuidp) {
+ /*
+ * Nope, so we will.
+ */
+ if (slp->ns_numuids < nuidhash_max) {
+ slp->ns_numuids++;
+ nuidp = (struct nfsuid *)
+ malloc(sizeof (struct nfsuid), M_NFSUID,
+ M_WAITOK);
+ } else
+ nuidp = (struct nfsuid *)0;
+ if ((slp->ns_flag & SLP_VALID) == 0) {
+ if (nuidp)
+ free((caddr_t)nuidp, M_NFSUID);
+ } else {
+ if (nuidp == (struct nfsuid *)0) {
+ nuidp = slp->ns_lruprev;
+ remque(nuidp);
+ if (nuidp->nu_hprev)
+ nuidp->nu_hprev->nu_hnext =
+ nuidp->nu_hnext;
+ if (nuidp->nu_hnext)
+ nuidp->nu_hnext->nu_hprev =
+ nuidp->nu_hprev;
+ }
+ nuidp->nu_cr = nsd->nsd_cr;
+ if (nuidp->nu_cr.cr_ngroups > NGROUPS)
+ nuidp->nu_cr.cr_ngroups = NGROUPS;
+ nuidp->nu_cr.cr_ref = 1;
+ nuidp->nu_uid = nsd->nsd_uid;
+ insque(nuidp, (struct nfsuid *)slp);
+ nuh = &slp->ns_uidh[NUIDHASH(nsd->nsd_uid)];
+ if (nuidp->nu_hnext = *nuh)
+ nuidp->nu_hnext->nu_hprev = nuidp;
+ nuidp->nu_hprev = (struct nfsuid *)0;
+ *nuh = nuidp;
+ }
+ }
+ }
+ if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd))
+ nfsd->nd_flag |= NFSD_AUTHFAIL;
+ error = nfssvc_nfsd(nsd, uap->argp, p);
+ }
+ if (error == EINTR || error == ERESTART)
+ error = 0;
+ return (error);
+}
+
+/*
+ * Adds a socket to the list for servicing by nfsds.
+ */
+nfssvc_addsock(fp, mynam)
+ struct file *fp;
+ struct mbuf *mynam;
+{
+ register struct mbuf *m;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ struct nfssvc_sock *tslp;
+ int error, s;
+
+ so = (struct socket *)fp->f_data;
+ tslp = (struct nfssvc_sock *)0;
+ /*
+ * Add it to the list, as required.
+ */
+ if (so->so_proto->pr_protocol == IPPROTO_UDP) {
+ tslp = nfs_udpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#ifdef ISO
+ } else if (so->so_proto->pr_protocol == ISOPROTO_CLTP) {
+ tslp = nfs_cltpsock;
+ if (tslp->ns_flag & SLP_VALID) {
+ m_freem(mynam);
+ return (EPERM);
+ }
+#endif /* ISO */
+ }
+ if (so->so_type == SOCK_STREAM)
+ siz = NFS_MAXPACKET + sizeof (u_long);
+ else
+ siz = NFS_MAXPACKET;
+ if (error = soreserve(so, siz, siz)) {
+ m_freem(mynam);
+ return (error);
+ }
+
+ /*
+ * Set protocol specific options { for now TCP only } and
+ * reserve some space. For datagram sockets, this can get called
+ * repeatedly for the same socket, but that isn't harmful.
+ */
+ if (so->so_type == SOCK_STREAM) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, SOL_SOCKET, SO_KEEPALIVE, m);
+ }
+ if (so->so_proto->pr_domain->dom_family == AF_INET &&
+ so->so_proto->pr_protocol == IPPROTO_TCP) {
+ MGET(m, M_WAIT, MT_SOOPTS);
+ *mtod(m, int *) = 1;
+ m->m_len = sizeof(int);
+ sosetopt(so, IPPROTO_TCP, TCP_NODELAY, m);
+ }
+ so->so_rcv.sb_flags &= ~SB_NOINTR;
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_flags &= ~SB_NOINTR;
+ so->so_snd.sb_timeo = 0;
+ if (tslp)
+ slp = tslp;
+ else {
+ slp = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)slp, sizeof (struct nfssvc_sock));
+ slp->ns_prev = nfssvc_sockhead.ns_prev;
+ slp->ns_prev->ns_next = slp;
+ slp->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = slp;
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ }
+ slp->ns_so = so;
+ slp->ns_nam = mynam;
+ fp->f_count++;
+ slp->ns_fp = fp;
+ s = splnet();
+ so->so_upcallarg = (caddr_t)slp;
+ so->so_upcall = nfsrv_rcv;
+ slp->ns_flag = (SLP_VALID | SLP_NEEDQ);
+ nfsrv_wakenfsd(slp);
+ splx(s);
+ return (0);
+}
+
+/*
+ * Called by nfssvc() for nfsds. Just loops around servicing rpc requests
+ * until it is killed by a signal.
+ */
+nfssvc_nfsd(nsd, argp, p)
+ struct nfsd_srvargs *nsd;
+ caddr_t argp;
+ struct proc *p;
+{
+ register struct mbuf *m, *nam2;
+ register int siz;
+ register struct nfssvc_sock *slp;
+ register struct socket *so;
+ register int *solockp;
+ struct nfsd *nd = nsd->nsd_nfsd;
+ struct mbuf *mreq, *nam;
+ struct timeval starttime;
+ struct nfsuid *uidp;
+ int error, cacherep, s;
+ int sotype;
+
+ s = splnet();
+ if (nd == (struct nfsd *)0) {
+ nsd->nsd_nfsd = nd = (struct nfsd *)
+ malloc(sizeof (struct nfsd), M_NFSD, M_WAITOK);
+ bzero((caddr_t)nd, sizeof (struct nfsd));
+ nd->nd_procp = p;
+ nd->nd_cr.cr_ref = 1;
+ insque(nd, &nfsd_head);
+ nd->nd_nqlflag = NQL_NOVAL;
+ nfs_numnfsd++;
+ }
+ /*
+ * Loop getting rpc requests until SIGKILL.
+ */
+ for (;;) {
+ if ((nd->nd_flag & NFSD_REQINPROG) == 0) {
+ while (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP) == 0) {
+ nd->nd_flag |= NFSD_WAITING;
+ nfsd_waiting++;
+ error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0);
+ nfsd_waiting--;
+ if (error)
+ goto done;
+ }
+ if (nd->nd_slp == (struct nfssvc_sock *)0 &&
+ (nfsd_head.nd_flag & NFSD_CHECKSLP)) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if ((slp->ns_flag & (SLP_VALID | SLP_DOREC))
+ == (SLP_VALID | SLP_DOREC)) {
+ slp->ns_flag &= ~SLP_DOREC;
+ slp->ns_sref++;
+ nd->nd_slp = slp;
+ break;
+ }
+ slp = slp->ns_next;
+ }
+ if (slp == &nfssvc_sockhead)
+ nfsd_head.nd_flag &= ~NFSD_CHECKSLP;
+ }
+ if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0)
+ continue;
+ if (slp->ns_flag & SLP_VALID) {
+ if (slp->ns_flag & SLP_DISCONN)
+ nfsrv_zapsock(slp);
+ else if (slp->ns_flag & SLP_NEEDQ) {
+ slp->ns_flag &= ~SLP_NEEDQ;
+ (void) nfs_sndlock(&slp->ns_solock,
+ (struct nfsreq *)0);
+ nfsrv_rcv(slp->ns_so, (caddr_t)slp,
+ M_WAIT);
+ nfs_sndunlock(&slp->ns_solock);
+ }
+ error = nfsrv_dorec(slp, nd);
+ nd->nd_flag |= NFSD_REQINPROG;
+ }
+ } else {
+ error = 0;
+ slp = nd->nd_slp;
+ }
+ if (error || (slp->ns_flag & SLP_VALID) == 0) {
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nfsrv_slpderef(slp);
+ continue;
+ }
+ splx(s);
+ so = slp->ns_so;
+ sotype = so->so_type;
+ starttime = time;
+ if (so->so_proto->pr_flags & PR_CONNREQUIRED)
+ solockp = &slp->ns_solock;
+ else
+ solockp = (int *)0;
+ /*
+ * nam == nam2 for connectionless protocols such as UDP
+ * nam2 == NULL for connection based protocols to disable
+ * recent request caching.
+ */
+ if (nam2 = nd->nd_nam) {
+ nam = nam2;
+ cacherep = RC_CHECKIT;
+ } else {
+ nam = slp->ns_nam;
+ cacherep = RC_DOIT;
+ }
+
+ /*
+ * Check to see if authorization is needed.
+ */
+ if (nd->nd_flag & NFSD_NEEDAUTH) {
+ static int logauth = 0;
+
+ nd->nd_flag &= ~NFSD_NEEDAUTH;
+ /*
+ * Check for a mapping already installed.
+ */
+ uidp = slp->ns_uidh[NUIDHASH(nd->nd_cr.cr_uid)];
+ while (uidp) {
+ if (uidp->nu_uid == nd->nd_cr.cr_uid)
+ break;
+ uidp = uidp->nu_hnext;
+ }
+ if (!uidp) {
+ nsd->nsd_uid = nd->nd_cr.cr_uid;
+ if (nam2 && logauth++ == 0)
+ log(LOG_WARNING, "Kerberized NFS using UDP\n");
+ nsd->nsd_haddr =
+ mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ nsd->nsd_authlen = nd->nd_authlen;
+ if (copyout(nd->nd_authstr, nsd->nsd_authstr,
+ nd->nd_authlen) == 0 &&
+ copyout((caddr_t)nsd, argp, sizeof (*nsd)) == 0)
+ return (ENEEDAUTH);
+ cacherep = RC_DROPIT;
+ }
+ }
+ if (cacherep == RC_CHECKIT)
+ cacherep = nfsrv_getcache(nam2, nd, &mreq);
+
+ /*
+ * Check for just starting up for NQNFS and send
+ * fake "try again later" replies to the NQNFS clients.
+ */
+ if (notstarted && nqnfsstarttime <= time.tv_sec) {
+ if (modify_flag) {
+ nqnfsstarttime = time.tv_sec + nqsrv_writeslack;
+ modify_flag = 0;
+ } else
+ notstarted = 0;
+ }
+ if (notstarted) {
+ if (nd->nd_nqlflag == NQL_NOVAL)
+ cacherep = RC_DROPIT;
+ else if (nd->nd_procnum != NFSPROC_WRITE) {
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_TRYLATER;
+ cacherep = RC_DOIT;
+ } else
+ modify_flag = 1;
+ } else if (nd->nd_flag & NFSD_AUTHFAIL) {
+ nd->nd_flag &= ~NFSD_AUTHFAIL;
+ nd->nd_procnum = NFSPROC_NOOP;
+ nd->nd_repstat = NQNFS_AUTHERR;
+ cacherep = RC_DOIT;
+ }
+
+ switch (cacherep) {
+ case RC_DOIT:
+ error = (*(nfsrv_procs[nd->nd_procnum]))(nd,
+ nd->nd_mrep, nd->nd_md, nd->nd_dpos, &nd->nd_cr,
+ nam, &mreq);
+ if (nd->nd_cr.cr_ref != 1) {
+ printf("nfssvc cref=%d\n", nd->nd_cr.cr_ref);
+ panic("nfssvc cref");
+ }
+ if (error) {
+ if (nd->nd_procnum != NQNFSPROC_VACATED)
+ nfsstats.srv_errs++;
+ if (nam2) {
+ nfsrv_updatecache(nam2, nd, FALSE, mreq);
+ m_freem(nam2);
+ }
+ break;
+ }
+ nfsstats.srvrpccnt[nd->nd_procnum]++;
+ if (nam2)
+ nfsrv_updatecache(nam2, nd, TRUE, mreq);
+ nd->nd_mrep = (struct mbuf *)0;
+ case RC_REPLY:
+ m = mreq;
+ siz = 0;
+ while (m) {
+ siz += m->m_len;
+ m = m->m_next;
+ }
+ if (siz <= 0 || siz > NFS_MAXPACKET) {
+ printf("mbuf siz=%d\n",siz);
+ panic("Bad nfs svc reply");
+ }
+ m = mreq;
+ m->m_pkthdr.len = siz;
+ m->m_pkthdr.rcvif = (struct ifnet *)0;
+ /*
+ * For stream protocols, prepend a Sun RPC
+ * Record Mark.
+ */
+ if (sotype == SOCK_STREAM) {
+ M_PREPEND(m, NFSX_UNSIGNED, M_WAIT);
+ *mtod(m, u_long *) = htonl(0x80000000 | siz);
+ }
+ if (solockp)
+ (void) nfs_sndlock(solockp, (struct nfsreq *)0);
+ if (slp->ns_flag & SLP_VALID)
+ error = nfs_send(so, nam2, m, (struct nfsreq *)0);
+ else {
+ error = EPIPE;
+ m_freem(m);
+ }
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ if (nam2)
+ MFREE(nam2, m);
+ if (nd->nd_mrep)
+ m_freem(nd->nd_mrep);
+ if (error == EPIPE)
+ nfsrv_zapsock(slp);
+ if (solockp)
+ nfs_sndunlock(solockp);
+ if (error == EINTR || error == ERESTART) {
+ nfsrv_slpderef(slp);
+ s = splnet();
+ goto done;
+ }
+ break;
+ case RC_DROPIT:
+ if (nfsrtton)
+ nfsd_rt(&starttime, sotype, nd, nam, cacherep);
+ m_freem(nd->nd_mrep);
+ m_freem(nam2);
+ break;
+ };
+ s = splnet();
+ if (nfsrv_dorec(slp, nd)) {
+ nd->nd_flag &= ~NFSD_REQINPROG;
+ nd->nd_slp = (struct nfssvc_sock *)0;
+ nfsrv_slpderef(slp);
+ }
+ }
+done:
+ remque(nd);
+ splx(s);
+ free((caddr_t)nd, M_NFSD);
+ nsd->nsd_nfsd = (struct nfsd *)0;
+ if (--nfs_numnfsd == 0)
+ nfsrv_init(TRUE); /* Reinitialize everything */
+ return (error);
+}
+
+/*
+ * Asynchronous I/O daemons for client nfs.
+ * They do read-ahead and write-behind operations on the block I/O cache.
+ * Never returns unless it fails or gets killed.
+ */
+nfssvc_iod(p)
+ struct proc *p;
+{
+ register struct buf *bp;
+ register int i, myiod;
+ int error = 0;
+
+ /*
+ * Assign my position or return error if too many already running
+ */
+ myiod = -1;
+ for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
+ if (nfs_asyncdaemon[i] == 0) {
+ nfs_asyncdaemon[i]++;
+ myiod = i;
+ break;
+ }
+ if (myiod == -1)
+ return (EBUSY);
+ nfs_numasync++;
+ /*
+ * Just loop around doin our stuff until SIGKILL
+ */
+ for (;;) {
+ while (nfs_bufq.tqh_first == NULL && error == 0) {
+ nfs_iodwant[myiod] = p;
+ error = tsleep((caddr_t)&nfs_iodwant[myiod],
+ PWAIT | PCATCH, "nfsidl", 0);
+ }
+ while ((bp = nfs_bufq.tqh_first) != NULL) {
+ /* Take one off the front of the list */
+ TAILQ_REMOVE(&nfs_bufq, bp, b_freelist);
+ if (bp->b_flags & B_READ)
+ (void) nfs_doio(bp, bp->b_rcred, (struct proc *)0);
+ else
+ (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0);
+ }
+ if (error) {
+ nfs_asyncdaemon[myiod] = 0;
+ nfs_numasync--;
+ return (error);
+ }
+ }
+}
+
+/*
+ * Shut down a socket associated with an nfssvc_sock structure.
+ * Should be called with the send lock set, if required.
+ * The trick here is to increment the sref at the start, so that the nfsds
+ * will stop using it and clear ns_flag at the end so that it will not be
+ * reassigned during cleanup.
+ */
+nfsrv_zapsock(slp)
+ register struct nfssvc_sock *slp;
+{
+ register struct nfsuid *nuidp, *onuidp;
+ register int i;
+ struct socket *so;
+ struct file *fp;
+ struct mbuf *m;
+
+ slp->ns_flag &= ~SLP_ALLFLAGS;
+ if (fp = slp->ns_fp) {
+ slp->ns_fp = (struct file *)0;
+ so = slp->ns_so;
+ so->so_upcall = NULL;
+ soshutdown(so, 2);
+ closef(fp, (struct proc *)0);
+ if (slp->ns_nam)
+ MFREE(slp->ns_nam, m);
+ m_freem(slp->ns_raw);
+ m_freem(slp->ns_rec);
+ nuidp = slp->ns_lrunext;
+ while (nuidp != (struct nfsuid *)slp) {
+ onuidp = nuidp;
+ nuidp = nuidp->nu_lrunext;
+ free((caddr_t)onuidp, M_NFSUID);
+ }
+ slp->ns_lrunext = slp->ns_lruprev = (struct nfsuid *)slp;
+ for (i = 0; i < NUIDHASHSIZ; i++)
+ slp->ns_uidh[i] = (struct nfsuid *)0;
+ }
+}
+
+/*
+ * Get an authorization string for the uid by having the mount_nfs sitting
+ * on this mount point porpous out of the kernel and do it.
+ */
+nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len)
+ register struct nfsmount *nmp;
+ struct nfsreq *rep;
+ struct ucred *cred;
+ int *auth_type;
+ char **auth_str;
+ int *auth_len;
+{
+ int error = 0;
+
+ while ((nmp->nm_flag & NFSMNT_WAITAUTH) == 0) {
+ nmp->nm_flag |= NFSMNT_WANTAUTH;
+ (void) tsleep((caddr_t)&nmp->nm_authtype, PSOCK,
+ "nfsauth1", 2 * hz);
+ if (error = nfs_sigintr(nmp, rep, rep->r_procp)) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ return (error);
+ }
+ }
+ nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH);
+ nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK);
+ nmp->nm_authuid = cred->cr_uid;
+ wakeup((caddr_t)&nmp->nm_authstr);
+
+ /*
+ * And wait for mount_nfs to do its stuff.
+ */
+ while ((nmp->nm_flag & NFSMNT_HASAUTH) == 0 && error == 0) {
+ (void) tsleep((caddr_t)&nmp->nm_authlen, PSOCK,
+ "nfsauth2", 2 * hz);
+ error = nfs_sigintr(nmp, rep, rep->r_procp);
+ }
+ if (nmp->nm_flag & NFSMNT_AUTHERR) {
+ nmp->nm_flag &= ~NFSMNT_AUTHERR;
+ error = EAUTH;
+ }
+ if (error)
+ free((caddr_t)*auth_str, M_TEMP);
+ else {
+ *auth_type = nmp->nm_authtype;
+ *auth_len = nmp->nm_authlen;
+ }
+ nmp->nm_flag &= ~NFSMNT_HASAUTH;
+ nmp->nm_flag |= NFSMNT_WAITAUTH;
+ if (nmp->nm_flag & NFSMNT_WANTAUTH) {
+ nmp->nm_flag &= ~NFSMNT_WANTAUTH;
+ wakeup((caddr_t)&nmp->nm_authtype);
+ }
+ return (error);
+}
+
+/*
+ * Derefence a server socket structure. If it has no more references and
+ * is no longer valid, you can throw it away.
+ */
+void
+nfsrv_slpderef(slp)
+ register struct nfssvc_sock *slp;
+{
+ if (--(slp->ns_sref) == 0 && (slp->ns_flag & SLP_VALID) == 0) {
+ slp->ns_prev->ns_next = slp->ns_next;
+ slp->ns_next->ns_prev = slp->ns_prev;
+ free((caddr_t)slp, M_NFSSVC);
+ }
+}
+
+/*
+ * Initialize the data structures for the server.
+ * Handshake with any new nfsds starting up to avoid any chance of
+ * corruption.
+ */
+void
+nfsrv_init(terminating)
+ int terminating;
+{
+ register struct nfssvc_sock *slp;
+ struct nfssvc_sock *oslp;
+
+ if (nfssvc_sockhead.ns_flag & SLP_INIT)
+ panic("nfsd init");
+ nfssvc_sockhead.ns_flag |= SLP_INIT;
+ if (terminating) {
+ slp = nfssvc_sockhead.ns_next;
+ while (slp != &nfssvc_sockhead) {
+ if (slp->ns_flag & SLP_VALID)
+ nfsrv_zapsock(slp);
+ slp->ns_next->ns_prev = slp->ns_prev;
+ slp->ns_prev->ns_next = slp->ns_next;
+ oslp = slp;
+ slp = slp->ns_next;
+ free((caddr_t)oslp, M_NFSSVC);
+ }
+ nfsrv_cleancache(); /* And clear out server cache */
+ }
+ nfs_udpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock));
+ nfs_cltpsock = (struct nfssvc_sock *)
+ malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK);
+ bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock));
+ nfssvc_sockhead.ns_next = nfs_udpsock;
+ nfs_udpsock->ns_next = nfs_cltpsock;
+ nfs_cltpsock->ns_next = &nfssvc_sockhead;
+ nfssvc_sockhead.ns_prev = nfs_cltpsock;
+ nfs_cltpsock->ns_prev = nfs_udpsock;
+ nfs_udpsock->ns_prev = &nfssvc_sockhead;
+ nfs_udpsock->ns_lrunext = nfs_udpsock->ns_lruprev =
+ (struct nfsuid *)nfs_udpsock;
+ nfs_cltpsock->ns_lrunext = nfs_cltpsock->ns_lruprev =
+ (struct nfsuid *)nfs_cltpsock;
+ nfsd_head.nd_next = nfsd_head.nd_prev = &nfsd_head;
+ nfsd_head.nd_flag = 0;
+ nfssvc_sockhead.ns_flag &= ~SLP_INIT;
+ if (nfssvc_sockhead.ns_flag & SLP_WANTINIT) {
+ nfssvc_sockhead.ns_flag &= ~SLP_WANTINIT;
+ wakeup((caddr_t)&nfssvc_sockhead);
+ }
+}
+
+/*
+ * Add entries to the server monitor log.
+ */
+static void
+nfsd_rt(startp, sotype, nd, nam, cacherep)
+ struct timeval *startp;
+ int sotype;
+ register struct nfsd *nd;
+ struct mbuf *nam;
+ int cacherep;
+{
+ register struct drt *rt;
+
+ rt = &nfsdrt.drt[nfsdrt.pos];
+ if (cacherep == RC_DOIT)
+ rt->flag = 0;
+ else if (cacherep == RC_REPLY)
+ rt->flag = DRT_CACHEREPLY;
+ else
+ rt->flag = DRT_CACHEDROP;
+ if (sotype == SOCK_STREAM)
+ rt->flag |= DRT_TCP;
+ if (nd->nd_nqlflag != NQL_NOVAL)
+ rt->flag |= DRT_NQNFS;
+ rt->proc = nd->nd_procnum;
+ if (mtod(nam, struct sockaddr *)->sa_family == AF_INET)
+ rt->ipadr = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
+ else
+ rt->ipadr = INADDR_ANY;
+ rt->resptime = ((time.tv_sec - startp->tv_sec) * 1000000) +
+ (time.tv_usec - startp->tv_usec);
+ rt->tstamp = time;
+ nfsdrt.pos = (nfsdrt.pos + 1) % NFSRTTLOGSIZ;
+}
diff --git a/sys/nfsserver/nfsm_subs.h b/sys/nfsserver/nfsm_subs.h
new file mode 100644
index 0000000..879db36
--- /dev/null
+++ b/sys/nfsserver/nfsm_subs.h
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93
+ */
+
+/*
+ * These macros do strange and peculiar things to mbuf chains for
+ * the assistance of the nfs code. To attempt to use them for any
+ * other purpose will be dangerous. (they make weird assumptions)
+ */
+
+/*
+ * First define what the actual subs. return
+ */
+extern struct mbuf *nfsm_reqh();
+
+#define M_HASCL(m) ((m)->m_flags & M_EXT)
+#define NFSMINOFF(m) \
+ if (M_HASCL(m)) \
+ (m)->m_data = (m)->m_ext.ext_buf; \
+ else if ((m)->m_flags & M_PKTHDR) \
+ (m)->m_data = (m)->m_pktdat; \
+ else \
+ (m)->m_data = (m)->m_dat
+#define NFSMADV(m, s) (m)->m_data += (s)
+#define NFSMSIZ(m) ((M_HASCL(m))?MCLBYTES: \
+ (((m)->m_flags & M_PKTHDR)?MHLEN:MLEN))
+
+/*
+ * Now for the macros that do the simple stuff and call the functions
+ * for the hard stuff.
+ * These macros use several vars. declared in nfsm_reqhead and these
+ * vars. must not be used elsewhere unless you are careful not to corrupt
+ * them. The vars. starting with pN and tN (N=1,2,3,..) are temporaries
+ * that may be used so long as the value is not expected to retained
+ * after a macro.
+ * I know, this is kind of dorkey, but it makes the actual op functions
+ * fairly clean and deals with the mess caused by the xdr discriminating
+ * unions.
+ */
+
+#define nfsm_build(a,c,s) \
+ { if ((s) > M_TRAILINGSPACE(mb)) { \
+ MGET(mb2, M_WAIT, MT_DATA); \
+ if ((s) > MLEN) \
+ panic("build > MLEN"); \
+ mb->m_next = mb2; \
+ mb = mb2; \
+ mb->m_len = 0; \
+ bpos = mtod(mb, caddr_t); \
+ } \
+ (a) = (c)(bpos); \
+ mb->m_len += (s); \
+ bpos += (s); }
+
+#define nfsm_dissect(a,c,s) \
+ { t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ (a) = (c)(dpos); \
+ dpos += (s); \
+ } else if (error = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } else { \
+ (a) = (c)cp2; \
+ } }
+
+#define nfsm_fhtom(v) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)&(VTONFS(v)->n_fh), cp, NFSX_FH)
+
+#define nfsm_srvfhtom(f) \
+ nfsm_build(cp,caddr_t,NFSX_FH); \
+ bcopy((caddr_t)(f), cp, NFSX_FH)
+
+#define nfsm_mtofh(d,v) \
+ { struct nfsnode *np; nfsv2fh_t *fhp; \
+ nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \
+ if (error = nfs_nget((d)->v_mount, fhp, &np)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = NFSTOV(np); \
+ nfsm_loadattr(v, (struct vattr *)0); \
+ }
+
+#define nfsm_loadattr(v,a) \
+ { struct vnode *tvp = (v); \
+ if (error = nfs_loadattrcache(&tvp, &md, &dpos, (a))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ } \
+ (v) = tvp; }
+
+#define nfsm_strsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m)) { \
+ m_freem(mrep); \
+ error = EBADRPC; \
+ goto nfsmout; \
+ } }
+
+#define nfsm_srvstrsiz(s,m) \
+ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \
+ if (((s) = fxdr_unsigned(long,*tl)) > (m) || (s) <= 0) { \
+ error = EBADRPC; \
+ nfsm_reply(0); \
+ } }
+
+#define nfsm_mtouio(p,s) \
+ if ((s) > 0 && \
+ (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_uiotom(p,s) \
+ if (error = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_reqhead(v,a,s) \
+ mb = mreq = nfsm_reqh((v),(a),(s),&bpos)
+
+#define nfsm_reqdone m_freem(mrep); \
+ nfsmout:
+
+#define nfsm_rndup(a) (((a)+3)&(~0x3))
+
+#define nfsm_request(v, t, p, c) \
+ if (error = nfs_request((v), mreq, (t), (p), \
+ (c), &mrep, &md, &dpos)) \
+ goto nfsmout
+
+#define nfsm_strtom(a,s,m) \
+ if ((s) > (m)) { \
+ m_freem(mreq); \
+ error = ENAMETOOLONG; \
+ goto nfsmout; \
+ } \
+ t2 = nfsm_rndup(s)+NFSX_UNSIGNED; \
+ if (t2 <= M_TRAILINGSPACE(mb)) { \
+ nfsm_build(tl,u_long *,t2); \
+ *tl++ = txdr_unsigned(s); \
+ *(tl+((t2>>2)-2)) = 0; \
+ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \
+ } else if (error = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \
+ m_freem(mreq); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvdone \
+ nfsmout: \
+ return(error)
+
+#define nfsm_reply(s) \
+ { \
+ nfsd->nd_repstat = error; \
+ if (error) \
+ (void) nfs_rephead(0, nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ else \
+ (void) nfs_rephead((s), nfsd, error, cache, &frev, \
+ mrq, &mb, &bpos); \
+ m_freem(mrep); \
+ mreq = *mrq; \
+ if (error) \
+ return(0); \
+ }
+
+#define nfsm_adv(s) \
+ t1 = mtod(md, caddr_t)+md->m_len-dpos; \
+ if (t1 >= (s)) { \
+ dpos += (s); \
+ } else if (error = nfs_adv(&md, &dpos, (s), t1)) { \
+ m_freem(mrep); \
+ goto nfsmout; \
+ }
+
+#define nfsm_srvmtofh(f) \
+ nfsm_dissect(tl, u_long *, NFSX_FH); \
+ bcopy((caddr_t)tl, (caddr_t)f, NFSX_FH)
+
+#define nfsm_clget \
+ if (bp >= be) { \
+ if (mp == mb) \
+ mp->m_len += bp-bpos; \
+ MGET(mp, M_WAIT, MT_DATA); \
+ MCLGET(mp, M_WAIT); \
+ mp->m_len = NFSMSIZ(mp); \
+ mp2->m_next = mp; \
+ mp2 = mp; \
+ bp = mtod(mp, caddr_t); \
+ be = bp+mp->m_len; \
+ } \
+ tl = (u_long *)bp
+
+#define nfsm_srvfillattr \
+ fp->fa_type = vtonfs_type(vap->va_type); \
+ fp->fa_mode = vtonfs_mode(vap->va_type, vap->va_mode); \
+ fp->fa_nlink = txdr_unsigned(vap->va_nlink); \
+ fp->fa_uid = txdr_unsigned(vap->va_uid); \
+ fp->fa_gid = txdr_unsigned(vap->va_gid); \
+ if (nfsd->nd_nqlflag == NQL_NOVAL) { \
+ fp->fa_nfsblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nfsrdev = 0xffffffff; \
+ else \
+ fp->fa_nfsrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nfsfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nfsfileid = txdr_unsigned(vap->va_fileid); \
+ fp->fa_nfssize = txdr_unsigned(vap->va_size); \
+ fp->fa_nfsblocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); \
+ txdr_nfstime(&vap->va_atime, &fp->fa_nfsatime); \
+ txdr_nfstime(&vap->va_mtime, &fp->fa_nfsmtime); \
+ fp->fa_nfsctime.nfs_sec = txdr_unsigned(vap->va_ctime.ts_sec); \
+ fp->fa_nfsctime.nfs_usec = txdr_unsigned(vap->va_gen); \
+ } else { \
+ fp->fa_nqblocksize = txdr_unsigned(vap->va_blocksize); \
+ if (vap->va_type == VFIFO) \
+ fp->fa_nqrdev = 0xffffffff; \
+ else \
+ fp->fa_nqrdev = txdr_unsigned(vap->va_rdev); \
+ fp->fa_nqfsid = txdr_unsigned(vap->va_fsid); \
+ fp->fa_nqfileid = txdr_unsigned(vap->va_fileid); \
+ txdr_hyper(&vap->va_size, &fp->fa_nqsize); \
+ txdr_hyper(&vap->va_bytes, &fp->fa_nqbytes); \
+ txdr_nqtime(&vap->va_atime, &fp->fa_nqatime); \
+ txdr_nqtime(&vap->va_mtime, &fp->fa_nqmtime); \
+ txdr_nqtime(&vap->va_ctime, &fp->fa_nqctime); \
+ fp->fa_nqflags = txdr_unsigned(vap->va_flags); \
+ fp->fa_nqgen = txdr_unsigned(vap->va_gen); \
+ txdr_hyper(&vap->va_filerev, &fp->fa_nqfilerev); \
+ }
+
diff --git a/sys/nfsserver/nfsrvcache.h b/sys/nfsserver/nfsrvcache.h
new file mode 100644
index 0000000..26da2c2
--- /dev/null
+++ b/sys/nfsserver/nfsrvcache.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfsrvcache.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Definitions for the server recent request cache
+ */
+
+#define NFSRVCACHESIZ 256
+
+struct nfsrvcache {
+ struct nfsrvcache *rc_forw; /* Hash chain links */
+ struct nfsrvcache **rc_back; /* Hash chain links */
+ struct nfsrvcache *rc_next; /* Lru list */
+ struct nfsrvcache **rc_prev; /* Lru list */
+ u_long rc_xid; /* rpc id number */
+ union {
+ struct mbuf *ru_repmb; /* Reply mbuf list OR */
+ int ru_repstat; /* Reply status */
+ } rc_un;
+ union nethostaddr rc_haddr; /* Host address */
+ short rc_proc; /* rpc proc number */
+ u_char rc_state; /* Current state of request */
+ u_char rc_flag; /* Flag bits */
+};
+
+#define rc_reply rc_un.ru_repmb
+#define rc_status rc_un.ru_repstat
+#define rc_inetaddr rc_haddr.had_inetaddr
+#define rc_nam rc_haddr.had_nam
+
+/* Cache entry states */
+#define RC_UNUSED 0
+#define RC_INPROG 1
+#define RC_DONE 2
+
+/* Return values */
+#define RC_DROPIT 0
+#define RC_REPLY 1
+#define RC_DOIT 2
+#define RC_CHECKIT 3
+
+/* Flag bits */
+#define RC_LOCKED 0x01
+#define RC_WANTED 0x02
+#define RC_REPSTATUS 0x04
+#define RC_REPMBUF 0x08
+#define RC_NQNFS 0x10
+#define RC_INETADDR 0x20
+#define RC_NAM 0x40
diff --git a/sys/nfsserver/nfsrvstats.h b/sys/nfsserver/nfsrvstats.h
new file mode 100644
index 0000000..261fd42
--- /dev/null
+++ b/sys/nfsserver/nfsrvstats.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ *
+ * @(#)nfs.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Tunable constants for nfs
+ */
+
+#define NFS_MAXIOVEC 34
+#define NFS_HZ 25 /* Ticks per second for NFS timeouts */
+#define NFS_TIMEO (1*NFS_HZ) /* Default timeout = 1 second */
+#define NFS_MINTIMEO (1*NFS_HZ) /* Min timeout to use */
+#define NFS_MAXTIMEO (60*NFS_HZ) /* Max timeout to backoff to */
+#define NFS_MINIDEMTIMEO (5*NFS_HZ) /* Min timeout for non-idempotent ops*/
+#define NFS_MAXREXMIT 100 /* Stop counting after this many */
+#define NFS_MAXWINDOW 1024 /* Max number of outstanding requests */
+#define NFS_RETRANS 10 /* Num of retrans for soft mounts */
+#define NFS_MAXGRPS 16 /* Max. size of groups list */
+#define NFS_MINATTRTIMO 5 /* Attribute cache timeout in sec */
+#define NFS_MAXATTRTIMO 60
+#define NFS_WSIZE 8192 /* Def. write data size <= 8192 */
+#define NFS_RSIZE 8192 /* Def. read data size <= 8192 */
+#define NFS_DEFRAHEAD 1 /* Def. read ahead # blocks */
+#define NFS_MAXRAHEAD 4 /* Max. read ahead # blocks */
+#define NFS_MAXREADDIR NFS_MAXDATA /* Max. size of directory read */
+#define NFS_MAXUIDHASH 64 /* Max. # of hashed uid entries/mp */
+#define NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */
+#define NFS_DIRBLKSIZ 1024 /* Size of an NFS directory block */
+#define NMOD(a) ((a) % nfs_asyncdaemons)
+
+/*
+ * Set the attribute timeout based on how recently the file has been modified.
+ */
+#define NFS_ATTRTIMEO(np) \
+ ((((np)->n_flag & NMODIFIED) || \
+ (time.tv_sec - (np)->n_mtime) / 10 < NFS_MINATTRTIMO) ? NFS_MINATTRTIMO : \
+ ((time.tv_sec - (np)->n_mtime) / 10 > NFS_MAXATTRTIMO ? NFS_MAXATTRTIMO : \
+ (time.tv_sec - (np)->n_mtime) / 10))
+
+/*
+ * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs
+ * should ever try and use it.
+ */
+struct nfsd_args {
+ int sock; /* Socket to serve */
+ caddr_t name; /* Client address for connection based sockets */
+ int namelen; /* Length of name */
+};
+
+struct nfsd_srvargs {
+ struct nfsd *nsd_nfsd; /* Pointer to in kernel nfsd struct */
+ uid_t nsd_uid; /* Effective uid mapped to cred */
+ u_long nsd_haddr; /* Ip address of client */
+ struct ucred nsd_cr; /* Cred. uid maps to */
+ int nsd_authlen; /* Length of auth string (ret) */
+ char *nsd_authstr; /* Auth string (ret) */
+};
+
+struct nfsd_cargs {
+ char *ncd_dirp; /* Mount dir path */
+ uid_t ncd_authuid; /* Effective uid */
+ int ncd_authtype; /* Type of authenticator */
+ int ncd_authlen; /* Length of authenticator string */
+ char *ncd_authstr; /* Authenticator string */
+};
+
+/*
+ * Stats structure
+ */
+struct nfsstats {
+ int attrcache_hits;
+ int attrcache_misses;
+ int lookupcache_hits;
+ int lookupcache_misses;
+ int direofcache_hits;
+ int direofcache_misses;
+ int biocache_reads;
+ int read_bios;
+ int read_physios;
+ int biocache_writes;
+ int write_bios;
+ int write_physios;
+ int biocache_readlinks;
+ int readlink_bios;
+ int biocache_readdirs;
+ int readdir_bios;
+ int rpccnt[NFS_NPROCS];
+ int rpcretries;
+ int srvrpccnt[NFS_NPROCS];
+ int srvrpc_errs;
+ int srv_errs;
+ int rpcrequests;
+ int rpctimeouts;
+ int rpcunexpected;
+ int rpcinvalid;
+ int srvcache_inproghits;
+ int srvcache_idemdonehits;
+ int srvcache_nonidemdonehits;
+ int srvcache_misses;
+ int srvnqnfs_leases;
+ int srvnqnfs_maxleases;
+ int srvnqnfs_getleases;
+};
+
+/*
+ * Flags for nfssvc() system call.
+ */
+#define NFSSVC_BIOD 0x002
+#define NFSSVC_NFSD 0x004
+#define NFSSVC_ADDSOCK 0x008
+#define NFSSVC_AUTHIN 0x010
+#define NFSSVC_GOTAUTH 0x040
+#define NFSSVC_AUTHINFAIL 0x080
+#define NFSSVC_MNTD 0x100
+
+/*
+ * The set of signals the interrupt an I/O in progress for NFSMNT_INT mounts.
+ * What should be in this set is open to debate, but I believe that since
+ * I/O system calls on ufs are never interrupted by signals the set should
+ * be minimal. My reasoning is that many current programs that use signals
+ * such as SIGALRM will not expect file I/O system calls to be interrupted
+ * by them and break.
+ */
+#ifdef KERNEL
+#define NFSINT_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+/*
+ * Socket errors ignored for connectionless sockets??
+ * For now, ignore them all
+ */
+#define NFSIGNORE_SOERROR(s, e) \
+ ((e) != EINTR && (e) != ERESTART && (e) != EWOULDBLOCK && \
+ ((s) & PR_CONNREQUIRED) == 0)
+
+/*
+ * Nfs outstanding request list element
+ */
+struct nfsreq {
+ struct nfsreq *r_next;
+ struct nfsreq *r_prev;
+ struct mbuf *r_mreq;
+ struct mbuf *r_mrep;
+ struct mbuf *r_md;
+ caddr_t r_dpos;
+ struct nfsmount *r_nmp;
+ struct vnode *r_vp;
+ u_long r_xid;
+ int r_flags; /* flags on request, see below */
+ int r_retry; /* max retransmission count */
+ int r_rexmit; /* current retrans count */
+ int r_timer; /* tick counter on reply */
+ int r_procnum; /* NFS procedure number */
+ int r_rtt; /* RTT for rpc */
+ struct proc *r_procp; /* Proc that did I/O system call */
+};
+
+/* Flag values for r_flags */
+#define R_TIMING 0x01 /* timing request (in mntp) */
+#define R_SENT 0x02 /* request has been sent */
+#define R_SOFTTERM 0x04 /* soft mnt, too many retries */
+#define R_INTR 0x08 /* intr mnt, signal pending */
+#define R_SOCKERR 0x10 /* Fatal error on socket */
+#define R_TPRINTFMSG 0x20 /* Did a tprintf msg. */
+#define R_MUSTRESEND 0x40 /* Must resend request */
+#define R_GETONEREP 0x80 /* Probe for one reply only */
+
+struct nfsstats nfsstats;
+
+/*
+ * A list of nfssvc_sock structures is maintained with all the sockets
+ * that require service by the nfsd.
+ * The nfsuid structs hang off of the nfssvc_sock structs in both lru
+ * and uid hash lists.
+ */
+#define NUIDHASHSIZ 32
+#define NUIDHASH(uid) ((uid) & (NUIDHASHSIZ - 1))
+
+/*
+ * Network address hash list element
+ */
+union nethostaddr {
+ u_long had_inetaddr;
+ struct mbuf *had_nam;
+};
+
+struct nfsuid {
+ struct nfsuid *nu_lrunext; /* MUST be first */
+ struct nfsuid *nu_lruprev;
+ struct nfsuid *nu_hnext;
+ struct nfsuid *nu_hprev;
+ int nu_flag; /* Flags */
+ uid_t nu_uid; /* Uid mapped by this entry */
+ union nethostaddr nu_haddr; /* Host addr. for dgram sockets */
+ struct ucred nu_cr; /* Cred uid mapped to */
+};
+
+#define nu_inetaddr nu_haddr.had_inetaddr
+#define nu_nam nu_haddr.had_nam
+/* Bits for nu_flag */
+#define NU_INETADDR 0x1
+
+struct nfssvc_sock {
+ struct nfsuid *ns_lrunext; /* MUST be first */
+ struct nfsuid *ns_lruprev;
+ struct nfssvc_sock *ns_next;
+ struct nfssvc_sock *ns_prev;
+ int ns_flag;
+ u_long ns_sref;
+ struct file *ns_fp;
+ struct socket *ns_so;
+ int ns_solock;
+ struct mbuf *ns_nam;
+ int ns_cc;
+ struct mbuf *ns_raw;
+ struct mbuf *ns_rawend;
+ int ns_reclen;
+ struct mbuf *ns_rec;
+ struct mbuf *ns_recend;
+ int ns_numuids;
+ struct nfsuid *ns_uidh[NUIDHASHSIZ];
+};
+
+/* Bits for "ns_flag" */
+#define SLP_VALID 0x01
+#define SLP_DOREC 0x02
+#define SLP_NEEDQ 0x04
+#define SLP_DISCONN 0x08
+#define SLP_GETSTREAM 0x10
+#define SLP_INIT 0x20
+#define SLP_WANTINIT 0x40
+
+#define SLP_ALLFLAGS 0xff
+
+/*
+ * One of these structures is allocated for each nfsd.
+ */
+struct nfsd {
+ struct nfsd *nd_next; /* Must be first */
+ struct nfsd *nd_prev;
+ int nd_flag; /* NFSD_ flags */
+ struct nfssvc_sock *nd_slp; /* Current socket */
+ struct mbuf *nd_nam; /* Client addr for datagram req. */
+ struct mbuf *nd_mrep; /* Req. mbuf list */
+ struct mbuf *nd_md;
+ caddr_t nd_dpos; /* Position in list */
+ int nd_procnum; /* RPC procedure number */
+ u_long nd_retxid; /* RPC xid */
+ int nd_repstat; /* Reply status value */
+ struct ucred nd_cr; /* Credentials for req. */
+ int nd_nqlflag; /* Leasing flag */
+ int nd_duration; /* Lease duration */
+ int nd_authlen; /* Authenticator len */
+ u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */
+ struct proc *nd_procp; /* Proc ptr */
+};
+
+#define NFSD_WAITING 0x01
+#define NFSD_CHECKSLP 0x02
+#define NFSD_REQINPROG 0x04
+#define NFSD_NEEDAUTH 0x08
+#define NFSD_AUTHFAIL 0x10
+#endif /* KERNEL */
OpenPOWER on IntegriCloud