diff options
Diffstat (limited to 'sys/nfsserver')
-rw-r--r-- | sys/nfsserver/nfs.h | 279 | ||||
-rw-r--r-- | sys/nfsserver/nfs_serv.c | 2904 | ||||
-rw-r--r-- | sys/nfsserver/nfs_srvcache.c | 50 | ||||
-rw-r--r-- | sys/nfsserver/nfs_srvsock.c | 518 | ||||
-rw-r--r-- | sys/nfsserver/nfs_srvsubs.c | 1035 | ||||
-rw-r--r-- | sys/nfsserver/nfs_syscalls.c | 571 | ||||
-rw-r--r-- | sys/nfsserver/nfsm_subs.h | 333 | ||||
-rw-r--r-- | sys/nfsserver/nfsproto.h | 441 | ||||
-rw-r--r-- | sys/nfsserver/nfsrvcache.h | 4 | ||||
-rw-r--r-- | sys/nfsserver/nfsrvstats.h | 279 |
10 files changed, 4937 insertions, 1477 deletions
diff --git a/sys/nfsserver/nfs.h b/sys/nfsserver/nfs.h index cbf80c6..e1a0f07 100644 --- a/sys/nfsserver/nfs.h +++ b/sys/nfsserver/nfs.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs.h 8.1 (Berkeley) 6/10/93 - * $Id: nfs.h,v 1.8 1994/11/02 00:11:00 wollman Exp $ + * $Id: nfs.h,v 1.9 1995/02/14 06:22:18 phk Exp $ */ #ifndef _NFS_NFS_H_ @@ -45,11 +45,12 @@ */ #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_TICKINTVL 5 /* Desired time for a tick (msec) */ +#define NFS_HZ (hz / nfs_ticks) /* Ticks/sec */ +#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 */ @@ -62,13 +63,81 @@ #endif #define NFS_WSIZE 8192 /* Def. write data size <= 8192 */ #define NFS_RSIZE 8192 /* Def. read data size <= 8192 */ +#define NFS_READDIRSIZE 8192 /* Def. readdir size */ #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 NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */ +#define NFS_MAXGATHERDELAY 100 /* Max. write gather delay (msec) */ +#ifndef NFS_GATHERDELAY +#define NFS_GATHERDELAY 10 /* Default write gather delay (msec) */ +#endif +#define NFS_DIRBLKSIZ 4096 /* Must be a multiple of DIRBLKSIZ */ + +/* + * Oddballs + */ #define NMOD(a) ((a) % nfs_asyncdaemons) +#define NFS_CMPFH(n, f, s) \ + ((n)->n_fhsize == (s) && !bcmp((caddr_t)(n)->n_fhp, (caddr_t)(f), (s))) +#define NFS_ISV3(v) (VFSTONFS((v)->v_mount)->nm_flag & NFSMNT_NFSV3) +#define NFS_SRVMAXDATA(n) \ + (((n)->nd_flag & ND_NFSV3) ? (((n)->nd_nam2) ? \ + NFS_MAXDGRAMDATA : NFS_MAXDATA) : NFS_V2MAXDATA) + +/* + * XXX + * sys/buf.h should be editted to change B_APPENDWRITE --> B_NEEDCOMMIT, but + * until then... + * Same goes for sys/malloc.h, which needs M_NFSDIROFF, + * M_NFSRVDESC and M_NFSBIGFH added. + * The VA_EXCLUSIVE flag should be added for va_vaflags and set for an + * exclusive create. + * The B_INVAFTERWRITE flag should be set to whatever is required by the + * buffer cache code to say "Invalidate the block after it is written back". + */ +#ifndef B_NEEDCOMMIT +#define B_NEEDCOMMIT B_APPENDWRITE +#endif +#ifndef M_NFSRVDESC +#define M_NFSRVDESC M_TEMP +#endif +#ifndef M_NFSDIROFF +#define M_NFSDIROFF M_TEMP +#endif +#ifndef M_NFSBIGFH +#define M_NFSBIGFH M_TEMP +#endif +#ifndef VA_EXCLUSIVE +#define VA_EXCLUSIVE 0 +#endif +#ifdef __FreeBSD__ +#define B_INVAFTERWRITE B_NOCACHE +#else +#define B_INVAFTERWRITE B_INVAL +#endif + +/* + * These ifdefs try to handle the differences between the various 4.4BSD-Lite + * based vfs interfaces. + * btw: NetBSD-current does have a VOP_LEASDE(), but I don't know how to + * differentiate between NetBSD-1.0 and NetBSD-current, so.. + * I also don't know about BSDi's 2.0 release. + */ +#if !defined(HAS_VOPLEASE) && !defined(__FreeBSD__) && !defined(__NetBSD__) +#define HAS_VOPLEASE 1 +#endif +#if !defined(HAS_VOPREVOKE) && !defined(__FreeBSD__) && !defined(__NetBSD__) +#define HAS_VOPREVOKE 1 +#endif + +/* + * The IO_METASYNC flag should be implemented for local file systems. + * (Until then, it is nothin at all.) + */ +#ifndef IO_METASYNC +#define IO_METASYNC 0 +#endif /* * Set the attribute timeout based on how recently the file has been modified. @@ -80,6 +149,20 @@ (time.tv_sec - (np)->n_mtime) / 10)) /* + * Expected allocation sizes for major data structures. If the actual size + * of the structure exceeds these sizes, then malloc() will be allocating + * almost twice the memory required. This is used in nfs_init() to warn + * the sysadmin that the size of a structure should be reduced. + * (These sizes are always a power of 2. If the kernel malloc() changes + * to one that does not allocate space in powers of 2 size, then this all + * becomes bunk!) + */ +#define NFS_NODEALLOC 256 +#define NFS_MNTALLOC 512 +#define NFS_SVCALLOC 256 +#define NFS_UIDALLOC 128 + +/* * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs * should ever try and use it. */ @@ -95,7 +178,12 @@ struct nfsd_srvargs { 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) */ + u_char *nsd_authstr; /* Auth string (ret) */ + int nsd_verflen; /* and the verfier */ + u_char *nsd_verfstr; + struct timeval nsd_timestamp; /* timestamp from verifier */ + u_long nsd_ttl; /* credential ttl (sec) */ + NFSKERBKEY_T nsd_key; /* Session key */ }; struct nfsd_cargs { @@ -103,7 +191,10 @@ struct nfsd_cargs { uid_t ncd_authuid; /* Effective uid */ int ncd_authtype; /* Type of authenticator */ int ncd_authlen; /* Length of authenticator string */ - char *ncd_authstr; /* Authenticator string */ + u_char *ncd_authstr; /* Authenticator string */ + int ncd_verflen; /* and the verifier */ + u_char *ncd_verfstr; + NFSKERBKEY_T ncd_key; /* Session key */ }; /* @@ -142,6 +233,7 @@ struct nfsstats { int srvnqnfs_leases; int srvnqnfs_maxleases; int srvnqnfs_getleases; + int srvvop_writes; }; /* @@ -173,7 +265,7 @@ struct nfsstats { * such as SIGALRM will not expect file I/O system calls to be interrupted * by them and break. */ -#ifdef KERNEL +#if defined(KERNEL) || defined(_KERNEL) struct uio; struct buf; struct vattr; struct nameidata; /* XXX */ @@ -224,17 +316,29 @@ TAILQ_HEAD(, nfsreq) nfs_reqq; #define R_MUSTRESEND 0x40 /* Must resend request */ #define R_GETONEREP 0x80 /* Probe for one reply only */ -extern 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 +#ifndef NFS_UIDHASHSIZ +#define NFS_UIDHASHSIZ 29 /* Tune the size of nfssvc_sock with this */ +#endif #define NUIDHASH(sock, uid) \ - (&(sock)->ns_uidhashtbl[(uid) & (sock)->ns_uidhash]) + (&(sock)->ns_uidhashtbl[(uid) % NFS_UIDHASHSIZ]) +#ifndef NFS_WDELAYHASHSIZ +#define NFS_WDELAYHASHSIZ 16 /* and with this */ +#endif +#define NWDELAYHASH(sock, f) \ + (&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ]) +#ifndef NFS_MUIDHASHSIZ +#define NFS_MUIDHASHSIZ 67 /* Tune the size of nfsmount with this */ +#endif +#define NMUIDHASH(nmp, uid) \ + (&(nmp)->nm_uidhashtbl[(uid) % NFS_MUIDHASHSIZ]) +#define NFSNOHASH(fhsum) \ + (&nfsnodehashtbl[(fhsum) & nfsnodehash]) /* * Network address hash list element @@ -248,35 +352,41 @@ struct nfsuid { TAILQ_ENTRY(nfsuid) nu_lru; /* LRU chain */ LIST_ENTRY(nfsuid) nu_hash; /* Hash list */ 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 */ + int nu_expire; /* Expiry time (sec) */ + struct timeval nu_timestamp; /* Kerb. timestamp */ + u_long nu_nickname; /* Nickname on server */ + NFSKERBKEY_T nu_key; /* and session key */ }; #define nu_inetaddr nu_haddr.had_inetaddr #define nu_nam nu_haddr.had_nam /* Bits for nu_flag */ #define NU_INETADDR 0x1 +#define NU_NAM 0x2 +#define NU_NETFAM(u) (((u)->nu_flag & NU_INETADDR) ? AF_INET : AF_ISO) struct nfssvc_sock { TAILQ_ENTRY(nfssvc_sock) ns_chain; /* List of all nfssvc_sock's */ TAILQ_HEAD(, nfsuid) ns_uidlruhead; - LIST_HEAD(, nfsuid) *ns_uidhashtbl; - u_long ns_uidhash; - - 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; + struct mbuf *ns_frag; + int ns_flag; + int ns_solock; + int ns_cc; + int ns_reclen; int ns_numuids; + u_long ns_sref; + LIST_HEAD(, nfsrv_descript) ns_tq; /* Write gather lists */ + LIST_HEAD(, nfsuid) ns_uidhashtbl[NFS_UIDHASHSIZ]; + LIST_HEAD(nfsrvw_delayhash, nfsrv_descript) ns_wdelayhashtbl[NFS_WDELAYHASHSIZ]; }; /* Bits for "ns_flag" */ @@ -285,6 +395,7 @@ struct nfssvc_sock { #define SLP_NEEDQ 0x04 #define SLP_DISCONN 0x08 #define SLP_GETSTREAM 0x10 +#define SLP_LASTFRAG 0x20 #define SLP_ALLFLAGS 0xff TAILQ_HEAD(, nfssvc_sock) nfssvc_sockhead; @@ -296,73 +407,124 @@ int nfssvc_sockhead_flag; * One of these structures is allocated for each nfsd. */ struct nfsd { - TAILQ_ENTRY(nfsd) nd_chain; /* List of all nfsd's */ - 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 */ - u_long nd_duration; /* Lease duration */ - int nd_authlen; /* Authenticator len */ - u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */ - struct proc *nd_procp; /* Proc ptr */ + TAILQ_ENTRY(nfsd) nfsd_chain; /* List of all nfsd's */ + int nfsd_flag; /* NFSD_ flags */ + struct nfssvc_sock *nfsd_slp; /* Current socket */ + int nfsd_authlen; /* Authenticator len */ + u_char nfsd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */ + int nfsd_verflen; /* and the Verifier */ + u_char nfsd_verfstr[RPCVERF_MAXSIZ]; + struct proc *nfsd_procp; /* Proc ptr */ + struct nfsrv_descript *nfsd_nd; /* Associated nfsrv_descript */ }; -/* Bits for "nd_flag" */ +/* Bits for "nfsd_flag" */ #define NFSD_WAITING 0x01 #define NFSD_REQINPROG 0x02 #define NFSD_NEEDAUTH 0x04 #define NFSD_AUTHFAIL 0x08 +/* + * This structure is used by the server for describing each request. + * Some fields are used only when write request gathering is performed. + */ +struct nfsrv_descript { + u_quad_t nd_time; /* Write deadline (usec) */ + off_t nd_off; /* Start byte offset */ + off_t nd_eoff; /* and end byte offset */ + LIST_ENTRY(nfsrv_descript) nd_hash; /* Hash list */ + LIST_ENTRY(nfsrv_descript) nd_tq; /* and timer list */ + LIST_HEAD(,nfsrv_descript) nd_coalesce; /* coalesced writes */ + struct mbuf *nd_mrep; /* Request mbuf list */ + struct mbuf *nd_md; /* Current dissect mbuf */ + struct mbuf *nd_mreq; /* Reply mbuf list */ + struct mbuf *nd_nam; /* and socket addr */ + struct mbuf *nd_nam2; /* return socket addr */ + caddr_t nd_dpos; /* Current dissect pos */ + int nd_procnum; /* RPC # */ + int nd_stable; /* storage type */ + int nd_flag; /* nd_flag */ + int nd_len; /* Length of this write */ + int nd_repstat; /* Reply status */ + u_long nd_retxid; /* Reply xid */ + u_long nd_duration; /* Lease duration */ + struct timeval nd_starttime; /* Time RPC initiated */ + fhandle_t nd_fh; /* File handle */ + struct ucred nd_cr; /* Credentials */ +}; + +/* Bits for "nd_flag" */ +#define ND_READ LEASE_READ +#define ND_WRITE LEASE_WRITE +#define ND_CHECK 0x04 +#define ND_LEASE (ND_READ | ND_WRITE | ND_CHECK) +#define ND_NFSV3 0x08 +#define ND_NQNFS 0x10 +#define ND_KERBNICK 0x20 +#define ND_KERBFULL 0x40 +#define ND_KERBAUTH (ND_KERBNICK | ND_KERBFULL) + TAILQ_HEAD(, nfsd) nfsd_head; int nfsd_head_flag; #define NFSD_CHECKSLP 0x01 +/* + * These macros compare nfsrv_descript structures. + */ +#define NFSW_CONTIG(o, n) \ + ((o)->nd_eoff >= (n)->nd_off && \ + !bcmp((caddr_t)&(o)->nd_fh, (caddr_t)&(n)->nd_fh, NFSX_V3FH)) + +#define NFSW_SAMECRED(o, n) \ + (((o)->nd_flag & ND_KERBAUTH) == ((n)->nd_flag & ND_KERBAUTH) && \ + !bcmp((caddr_t)&(o)->nd_cr, (caddr_t)&(n)->nd_cr, \ + sizeof (struct ucred))) + int nfs_reply __P((struct nfsreq *)); -int nfs_getreq __P((struct nfsd *,int)); +int nfs_getreq __P((struct nfsrv_descript *,struct nfsd *,int)); int nfs_send __P((struct socket *,struct mbuf *,struct mbuf *,struct nfsreq *)); -int nfs_rephead __P((int,struct nfsd *,int,int,u_quad_t *,struct mbuf **,struct mbuf **,caddr_t *)); +int nfs_rephead __P((int,struct nfsrv_descript *,struct nfssvc_sock *,int,int,u_quad_t *,struct mbuf **,struct mbuf **,caddr_t *)); int nfs_sndlock __P((int *,struct nfsreq *)); int nfs_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *)); int nfs_vinvalbuf __P((struct vnode *,int,struct ucred *,struct proc *,int)); int nfs_readrpc __P((struct vnode *,struct uio *,struct ucred *)); -int nfs_writerpc __P((struct vnode *,struct uio *,struct ucred *,int)); +int nfs_writerpc __P((struct vnode *,struct uio *,struct ucred *,int *,int *)); int nfs_readdirrpc __P((register struct vnode *,struct uio *,struct ucred *)); +int nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *)); int nfs_asyncio __P((struct buf *,struct ucred *)); int nfs_doio __P((struct buf *,struct ucred *,struct proc *)); int nfs_readlinkrpc __P((struct vnode *,struct uio *,struct ucred *)); int nfs_sigintr __P((struct nfsmount *,struct nfsreq *r,struct proc *)); -int nfs_readdirlookrpc __P((struct vnode *,register struct uio *,struct ucred *)); +int nfs_readdirplusrpc __P((struct vnode *,register struct uio *,struct ucred *)); int nfsm_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *)); -int nfsrv_fhtovp __P((fhandle_t *,int,struct vnode **,struct ucred *,struct nfssvc_sock *,struct mbuf *,int *)); +void nfsm_srvfattr __P((struct nfsrv_descript *,struct vattr *,struct nfs_fattr *)); +void nfsm_srvwcc __P((struct nfsrv_descript *,int,struct vattr *,int,struct vattr *,struct mbuf **,char **)); +void nfsm_srvpostopattr __P((struct nfsrv_descript *,int,struct vattr *,struct mbuf **,char **)); +int nfsrv_fhtovp __P((fhandle_t *,int,struct vnode **,struct ucred *,struct nfssvc_sock *,struct mbuf *,int *,int)); int nfsrv_access __P((struct vnode *,int,struct ucred *,int,struct proc *)); int netaddr_match __P((int,union nethostaddr *,struct mbuf *)); int nfs_request __P((struct vnode *,struct mbuf *,int,struct proc *,struct ucred *,struct mbuf **,struct mbuf **,caddr_t *)); int nfs_loadattrcache __P((struct vnode **,struct mbuf **,caddr_t *,struct vattr *)); -int nfs_namei __P((struct nameidata *,fhandle_t *,int,struct nfssvc_sock *,struct mbuf *,struct mbuf **,caddr_t *,struct proc *)); +int nfs_namei __P((struct nameidata *,fhandle_t *,int,struct nfssvc_sock *,struct mbuf *,struct mbuf **,caddr_t *,struct vnode **,struct proc *,int)); void nfsm_adj __P((struct mbuf *,int,int)); int nfsm_mbuftouio __P((struct mbuf **,struct uio *,int,caddr_t *)); void nfsrv_initcache __P((void)); int nfs_rcvlock __P((struct nfsreq *)); -int nfs_getauth __P((struct nfsmount *,struct nfsreq *,struct ucred *,int *,char **,int *)); +int nfs_getauth __P((struct nfsmount *,struct nfsreq *,struct ucred *,char **,int *,char *,int *,NFSKERBKEY_T)); +int nfs_getnickauth __P((struct nfsmount *,struct ucred *,char **,int *,char *,int)); +int nfs_savenickauth __P((struct nfsmount *,struct ucred *,int,NFSKERBKEY_T,struct mbuf **,char **,struct mbuf *)); int nfs_msg __P((struct proc *,char *,char *)); int nfs_adv __P((struct mbuf **,caddr_t *,int,int)); int nfsrv_getstream __P((struct nfssvc_sock *,int)); void nfs_nhinit __P((void)); void nfs_timer __P((void*)); -struct nfsnodehashhead * nfs_hash __P((nfsv2fh_t *)); +u_long nfs_hash __P((nfsfh_t *,int)); int nfssvc_iod __P((struct proc *)); int nfssvc_nfsd __P((struct nfsd_srvargs *,caddr_t,struct proc *)); int nfssvc_addsock __P((struct file *,struct mbuf *)); -int nfsrv_dorec __P((struct nfssvc_sock *,struct nfsd *)); -int nfsrv_getcache __P((struct mbuf *,struct nfsd *,struct mbuf **)); -void nfsrv_updatecache __P((struct mbuf *,struct nfsd *,int,struct mbuf *)); +int nfsrv_dorec __P((struct nfssvc_sock *,struct nfsd *,struct nfsrv_descript **)); +int nfsrv_getcache __P((struct nfsrv_descript *,struct nfssvc_sock *,struct mbuf **)); +void nfsrv_updatecache __P((struct nfsrv_descript *,int,struct mbuf *)); int mountnfs __P((struct nfs_args *,struct mount *,struct mbuf *,char *,char *,struct vnode **)); int nfs_connect __P((struct nfsmount *,struct nfsreq *)); int nfs_getattrcache __P((struct vnode *,struct vattr *)); @@ -370,9 +532,20 @@ int nfsm_strtmbuf __P((struct mbuf **,char **,char *,long)); int nfs_bioread __P((struct vnode *,struct uio *,int,struct ucred *)); int nfsm_uiotombuf __P((struct uio *,struct mbuf **,int,caddr_t *)); void nfsrv_init __P((int)); +void nfs_clearcommit __P((struct mount *)); +int nfsrv_errmap __P((struct nfsrv_descript *, int)); +void nfsrvw_coalesce __P((struct nfsrv_descript *,struct nfsrv_descript *)); +void nfsrvw_sort __P((gid_t [],int)); +void nfsrv_setcred __P((struct ucred *,struct ucred *)); +int nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int)); +int nfs_writebp __P((struct buf *,int)); int nfsrv_vput __P(( struct vnode * )); int nfsrv_vrele __P(( struct vnode * )); int nfsrv_vmio __P(( struct vnode * )); +int nfsrv_writegather __P((struct nfsrv_descript **, struct nfssvc_sock *, + struct proc *, struct mbuf **)); +int nfs_fsinfo __P((struct nfsmount *, struct vnode *, struct ucred *, + struct proc *p)); #endif /* KERNEL */ diff --git a/sys/nfsserver/nfs_serv.c b/sys/nfsserver/nfs_serv.c index 033dae4..f82db3b 100644 --- a/sys/nfsserver/nfs_serv.c +++ b/sys/nfsserver/nfs_serv.c @@ -34,11 +34,11 @@ * SUCH DAMAGE. * * @(#)nfs_serv.c 8.3 (Berkeley) 1/12/94 - * $Id: nfs_serv.c,v 1.14.2.1 1995/06/07 07:25:09 davidg Exp $ + * $Id: nfs_serv.c,v 1.15 1995/06/11 19:31:45 rgrimes Exp $ */ /* - * nfs version 2 server calls to vnode ops + * nfs version 2 and 3 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 @@ -53,6 +53,8 @@ * 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. + * For Version 3, nfsm_reply() does not return for the error case, since + * most version 3 rpcs return more than the status for error cases. */ #include <sys/param.h> @@ -62,67 +64,98 @@ #include <sys/namei.h> #include <sys/vnode.h> #include <sys/mount.h> +#include <sys/socket.h> +#include <sys/socketvar.h> #include <sys/mbuf.h> #include <sys/dirent.h> #include <sys/stat.h> +#include <sys/kernel.h> +#include <ufs/ufs/dir.h> #include <vm/vm.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.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, +extern enum vtype nv3tov_type[8]; +extern struct nfsstats nfsstats; +nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON, NFCHR, NFNON }; +nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, + NFFIFO, NFNON }; +int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000; /* - * nqnfs access service + * nfs v3 access service */ int -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; +nfsrv3_access(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; struct vnode *vp; - nfsv2fh_t nfh; + nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, rdonly, cache = 0, mode = 0; + int error = 0, rdonly, cache, getret; char *cp2; - struct mbuf *mb, *mreq; + struct mbuf *mb, *mreq, *mb2; + struct vattr vattr, *vap = &vattr; + u_long testmode, nfsmode; u_quad_t frev; +#ifndef nolint + cache = 0; +#endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - 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); + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(1, (struct vattr *)0); + return (0); + } + nfsmode = fxdr_unsigned(u_long, *tl); + if ((nfsmode & NFSV3ACCESS_READ) && + nfsrv_access(vp, VREAD, cred, rdonly, procp)) + nfsmode &= ~NFSV3ACCESS_READ; + if (vp->v_type == VDIR) + testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | + NFSV3ACCESS_DELETE); + else + testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); + if ((nfsmode & testmode) && + nfsrv_access(vp, VWRITE, cred, rdonly, procp)) + nfsmode &= ~testmode; + if (vp->v_type == VDIR) + testmode = NFSV3ACCESS_LOOKUP; + else + testmode = NFSV3ACCESS_EXECUTE; + if ((nfsmode & testmode) && + nfsrv_access(vp, VEXEC, cred, rdonly, procp)) + nfsmode &= ~testmode; + getret = VOP_GETATTR(vp, vap, cred, procp); nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, vap); + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = txdr_unsigned(nfsmode); nfsm_srvdone; } @@ -130,18 +163,21 @@ nqnfsrv_access(nfsd, mrep, md, dpos, cred, nam, mrq) * nfs getattr service */ int -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; +nfsrv_getattr(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { - register struct nfsv2_fattr *fp; + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register struct nfs_fattr *fp; struct vattr va; register struct vattr *vap = &va; struct vnode *vp; - nfsv2fh_t nfh; + nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; @@ -153,15 +189,19 @@ nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq) fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { nfsm_reply(0); - nqsrv_getl(vp, NQL_READ); - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); + return (0); + } + nqsrv_getl(vp, ND_READ); + error = VOP_GETATTR(vp, vap, cred, procp); nfsrv_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_reply(NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); + if (error) + return (0); + nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); + nfsm_srvfillattr(vap, fp); nfsm_srvdone; } @@ -169,68 +209,97 @@ nfsrv_getattr(nfsd, mrep, md, dpos, cred, nam, mrq) * nfs setattr service */ int -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; +nfsrv_setattr(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { - struct vattr va; + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + struct vattr va, preat; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; - register struct nfsv2_fattr *fp; + register struct nfs_fattr *fp; struct vnode *vp; - nfsv2fh_t nfh; + nfsfh_t nfh; fhandle_t *fhp; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, rdonly, cache; + int error = 0, rdonly, cache, preat_ret = 1, postat_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; - u_quad_t frev, frev2; + u_quad_t frev; + struct timespec guard; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL)); - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - 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) { + if (v3) { + nfsm_srvsattr(vap); + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + gcheck = fxdr_unsigned(int, *tl); + if (gcheck) { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + fxdr_nfsv3time(tl, &guard); + } + } else { + nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + /* + * 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 (sp->sa_size != nfs_xdrneg1) + vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_size); + if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) { #ifdef notyet - fxdr_nfstime(&sp->sa_nfsatime, &vap->va_atime); + fxdr_nfsv2time(&sp->sa_atime, &vap->va_atime); #else vap->va_atime.ts_sec = - fxdr_unsigned(long, sp->sa_nfsatime.nfs_sec); + fxdr_unsigned(long, sp->sa_atime.nfsv2_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 (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) + fxdr_nfsv2time(&sp->sa_mtime, &vap->va_mtime); + + } + + /* + * Now that we have all the fields, lets do it. + */ + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); + return (0); + } + nqsrv_getl(vp, ND_WRITE); + if (v3) { + error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp); + if (!error && gcheck && + (preat.va_ctime.ts_sec != guard.ts_sec || + preat.va_ctime.ts_nsec != guard.ts_nsec)) + error = NFSERR_NOT_SYNC; + if (error) { + nfsrv_vput(vp); + nfsm_reply(NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); + return (0); + } } /* @@ -246,27 +315,23 @@ nfsrv_setattr(nfsd, mrep, md, dpos, cred, nam, mrq) if (vp->v_type == VDIR) { error = EISDIR; goto out; - } else { - error = nfsrv_access(vp, VWRITE, cred, rdonly, - nfsd->nd_procp); - if (error) - goto out; - } - } - error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp); - if (error) { - nfsrv_vput(vp); - nfsm_reply(0); + } else if (error = nfsrv_access(vp, VWRITE, cred, rdonly, + procp)) + goto out; } - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); + error = VOP_SETATTR(vp, vap, cred, procp); + postat_ret = VOP_GETATTR(vp, vap, cred, procp); + if (!error) + error = postat_ret; out: nfsrv_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_reply(NFSX_WCCORFATTR(v3)); + if (v3) { + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); + return (0); + } else { + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(vap, fp); } nfsm_srvdone; } @@ -275,76 +340,74 @@ out: * nfs lookup rpc */ int -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; +nfsrv_lookup(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { - register struct nfsv2_fattr *fp; + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register struct nfs_fattr *fp; struct nameidata nd; - struct vnode *vp; - nfsv2fh_t nfh; + struct vnode *vp, *dirp; + nfsfh_t nfh; fhandle_t *fhp; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, cache, cache2, len; - u_long duration2; + int error = 0, cache, len, dirattr_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; - struct vattr va, *vap = &va; - u_quad_t frev, frev2; + struct vattr va, dirattr, *vap = &va; + u_quad_t frev; 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); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = LOOKUP; nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if (error) - nfsm_reply(0); - nqsrv_getl(nd.ni_startdir, NQL_READ); + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred, + procp); + nfsrv_vrele(dirp); + } + if (error) { + nfsm_reply(NFSX_POSTOPATTR(v3)); + nfsm_srvpostop_attr(dirattr_ret, &dirattr); + return (0); + } + nqsrv_getl(nd.ni_startdir, ND_READ); nfsrv_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; error = VFS_VPTOFH(vp, &fhp->fh_fid); + if (!error) + error = VOP_GETATTR(vp, vap, cred, procp); + nfsrv_vput(vp); + nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3)); if (error) { - nfsrv_vput(vp); - nfsm_reply(0); + nfsm_srvpostop_attr(dirattr_ret, &dirattr); + return (0); } - if (duration2) - (void) nqsrv_getlease(vp, &duration2, NQL_READ, nfsd, - nam, &cache2, &frev2, cred); - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); - nfsrv_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, v3); + if (v3) { + nfsm_srvpostop_attr(0, vap); + nfsm_srvpostop_attr(dirattr_ret, &dirattr); + } else { + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(vap, fp); } - nfsm_srvfhtom(fhp); - nfsm_build(fp, struct nfsv2_fattr *, NFSX_FATTR(nfsd->nd_nqlflag != NQL_NOVAL)); - nfsm_srvfillattr; nfsm_srvdone; } @@ -352,28 +415,36 @@ nfsrv_lookup(nfsd, mrep, md, dpos, cred, nam, mrq) * nfs readlink service */ int -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; +nfsrv_readlink(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; 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; + int error = 0, rdonly, cache, i, tlen, len, getret; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mb2, *mp2 = 0, *mp3 = 0, *mreq; + struct mbuf *mb, *mb2, *mp2, *mp3, *mreq; struct vnode *vp; - nfsv2fh_t nfh; + struct vattr attr; + nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; u_quad_t frev; +#ifndef nolint + mp2 = mp3 = (struct mbuf *)0; +#endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); len = 0; @@ -405,22 +476,33 @@ nfsrv_readlink(nfsd, mrep, md, dpos, cred, nam, mrq) uiop->uio_rw = UIO_READ; uiop->uio_segflg = UIO_SYSSPACE; uiop->uio_procp = (struct proc *)0; - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) { + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { m_freem(mp3); - nfsm_reply(0); + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvpostop_attr(1, (struct vattr *)0); + return (0); } if (vp->v_type != VLNK) { - error = EINVAL; + if (v3) + error = EINVAL; + else + error = ENXIO; goto out; } - nqsrv_getl(vp, NQL_READ); + nqsrv_getl(vp, ND_READ); error = VOP_READLINK(vp, uiop, cred); out: + getret = VOP_GETATTR(vp, &attr, cred, procp); nfsrv_vput(vp); if (error) m_freem(mp3); - nfsm_reply(NFSX_UNSIGNED); + nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED); + if (v3) { + nfsm_srvpostop_attr(getret, &attr); + if (error) + return (0); + } if (uiop->uio_resid > 0) { len -= uiop->uio_resid; tlen = nfsm_rndup(len); @@ -436,26 +518,31 @@ out: * nfs read service */ int -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; +nfsrv_read(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register struct iovec *iv; struct iovec *iv2; register struct mbuf *m; - register struct nfsv2_fattr *fp; + register struct nfs_fattr *fp; register u_long *tl; register long t1; + register int i; caddr_t bpos; - int error = 0, rdonly, cache, i, cnt, len, left, siz, tlen; + int error = 0, rdonly, cache, cnt, len, left, siz, tlen, getret; + int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen; char *cp2; struct mbuf *mb, *mb2, *mreq; struct mbuf *m2; struct vnode *vp; - nfsv2fh_t nfh; + nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; struct vattr va, *vap = &va; @@ -464,40 +551,57 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq) 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 { + if (v3) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); + } else { + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + off = (off_t)fxdr_unsigned(u_long, *tl); + } + nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd)); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvpostop_attr(1, (struct vattr *)0); + return (0); } - nfsm_srvstrsiz(cnt, NFS_MAXDATA); - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); if (vp->v_type != VREG) { - error = (vp->v_type == VDIR) ? EISDIR : EACCES; - nfsrv_vput(vp); - nfsm_reply(0); + if (v3) + error = EINVAL; + else + error = (vp->v_type == VDIR) ? EISDIR : EACCES; } - 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))) { - nfsrv_vput(vp); - nfsm_reply(0); + if (!error) { + nqsrv_getl(vp, ND_READ); + if (error = nfsrv_access(vp, VREAD, cred, rdonly, procp)) + error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); } - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); + getret = VOP_GETATTR(vp, vap, cred, procp); + if (!error) + error = getret; if (error) { nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_POSTOPATTR(v3)); + nfsm_srvpostop_attr(getret, vap); + return (0); } if (off >= vap->va_size) cnt = 0; - else if ((off + cnt) > vap->va_size) + else if ((off + reqlen) > 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); + else + cnt = reqlen; + nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt)); + if (v3) { + nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED); + *tl++ = nfs_true; + fp = (struct nfs_fattr *)tl; + tl += (NFSX_V3FATTR / sizeof (u_long)); + } else { + nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED); + fp = (struct nfs_fattr *)tl; + tl += (NFSX_V2FATTR / sizeof (u_long)); + } len = left = cnt; if (cnt > 0) { /* @@ -505,19 +609,11 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq) */ 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; + i++; } if (left > 0) { MGET(m, M_WAIT, MT_DATA); @@ -525,10 +621,28 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq) m->m_len = 0; m2->m_next = m; m2 = m; - bpos = mtod(m, caddr_t); } } - uiop->uio_iov = iv2; + MALLOC(iv, struct iovec *, i * sizeof (struct iovec), + M_TEMP, M_WAITOK); + uiop->uio_iov = iv2 = iv; + m = mb; + left = cnt; + i = 0; + while (left > 0) { + if (m == NULL) + panic("nfsrv_read iov"); + siz = min(M_TRAILINGSPACE(m), left); + if (siz > 0) { + iv->iov_base = mtod(m, caddr_t) + m->m_len; + iv->iov_len = siz; + m->m_len += siz; + left -= siz; + iv++; + i++; + } + m = m->m_next; + } uiop->uio_iovcnt = i; uiop->uio_offset = off; uiop->uio_resid = cnt; @@ -537,19 +651,30 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq) 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))) { + if (error || (getret = VOP_GETATTR(vp, vap, cred, procp))) { + if (!error) + error = getret; m_freem(mreq); nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_POSTOPATTR(v3)); + nfsm_srvpostop_attr(getret, vap); + return (0); } } else uiop->uio_resid = 0; nfsrv_vput(vp); - nfsm_srvfillattr; + nfsm_srvfillattr(vap, fp); len -= uiop->uio_resid; tlen = nfsm_rndup(len); if (cnt != tlen || tlen != len) - nfsm_adj(mb, cnt-tlen, tlen-len); + nfsm_adj(mb, cnt - tlen, tlen - len); + if (v3) { + *tl++ = txdr_unsigned(len); + if (len < reqlen) + *tl++ = nfs_true; + else + *tl++ = nfs_false; + } *tl = txdr_unsigned(len); nfsm_srvdone; } @@ -558,132 +683,580 @@ nfsrv_read(nfsd, mrep, md, dpos, cred, nam, mrq) * nfs write service */ int -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; +nfsrv_write(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register struct iovec *ivp; + register int i, cnt; register struct mbuf *mp; - register struct nfsv2_fattr *fp; - struct iovec iv[NFS_MAXIOVEC]; - struct vattr va; + register struct nfs_fattr *fp; + struct iovec *iv; + struct vattr va, forat; 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; + int error = 0, rdonly, cache, siz, len, xfer, forat_ret = 1; + int ioflags, aftat_ret = 1, retlen, zeroing, adjust; + int stable = NFSV3WRITE_FILESYNC; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; - nfsv2fh_t nfh; + nfsfh_t nfh; fhandle_t *fhp; struct uio io, *uiop = &io; off_t off; u_quad_t frev; + if (mrep == NULL) { + *mrq = NULL; + return (0); + } 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 { + if (v3) { + nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); + tl += 3; + stable = fxdr_unsigned(int, *tl++); + } else { + nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); + off = (off_t)fxdr_unsigned(u_long, *++tl); 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); + retlen = len = fxdr_unsigned(long, *tl); + cnt = i = 0; + + /* + * For NFS Version 2, it is not obvious what a write of zero length + * should do, but I might as well be consistent with Version 3, + * which is to return ok so long as there are no permission problems. + */ + if (len > 0) { + zeroing = 1; + mp = mrep; + while (mp) { + if (mp == md) { + zeroing = 0; + adjust = dpos - mtod(mp, caddr_t); + mp->m_len -= adjust; + if (mp->m_len > 0 && adjust > 0) + NFSMADV(mp, adjust); } - } else { - mp = md; - siz = dpos-mtod(mp, caddr_t); - mp->m_len -= siz; - NFSMADV(mp, siz); + if (zeroing) + mp->m_len = 0; + else if (mp->m_len > 0) { + i += mp->m_len; + if (i > len) { + mp->m_len -= (i - len); + zeroing = 1; + } + if (mp->m_len > 0) + cnt++; + } + mp = mp->m_next; + } } - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); + if (len > NFS_MAXDATA || len < 0 || i < len) { + error = EIO; + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); + return (0); + } + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); + return (0); + } + if (v3) + forat_ret = VOP_GETATTR(vp, &forat, cred, procp); if (vp->v_type != VREG) { - error = (vp->v_type == VDIR) ? EISDIR : EACCES; - nfsrv_vput(vp); - nfsm_reply(0); + if (v3) + error = EINVAL; + else + error = (vp->v_type == VDIR) ? EISDIR : EACCES; + } + if (!error) { + nqsrv_getl(vp, ND_WRITE); + error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); } - nqsrv_getl(vp, NQL_WRITE); - error = nfsrv_access(vp, VWRITE, cred, rdonly, nfsd->nd_procp); if (error) { nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); + return (0); } - uiop->uio_resid = 0; - uiop->uio_rw = UIO_WRITE; - uiop->uio_segflg = UIO_SYSSPACE; - uiop->uio_procp = (struct proc *)0; + + if (len > 0) { + MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP, + M_WAITOK); + uiop->uio_iov = iv = ivp; + uiop->uio_iovcnt = cnt; + mp = mrep; + while (mp) { + if (mp->m_len > 0) { + ivp->iov_base = mtod(mp, caddr_t); + ivp->iov_len = mp->m_len; + ivp++; + } + mp = mp->m_next; + } + + /* + * XXX + * The IO_METASYNC flag indicates that all metadata (and not just + * enough to ensure data integrity) mus be written to stable storage + * synchronously. + * (IO_METASYNC is not yet implemented in 4.4BSD-Lite.) + */ + if (stable == NFSV3WRITE_UNSTABLE) + ioflags = IO_NODELOCKED; + else if (stable == NFSV3WRITE_DATASYNC) + ioflags = (IO_SYNC | IO_NODELOCKED); + else + ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); + uiop->uio_resid = len; + uiop->uio_rw = UIO_WRITE; + uiop->uio_segflg = UIO_SYSSPACE; + uiop->uio_procp = (struct proc *)0; + uiop->uio_offset = off; + error = VOP_WRITE(vp, uiop, ioflags, cred); + nfsstats.srvvop_writes++; + FREE((caddr_t)iv, M_TEMP); + } + aftat_ret = VOP_GETATTR(vp, vap, cred, procp); + nfsrv_vput(vp); + if (!error) + error = aftat_ret; + nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) + + 2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3)); + if (v3) { + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); + if (error) + return (0); + nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(retlen); + if (stable == NFSV3WRITE_UNSTABLE) + *tl++ = txdr_unsigned(stable); + else + *tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC); + /* + * Actually, there is no need to txdr these fields, + * but it may make the values more human readable, + * for debugging purposes. + */ + *tl++ = txdr_unsigned(boottime.tv_sec); + *tl = txdr_unsigned(boottime.tv_usec); + } else { + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(vap, fp); + } + nfsm_srvdone; +} + +/* + * NFS write service with write gathering support. Called when + * nfsrvw_procrastinate > 0. + * See: Chet Juszczak, "Improving the Write Performance of an NFS Server", + * in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco, + * Jan. 1994. + */ +int +nfsrv_writegather(ndp, slp, procp, mrq) + struct nfsrv_descript **ndp; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; +{ + register struct iovec *ivp; + register struct mbuf *mp; + register struct nfsrv_descript *wp, *nfsd, *owp, *swp; + register struct nfs_fattr *fp; + register int i; + struct iovec *iov; + struct nfsrvw_delayhash *wpp; + struct ucred *cred; + struct vattr va, forat; + register u_long *tl; + register long t1; + caddr_t bpos, dpos; + int error = 0, rdonly, cache, len, forat_ret = 1; + int ioflags, aftat_ret = 1, s, adjust, v3, zeroing; + char *cp2; + struct mbuf *mb, *mb2, *mreq, *mrep, *md; + struct vnode *vp; + struct uio io, *uiop = &io; + off_t off; + u_quad_t frev, cur_usec; + +#ifndef nolint + i = 0; + len = 0; +#endif + *mrq = NULL; + if (*ndp) { + nfsd = *ndp; + *ndp = NULL; + mrep = nfsd->nd_mrep; + md = nfsd->nd_md; + dpos = nfsd->nd_dpos; + cred = &nfsd->nd_cr; + v3 = (nfsd->nd_flag & ND_NFSV3); + LIST_INIT(&nfsd->nd_coalesce); + nfsd->nd_mreq = NULL; + nfsd->nd_stable = NFSV3WRITE_FILESYNC; + cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; + nfsd->nd_time = cur_usec + nfsrvw_procrastinate; + + /* + * Now, get the write header.. + */ + nfsm_srvmtofh(&nfsd->nd_fh); + if (v3) { + nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); + fxdr_hyper(tl, &nfsd->nd_off); + tl += 3; + nfsd->nd_stable = fxdr_unsigned(int, *tl++); + } else { + nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); + nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl); + tl += 2; + } + len = fxdr_unsigned(long, *tl); + nfsd->nd_len = len; + nfsd->nd_eoff = nfsd->nd_off + len; + + /* + * Trim the header out of the mbuf list and trim off any trailing + * junk so that the mbuf list has only the write data. + */ + zeroing = 1; + i = 0; + mp = mrep; + while (mp) { + if (mp == md) { + zeroing = 0; + adjust = dpos - mtod(mp, caddr_t); + mp->m_len -= adjust; + if (mp->m_len > 0 && adjust > 0) + NFSMADV(mp, adjust); + } + if (zeroing) + mp->m_len = 0; + else { + i += mp->m_len; + if (i > len) { + mp->m_len -= (i - len); + zeroing = 1; + } + } + mp = mp->m_next; + } + if (len > NFS_MAXDATA || len < 0 || i < len) { +nfsmout: + m_freem(mrep); + error = EIO; + nfsm_writereply(2 * NFSX_UNSIGNED, v3); + if (v3) + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); + nfsd->nd_mreq = mreq; + nfsd->nd_mrep = NULL; + nfsd->nd_time = 0; + } + + /* + * Add this entry to the hash and time queues. + */ + s = splsoftclock(); + owp = NULL; + wp = slp->ns_tq.lh_first; + while (wp && wp->nd_time < nfsd->nd_time) { + owp = wp; + wp = wp->nd_tq.le_next; + } + if (owp) { + LIST_INSERT_AFTER(owp, nfsd, nd_tq); + } else { + LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); + } + if (nfsd->nd_mrep) { + wpp = NWDELAYHASH(slp, nfsd->nd_fh.fh_fid.fid_data); + owp = NULL; + wp = wpp->lh_first; + while (wp && + bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { + owp = wp; + wp = wp->nd_hash.le_next; + } + while (wp && wp->nd_off < nfsd->nd_off && + !bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { + owp = wp; + wp = wp->nd_hash.le_next; + } + if (owp) { + LIST_INSERT_AFTER(owp, nfsd, nd_hash); + + /* + * Search the hash list for overlapping entries and + * coalesce. + */ + for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) { + wp = nfsd->nd_hash.le_next; + if (NFSW_SAMECRED(owp, nfsd)) + nfsrvw_coalesce(owp, nfsd); + } + } else { + LIST_INSERT_HEAD(wpp, nfsd, nd_hash); + } + } + splx(s); + } + /* - * Do up to NFS_MAXIOVEC mbufs of write each iteration of the - * loop until done. + * Now, do VOP_WRITE()s for any one(s) that need to be done now + * and generate the associated reply mbuf list(s). */ - 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; +loop1: + cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; + s = splsoftclock(); + for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) { + owp = nfsd->nd_tq.le_next; + if (nfsd->nd_time > cur_usec) + break; + if (nfsd->nd_mreq) + continue; + LIST_REMOVE(nfsd, nd_tq); + LIST_REMOVE(nfsd, nd_hash); + splx(s); + mrep = nfsd->nd_mrep; + nfsd->nd_mrep = NULL; + cred = &nfsd->nd_cr; + v3 = (nfsd->nd_flag & ND_NFSV3); + forat_ret = aftat_ret = 1; + error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp, + nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH)); + if (!error) { + if (v3) + forat_ret = VOP_GETATTR(vp, &forat, cred, procp); + if (vp->v_type != VREG) { + if (v3) + error = EINVAL; 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; + error = (vp->v_type == VDIR) ? EISDIR : EACCES; + } + } else + vp = NULL; + if (!error) { + nqsrv_getl(vp, ND_WRITE); + error = nfsrv_access(vp, VWRITE, cred, rdonly, procp); + } + + if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE) + ioflags = IO_NODELOCKED; + else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC) + ioflags = (IO_SYNC | IO_NODELOCKED); + else + ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); + uiop->uio_rw = UIO_WRITE; + uiop->uio_segflg = UIO_SYSSPACE; + uiop->uio_procp = (struct proc *)0; + uiop->uio_offset = nfsd->nd_off; + uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off; + if (uiop->uio_resid > 0) { + mp = mrep; + i = 0; + while (mp) { + if (mp->m_len > 0) + i++; + mp = mp->m_next; + } + uiop->uio_iovcnt = i; + MALLOC(iov, struct iovec *, i * sizeof (struct iovec), + M_TEMP, M_WAITOK); + uiop->uio_iov = ivp = iov; + mp = mrep; + while (mp) { + if (mp->m_len > 0) { + ivp->iov_base = mtod(mp, caddr_t); + ivp->iov_len = mp->m_len; + ivp++; + } mp = mp->m_next; + } + if (!error) { + error = VOP_WRITE(vp, uiop, ioflags, cred); + nfsstats.srvvop_writes++; + } + FREE((caddr_t)iov, M_TEMP); } - if (len > 0 && mp == NULL) { - error = EBADRPC; - nfsrv_vput(vp); - nfsm_reply(0); + m_freem(mrep); + if (vp) { + aftat_ret = VOP_GETATTR(vp, &va, cred, procp); + nfsrv_vput(vp); } - uiop->uio_resid = siz; - error = VOP_WRITE(vp, uiop, ioflags, cred); - if (error) { - nfsrv_vput(vp); - nfsm_reply(0); - } - off = uiop->uio_offset; + + /* + * Loop around generating replies for all write rpcs that have + * now been completed. + */ + swp = nfsd; + do { + if (error) { + nfsm_writereply(NFSX_WCCDATA(v3), v3); + if (v3) { + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); + } + } else { + nfsm_writereply(NFSX_PREOPATTR(v3) + + NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED + + NFSX_WRITEVERF(v3), v3); + if (v3) { + nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va); + nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(nfsd->nd_len); + *tl++ = txdr_unsigned(swp->nd_stable); + /* + * Actually, there is no need to txdr these fields, + * but it may make the values more human readable, + * for debugging purposes. + */ + *tl++ = txdr_unsigned(boottime.tv_sec); + *tl = txdr_unsigned(boottime.tv_usec); + } else { + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(&va, fp); + } + } + nfsd->nd_mreq = mreq; + if (nfsd->nd_mrep) + panic("nfsrv_write: nd_mrep not free"); + + /* + * Done. Put it at the head of the timer queue so that + * the final phase can return the reply. + */ + s = splsoftclock(); + if (nfsd != swp) { + nfsd->nd_time = 0; + LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq); + } + nfsd = swp->nd_coalesce.lh_first; + if (nfsd) { + LIST_REMOVE(nfsd, nd_tq); + } + splx(s); + } while (nfsd); + s = splsoftclock(); + swp->nd_time = 0; + LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq); + splx(s); + goto loop1; } - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); - nfsrv_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); + splx(s); + + /* + * Search for a reply to return. + */ + s = splsoftclock(); + for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next) + if (nfsd->nd_mreq) { + LIST_REMOVE(nfsd, nd_tq); + *mrq = nfsd->nd_mreq; + *ndp = nfsd; + break; + } + splx(s); + return (0); +} + +/* + * Coalesce the write request nfsd into owp. To do this we must: + * - remove nfsd from the queues + * - merge nfsd->nd_mrep into owp->nd_mrep + * - update the nd_eoff and nd_stable for owp + * - put nfsd on owp's nd_coalesce list + * NB: Must be called at splsoftclock(). + */ +void +nfsrvw_coalesce(owp, nfsd) + register struct nfsrv_descript *owp; + register struct nfsrv_descript *nfsd; +{ + register int overlap; + register struct mbuf *mp; + + LIST_REMOVE(nfsd, nd_hash); + LIST_REMOVE(nfsd, nd_tq); + if (owp->nd_eoff < nfsd->nd_eoff) { + overlap = owp->nd_eoff - nfsd->nd_off; + if (overlap < 0) + panic("nfsrv_coalesce: bad off"); + if (overlap > 0) + m_adj(nfsd->nd_mrep, overlap); + mp = owp->nd_mrep; + while (mp->m_next) + mp = mp->m_next; + mp->m_next = nfsd->nd_mrep; + owp->nd_eoff = nfsd->nd_eoff; + } else + m_freem(nfsd->nd_mrep); + nfsd->nd_mrep = NULL; + if (nfsd->nd_stable == NFSV3WRITE_FILESYNC) + owp->nd_stable = NFSV3WRITE_FILESYNC; + else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC && + owp->nd_stable == NFSV3WRITE_UNSTABLE) + owp->nd_stable = NFSV3WRITE_DATASYNC; + LIST_INSERT_HEAD(&owp->nd_coalesce, nfsd, nd_tq); +} + +/* + * Sort the group list in increasing numerical order. + * (Insertion sort by Chris Torek, who was grossed out by the bubble sort + * that used to be here.) + */ +void +nfsrvw_sort(list, num) + register gid_t *list; + register int num; +{ + register int i, j; + gid_t v; + + /* Insertion sort. */ + for (i = 1; i < num; i++) { + v = list[i]; + /* find correct slot for value v, moving others up */ + for (j = i; --j >= 0 && v < list[j];) + list[j + 1] = list[j]; + list[j + 1] = v; } - nfsm_srvdone; +} + +/* + * copy credentials making sure that the result can be compared with bcmp(). + */ +void +nfsrv_setcred(incred, outcred) + register struct ucred *incred, *outcred; +{ + register int i; + + bzero((caddr_t)outcred, sizeof (struct ucred)); + outcred->cr_ref = 1; + outcred->cr_uid = incred->cr_uid; + outcred->cr_ngroups = incred->cr_ngroups; + for (i = 0; i < incred->cr_ngroups; i++) + outcred->cr_groups[i] = incred->cr_groups[i]; + nfsrvw_sort(outcred->cr_groups, outcred->cr_ngroups); } /* @@ -691,15 +1264,18 @@ nfsrv_write(nfsd, mrep, md, dpos, cred, nam, mrq) * now does a truncate to 0 length via. setattr if it already exists */ int -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; +nfsrv_create(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { - register struct nfsv2_fattr *fp; - struct vattr va; + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register struct nfs_fattr *fp; + struct vattr va, dirfor, diraft; register struct vattr *vap = &va; register struct nfsv2_sattr *sp; register u_long *tl; @@ -707,75 +1283,131 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq) register caddr_t cp; register long t1; caddr_t bpos; - int error = 0, rdev, cache, len, tsize; + int error = 0, rdev, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0; char *cp2; struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - nfsv2fh_t nfh; + struct vnode *vp, *dirp = (struct vnode *)0; + nfsfh_t nfh; fhandle_t *fhp; - u_quad_t frev; + u_quad_t frev, tempsize; + u_char cverf[NFSX_V3CREATEVERF]; +#ifndef nolint + rdev = 0; +#endif nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if (error) - nfsm_reply(0); + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else { + nfsrv_vrele(dirp); + dirp = (struct vnode *)0; + } + } + if (error) { + nfsm_reply(NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + if (dirp) + nfsrv_vrele(dirp); + return (0); + } VATTR_NULL(vap); - nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_SATTR(nfsd->nd_nqlflag != NQL_NOVAL)); + if (v3) { + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + how = fxdr_unsigned(int, *tl); + switch (how) { + case NFSV3CREATE_GUARDED: + if (nd.ni_vp) { + error = EEXIST; + break; + } + case NFSV3CREATE_UNCHECKED: + nfsm_srvsattr(vap); + break; + case NFSV3CREATE_EXCLUSIVE: + nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF); + bcopy(cp, cverf, NFSX_V3CREATEVERF); + exclusive_flag = 1; + if (nd.ni_vp == NULL) + vap->va_mode = 0; + break; + }; + vap->va_type = VREG; + } else { + nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + 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); + switch (vap->va_type) { + case VREG: + tsize = fxdr_unsigned(long, sp->sa_size); + if (tsize != -1) + vap->va_size = (u_quad_t)tsize; + break; + case VCHR: + case VBLK: + case VFIFO: + rdev = fxdr_unsigned(long, sp->sa_size); + break; + }; + } + /* * 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) { nfsrv_vrele(nd.ni_startdir); - nqsrv_getl(nd.ni_dvp, NQL_WRITE); - error=VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); - if (error) - nfsm_reply(0); - nfsrv_vmio(nd.ni_vp); - FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); + nqsrv_getl(nd.ni_dvp, ND_WRITE); + error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); + if (!error) { + nfsrv_vmio(nd.ni_vp); + FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); + if (exclusive_flag) { + exclusive_flag = 0; + VATTR_NULL(vap); + bcopy(cverf, (caddr_t)&vap->va_atime, + NFSX_V3CREATEVERF); + error = VOP_SETATTR(nd.ni_vp, vap, cred, + procp); + } + } } 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) { - } else { - error = suser(cred, (u_short *)0); - if (error) { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - nfsrv_vput(nd.ni_dvp); - goto out; - } else - vap->va_rdev = (dev_t)rdev; - } - nqsrv_getl(nd.ni_dvp, NQL_WRITE); - error=VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); - if (error) { + if (error = suser(cred, (u_short *)0)) { + nfsrv_vrele(nd.ni_startdir); + free(nd.ni_cnd.cn_pnbuf, M_NAMEI); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + nfsrv_vput(nd.ni_dvp); + nfsm_reply(0); + return (error); + } else + vap->va_rdev = (dev_t)rdev; + nqsrv_getl(nd.ni_dvp, ND_WRITE); + if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { nfsrv_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; - error = lookup(&nd); - if (error) { + nd.ni_cnd.cn_proc = procp; + nd.ni_cnd.cn_cred = cred; + if (error = lookup(&nd)) { free(nd.ni_cnd.cn_pnbuf, M_NAMEI); nfsm_reply(0); } @@ -789,10 +1421,11 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq) nfsm_reply(0); } } else { + nfsrv_vrele(nd.ni_startdir); + free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); nfsrv_vput(nd.ni_dvp); error = ENXIO; - goto out; } vp = nd.ni_vp; } else { @@ -804,46 +1437,56 @@ nfsrv_create(nfsd, mrep, md, dpos, cred, nam, mrq) else nfsrv_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) { error = nfsrv_access(vp, VWRITE, cred, - (nd.ni_cnd.cn_flags & RDONLY), nfsd->nd_procp); - if (error) { - nfsrv_vput(vp); - nfsm_reply(0); + (nd.ni_cnd.cn_flags & RDONLY), procp); + if (!error) { + nqsrv_getl(vp, ND_WRITE); + tempsize = vap->va_size; + VATTR_NULL(vap); + vap->va_size = tempsize; + error = VOP_SETATTR(vp, vap, cred, + procp); } - nqsrv_getl(vp, NQL_WRITE); - error = VOP_SETATTR(vp, vap, cred, nfsd->nd_procp); - if (error) { + if (error) nfsrv_vput(vp); - nfsm_reply(0); - } } } - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (error) { + if (!error) { + bzero((caddr_t)fhp, sizeof(nfh)); + fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(vp, &fhp->fh_fid); + if (!error) + error = VOP_GETATTR(vp, vap, cred, procp); nfsrv_vput(vp); - nfsm_reply(0); } - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); - nfsrv_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); + if (v3) { + if (exclusive_flag && !error && + bcmp(cverf, (caddr_t)&vap->va_atime, NFSX_V3CREATEVERF)) + error = EEXIST; + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } + nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3)); + if (v3) { + if (!error) { + nfsm_srvpostop_fh(fhp); + nfsm_srvpostop_attr(0, vap); + } + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + } else { + nfsm_srvfhtom(fhp, v3); + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(vap, fp); + } + return (0); nfsmout: - if (nd.ni_cnd.cn_nameiop || nd.ni_cnd.cn_flags) + if (dirp) + nfsrv_vrele(dirp); + if (nd.ni_cnd.cn_nameiop) { nfsrv_vrele(nd.ni_startdir); + free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); + } VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) nfsrv_vrele(nd.ni_dvp); @@ -852,79 +1495,248 @@ nfsmout: if (nd.ni_vp) nfsrv_vput(nd.ni_vp); return (error); +} + +/* + * nfs v3 mknod service + */ +int +nfsrv_mknod(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; +{ + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register struct nfs_fattr *fp; + struct vattr va, dirfor, diraft; + register struct vattr *vap = &va; + register u_long *tl; + struct nameidata nd; + register caddr_t cp; + register long t1; + caddr_t bpos; + int error = 0, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1; + u_long major, minor; + enum vtype vtyp; + char *cp2; + struct mbuf *mb, *mb2, *mreq; + struct vnode *vp, *dirp = (struct vnode *)0; + nfsfh_t nfh; + fhandle_t *fhp; + u_quad_t frev; + nd.ni_cnd.cn_nameiop = 0; + fhp = &nfh.fh_generic; + nfsm_srvmtofh(fhp); + nfsm_srvnamesiz(len); + nd.ni_cnd.cn_cred = cred; + nd.ni_cnd.cn_nameiop = CREATE; + nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); + if (error) { + nfsm_reply(NFSX_WCCDATA(1)); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + if (dirp) + nfsrv_vrele(dirp); + return (0); + } + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + vtyp = nfsv3tov_type(*tl); + if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) { + nfsrv_vrele(nd.ni_startdir); + free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); + error = NFSERR_BADTYPE; + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + nfsrv_vput(nd.ni_dvp); + goto out; + } + VATTR_NULL(vap); + nfsm_srvsattr(vap); + if (vtyp == VCHR || vtyp == VBLK) { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + major = fxdr_unsigned(u_long, *tl++); + minor = fxdr_unsigned(u_long, *tl); + vap->va_rdev = makedev(major, minor); + } + + /* + * Iff doesn't exist, create it. + */ + if (nd.ni_vp) { + nfsrv_vrele(nd.ni_startdir); + free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); + error = EEXIST; + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + nfsrv_vput(nd.ni_dvp); + goto out; + } + vap->va_type = vtyp; + if (vtyp == VSOCK) { + nfsrv_vrele(nd.ni_startdir); + nqsrv_getl(nd.ni_dvp, ND_WRITE); + error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); + if (!error) + FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); + } else { + if (error = suser(cred, (u_short *)0)) { + nfsrv_vrele(nd.ni_startdir); + free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + nfsrv_vput(nd.ni_dvp); + goto out; + } + nqsrv_getl(nd.ni_dvp, ND_WRITE); + if (error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap)) { + nfsrv_vrele(nd.ni_startdir); + goto out; + } + nd.ni_cnd.cn_nameiop = LOOKUP; + nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART); + nd.ni_cnd.cn_proc = procp; + nd.ni_cnd.cn_cred = procp->p_ucred; + error = lookup(&nd); + FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); + if (error) + goto out; + if (nd.ni_cnd.cn_flags & ISSYMLINK) { + nfsrv_vrele(nd.ni_dvp); + nfsrv_vput(nd.ni_vp); + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + error = EINVAL; + } + } out: - nfsrv_vrele(nd.ni_startdir); - free(nd.ni_cnd.cn_pnbuf, M_NAMEI); - nfsm_reply(0); + vp = nd.ni_vp; + if (!error) { + bzero((caddr_t)fhp, sizeof(nfh)); + fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(vp, &fhp->fh_fid); + if (!error) + error = VOP_GETATTR(vp, vap, cred, procp); + nfsrv_vput(vp); + } + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1)); + if (!error) { + nfsm_srvpostop_fh(fhp); + nfsm_srvpostop_attr(0, vap); + } + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); +nfsmout: + if (dirp) + nfsrv_vrele(dirp); + if (nd.ni_cnd.cn_nameiop) { + nfsrv_vrele(nd.ni_startdir); + free((caddr_t)nd.ni_cnd.cn_pnbuf, M_NAMEI); + } + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + nfsrv_vrele(nd.ni_dvp); + else + nfsrv_vput(nd.ni_dvp); + if (nd.ni_vp) + nfsrv_vput(nd.ni_vp); + return (error); } /* * nfs remove service */ int -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; +nfsrv_remove(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, cache, len; + int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp; - nfsv2fh_t nfh; + struct mbuf *mb, *mreq, *mb2; + struct vnode *vp, *dirp; + struct vattr dirfor, diraft; + nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; +#ifndef nolint + vp = (struct vnode *)0; +#endif fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if (error) - 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; + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else + nfsrv_vrele(dirp); } - (void) vnode_pager_uncache(vp); -out: if (!error) { - int deallocobj = 0; - nqsrv_getl(nd.ni_dvp, NQL_WRITE); - nqsrv_getl(vp, NQL_WRITE); - - if ((vp->v_flag & VVMIO) && vp->v_vmdata) - deallocobj = 1; - error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); - if (error == 0 && deallocobj) - vm_object_deallocate((vm_object_t) vp->v_vmdata); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == vp) - nfsrv_vrele(nd.ni_dvp); - else - nfsrv_vput(nd.ni_dvp); - nfsrv_vput(vp); + 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) { + int deallocobj = 0; + nqsrv_getl(nd.ni_dvp, ND_WRITE); + nqsrv_getl(vp, ND_WRITE); + + if ((vp->v_flag & VVMIO) && vp->v_vmdata) + deallocobj = 1; + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + if (error == 0 && deallocobj) + vm_object_deallocate((vm_object_t) vp->v_vmdata); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + nfsrv_vrele(nd.ni_dvp); + else + nfsrv_vput(nd.ni_dvp); + nfsrv_vput(vp); + } + } + if (dirp && v3) { + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } + nfsm_reply(NFSX_WCCDATA(v3)); + if (v3) { + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + return (0); } - nfsm_reply(0); nfsm_srvdone; } @@ -932,32 +1744,42 @@ out: * nfs rename service */ int -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; +nfsrv_rename(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, cache, len, len2; + int error = 0, cache, len, len2, fdirfor_ret = 1, fdiraft_ret = 1; + int tdirfor_ret = 1, tdiraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; + struct mbuf *mb, *mreq, *mb2; struct nameidata fromnd, tond; - struct vnode *fvp = 0, *tvp, *tdvp; - nfsv2fh_t fnfh, tnfh; + struct vnode *fvp, *tvp, *tdvp, *fdirp = (struct vnode *)0; + struct vnode *tdirp = (struct vnode *)0; + struct vattr fdirfor, fdiraft, tdirfor, tdiraft; + nfsfh_t fnfh, tnfh; fhandle_t *ffhp, *tfhp; u_quad_t frev; uid_t saved_uid; +#ifndef nolint + fvp = (struct vnode *)0; +#endif 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); + nfsm_srvnamesiz(len); /* * Remember our original uid so that we can reset cr_uid before * the second nfs_namei() call, in case it is remapped. @@ -966,10 +1788,25 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq) fromnd.ni_cnd.cn_cred = cred; fromnd.ni_cnd.cn_nameiop = DELETE; fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART; - error = nfs_namei(&fromnd, ffhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if(error) - nfsm_reply(0); + error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md, + &dpos, &fdirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (fdirp) { + if (v3) + fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred, + procp); + else { + nfsrv_vrele(fdirp); + fdirp = (struct vnode *)0; + } + } + if (error) { + nfsm_reply(2 * NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); + nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); + if (fdirp) + nfsrv_vrele(fdirp); + return (0); + } fvp = fromnd.ni_vp; nfsm_srvmtofh(tfhp); nfsm_strsiz(len2, NFS_MAXNAMLEN); @@ -977,8 +1814,17 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq) tond.ni_cnd.cn_cred = cred; tond.ni_cnd.cn_nameiop = RENAME; tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; - error = nfs_namei(&tond, tfhp, len2, nfsd->nd_slp, nam, &md, - &dpos, nfsd->nd_procp); + error = nfs_namei(&tond, tfhp, len2, slp, nam, &md, + &dpos, &tdirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (tdirp) { + if (v3) + tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred, + procp); + else { + nfsrv_vrele(tdirp); + tdirp = (struct vnode *)0; + } + } if (error) { VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); nfsrv_vrele(fromnd.ni_dvp); @@ -989,27 +1835,45 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq) tvp = tond.ni_vp; if (tvp != NULL) { if (fvp->v_type == VDIR && tvp->v_type != VDIR) { - error = EISDIR; + if (v3) + error = EEXIST; + else + error = EISDIR; goto out; } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { - error = ENOTDIR; + if (v3) + error = EEXIST; + else + error = ENOTDIR; goto out; } if (tvp->v_type == VDIR && tvp->v_mountedhere) { - error = EXDEV; + if (v3) + error = EXDEV; + else + error = ENOTEMPTY; goto out; } } if (fvp->v_type == VDIR && fvp->v_mountedhere) { - error = EBUSY; + if (v3) + error = EXDEV; + else + error = ENOTEMPTY; goto out; } if (fvp->v_mount != tdvp->v_mount) { - error = EXDEV; + if (v3) + error = EXDEV; + else + error = ENOTEMPTY; goto out; } if (fvp == tdvp) - error = EINVAL; + if (v3) + error = EINVAL; + else + error = ENOTEMPTY; /* * If source is the same as the destination (that is the * same vnode with the same name in the same directory), @@ -1023,10 +1887,10 @@ nfsrv_rename(nfsd, mrep, md, dpos, cred, nam, mrq) out: if (!error) { int deallocobjfrom = 0, deallocobjto = 0; - nqsrv_getl(fromnd.ni_dvp, NQL_WRITE); - nqsrv_getl(tdvp, NQL_WRITE); + nqsrv_getl(fromnd.ni_dvp, ND_WRITE); + nqsrv_getl(tdvp, ND_WRITE); if (tvp) { - nqsrv_getl(tvp, NQL_WRITE); + nqsrv_getl(tvp, ND_WRITE); if ((tvp->v_flag & VVMIO) && tvp->v_vmdata) deallocobjto = 1; } @@ -1050,21 +1914,39 @@ out: VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); nfsrv_vrele(fromnd.ni_dvp); nfsrv_vrele(fvp); + if (error == -1) + error = 0; } nfsrv_vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); out1: + if (fdirp) { + fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, procp); + nfsrv_vrele(fdirp); + } + if (tdirp) { + tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, procp); + nfsrv_vrele(tdirp); + } nfsrv_vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); - nfsm_reply(0); - return (error); + nfsm_reply(2 * NFSX_WCCDATA(v3)); + if (v3) { + nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); + nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); + } + return (0); nfsmout: - if (tond.ni_cnd.cn_nameiop || tond.ni_cnd.cn_flags) { + if (fdirp) + nfsrv_vrele(fdirp); + if (tdirp) + nfsrv_vrele(tdirp); + if (tond.ni_cnd.cn_nameiop) { nfsrv_vrele(tond.ni_startdir); FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); } - if (fromnd.ni_cnd.cn_nameiop || fromnd.ni_cnd.cn_flags) { + if (fromnd.ni_cnd.cn_nameiop) { nfsrv_vrele(fromnd.ni_startdir); FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); @@ -1078,22 +1960,27 @@ nfsmout: * nfs link service */ int -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; +nfsrv_link(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, rdonly, cache, len; + int error = 0, rdonly, cache, len, dirfor_ret = 1, diraft_ret = 1; + int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp, *xp; - nfsv2fh_t nfh, dnfh; + struct mbuf *mb, *mreq, *mb2; + struct vnode *vp, *xp, *dirp = (struct vnode *)0; + struct vattr dirfor, diraft, at; + nfsfh_t nfh, dnfh; fhandle_t *fhp, *dfhp; u_quad_t frev; @@ -1101,17 +1988,30 @@ nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq) dfhp = &dnfh.fh_generic; nfsm_srvmtofh(fhp); nfsm_srvmtofh(dfhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); - error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); + nfsm_srvnamesiz(len); + if (error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + nfsm_srvpostop_attr(getret, &at); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + return (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; - error = nfs_namei(&nd, dfhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); + error = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else { + nfsrv_vrele(dirp); + dirp = (struct vnode *)0; + } + } if (error) goto out1; xp = nd.ni_vp; @@ -1124,9 +2024,13 @@ nfsrv_link(nfsd, mrep, md, dpos, cred, nam, mrq) error = EXDEV; out: if (!error) { - nqsrv_getl(vp, NQL_WRITE); - nqsrv_getl(xp, NQL_WRITE); + nqsrv_getl(vp, ND_WRITE); + nqsrv_getl(xp, ND_WRITE); +#if defined(__FreeBSD__) || defined(__NetBSD__) error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); +#else + error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd); +#endif } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) @@ -1137,8 +2041,19 @@ out: nfsrv_vrele(nd.ni_vp); } out1: + if (v3) + getret = VOP_GETATTR(vp, &at, cred, procp); + if (dirp) { + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } nfsrv_vrele(vp); - nfsm_reply(0); + nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + if (v3) { + nfsm_srvpostop_attr(getret, &at); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + return (0); + } nfsm_srvdone; } @@ -1146,42 +2061,58 @@ out1: * nfs symbolic link service */ int -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; +nfsrv_symlink(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + struct vattr va, dirfor, diraft; struct vnode *ovp; - 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; + char *bpos, *cp, *pathcp = (char *)0, *cp2; struct uio io; struct iovec iv; - int error = 0, cache, len, len2; - char *pathcp, *cp2; - struct mbuf *mb, *mreq; - nfsv2fh_t nfh; + int error = 0, cache, len, len2, dirfor_ret = 1, diraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); + struct mbuf *mb, *mreq, *mb2; + struct vnode *dirp = (struct vnode *)0; + nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; int deallocobj = 0; - pathcp = (char *)0; + nd.ni_cnd.cn_nameiop = 0; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); + nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART; + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else { + nfsrv_vrele(dirp); + dirp = (struct vnode *)0; + } + } if (error) goto out; + VATTR_NULL(vap); + if (v3) + nfsm_srvsattr(vap); nfsm_strsiz(len2, NFS_MAXPATHLEN); MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK); iv.iov_base = pathcp; @@ -1194,9 +2125,14 @@ nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq) 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)); + if (!v3) { + nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode); + } *(pathcp + len2) = '\0'; if (nd.ni_vp) { + nfsrv_vrele(nd.ni_startdir); + free(nd.ni_cnd.cn_pnbuf, M_NAMEI); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) nfsrv_vrele(nd.ni_dvp); @@ -1206,20 +2142,58 @@ nfsrv_symlink(nfsd, mrep, md, dpos, cred, nam, mrq) error = EEXIST; goto out; } - VATTR_NULL(vap); - vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode); - nqsrv_getl(nd.ni_dvp, NQL_WRITE); + nqsrv_getl(nd.ni_dvp, ND_WRITE); if ((ovp = nd.ni_vp) && (ovp->v_flag & VVMIO) && ovp->v_vmdata) deallocobj = 1; error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp); if (error == 0 && deallocobj) vm_object_deallocate( (vm_object_t) ovp->v_vmdata); + if (error) + nfsrv_vrele(nd.ni_startdir); + else { + if (v3) { + nd.ni_cnd.cn_nameiop = LOOKUP; + nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART | FOLLOW); + nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF); + nd.ni_cnd.cn_proc = procp; + nd.ni_cnd.cn_cred = cred; + error = lookup(&nd); + if (!error) { + bzero((caddr_t)fhp, sizeof(nfh)); + fhp->fh_fsid = nd.ni_vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(nd.ni_vp, &fhp->fh_fid); + if (!error) + error = VOP_GETATTR(nd.ni_vp, vap, cred, + procp); + nfsrv_vput(nd.ni_vp); + } + } else + nfsrv_vrele(nd.ni_startdir); + FREE(nd.ni_cnd.cn_pnbuf, M_NAMEI); + } out: if (pathcp) FREE(pathcp, M_TEMP); - nfsm_reply(0); - return (error); + if (dirp) { + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } + nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + if (v3) { + if (!error) { + nfsm_srvpostop_fh(fhp); + nfsm_srvpostop_attr(0, vap); + } + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + } + return (0); nfsmout: + if (nd.ni_cnd.cn_nameiop) { + nfsrv_vrele(nd.ni_startdir); + free(nd.ni_cnd.cn_pnbuf, M_NAMEI); + } + if (dirp) + nfsrv_vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) nfsrv_vrele(nd.ni_dvp); @@ -1236,43 +2210,65 @@ nfsmout: * nfs mkdir service */ int -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; +nfsrv_mkdir(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { - struct vattr va; + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + struct vattr va, dirfor, diraft; register struct vattr *vap = &va; - register struct nfsv2_fattr *fp; + register struct nfs_fattr *fp; struct nameidata nd; register caddr_t cp; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, cache, len; + int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - nfsv2fh_t nfh; + struct vnode *vp, *dirp = (struct vnode *)0; + nfsfh_t nfh; fhandle_t *fhp; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = CREATE; nd.ni_cnd.cn_flags = LOCKPARENT; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if (error) - nfsm_reply(0); - nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else { + nfsrv_vrele(dirp); + dirp = (struct vnode *)0; + } + } + if (error) { + nfsm_reply(NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + if (dirp) + nfsrv_vrele(dirp); + return (0); + } VATTR_NULL(vap); + if (v3) { + nfsm_srvsattr(vap); + } else { + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + vap->va_mode = nfstov_mode(*tl++); + } 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); @@ -1282,28 +2278,40 @@ nfsrv_mkdir(nfsd, mrep, md, dpos, cred, nam, mrq) nfsrv_vput(nd.ni_dvp); nfsrv_vrele(vp); error = EEXIST; - nfsm_reply(0); + goto out; } - nqsrv_getl(nd.ni_dvp, NQL_WRITE); + nqsrv_getl(nd.ni_dvp, ND_WRITE); error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); - if (error) - nfsm_reply(0); - vp = nd.ni_vp; - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (error) { + if (!error) { + vp = nd.ni_vp; + bzero((caddr_t)fhp, sizeof(nfh)); + fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(vp, &fhp->fh_fid); + if (!error) + error = VOP_GETATTR(vp, vap, cred, procp); nfsrv_vput(vp); - nfsm_reply(0); } - error = VOP_GETATTR(vp, vap, cred, nfsd->nd_procp); - nfsrv_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); +out: + if (dirp) { + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } + nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + if (v3) { + if (!error) { + nfsm_srvpostop_fh(fhp); + nfsm_srvpostop_attr(0, vap); + } + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + } else { + nfsm_srvfhtom(fhp, v3); + nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); + nfsm_srvfillattr(vap, fp); + } + return (0); nfsmout: + if (dirp) + nfsrv_vrele(dirp); VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); if (nd.ni_dvp == nd.ni_vp) nfsrv_vrele(nd.ni_dvp); @@ -1318,35 +2326,54 @@ nfsmout: * nfs rmdir service */ int -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; +nfsrv_rmdir(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, cache, len; + int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp; - nfsv2fh_t nfh; + struct mbuf *mb, *mreq, *mb2; + struct vnode *vp, *dirp = (struct vnode *)0; + struct vattr dirfor, diraft; + nfsfh_t nfh; fhandle_t *fhp; struct nameidata nd; u_quad_t frev; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_srvstrsiz(len, NFS_MAXNAMLEN); + nfsm_srvnamesiz(len); nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; - error = nfs_namei(&nd, fhp, len, nfsd->nd_slp, nam, &md, &dpos, - nfsd->nd_procp); - if (error) - nfsm_reply(0); + error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, + &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH)); + if (dirp) { + if (v3) + dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, + procp); + else { + nfsrv_vrele(dirp); + dirp = (struct vnode *)0; + } + } + if (error) { + nfsm_reply(NFSX_WCCDATA(v3)); + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + if (dirp) + nfsrv_vrele(dirp); + return (0); + } vp = nd.ni_vp; if (vp->v_type != VDIR) { error = ENOTDIR; @@ -1366,8 +2393,8 @@ nfsrv_rmdir(nfsd, mrep, md, dpos, cred, nam, mrq) error = EBUSY; out: if (!error) { - nqsrv_getl(nd.ni_dvp, NQL_WRITE); - nqsrv_getl(vp, NQL_WRITE); + nqsrv_getl(nd.ni_dvp, ND_WRITE); + nqsrv_getl(vp, ND_WRITE); error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); } else { VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); @@ -1377,7 +2404,15 @@ out: nfsrv_vput(nd.ni_dvp); nfsrv_vput(vp); } - nfsm_reply(0); + if (dirp) { + diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); + nfsrv_vrele(dirp); + } + nfsm_reply(NFSX_WCCDATA(v3)); + if (v3) { + nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); + return (0); + } nfsm_srvdone; } @@ -1401,7 +2436,7 @@ out: * 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. + * The alternate call nfsrv_readdirplus() 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 ... @@ -1411,21 +2446,25 @@ out: * "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)]; + nfsuint64 fl_off; + u_long fl_postopok; + u_long fl_fattr[NFSX_V3FATTR / sizeof (u_long)]; + u_long fl_fhok; + u_long fl_fhsize; + u_long fl_nfh[NFSX_V3FH / sizeof (u_long)]; }; int -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; +nfsrv_readdir(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; @@ -1436,38 +2475,63 @@ nfsrv_readdir(nfsd, mrep, md, dpos, cred, nam, mrq) struct mbuf *mb, *mb2, *mreq, *mp2; char *cpos, *cend, *cp2, *rbuf; struct vnode *vp; - nfsv2fh_t nfh; + struct vattr at; + nfsfh_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 off, toff; - int ncookies; - u_int *cookies; - u_int *cookiep; + int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; + int siz, cnt, fullsiz, eofflag, rdonly, cache, ncookies; + int v3 = (nfsd->nd_flag & ND_NFSV3); + u_quad_t frev, off, toff, verf; + u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); - off = toff = fxdr_unsigned(u_long, *tl++); + if (v3) { + nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); + fxdr_hyper(tl, &toff); + tl += 2; + fxdr_hyper(tl, &verf); + tl += 2; + } else { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + toff = fxdr_unsigned(u_quad_t, *tl++); + } + off = toff; cnt = fxdr_unsigned(int, *tl); - siz = ((cnt+NFS_DIRBLKSIZ-1) & ~(NFS_DIRBLKSIZ-1)); - if (cnt > NFS_MAXREADDIR) - siz = NFS_MAXREADDIR; + siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); + xfer = NFS_SRVMAXDATA(nfsd); + if (siz > xfer) + siz = xfer; fullsiz = siz; - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); - nqsrv_getl(vp, NQL_READ); - error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + return (0); + } + nqsrv_getl(vp, ND_READ); + if (v3) { + error = getret = VOP_GETATTR(vp, &at, cred, procp); + if (!error && toff && verf != at.va_filerev) + error = NFSERR_BAD_COOKIE; + } + if (!error) + error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); if (error) { nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_POSTOPATTR(v3)); + nfsm_srvpostop_attr(getret, &at); + return (0); } VOP_UNLOCK(vp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); +#ifdef __NetBSD__ + ncookies = siz / (5 * NFSX_UNSIGNED); /*7 for V3, but it's an est. so*/ + MALLOC(cookies, u_long *, ncookies * sizeof (u_long *), M_TEMP, + M_WAITOK); +#endif again: iv.iov_base = rbuf; iv.iov_len = fullsiz; @@ -1478,25 +2542,31 @@ again: io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; - cookies = NULL; eofflag = 0; +#ifndef __NetBSD__ + if (cookies) { + free((caddr_t)cookies, M_TEMP); + cookies = NULL; + } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); +#else + error = VOP_READDIR(vp, &io, cred, &eofflag, cookies, ncookies); +#endif off = (off_t)io.uio_offset; + if (!cookies && !error) + error = NFSERR_PERM; + if (v3) { + getret = VOP_GETATTR(vp, &at, cred, procp); + if (!error) + error = getret; + } if (error) { nfsrv_vrele(vp); free((caddr_t)rbuf, M_TEMP); - nfsm_reply(0); - } - if (cookies == NULL) { - /* - * If the filesystem doen't support cookies, return eof. - */ - nfsrv_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); + if (cookies) + free((caddr_t)cookies, M_TEMP); + nfsm_reply(NFSX_POSTOPATTR(v3)); + nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { @@ -1508,12 +2578,19 @@ again: */ if (siz == 0) { nfsrv_vrele(vp); - nfsm_reply(2*NFSX_UNSIGNED); - nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED); + nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + + 2 * NFSX_UNSIGNED); + if (v3) { + nfsm_srvpostop_attr(getret, &at); + nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); + txdr_hyper(&at.va_filerev, tl); + tl += 2; + } else + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_false; *tl = nfs_true; FREE((caddr_t)rbuf, M_TEMP); - FREE(cookies, M_TEMP); + FREE((caddr_t)cookies, M_TEMP); return (0); } } @@ -1526,32 +2603,49 @@ again: cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; - while (cpos < cend && ncookies > 0 - && (*cookiep <= toff || dp->d_fileno == 0)) { +#ifdef __FreeBSD__ + /* + * For some reason FreeBSD's ufs_readdir() chooses to back the + * directory offset up to a block boundary, so it is necessary to + * skip over the records that preceed the requested offset. This + * requires the assumption that file offset cookies monotonically + * increase. + */ + while (cpos < cend && ncookies > 0 && + (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { +#else + while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) { +#endif cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } - if (cpos >= cend) { + if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; - FREE(cookies, M_TEMP); goto again; } - len = 3*NFSX_UNSIGNED; /* paranoia, probably can be 0 */ - nfsm_reply(siz); + len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */ + nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz); + if (v3) { + nfsm_srvpostop_attr(getret, &at); + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + txdr_hyper(&at.va_filerev, tl); + } mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ - while (cpos < cend) { + while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; - len += (4*NFSX_UNSIGNED + nlen + rem); + len += (4 * NFSX_UNSIGNED + nlen + rem); + if (v3) + len += 2 * NFSX_UNSIGNED; if (len > cnt) { eofflag = 0; break; @@ -1563,6 +2657,11 @@ again: nfsm_clget; *tl = nfs_true; bp += NFSX_UNSIGNED; + if (v3) { + nfsm_clget; + *tl = 0; + bp += NFSX_UNSIGNED; + } nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; @@ -1591,12 +2690,18 @@ again: nfsm_clget; /* Finish off the record */ + if (v3) { + *tl = 0; + bp += NFSX_UNSIGNED; + nfsm_clget; + } *tl = txdr_unsigned(*cookiep); bp += NFSX_UNSIGNED; } cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; + ncookies--; } nfsrv_vrele(vp); nfsm_clget; @@ -1613,19 +2718,22 @@ again: mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; - FREE(rbuf, M_TEMP); - FREE(cookies, M_TEMP); + FREE((caddr_t)rbuf, M_TEMP); + FREE((caddr_t)cookies, M_TEMP); nfsm_srvdone; } int -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; +nfsrv_readdirplus(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register char *bp, *be; register struct mbuf *mp; register struct dirent *dp; @@ -1637,42 +2745,58 @@ nqnfsrv_readdirlook(nfsd, mrep, md, dpos, cred, nam, mrq) char *cpos, *cend, *cp2, *rbuf; struct vnode *vp, *nvp; struct flrep fl; - nfsv2fh_t nfh; - fhandle_t *fhp; + nfsfh_t nfh; + fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh; struct uio io; struct iovec iv; - struct vattr va, *vap = &va; - struct nfsv2_fattr *fp; - int len, nlen, rem, xfer, tsiz, i, error = 0, cache2; - int siz, cnt, fullsiz, eofflag, rdonly, cache; - u_long duration2; - u_quad_t frev, frev2; - u_long off, toff; - int ncookies; - u_int *cookies; - u_int *cookiep; + struct vattr va, at, *vap = &va; + struct nfs_fattr *fp; + int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1; + int siz, cnt, fullsiz, eofflag, rdonly, cache, dirlen, ncookies; + u_quad_t frev, off, toff, verf; + u_long *cookies = NULL, *cookiep; fhp = &nfh.fh_generic; nfsm_srvmtofh(fhp); - nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED); - toff = off = fxdr_unsigned(u_long, *tl++); - 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; + nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED); + fxdr_hyper(tl, &toff); + tl += 2; + fxdr_hyper(tl, &verf); + tl += 2; + siz = fxdr_unsigned(int, *tl++); + cnt = fxdr_unsigned(int, *tl); + off = toff; + siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); + xfer = NFS_SRVMAXDATA(nfsd); + if (siz > xfer) + siz = xfer; fullsiz = siz; - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); - nqsrv_getl(vp, NQL_READ); - error = nfsrv_access(vp, VEXEC, cred, rdonly, nfsd->nd_procp); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + return (0); + } + error = getret = VOP_GETATTR(vp, &at, cred, procp); + if (!error && toff && verf != at.va_filerev) + error = NFSERR_BAD_COOKIE; + if (!error) { + nqsrv_getl(vp, ND_READ); + error = nfsrv_access(vp, VEXEC, cred, rdonly, procp); + } if (error) { nfsrv_vput(vp); - nfsm_reply(0); + nfsm_reply(NFSX_V3POSTOPATTR); + nfsm_srvpostop_attr(getret, &at); + return (0); } VOP_UNLOCK(vp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); +#ifdef __NetBSD__ + ncookies = siz / (7 * NFSX_UNSIGNED); + MALLOC(cookies, u_long *, ncookies * sizeof (u_long *), M_TEMP, + M_WAITOK); +#endif again: iv.iov_base = rbuf; iv.iov_len = fullsiz; @@ -1683,25 +2807,29 @@ again: io.uio_segflg = UIO_SYSSPACE; io.uio_rw = UIO_READ; io.uio_procp = (struct proc *)0; - cookies = NULL; eofflag = 0; +#ifndef __NetBSD__ + if (cookies) { + free((caddr_t)cookies, M_TEMP); + cookies = NULL; + } error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); - off = (u_long)io.uio_offset; +#else + error = VOP_READDIR(vp, &io, cred, &eofflag, cookies, ncookies); +#endif + off = (u_quad_t)io.uio_offset; + getret = VOP_GETATTR(vp, &at, cred, procp); + if (!cookies && !error) + error = NFSERR_PERM; + if (!error) + error = getret; if (error) { nfsrv_vrele(vp); + if (cookies) + free((caddr_t)cookies, M_TEMP); free((caddr_t)rbuf, M_TEMP); - nfsm_reply(0); - } - if (cookies == NULL) { - /* - * If the filesystem doen't support cookies, return eof. - */ - nfsrv_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); + nfsm_reply(NFSX_V3POSTOPATTR); + nfsm_srvpostop_attr(getret, &at); return (0); } if (io.uio_resid) { @@ -1713,12 +2841,16 @@ again: */ if (siz == 0) { nfsrv_vrele(vp); - nfsm_reply(2 * NFSX_UNSIGNED); - nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + + 2 * NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); + txdr_hyper(&at.va_filerev, tl); + tl += 2; *tl++ = nfs_false; *tl = nfs_true; + FREE((caddr_t)cookies, M_TEMP); FREE((caddr_t)rbuf, M_TEMP); - FREE(cookies, M_TEMP); return (0); } } @@ -1731,28 +2863,41 @@ again: cend = rbuf + siz; dp = (struct dirent *)cpos; cookiep = cookies; - while (cpos < cend && ncookies > 0 - && (*cookiep <= toff || dp->d_fileno == 0)) { +#ifdef __FreeBSD__ + /* + * For some reason FreeBSD's ufs_readdir() chooses to back the + * directory offset up to a block boundary, so it is necessary to + * skip over the records that preceed the requested offset. This + * requires the assumption that file offset cookies monotonically + * increase. + */ + while (cpos < cend && ncookies > 0 && + (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { +#else + while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) { +#endif cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; ncookies--; } - if (cpos >= cend) { + if (cpos >= cend || ncookies == 0) { toff = off; siz = fullsiz; - FREE(cookies, M_TEMP); goto again; } - len = 3 * NFSX_UNSIGNED; /* paranoia, probably can be 0 */ - nfsm_reply(siz); + dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED; + nfsm_reply(cnt); + nfsm_srvpostop_attr(getret, &at); + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + txdr_hyper(&at.va_filerev, tl); mp = mp2 = mb; bp = bpos; be = bp + M_TRAILINGSPACE(mp); /* Loop through the records and build reply */ - while (cpos < cend) { + while (cpos < cend && ncookies > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; @@ -1763,59 +2908,51 @@ again: */ 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 = + bzero((caddr_t)nfhp, NFSX_V3FH); + nfhp->fh_fsid = nvp->v_mount->mnt_stat.f_fsid; - if (VFS_VPTOFH(nvp, &fl.fl_nfh.fh_generic.fh_fid)) { + if (VFS_VPTOFH(nvp, &nfhp->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)) { + if (VOP_GETATTR(nvp, vap, cred, 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) { + + /* + * If either the dircount or maxcount will be + * exceeded, get out now. Both of these lengths + * are calculated conservatively, including all + * XDR overheads. + */ + len += (7 * NFSX_UNSIGNED + nlen + rem + NFSX_V3FH + + NFSX_V3POSTOPATTR); + dirlen += (6 * NFSX_UNSIGNED + nlen + rem); + if (len > cnt || dirlen > fullsiz) { eofflag = 0; break; } + /* * Build the directory record xdr from * the dirent entry. */ + fp = (struct nfs_fattr *)&fl.fl_fattr; + nfsm_srvfillattr(vap, fp); + fl.fl_fhsize = txdr_unsigned(NFSX_V3FH); + fl.fl_fhok = nfs_true; + fl.fl_postopok = nfs_true; + fl.fl_off.nfsuquad[0] = 0; + fl.fl_off.nfsuquad[1] = txdr_unsigned(*cookiep); + 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 = 0; + bp += NFSX_UNSIGNED; nfsm_clget; *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; @@ -1828,8 +2965,8 @@ again: cp = dp->d_name; while (xfer > 0) { nfsm_clget; - if ((bp+xfer) > be) - tsiz = be-bp; + if ((bp + xfer) > be) + tsiz = be - bp; else tsiz = xfer; bcopy(cp, bp, tsiz); @@ -1841,16 +2978,30 @@ again: /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; - nfsm_clget; - - /* Finish off the record */ - *tl = txdr_unsigned(*cookiep); - bp += NFSX_UNSIGNED; + + /* + * Now copy the flrep structure 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; + } } invalid: cpos += dp->d_reclen; dp = (struct dirent *)cpos; cookiep++; + ncookies--; } nfsrv_vrele(vp); nfsm_clget; @@ -1867,8 +3018,69 @@ invalid: mp->m_len = bp - mtod(mp, caddr_t); } else mp->m_len += bp - bpos; - FREE(rbuf, M_TEMP); - FREE(cookies, M_TEMP); + FREE((caddr_t)cookies, M_TEMP); + FREE((caddr_t)rbuf, M_TEMP); + nfsm_srvdone; +} + +/* + * nfs commit service + */ +int +nfsrv_commit(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; +{ + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + struct vattr bfor, aft; + struct vnode *vp; + nfsfh_t nfh; + fhandle_t *fhp; + register u_long *tl; + register long t1; + caddr_t bpos; + int error = 0, rdonly, for_ret = 1, aft_ret = 1, cnt, cache; + char *cp2; + struct mbuf *mb, *mb2, *mreq; + u_quad_t frev, off; + +#ifndef nolint + cache = 0; +#endif + fhp = &nfh.fh_generic; + nfsm_srvmtofh(fhp); + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + + /* + * XXX At this time VOP_FSYNC() does not accept offset and byte + * count parameters, so these arguments are useless (someday maybe). + */ + fxdr_hyper(tl, &off); + tl += 2; + cnt = fxdr_unsigned(int, *tl); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(2 * NFSX_UNSIGNED); + nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); + return (0); + } + for_ret = VOP_GETATTR(vp, &bfor, cred, procp); + error = VOP_FSYNC(vp, cred, MNT_WAIT, procp); + aft_ret = VOP_GETATTR(vp, &aft, cred, procp); + nfsrv_vput(vp); + nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF); + nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft); + if (!error) { + nfsm_build(tl, u_long *, NFSX_V3WRITEVERF); + *tl++ = txdr_unsigned(boottime.tv_sec); + *tl = txdr_unsigned(boottime.tv_usec); + } else + return (0); nfsm_srvdone; } @@ -1876,69 +3088,244 @@ invalid: * nfs statfs service */ int -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; +nfsrv_statfs(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; register struct statfs *sf; - register struct nfsv2_statfs *sfp; + register struct nfs_statfs *sfp; register u_long *tl; register long t1; caddr_t bpos; - int error = 0, rdonly, cache = 0, isnq; + int error = 0, rdonly, cache, getret = 1; + int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; struct mbuf *mb, *mb2, *mreq; struct vnode *vp; - nfsv2fh_t nfh; + struct vattr at; + nfsfh_t nfh; fhandle_t *fhp; struct statfs statfs; - u_quad_t frev; + u_quad_t frev, tval; +#ifndef nolint + cache = 0; +#endif fhp = &nfh.fh_generic; - isnq = (nfsd->nd_nqlflag != NQL_NOVAL); nfsm_srvmtofh(fhp); - error = nfsrv_fhtovp(fhp, TRUE, &vp, cred, nfsd->nd_slp, nam, &rdonly); - if (error) - nfsm_reply(0); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + return (0); + } sf = &statfs; - error = VFS_STATFS(vp->v_mount, sf, nfsd->nd_procp); + error = VFS_STATFS(vp->v_mount, sf, procp); + getret = VOP_GETATTR(vp, &at, cred, procp); nfsrv_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_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3)); + if (v3) + nfsm_srvpostop_attr(getret, &at); + if (error) + return (0); + nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); + if (v3) { + tval = (u_quad_t)sf->f_blocks; + tval *= (u_quad_t)sf->f_bsize; + txdr_hyper(&tval, &sfp->sf_tbytes); + tval = (u_quad_t)sf->f_bfree; + tval *= (u_quad_t)sf->f_bsize; + txdr_hyper(&tval, &sfp->sf_fbytes); + tval = (u_quad_t)sf->f_bavail; + tval *= (u_quad_t)sf->f_bsize; + txdr_hyper(&tval, &sfp->sf_abytes); + sfp->sf_tfiles.nfsuquad[0] = 0; + sfp->sf_tfiles.nfsuquad[1] = txdr_unsigned(sf->f_files); + sfp->sf_ffiles.nfsuquad[0] = 0; + sfp->sf_ffiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); + sfp->sf_afiles.nfsuquad[0] = 0; + sfp->sf_afiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); + sfp->sf_invarsec = 0; + } else { + 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); } nfsm_srvdone; } /* + * nfs fsinfo service + */ +int +nfsrv_fsinfo(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; +{ + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register u_long *tl; + register struct nfsv3_fsinfo *sip; + register long t1; + caddr_t bpos; + int error = 0, rdonly, cache, getret = 1, pref; + char *cp2; + struct mbuf *mb, *mb2, *mreq; + struct vnode *vp; + struct vattr at; + nfsfh_t nfh; + fhandle_t *fhp; + u_quad_t frev; + +#ifndef nolint + cache = 0; +#endif + fhp = &nfh.fh_generic; + nfsm_srvmtofh(fhp); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + return (0); + } + getret = VOP_GETATTR(vp, &at, cred, procp); + nfsrv_vput(vp); + nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO); + nfsm_srvpostop_attr(getret, &at); + nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO); + + /* + * XXX + * There should be file system VFS OP(s) to get this information. + * For now, assume ufs. + */ + if (slp->ns_so->so_type == SOCK_DGRAM) + pref = NFS_MAXDGRAMDATA; + else + pref = NFS_MAXDATA; + sip->fs_rtmax = txdr_unsigned(NFS_MAXDATA); + sip->fs_rtpref = txdr_unsigned(pref); + sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE); + sip->fs_wtmax = txdr_unsigned(NFS_MAXDATA); + sip->fs_wtpref = txdr_unsigned(pref); + sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE); + sip->fs_dtpref = txdr_unsigned(pref); + sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff; + sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff; + sip->fs_timedelta.nfsv3_sec = 0; + sip->fs_timedelta.nfsv3_nsec = txdr_unsigned(1); + sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK | + NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS | + NFSV3FSINFO_CANSETTIME); + nfsm_srvdone; +} + +/* + * nfs pathconf service + */ +int +nfsrv_pathconf(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; +{ + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; + register u_long *tl; + register struct nfsv3_pathconf *pc; + register long t1; + caddr_t bpos; + int error = 0, rdonly, cache, getret = 1, linkmax, namemax; + int chownres, notrunc; + char *cp2; + struct mbuf *mb, *mb2, *mreq; + struct vnode *vp; + struct vattr at; + nfsfh_t nfh; + fhandle_t *fhp; + u_quad_t frev; + +#ifndef nolint + cache = 0; +#endif + fhp = &nfh.fh_generic; + nfsm_srvmtofh(fhp); + if (error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, + &rdonly, (nfsd->nd_flag & ND_KERBAUTH))) { + nfsm_reply(NFSX_UNSIGNED); + nfsm_srvpostop_attr(getret, &at); + return (0); + } + error = VOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax); + if (!error) + error = VOP_PATHCONF(vp, _PC_NAME_MAX, &namemax); + if (!error) + error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres); + if (!error) + error = VOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc); + getret = VOP_GETATTR(vp, &at, cred, procp); + nfsrv_vput(vp); + nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF); + nfsm_srvpostop_attr(getret, &at); + if (error) + return (0); + nfsm_build(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF); + + pc->pc_linkmax = txdr_unsigned(linkmax); + pc->pc_namemax = txdr_unsigned(namemax); + pc->pc_notrunc = txdr_unsigned(notrunc); + pc->pc_chownrestricted = txdr_unsigned(chownres); + + /* + * These should probably be supported by VOP_PATHCONF(), but + * until msdosfs is exportable (why would you want to?), the + * Unix defaults should be ok. + */ + pc->pc_caseinsensitive = nfs_false; + pc->pc_casepreserving = nfs_true; + nfsm_srvdone; +} + +/* * Null operation, used by clients to ping server */ /* ARGSUSED */ int -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; +nfsrv_null(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; caddr_t bpos; - int error = VNOVAL, cache = 0; + int error = NFSERR_RETVOID, cache; struct mbuf *mb, *mreq; u_quad_t frev; +#ifndef nolint + cache = 0; +#endif nfsm_reply(0); - return (error); + return (0); } /* @@ -1946,24 +3333,30 @@ nfsrv_null(nfsd, mrep, md, dpos, cred, nam, mrq) */ /* ARGSUSED */ int -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; +nfsrv_noop(nfsd, slp, procp, mrq) + struct nfsrv_descript *nfsd; + struct nfssvc_sock *slp; + struct proc *procp; + struct mbuf **mrq; { + struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; + struct mbuf *nam = nfsd->nd_nam; + caddr_t dpos = nfsd->nd_dpos; + struct ucred *cred = &nfsd->nd_cr; caddr_t bpos; - int error, cache = 0; + int error, cache; struct mbuf *mb, *mreq; u_quad_t frev; +#ifndef nolint + cache = 0; +#endif if (nfsd->nd_repstat) error = nfsd->nd_repstat; else error = EPROCUNAVAIL; nfsm_reply(0); - return (error); + return (0); } /* @@ -1997,8 +3390,6 @@ nfsrv_access(vp, flags, cred, rdonly, p) switch (vp->v_type) { case VREG: case VDIR: case VLNK: return (EROFS); - default: - break; } } /* @@ -2008,8 +3399,7 @@ nfsrv_access(vp, flags, cred, rdonly, p) if (vp->v_flag & VTEXT) return (ETXTBSY); } - error = VOP_GETATTR(vp, &vattr, cred, p); - if (error) + if (error = VOP_GETATTR(vp, &vattr, cred, p)) return (error); if ((error = VOP_ACCESS(vp, flags, cred, p)) && cred->cr_uid != vattr.va_uid) diff --git a/sys/nfsserver/nfs_srvcache.c b/sys/nfsserver/nfs_srvcache.c index 0f31ae0..acdce0d 100644 --- a/sys/nfsserver/nfs_srvcache.c +++ b/sys/nfsserver/nfs_srvcache.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_srvcache.c 8.1 (Berkeley) 6/10/93 - * $Id: nfs_srvcache.c,v 1.4 1994/10/02 17:27:00 phk Exp $ + * $Id: nfs_srvcache.c,v 1.5 1994/10/17 17:47:36 phk Exp $ */ /* @@ -59,11 +59,13 @@ #endif #include <nfs/nfsm_subs.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/nfsrvcache.h> #include <nfs/nqnfs.h> +extern struct nfsstats nfsstats; +extern int nfsv2_procid[NFS_NPROCS]; long numnfsrvcache, desirednfsrvcache = NFSRVCACHESIZ; #define NFSRCHASH(xid) \ @@ -89,7 +91,6 @@ int nonidempotent[NFS_NPROCS] = { FALSE, FALSE, FALSE, - FALSE, TRUE, TRUE, TRUE, @@ -98,6 +99,10 @@ int nonidempotent[NFS_NPROCS] = { TRUE, TRUE, TRUE, + TRUE, + FALSE, + FALSE, + FALSE, FALSE, FALSE, FALSE, @@ -108,7 +113,7 @@ int nonidempotent[NFS_NPROCS] = { }; /* True iff the rpc reply is an nfs status ONLY! */ -static int repliesstatus[NFS_NPROCS] = { +static int nfsv2_repstat[NFS_NPROCS] = { FALSE, FALSE, FALSE, @@ -127,11 +132,6 @@ static int repliesstatus[NFS_NPROCS] = { TRUE, FALSE, FALSE, - FALSE, - FALSE, - FALSE, - FALSE, - TRUE, }; /* @@ -160,9 +160,9 @@ nfsrv_initcache() * Update/add new request at end of lru list */ int -nfsrv_getcache(nam, nd, repp) - struct mbuf *nam; - register struct nfsd *nd; +nfsrv_getcache(nd, slp, repp) + register struct nfsrv_descript *nd; + struct nfssvc_sock *slp; struct mbuf **repp; { register struct nfsrvcache *rp; @@ -171,13 +171,17 @@ nfsrv_getcache(nam, nd, repp) caddr_t bpos; int ret; - if (nd->nd_nqlflag != NQL_NOVAL) + /* + * Don't cache recent requests for reliable transport protocols. + * (Maybe we should for the case of a reconnect, but..) + */ + if (!nd->nd_nam2) return (RC_DOIT); loop: for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; rp = rp->rc_hash.le_next) { if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && - netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { + netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { if ((rp->rc_flag & RC_LOCKED) != 0) { rp->rc_flag |= RC_WANTED; (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); @@ -196,7 +200,7 @@ loop: ret = RC_DROPIT; } else if (rp->rc_flag & RC_REPSTATUS) { nfsstats.srvcache_nonidemdonehits++; - nfs_rephead(0, nd, rp->rc_status, + nfs_rephead(0, nd, slp, rp->rc_status, 0, (u_quad_t *)0, repp, &mb, &bpos); ret = RC_REPLY; } else if (rp->rc_flag & RC_REPMBUF) { @@ -243,7 +247,7 @@ loop: TAILQ_INSERT_TAIL(&nfsrvlruhead, rp, rc_lru); rp->rc_state = RC_INPROG; rp->rc_xid = nd->nd_retxid; - saddr = mtod(nam, struct sockaddr_in *); + saddr = mtod(nd->nd_nam, struct sockaddr_in *); switch (saddr->sin_family) { case AF_INET: rp->rc_flag |= RC_INETADDR; @@ -252,7 +256,7 @@ loop: case AF_ISO: default: rp->rc_flag |= RC_NAM; - rp->rc_nam = m_copym(nam, 0, M_COPYALL, M_WAIT); + rp->rc_nam = m_copym(nd->nd_nam, 0, M_COPYALL, M_WAIT); break; }; rp->rc_proc = nd->nd_procnum; @@ -269,21 +273,20 @@ loop: * 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; +nfsrv_updatecache(nd, repvalid, repmbuf) + register struct nfsrv_descript *nd; int repvalid; struct mbuf *repmbuf; { register struct nfsrvcache *rp; - if (nd->nd_nqlflag != NQL_NOVAL) + if (!nd->nd_nam2) return; loop: for (rp = NFSRCHASH(nd->nd_retxid)->lh_first; rp != 0; rp = rp->rc_hash.le_next) { if (nd->nd_retxid == rp->rc_xid && nd->nd_procnum == rp->rc_proc && - netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nam)) { + netaddr_match(NETFAMILY(rp), &rp->rc_haddr, nd->nd_nam)) { if ((rp->rc_flag & RC_LOCKED) != 0) { rp->rc_flag |= RC_WANTED; (void) tsleep((caddr_t)rp, PZERO-1, "nfsrc", 0); @@ -296,7 +299,8 @@ loop: * the reply for non-idempotent rpc's. */ if (repvalid && nonidempotent[nd->nd_procnum]) { - if (repliesstatus[nd->nd_procnum]) { + if ((nd->nd_flag & ND_NFSV3) == 0 && + nfsv2_repstat[nfsv2_procid[nd->nd_procnum]]) { rp->rc_status = nd->nd_repstat; rp->rc_flag |= RC_REPSTATUS; } else { diff --git a/sys/nfsserver/nfs_srvsock.c b/sys/nfsserver/nfs_srvsock.c index 20253ef..8d15b24 100644 --- a/sys/nfsserver/nfs_srvsock.c +++ b/sys/nfsserver/nfs_srvsock.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_socket.c 8.3 (Berkeley) 1/12/94 - * $Id: nfs_socket.c,v 1.6 1995/03/16 18:15:37 bde Exp $ + * $Id: nfs_socket.c,v 1.7 1995/05/30 08:12:40 rgrimes Exp $ */ /* @@ -59,7 +59,7 @@ #include <netinet/tcp.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/xdr_subs.h> #include <nfs/nfsm_subs.h> @@ -94,36 +94,13 @@ * 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_msgaccepted, rpc_call, rpc_autherr, rpc_auth_kerb; -extern u_long nfs_prog, nfs_vers, nqnfs_prog, nqnfs_vers; +extern u_long nfs_prog, nqnfs_prog; 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, -}; +extern struct nfsstats nfsstats; +extern int nfsv3_procid[NFS_NPROCS]; +extern int nfs_ticks; /* * Defines which timer to use for the procnum. @@ -134,7 +111,8 @@ static int nfsrv_errmap[ELAST] = { * 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, + 0, 1, 0, 2, 1, 3, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, + 0, 0, 0, }; /* @@ -164,10 +142,12 @@ int nfsrv_null(), nfsrv_getattr(), nfsrv_setattr(), nfsrv_lookup(), + nfsrv3_access(), nfsrv_readlink(), nfsrv_read(), nfsrv_write(), nfsrv_create(), + nfsrv_mknod(), nfsrv_remove(), nfsrv_rename(), nfsrv_link(), @@ -175,37 +155,42 @@ int nfsrv_null(), nfsrv_mkdir(), nfsrv_rmdir(), nfsrv_readdir(), + nfsrv_readdirplus(), nfsrv_statfs(), + nfsrv_fsinfo(), + nfsrv_pathconf(), + nfsrv_commit(), nfsrv_noop(), - nqnfsrv_readdirlook(), nqnfsrv_getlease(), - nqnfsrv_vacated(), - nqnfsrv_access(); + nqnfsrv_vacated(); -int (*nfsrv_procs[NFS_NPROCS])() = { +int (*nfsrv3_procs[NFS_NPROCS])() = { nfsrv_null, nfsrv_getattr, nfsrv_setattr, - nfsrv_noop, nfsrv_lookup, + nfsrv3_access, nfsrv_readlink, nfsrv_read, - nfsrv_noop, nfsrv_write, nfsrv_create, + nfsrv_mkdir, + nfsrv_symlink, + nfsrv_mknod, nfsrv_remove, + nfsrv_rmdir, nfsrv_rename, nfsrv_link, - nfsrv_symlink, - nfsrv_mkdir, - nfsrv_rmdir, nfsrv_readdir, + nfsrv_readdirplus, nfsrv_statfs, - nqnfsrv_readdirlook, + nfsrv_fsinfo, + nfsrv_pathconf, + nfsrv_commit, nqnfsrv_getlease, nqnfsrv_vacated, nfsrv_noop, - nqnfsrv_access, + nfsrv_noop }; /* @@ -874,13 +859,16 @@ nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp) struct nfsmount *nmp; struct mbuf *md, *mheadend; struct nfsnode *np; + char nickv[RPCX_NICKVERF]; 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; + int verf_len, verf_type; u_long xid; u_quad_t frev; - char *auth_str; + char *auth_str, *verf_str; + NFSKERBKEY_T key; /* save session key */ nmp = VFSTONFS(vp->v_mount); MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq), M_NFSREQ, M_WAITOK); @@ -900,19 +888,21 @@ nfs_request(vp, mrest, procnum, procp, cred, mrp, mdp, dposp) * Get the RPC header with authorization. */ kerbauth: - auth_str = (char *)0; + verf_str = 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); + verf_str = nickv; + verf_len = sizeof (nickv); + auth_type = RPCAUTH_KERB4; + bzero((caddr_t)key, sizeof (key)); + if (failed_auth || nfs_getnickauth(nmp, cred, &auth_str, + &auth_len, verf_str, verf_len)) { + error = nfs_getauth(nmp, rep, cred, &auth_str, + &auth_len, verf_str, &verf_len, key); 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; @@ -922,8 +912,8 @@ kerbauth: 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); + m = nfsm_rpchead(cred, nmp->nm_flag, procnum, auth_type, auth_len, + auth_str, verf_len, verf_str, mrest, mrest_len, &mheadend, &xid); if (auth_str) free(auth_str, M_TEMP); @@ -1029,12 +1019,12 @@ tryagain: /* * break down the rpc header and check if ok */ - nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED); + 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) { + if (!failed_auth) { failed_auth++; mheadend->m_next = (struct mbuf *)0; m_freem(mrep); @@ -1051,22 +1041,25 @@ tryagain: } /* - * skip over the auth_verf, someday we may want to cache auth_short's - * for nfs_reqhead(), but for now just dump it + * Grab any Kerberos verifier, otherwise just throw it away. */ - if (*++tl != 0) { - i = nfsm_rndup(fxdr_unsigned(long, *tl)); - nfsm_adv(i); - } + verf_type = fxdr_unsigned(int, *tl++); + i = fxdr_unsigned(int, *tl); + if ((nmp->nm_flag & NFSMNT_KERB) && verf_type == RPCAUTH_KERB4) { + error = nfs_savenickauth(nmp, cred, i, key, &md, &dpos, mrep); + if (error) + goto nfsmout; + } else if (i > 0) + nfsm_adv(nfsm_rndup(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) { + if ((nmp->nm_flag & NFSMNT_NFSV3) && + error == NFSERR_TRYLATER) { + m_freem(mrep); error = 0; waituntil = time.tv_sec + trylater_delay; while (time.tv_sec < waituntil) @@ -1084,6 +1077,13 @@ tryagain: */ if (error == ESTALE) cache_purge(vp); + if (nmp->nm_flag & NFSMNT_NFSV3) { + *mrp = mrep; + *mdp = md; + *dposp = dpos; + error |= NFSERR_RETERR; + } else + m_freem(mrep); m_freem(rep->r_mreq); free((caddr_t)rep, M_NFSREQ); return (error); @@ -1115,10 +1115,10 @@ tryagain: return (0); } m_freem(mrep); - m_freem(rep->r_mreq); - free((caddr_t)rep, M_NFSREQ); error = EPROTONOSUPPORT; nfsmout: + m_freem(rep->r_mreq); + free((caddr_t)rep, M_NFSREQ); return (error); } @@ -1127,9 +1127,10 @@ nfsmout: * siz arg. is used to decide if adding a cluster is worthwhile */ int -nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp) +nfs_rephead(siz, nd, slp, err, cache, frev, mrq, mbp, bposp) int siz; - struct nfsd *nd; + struct nfsrv_descript *nd; + struct nfssvc_sock *slp; int err; int cache; u_quad_t *frev; @@ -1154,47 +1155,98 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp) } else mreq->m_data += max_hdr; tl = mtod(mreq, u_long *); - mreq->m_len = 6*NFSX_UNSIGNED; - bpos = ((caddr_t)tl)+mreq->m_len; + mreq->m_len = 6 * NFSX_UNSIGNED; + bpos = ((caddr_t)tl) + mreq->m_len; *tl++ = txdr_unsigned(nd->nd_retxid); *tl++ = rpc_reply; - if (err == ERPCMISMATCH || err == NQNFS_AUTHERR) { + if (err == ERPCMISMATCH || (err & NFSERR_AUTHERR)) { *tl++ = rpc_msgdenied; - if (err == NQNFS_AUTHERR) { + if (err & NFSERR_AUTHERR) { *tl++ = rpc_autherr; - *tl = rpc_rejectedcred; + *tl = txdr_unsigned(err & ~NFSERR_AUTHERR); mreq->m_len -= NFSX_UNSIGNED; bpos -= NFSX_UNSIGNED; } else { *tl++ = rpc_mismatch; - *tl++ = txdr_unsigned(2); - *tl = txdr_unsigned(2); + *tl++ = txdr_unsigned(RPC_VER2); + *tl = txdr_unsigned(RPC_VER2); } } else { *tl++ = rpc_msgaccepted; - *tl++ = 0; - *tl++ = 0; + + /* + * For Kerberos authentication, we must send the nickname + * verifier back, otherwise just RPCAUTH_NULL. + */ + if (nd->nd_flag & ND_KERBFULL) { + register struct nfsuid *nuidp; + struct timeval ktvin, ktvout; + NFSKERBKEYSCHED_T keys; /* stores key schedule */ + + for (nuidp = NUIDHASH(slp, nd->nd_cr.cr_uid)->lh_first; + nuidp != 0; nuidp = nuidp->nu_hash.le_next) { + if (nuidp->nu_cr.cr_uid == nd->nd_cr.cr_uid && + (!nd->nd_nam2 || netaddr_match(NU_NETFAM(nuidp), + &nuidp->nu_haddr, nd->nd_nam2))) + break; + } + if (nuidp) { + ktvin.tv_sec = + txdr_unsigned(nuidp->nu_timestamp.tv_sec - 1); + ktvin.tv_usec = + txdr_unsigned(nuidp->nu_timestamp.tv_usec); + + /* + * Encrypt the timestamp in ecb mode using the + * session key. + */ +#ifdef NFSKERB + XXX +#endif + + *tl++ = rpc_auth_kerb; + *tl++ = txdr_unsigned(3 * NFSX_UNSIGNED); + *tl = ktvout.tv_sec; + nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + *tl++ = ktvout.tv_usec; + *tl++ = txdr_unsigned(nuidp->nu_cr.cr_uid); + } else { + *tl++ = 0; + *tl++ = 0; + } + } else { + *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 */ + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + if (nd->nd_flag & ND_NQNFS) { + *tl++ = txdr_unsigned(3); + *tl = txdr_unsigned(3); + } else { + *tl++ = txdr_unsigned(2); + *tl = txdr_unsigned(3); + } break; case EPROCUNAVAIL: *tl = txdr_unsigned(RPC_PROCUNAVAIL); break; + case EBADRPC: + *tl = txdr_unsigned(RPC_GARBAGE); + break; default: *tl = 0; - if (err != VNOVAL) { + if (err != NFSERR_RETVOID) { nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (err) - *tl = txdr_unsigned(nfsrv_errmap[err - 1]); + *tl = txdr_unsigned(nfsrv_errmap(nd, err)); else - *tl = 0; + *tl = 0; } break; }; @@ -1203,16 +1255,14 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp) /* * 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); + if ((nd->nd_flag & ND_NQNFS) && err == 0) { + if (nd->nd_flag & ND_LEASE) { + nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(nd->nd_flag & ND_LEASE); *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; } @@ -1220,7 +1270,7 @@ nfs_rephead(siz, nd, err, cache, frev, mrq, mbp, bposp) *mrq = mreq; *mbp = mb; *bposp = bpos; - if (err != 0 && err != VNOVAL) + if (err != 0 && err != NFSERR_RETVOID) nfsstats.srvrpc_errs++; return (0); } @@ -1240,8 +1290,10 @@ nfs_timer(arg) register struct socket *so; register struct nfsmount *nmp; register int timeo; + register struct nfssvc_sock *slp; static long lasttime = 0; int s, error; + u_quad_t cur_usec; s = splnet(); for (rep = nfs_reqq.tqh_first; rep != 0; rep = rep->r_chain.tqe_next) { @@ -1338,8 +1390,19 @@ nfs_timer(arg) lasttime = time.tv_sec; nqnfs_serverd(); } + + /* + * Scan the write gathering queues for writes that need to be + * completed now. + */ + cur_usec = (u_quad_t)time.tv_sec * 1000000 + (u_quad_t)time.tv_usec; + for (slp = nfssvc_sockhead.tqh_first; slp != 0; + slp = slp->ns_chain.tqe_next) { + if (slp->ns_tq.lh_first && slp->ns_tq.lh_first->nd_time<=cur_usec) + nfsrv_wakenfsd(slp); + } splx(s); - timeout(nfs_timer, (void *)0, hz / NFS_HZ); + timeout(nfs_timer, (void *)0, nfs_ticks); } /* @@ -1673,7 +1736,7 @@ nfsrv_getstream(slp, waitflag) register struct nfssvc_sock *slp; int waitflag; { - register struct mbuf *m; + register struct mbuf *m, **mpp; register char *cp1, *cp2; register int len; struct mbuf *om, *m2, *recm = 0; @@ -1707,7 +1770,12 @@ nfsrv_getstream(slp, waitflag) } } slp->ns_cc -= NFSX_UNSIGNED; - slp->ns_reclen = ntohl(recmark) & ~0x80000000; + recmark = ntohl(recmark); + slp->ns_reclen = recmark & ~0x80000000; + if (recmark & 0x80000000) + slp->ns_flag |= SLP_LASTFRAG; + else + slp->ns_flag &= ~SLP_LASTFRAG; if (slp->ns_reclen < NFS_MINPACKET || slp->ns_reclen > NFS_MAXPACKET) { slp->ns_flag &= ~SLP_GETSTREAM; return (EPERM); @@ -1761,12 +1829,23 @@ nfsrv_getstream(slp, waitflag) 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; + + /* + * Accumulate the fragments into a record. + */ + mpp = &slp->ns_frag; + while (*mpp) + mpp = &((*mpp)->m_next); + *mpp = recm; + if (slp->ns_flag & SLP_LASTFRAG) { + nfs_realign(slp->ns_frag, 10 * NFSX_UNSIGNED); + if (slp->ns_recend) + slp->ns_recend->m_nextpkt = slp->ns_frag; + else + slp->ns_rec = slp->ns_frag; + slp->ns_recend = slp->ns_frag; + slp->ns_frag = (struct mbuf *)0; + } } } @@ -1774,13 +1853,16 @@ nfsrv_getstream(slp, waitflag) * Parse an RPC header. */ int -nfsrv_dorec(slp, nd) +nfsrv_dorec(slp, nfsd, ndp) register struct nfssvc_sock *slp; - register struct nfsd *nd; + struct nfsd *nfsd; + struct nfsrv_descript **ndp; { - register struct mbuf *m; + register struct mbuf *m, *nam; + register struct nfsrv_descript *nd; int error; + *ndp = NULL; if ((slp->ns_flag & SLP_VALID) == 0 || (m = slp->ns_rec) == (struct mbuf *)0) return (ENOBUFS); @@ -1790,19 +1872,24 @@ nfsrv_dorec(slp, nd) 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); - error = nfs_getreq(nd, TRUE); + nam = m; + m = m->m_next; + nam->m_next = NULL; + } else + nam = NULL; + MALLOC(nd, struct nfsrv_descript *, sizeof (struct nfsrv_descript), + M_NFSRVDESC, M_WAITOK); + nd->nd_md = nd->nd_mrep = m; + nd->nd_nam2 = nam; + nd->nd_dpos = mtod(m, caddr_t); + error = nfs_getreq(nd, nfsd, TRUE); if (error) { - m_freem(nd->nd_nam); + m_freem(nam); + free((caddr_t)nd, M_NFSRVDESC); return (error); } + *ndp = nd; + nfsd->nfsd_nd = nd; return (0); } @@ -1812,8 +1899,9 @@ nfsrv_dorec(slp, nd) * - fill in the cred struct. */ int -nfs_getreq(nd, has_header) - register struct nfsd *nd; +nfs_getreq(nd, nfsd, has_header) + register struct nfsrv_descript *nd; + struct nfsd *nfsd; int has_header; { register int len, i; @@ -1821,57 +1909,67 @@ nfs_getreq(nd, has_header) register long t1; struct uio uio; struct iovec iov; - caddr_t dpos, cp2; + caddr_t dpos, cp2, cp; u_long nfsvers, auth_type; - int error = 0, nqnfs = 0; + uid_t nickuid; + int error = 0, nqnfs = 0, ticklen; struct mbuf *mrep, *md; + register struct nfsuid *nuidp; + struct timeval tvin, tvout; + NFSKERBKEYSCHED_T keys; /* stores key schedule */ mrep = nd->nd_mrep; md = nd->nd_md; dpos = nd->nd_dpos; if (has_header) { - nfsm_dissect(tl, u_long *, 10*NFSX_UNSIGNED); + nfsm_dissect(tl, u_long *, 10 * NFSX_UNSIGNED); nd->nd_retxid = fxdr_unsigned(u_long, *tl++); if (*tl++ != rpc_call) { m_freem(mrep); return (EBADRPC); } - } else { - nfsm_dissect(tl, u_long *, 8*NFSX_UNSIGNED); - } + } else + nfsm_dissect(tl, u_long *, 8 * NFSX_UNSIGNED); nd->nd_repstat = 0; + nd->nd_flag = 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) { + if (*tl == nqnfs_prog) nqnfs++; - nfsvers = nqnfs_vers; - } else { + else { nd->nd_repstat = EPROGUNAVAIL; nd->nd_procnum = NFSPROC_NOOP; return (0); } } tl++; - if (*tl++ != nfsvers) { + nfsvers = fxdr_unsigned(u_long, *tl++); + if (((nfsvers < NFS_VER2 || nfsvers > NFS_VER3) && !nqnfs) || + (nfsvers != NQNFS_VER3 && nqnfs)) { nd->nd_repstat = EPROGMISMATCH; nd->nd_procnum = NFSPROC_NOOP; return (0); } + if (nqnfs) + nd->nd_flag = (ND_NFSV3 | ND_NQNFS); + else if (nfsvers == NFS_VER3) + nd->nd_flag = ND_NFSV3; 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)) { + (!nqnfs && nd->nd_procnum >= NQNFSPROC_GETLEASE) || + (!nd->nd_flag && nd->nd_procnum > NFSV2PROC_STATFS)) { nd->nd_repstat = EPROCUNAVAIL; nd->nd_procnum = NFSPROC_NOOP; return (0); } + if ((nd->nd_flag & ND_NFSV3) == 0) + nd->nd_procnum = nfsv3_procid[nd->nd_procnum]; auth_type = *tl++; len = fxdr_unsigned(int, *tl++); if (len < 0 || len > RPCAUTH_MAXSIZ) { @@ -1879,6 +1977,7 @@ nfs_getreq(nd, has_header) return (EBADRPC); } + nd->nd_flag &= ~ND_KERBAUTH; /* * Handle auth_unix or auth_kerb. */ @@ -1889,7 +1988,9 @@ nfs_getreq(nd, has_header) return (EBADRPC); } nfsm_adv(nfsm_rndup(len)); - nfsm_dissect(tl, u_long *, 3*NFSX_UNSIGNED); + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + bzero((caddr_t)&nd->nd_cr, sizeof (struct ucred)); + nd->nd_cr.cr_ref = 1; 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); @@ -1897,45 +1998,124 @@ nfs_getreq(nd, has_header) m_freem(mrep); return (EBADRPC); } - nfsm_dissect(tl, u_long *, (len + 2)*NFSX_UNSIGNED); + 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++; + 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)) { + if (nd->nd_cr.cr_ngroups > 1) + nfsrvw_sort(nd->nd_cr.cr_groups, nd->nd_cr.cr_ngroups); + len = fxdr_unsigned(int, *++tl); + if (len < 0 || len > RPCAUTH_MAXSIZ) { 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; - } + if (len > 0) + nfsm_adv(nfsm_rndup(len)); + } else if (auth_type == rpc_auth_kerb) { + switch (fxdr_unsigned(int, *tl++)) { + case RPCAKN_FULLNAME: + ticklen = fxdr_unsigned(int, *tl); + *((u_long *)nfsd->nfsd_authstr) = *tl; + uio.uio_resid = nfsm_rndup(ticklen) + NFSX_UNSIGNED; + nfsd->nfsd_authlen = uio.uio_resid + NFSX_UNSIGNED; + 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)&nfsd->nfsd_authstr[4]; + iov.iov_len = RPCAUTH_MAXSIZ - 4; + nfsm_mtouio(&uio, uio.uio_resid); + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + if (*tl++ != rpc_auth_kerb || + fxdr_unsigned(int, *tl) != 4 * NFSX_UNSIGNED) { + printf("Bad kerb verifier\n"); + nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } + nfsm_dissect(cp, caddr_t, 4 * NFSX_UNSIGNED); + tl = (u_long *)cp; + if (fxdr_unsigned(int, *tl) != RPCAKN_FULLNAME) { + printf("Not fullname kerb verifier\n"); + nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } + cp += NFSX_UNSIGNED; + bcopy(cp, nfsd->nfsd_verfstr, 3 * NFSX_UNSIGNED); + nfsd->nfsd_verflen = 3 * NFSX_UNSIGNED; + nd->nd_flag |= ND_KERBFULL; + nfsd->nfsd_flag |= NFSD_NEEDAUTH; + break; + case RPCAKN_NICKNAME: + if (len != 2 * NFSX_UNSIGNED) { + printf("Kerb nickname short\n"); + nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADCRED); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } + nickuid = fxdr_unsigned(uid_t, *tl); + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + if (*tl++ != rpc_auth_kerb || + fxdr_unsigned(int, *tl) != 3 * NFSX_UNSIGNED) { + printf("Kerb nick verifier bad\n"); + nd->nd_repstat = (NFSERR_AUTHERR|AUTH_BADVERF); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + tvin.tv_sec = *tl++; + tvin.tv_usec = *tl; + + for (nuidp = NUIDHASH(nfsd->nfsd_slp,nickuid)->lh_first; + nuidp != 0; nuidp = nuidp->nu_hash.le_next) { + if (nuidp->nu_cr.cr_uid == nickuid && + (!nd->nd_nam2 || + netaddr_match(NU_NETFAM(nuidp), + &nuidp->nu_haddr, nd->nd_nam2))) + break; + } + if (!nuidp) { + nd->nd_repstat = + (NFSERR_AUTHERR|AUTH_REJECTCRED); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } - /* - * 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)); + /* + * Now, decrypt the timestamp using the session key + * and validate it. + */ +#ifdef NFSKERB + XXX +#endif + + tvout.tv_sec = fxdr_unsigned(long, tvout.tv_sec); + tvout.tv_usec = fxdr_unsigned(long, tvout.tv_usec); + if (nuidp->nu_expire < time.tv_sec || + nuidp->nu_timestamp.tv_sec > tvout.tv_sec || + (nuidp->nu_timestamp.tv_sec == tvout.tv_sec && + nuidp->nu_timestamp.tv_usec > tvout.tv_usec)) { + nuidp->nu_expire = 0; + nd->nd_repstat = + (NFSERR_AUTHERR|AUTH_REJECTVERF); + nd->nd_procnum = NFSPROC_NOOP; + return (0); + } + nfsrv_setcred(&nuidp->nu_cr, &nd->nd_cr); + nd->nd_flag |= ND_KERBNICK; + }; + } else { + nd->nd_repstat = (NFSERR_AUTHERR | AUTH_REJECTCRED); + nd->nd_procnum = NFSPROC_NOOP; + return (0); } /* @@ -1943,16 +2123,14 @@ nfs_getreq(nd, has_header) */ 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) { + nd->nd_flag |= fxdr_unsigned(int, *tl); + if (nd->nd_flag & ND_LEASE) { 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; + } else nd->nd_duration = NQ_MINLEASE; - } nd->nd_md = md; nd->nd_dpos = dpos; return (0); @@ -1973,13 +2151,13 @@ nfsrv_wakenfsd(slp) if ((slp->ns_flag & SLP_VALID) == 0) return; - for (nd = nfsd_head.tqh_first; nd != 0; nd = nd->nd_chain.tqe_next) { - if (nd->nd_flag & NFSD_WAITING) { - nd->nd_flag &= ~NFSD_WAITING; - if (nd->nd_slp) + for (nd = nfsd_head.tqh_first; nd != 0; nd = nd->nfsd_chain.tqe_next) { + if (nd->nfsd_flag & NFSD_WAITING) { + nd->nfsd_flag &= ~NFSD_WAITING; + if (nd->nfsd_slp) panic("nfsd wakeup"); slp->ns_sref++; - nd->nd_slp = slp; + nd->nfsd_slp = slp; wakeup((caddr_t)nd); return; } diff --git a/sys/nfsserver/nfs_srvsubs.c b/sys/nfsserver/nfs_srvsubs.c index 1653c8b..350ba46 100644 --- a/sys/nfsserver/nfs_srvsubs.c +++ b/sys/nfsserver/nfs_srvsubs.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_subs.c 8.3 (Berkeley) 1/4/94 - * $Id: nfs_subs.c,v 1.15 1995/05/30 08:12:43 rgrimes Exp $ + * $Id: nfs_subs.c,v 1.16 1995/06/14 06:23:38 joerg Exp $ */ /* @@ -52,6 +52,7 @@ #include <sys/mbuf.h> #include <sys/socket.h> #include <sys/stat.h> +#include <sys/malloc.h> #ifdef VFS_LKM #include <sys/sysent.h> #include <sys/syscall.h> @@ -60,7 +61,7 @@ #include <vm/vm.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfsnode.h> #include <nfs/nfs.h> #include <nfs/xdr_subs.h> @@ -78,31 +79,455 @@ #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_mismatch, rpc_auth_unix, rpc_msgaccepted, rpc_auth_kerb; -u_long nfs_vers, nfs_prog, nfs_true, nfs_false; +u_long nfs_prog, nqnfs_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 }; +enum vtype nv2tov_type[8] = { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VNON, VNON }; +enum vtype nv3tov_type[8]={ VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO }; +int nfs_ticks; + +/* + * Mapping of old NFS Version 2 RPC numbers to generic numbers. + */ +int nfsv3_procid[NFS_NPROCS] = { + NFSPROC_NULL, + NFSPROC_GETATTR, + NFSPROC_SETATTR, + NFSPROC_NOOP, + NFSPROC_LOOKUP, + NFSPROC_READLINK, + NFSPROC_READ, + NFSPROC_NOOP, + NFSPROC_WRITE, + NFSPROC_CREATE, + NFSPROC_REMOVE, + NFSPROC_RENAME, + NFSPROC_LINK, + NFSPROC_SYMLINK, + NFSPROC_MKDIR, + NFSPROC_RMDIR, + NFSPROC_READDIR, + NFSPROC_FSSTAT, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP, + NFSPROC_NOOP +}; + +/* + * and the reverse mapping from generic to Version 2 procedure numbers + */ +int nfsv2_procid[NFS_NPROCS] = { + NFSV2PROC_NULL, + NFSV2PROC_GETATTR, + NFSV2PROC_SETATTR, + NFSV2PROC_LOOKUP, + NFSV2PROC_NOOP, + NFSV2PROC_READLINK, + NFSV2PROC_READ, + NFSV2PROC_WRITE, + NFSV2PROC_CREATE, + NFSV2PROC_MKDIR, + NFSV2PROC_SYMLINK, + NFSV2PROC_CREATE, + NFSV2PROC_REMOVE, + NFSV2PROC_RMDIR, + NFSV2PROC_RENAME, + NFSV2PROC_LINK, + NFSV2PROC_READDIR, + NFSV2PROC_NOOP, + NFSV2PROC_STATFS, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, + NFSV2PROC_NOOP, +}; + +/* + * Maps errno values to nfs error numbers. + * Use NFSERR_IO as the catch all for ones not specifically defined in + * RFC 1094. + */ +static u_char nfsrv_v2errmap[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, +}; + +/* + * Maps errno values to nfs error numbers. + * Although it is not obvious whether or not NFS clients really care if + * a returned error value is in the specified list for the procedure, the + * safest thing to do is filter them appropriately. For Version 2, the + * X/Open XNFS document is the only specification that defines error values + * for each RPC (The RFC simply lists all possible error values for all RPCs), + * so I have decided to not do this for Version 2. + * The first entry is the default error return and the rest are the valid + * errors for that RPC in increasing numeric order. + */ +static short nfsv3err_null[] = { + 0, + 0, +}; + +static short nfsv3err_getattr[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_setattr[] = { + NFSERR_IO, + NFSERR_PERM, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_INVAL, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOT_SYNC, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_lookup[] = { + NFSERR_IO, + NFSERR_NOENT, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_NOTDIR, + NFSERR_NAMETOL, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_access[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_readlink[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_INVAL, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_read[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_NXIO, + NFSERR_ACCES, + NFSERR_INVAL, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_write[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_INVAL, + NFSERR_FBIG, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_create[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_NOTDIR, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_mkdir[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_NOTDIR, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_symlink[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_NOTDIR, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_mknod[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_NOTDIR, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + NFSERR_BADTYPE, + 0, +}; + +static short nfsv3err_remove[] = { + NFSERR_IO, + NFSERR_NOENT, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_NOTDIR, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_rmdir[] = { + NFSERR_IO, + NFSERR_NOENT, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_NOTDIR, + NFSERR_INVAL, + NFSERR_ROFS, + NFSERR_NAMETOL, + NFSERR_NOTEMPTY, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_rename[] = { + NFSERR_IO, + NFSERR_NOENT, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_XDEV, + NFSERR_NOTDIR, + NFSERR_ISDIR, + NFSERR_INVAL, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_MLINK, + NFSERR_NAMETOL, + NFSERR_NOTEMPTY, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_link[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_EXIST, + NFSERR_XDEV, + NFSERR_NOTDIR, + NFSERR_INVAL, + NFSERR_NOSPC, + NFSERR_ROFS, + NFSERR_MLINK, + NFSERR_NAMETOL, + NFSERR_DQUOT, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_NOTSUPP, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_readdir[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_NOTDIR, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_BAD_COOKIE, + NFSERR_TOOSMALL, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_readdirplus[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_ACCES, + NFSERR_NOTDIR, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_BAD_COOKIE, + NFSERR_NOTSUPP, + NFSERR_TOOSMALL, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_fsstat[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_fsinfo[] = { + NFSERR_STALE, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_pathconf[] = { + NFSERR_STALE, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short nfsv3err_commit[] = { + NFSERR_IO, + NFSERR_IO, + NFSERR_STALE, + NFSERR_BADHANDLE, + NFSERR_SERVERFAULT, + 0, +}; + +static short *nfsrv_v3errmap[] = { + nfsv3err_null, + nfsv3err_getattr, + nfsv3err_setattr, + nfsv3err_lookup, + nfsv3err_access, + nfsv3err_readlink, + nfsv3err_read, + nfsv3err_write, + nfsv3err_create, + nfsv3err_mkdir, + nfsv3err_symlink, + nfsv3err_mknod, + nfsv3err_remove, + nfsv3err_rmdir, + nfsv3err_rename, + nfsv3err_link, + nfsv3err_readdir, + nfsv3err_readdirplus, + nfsv3err_fsstat, + nfsv3err_fsinfo, + nfsv3err_pathconf, + nfsv3err_commit, +}; + extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; -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; +extern struct nfsstats nfsstats; +extern int nqnfs_piggy[NFS_NPROCS]; +extern nfstype nfsv2_type[9]; +extern nfstype nfsv3_type[9]; +extern struct nfsnodehashhead *nfsnodehashtbl; +extern u_long nfsnodehash; #ifdef VFS_LKM struct getfh_args; @@ -167,14 +592,16 @@ nfsm_reqh(vp, procid, hsiz, bposp) * 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) +nfsm_rpchead(cr, nmflag, procid, auth_type, auth_len, auth_str, verf_len, + verf_str, mrest, mrest_len, mbp, xidp) register struct ucred *cr; - int nqnfs; + int nmflag; int procid; int auth_type; int auth_len; char *auth_str; + int verf_len; + char *verf_str; struct mbuf *mrest; int mrest_len; struct mbuf **mbp; @@ -188,15 +615,13 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest, 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) { + 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 if ((authsiz + 10 * NFSX_UNSIGNED) < MHLEN) { + MH_ALIGN(mb, authsiz + 10 * NFSX_UNSIGNED); } else { - MH_ALIGN(mb, 8*NFSX_UNSIGNED); + MH_ALIGN(mb, 8 * NFSX_UNSIGNED); } mb->m_len = 0; mreq = mb; @@ -205,20 +630,26 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest, /* * First the RPC header. */ - nfsm_build(tl, u_long *, 8*NFSX_UNSIGNED); + 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) { + if (nmflag & NFSMNT_NQNFS) { *tl++ = txdr_unsigned(NQNFS_PROG); - *tl++ = txdr_unsigned(NQNFS_VER1); + *tl++ = txdr_unsigned(NQNFS_VER3); } else { *tl++ = txdr_unsigned(NFS_PROG); - *tl++ = txdr_unsigned(NFS_VER2); + if (nmflag & NFSMNT_NFSV3) + *tl++ = txdr_unsigned(NFS_VER3); + else + *tl++ = txdr_unsigned(NFS_VER2); } - *tl++ = txdr_unsigned(procid); + if (nmflag & NFSMNT_NFSV3) + *tl++ = txdr_unsigned(procid); + else + *tl++ = txdr_unsigned(nfsv2_procid[procid]); /* * And then the authorization cred. @@ -237,10 +668,7 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest, 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); + case RPCAUTH_KERB4: siz = auth_len; while (siz > 0) { if (M_TRAILINGSPACE(mb) == 0) { @@ -266,11 +694,43 @@ nfsm_rpchead(cr, nqnfs, procid, auth_type, auth_len, auth_str, mrest, } break; }; - nfsm_build(tl, u_long *, 2*NFSX_UNSIGNED); - *tl++ = txdr_unsigned(RPCAUTH_NULL); - *tl = 0; + + /* + * And the verifier... + */ + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + if (verf_str) { + *tl++ = txdr_unsigned(RPCAUTH_KERB4); + *tl = txdr_unsigned(verf_len); + siz = verf_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(verf_str, bpos, i); + mb->m_len += i; + verf_str += i; + bpos += i; + siz -= i; + } + if ((siz = (nfsm_rndup(verf_len) - verf_len)) > 0) { + for (i = 0; i < siz; i++) + *bpos++ = '\0'; + mb->m_len += siz; + } + } else { + *tl++ = txdr_unsigned(RPCAUTH_NULL); + *tl = 0; + } mb->m_next = mrest; - mreq->m_pkthdr.len = authsiz + 10*NFSX_UNSIGNED + mrest_len; + mreq->m_pkthdr.len = authsiz + 10 * NFSX_UNSIGNED + mrest_len; mreq->m_pkthdr.rcvif = (struct ifnet *)0; *mbp = mb; return (mreq); @@ -603,6 +1063,25 @@ nfs_init() { register int i; + /* + * Check to see if major data structures haven't bloated. + */ + if (sizeof (struct nfsnode) > NFS_NODEALLOC) { + printf("struct nfsnode bloated (> %dbytes)\n", NFS_NODEALLOC); + printf("Try reducing NFS_SMALLFH\n"); + } + if (sizeof (struct nfsmount) > NFS_MNTALLOC) { + printf("struct nfsmount bloated (> %dbytes)\n", NFS_MNTALLOC); + printf("Try reducing NFS_MUIDHASHSIZ\n"); + } + if (sizeof (struct nfssvc_sock) > NFS_SVCALLOC) { + printf("struct nfssvc_sock bloated (> %dbytes)\n",NFS_SVCALLOC); + printf("Try reducing NFS_UIDHASHSIZ\n"); + } + if (sizeof (struct nfsuid) > NFS_UIDALLOC) { + printf("struct nfsuid bloated (> %dbytes)\n",NFS_UIDALLOC); + printf("Try unionizing the nu_nickname and nu_flag fields\n"); + } nfsrtt.pos = 0; rpc_vers = txdr_unsigned(RPC_VER2); rpc_call = txdr_unsigned(RPC_CALL); @@ -611,17 +1090,16 @@ nfs_init() 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); + rpc_auth_kerb = txdr_unsigned(RPCAUTH_KERB4); nfs_prog = txdr_unsigned(NFS_PROG); + nqnfs_prog = txdr_unsigned(NQNFS_PROG); nfs_true = txdr_unsigned(TRUE); nfs_false = txdr_unsigned(FALSE); nfs_xdrneg1 = txdr_unsigned(-1); - /* Loop thru nfs procids */ - for (i = 0; i < NFS_NPROCS; i++) - nfs_procids[i] = txdr_unsigned(i); + nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000; + if (nfs_ticks < 1) + nfs_ticks = 1; /* Ensure async daemons disabled */ for (i = 0; i < NFS_MAXASYNCDAEMON; i++) nfs_iodwant[i] = (struct proc *)0; @@ -637,8 +1115,6 @@ nfs_init() nqnfsstarttime = boottime.tv_sec + nqsrv_maxlease + nqsrv_clockskew + nqsrv_writeslack; NQLOADNOVRAM(nqnfsstarttime); - nqnfs_prog = txdr_unsigned(NQNFS_PROG); - nqnfs_vers = txdr_unsigned(NQNFS_VER1); CIRCLEQ_INIT(&nqtimerhead); nqfhhashtbl = hashinit(NQLCHSZ, M_NQLEASE, &nqfhhash); } @@ -649,6 +1125,7 @@ nfs_init() TAILQ_INIT(&nfs_reqq); nfs_timer(0); +#ifdef __FreeBSD__ /* * Set up lease_check and lease_updatetime so that other parts * of the system can call us, if we are loadable. @@ -662,6 +1139,7 @@ nfs_init() sysent[SYS_getfh].sy_narg = 2; sysent[SYS_getfh].sy_call = getfh; #endif +#endif return (0); } @@ -689,57 +1167,65 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) { register struct vnode *vp = *vpp; register struct vattr *vap; - register struct nfsv2_fattr *fp; + register struct nfs_fattr *fp; register struct nfsnode *np; register struct nfsnodehashhead *nhpp; register long t1; - caddr_t dpos, cp2; - int error = 0, isnq; + caddr_t cp2; + int error = 0, rdev; struct mbuf *md; enum vtype vtyp; u_short vmode; - long rdev; struct timespec mtime; struct vnode *nvp; + quad_t tval; + int v3 = NFS_ISV3(vp); md = *mdp; - dpos = *dposp; - t1 = (mtod(md, caddr_t) + md->m_len) - dpos; - isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); - error = nfsm_disct(&md, &dpos, NFSX_FATTR(isnq), t1, &cp2); - if (error) + t1 = (mtod(md, caddr_t) + md->m_len) - *dposp; + if (error = nfsm_disct(mdp, dposp, NFSX_FATTR(v3), t1, &cp2)) return (error); - fp = (struct nfsv2_fattr *)cp2; - vtyp = nfstov_type(fp->fa_type); - vmode = fxdr_unsigned(u_short, fp->fa_mode); - /* - * XXX - * - * The duplicate information returned in fa_type and fa_mode - * is an ambiguity in the NFS version 2 protocol. - * - * VREG should be taken literally as a regular file. If a - * server intents to return some type information differently - * in the upper bits of the mode field (e.g. for sockets, or - * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we - * leave the examination of the mode bits even in the VREG - * case to avoid breakage for bogus servers, but we make sure - * that there are actually type bits set in the upper part of - * fa_mode (and failing that, trust the va_type field). - * - * NFSv3 cleared the issue, and requires fa_mode to not - * contain any type information (while also introduing sockets - * and FIFOs for fa_type). - */ - if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0)) - vtyp = IFTOVT(vmode); - if (isnq) { - rdev = fxdr_unsigned(long, fp->fa_nqrdev); - fxdr_nqtime(&fp->fa_nqmtime, &mtime); + fp = (struct nfs_fattr *)cp2; + if (v3) { + vtyp = nfsv3tov_type(fp->fa_type); + vmode = fxdr_unsigned(u_short, fp->fa_mode); + rdev = makedev(fxdr_unsigned(u_char, fp->fa3_rdev.specdata1), + fxdr_unsigned(u_char, fp->fa3_rdev.specdata2)); + fxdr_nfsv3time(&fp->fa3_mtime, &mtime); } else { - rdev = fxdr_unsigned(long, fp->fa_nfsrdev); - fxdr_nfstime(&fp->fa_nfsmtime, &mtime); + vtyp = nfsv2tov_type(fp->fa_type); + vmode = fxdr_unsigned(u_short, fp->fa_mode); + /* + * XXX + * + * The duplicate information returned in fa_type and fa_mode + * is an ambiguity in the NFS version 2 protocol. + * + * VREG should be taken literally as a regular file. If a + * server intents to return some type information differently + * in the upper bits of the mode field (e.g. for sockets, or + * FIFOs), NFSv2 mandates fa_type to be VNON. Anyway, we + * leave the examination of the mode bits even in the VREG + * case to avoid breakage for bogus servers, but we make sure + * that there are actually type bits set in the upper part of + * fa_mode (and failing that, trust the va_type field). + * + * NFSv3 cleared the issue, and requires fa_mode to not + * contain any type information (while also introduing sockets + * and FIFOs for fa_type). + */ + if (vtyp == VNON || (vtyp == VREG && (vmode & S_IFMT) != 0)) + vtyp = IFTOVT(vmode); + rdev = fxdr_unsigned(long, fp->fa2_rdev); + fxdr_nfsv2time(&fp->fa2_mtime, &mtime); + + /* + * Really ugly NFSv2 kludge. + */ + if (vtyp == VCHR && rdev == 0xffffffff) + vtyp = VFIFO; } + /* * 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 @@ -749,10 +1235,15 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) */ np = VTONFS(vp); if (vp->v_type == VNON) { - if (vtyp == VCHR && rdev == 0xffffffff) - vp->v_type = vtyp = VFIFO; - else - vp->v_type = vtyp; + /* + * If we had a lock and it turns out that the vnode + * is an object which we don't want to lock (e.g. VDIR) + * to avoid nasty hanging problems on a server crash, + * then release it here. + */ + if (vtyp != VREG && VOP_ISLOCKED(vp)) + VOP_UNLOCK(vp); + vp->v_type = vtyp; if (vp->v_type == VFIFO) { vp->v_op = fifo_nfsv2nodeop_p; } @@ -773,7 +1264,7 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) * Reinitialize aliased node. */ np->n_vnode = nvp; - nhpp = nfs_hash(&np->n_fh); + nhpp = NFSNOHASH(nfs_hash(np->n_fhp, np->n_fhsize)); LIST_INSERT_HEAD(nhpp, np, n_hash); *vpp = vp = nvp; } @@ -783,31 +1274,34 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) 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); + if (v3) { + 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); + fxdr_hyper(&fp->fa3_size, &vap->va_size); + vap->va_blocksize = NFS_FABLKSIZE; + fxdr_hyper(&fp->fa3_used, &vap->va_bytes); + vap->va_fileid = fxdr_unsigned(int, fp->fa3_fileid.nfsuquad[1]); + fxdr_nfsv3time(&fp->fa3_atime, &vap->va_atime); + fxdr_nfsv3time(&fp->fa3_ctime, &vap->va_ctime); + vap->va_flags = 0; + vap->va_filerev = 0; } 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_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_size = fxdr_unsigned(u_long, fp->fa2_size); + vap->va_blocksize = fxdr_unsigned(long, fp->fa2_blocksize); + vap->va_bytes = fxdr_unsigned(long, fp->fa2_blocks) * NFS_FABLKSIZE; + vap->va_fileid = fxdr_unsigned(long, fp->fa2_fileid); + fxdr_nfsv2time(&fp->fa2_atime, &vap->va_atime); vap->va_flags = 0; - fxdr_nfstime(&fp->fa_nfsctime, &vap->va_ctime); - vap->va_gen = 0; + vap->va_ctime.ts_sec = fxdr_unsigned(long, fp->fa2_ctime.nfsv2_sec); + vap->va_ctime.ts_nsec = 0; + vap->va_gen = fxdr_unsigned(u_long, fp->fa2_ctime.nfsv2_usec); vap->va_filerev = 0; } if (vap->va_size != np->n_size) { @@ -824,26 +1318,13 @@ nfs_loadattrcache(vpp, mdp, dposp, vaper) 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; - } + if (np->n_flag & NACC) + vaper->va_atime = np->n_atim; + if (np->n_flag & NUPD) + vaper->va_mtime = np->n_mtim; } } return (0); @@ -862,12 +1343,7 @@ nfs_getattrcache(vp, 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)) { + if ((time.tv_sec - np->n_attrstamp) >= NFS_ATTRTIMEO(np)) { nfsstats.attrcache_misses++; return (ENOENT); } @@ -887,23 +1363,11 @@ nfs_getattrcache(vp, vaper) 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; - } + if (np->n_flag & NACC) + vaper->va_atime = np->n_atim; + if (np->n_flag & NUPD) + vaper->va_mtime = np->n_mtim; } return (0); } @@ -912,7 +1376,7 @@ nfs_getattrcache(vp, vaper) * Set up nameidata for a lookup() call and do it */ int -nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) +nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, retdirp, p, kerbflag) register struct nameidata *ndp; fhandle_t *fhp; int len; @@ -920,7 +1384,9 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) struct mbuf *nam; struct mbuf **mdp; caddr_t *dposp; + struct vnode **retdirp; struct proc *p; + int kerbflag; { register int i, rem; register struct mbuf *md; @@ -929,6 +1395,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) int error, rdonly; struct componentname *cnp = &ndp->ni_cnd; + *retdirp = (struct vnode *)0; MALLOC(cnp->cn_pnbuf, char *, len + 1, M_NAMEI, M_WAITOK); /* * Copy the name from the mbuf list to ndp->ni_pnbuf @@ -950,7 +1417,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) rem = md->m_len; } if (*fromcp == '\0' || *fromcp == '/') { - error = EINVAL; + error = EACCES; goto out; } cnp->cn_hash += (unsigned char)*fromcp; @@ -964,26 +1431,24 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) if (len > 0) { if (rem >= len) *dposp += len; - else { - error = nfs_adv(mdp, dposp, len, rem); - if (error) - goto out; - } + 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. */ - error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp, - nam, &rdonly); - if (error) + if (error = nfsrv_fhtovp(fhp, FALSE, &dp, ndp->ni_cnd.cn_cred, slp, + nam, &rdonly, kerbflag)) goto out; if (dp->v_type != VDIR) { nfsrv_vrele(dp); error = ENOTDIR; goto out; } + VREF(dp); + *retdirp = dp; ndp->ni_startdir = dp; if (rdonly) cnp->cn_flags |= (NOCROSSMOUNT | RDONLY); @@ -993,8 +1458,7 @@ nfs_namei(ndp, fhp, len, slp, nam, mdp, dposp, p) * And call lookup() to do the real work */ cnp->cn_proc = p; - error = lookup(ndp); - if (error) + if (error = lookup(ndp)) goto out; /* * Check for encountering a symbolic link @@ -1087,6 +1551,109 @@ nfsm_adj(mp, len, nul) } /* + * Make these functions instead of macros, so that the kernel text size + * doesn't get too big... + */ +void +nfsm_srvwcc(nfsd, before_ret, before_vap, after_ret, after_vap, mbp, bposp) + struct nfsrv_descript *nfsd; + int before_ret; + register struct vattr *before_vap; + int after_ret; + struct vattr *after_vap; + struct mbuf **mbp; + char **bposp; +{ + register struct mbuf *mb = *mbp, *mb2; + register char *bpos = *bposp; + register u_long *tl; + + if (before_ret) { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } else { + nfsm_build(tl, u_long *, 7 * NFSX_UNSIGNED); + *tl++ = nfs_true; + txdr_hyper(&(before_vap->va_size), tl); + tl += 2; + txdr_nfsv3time(&(before_vap->va_mtime), tl); + tl += 2; + txdr_nfsv3time(&(before_vap->va_ctime), tl); + } + *bposp = bpos; + *mbp = mb; + nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp); +} + +void +nfsm_srvpostopattr(nfsd, after_ret, after_vap, mbp, bposp) + struct nfsrv_descript *nfsd; + int after_ret; + struct vattr *after_vap; + struct mbuf **mbp; + char **bposp; +{ + register struct mbuf *mb = *mbp, *mb2; + register char *bpos = *bposp; + register u_long *tl; + register struct nfs_fattr *fp; + + if (after_ret) { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3FATTR); + *tl++ = nfs_true; + fp = (struct nfs_fattr *)tl; + nfsm_srvfattr(nfsd, after_vap, fp); + } + *mbp = mb; + *bposp = bpos; +} + +void +nfsm_srvfattr(nfsd, vap, fp) + register struct nfsrv_descript *nfsd; + register struct vattr *vap; + register struct nfs_fattr *fp; +{ + + 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_flag & ND_NFSV3) { + fp->fa_type = vtonfsv3_type(vap->va_type); + fp->fa_mode = vtonfsv3_mode(vap->va_mode); + txdr_hyper(&vap->va_size, &fp->fa3_size); + txdr_hyper(&vap->va_bytes, &fp->fa3_used); + fp->fa3_rdev.specdata1 = txdr_unsigned(major(vap->va_rdev)); + fp->fa3_rdev.specdata2 = txdr_unsigned(minor(vap->va_rdev)); + fp->fa3_fsid.nfsuquad[0] = 0; + fp->fa3_fsid.nfsuquad[1] = txdr_unsigned(vap->va_fsid); + fp->fa3_fileid.nfsuquad[0] = 0; + fp->fa3_fileid.nfsuquad[1] = txdr_unsigned(vap->va_fileid); + txdr_nfsv3time(&vap->va_atime, &fp->fa3_atime); + txdr_nfsv3time(&vap->va_mtime, &fp->fa3_mtime); + txdr_nfsv3time(&vap->va_ctime, &fp->fa3_ctime); + } else { + fp->fa_type = vtonfsv2_type(vap->va_type); + fp->fa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); + fp->fa2_size = txdr_unsigned(vap->va_size); + fp->fa2_blocksize = txdr_unsigned(vap->va_blocksize); + if (vap->va_type == VFIFO) + fp->fa2_rdev = 0xffffffff; + else + fp->fa2_rdev = txdr_unsigned(vap->va_rdev); + fp->fa2_blocks = txdr_unsigned(vap->va_bytes / NFS_FABLKSIZE); + fp->fa2_fsid = txdr_unsigned(vap->va_fsid); + fp->fa2_fileid = txdr_unsigned(vap->va_fileid); + txdr_nfsv2time(&vap->va_atime, &fp->fa2_atime); + txdr_nfsv2time(&vap->va_mtime, &fp->fa2_mtime); + txdr_nfsv2time(&vap->va_ctime, &fp->fa2_ctime); + } +} + +/* * 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() @@ -1094,7 +1661,7 @@ nfsm_adj(mp, len, nul) * - if not lockflag unlock it with VOP_UNLOCK() */ int -nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp) +nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp, kerbflag) fhandle_t *fhp; int lockflag; struct vnode **vpp; @@ -1102,6 +1669,7 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp) struct nfssvc_sock *slp; struct mbuf *nam; int *rdonlyp; + int kerbflag; { register struct mount *mp; register struct nfsuid *uidp; @@ -1120,19 +1688,13 @@ nfsrv_fhtovp(fhp, lockflag, vpp, cred, slp, nam, rdonlyp) * Check/setup credentials. */ if (exflags & MNT_EXKERB) { - for (uidp = NUIDHASH(slp, cred->cr_uid)->lh_first; uidp != 0; - uidp = uidp->nu_hash.le_next) { - if (uidp->nu_uid == cred->cr_uid) - break; - } - if (uidp == 0) { + if (!kerbflag) { vput(*vpp); - return (NQNFS_AUTHERR); + return (NFSERR_AUTHERR | AUTH_TOOWEAK); } - 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]; - cred->cr_ngroups = i; + } else if (kerbflag) { + vput(*vpp); + return (NFSERR_AUTHERR | AUTH_TOOWEAK); } 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++) @@ -1194,6 +1756,145 @@ netaddr_match(family, haddr, nam) return (0); } +static nfsuint64 nfs_nullcookie = { 0, 0 }; +/* + * This function finds the directory cookie that corresponds to the + * logical byte offset given. + */ +nfsuint64 * +nfs_getcookie(np, off, add) + register struct nfsnode *np; + off_t off; + int add; +{ + register struct nfsdmap *dp, *dp2; + register int pos; + + pos = off / NFS_DIRBLKSIZ; + if (pos == 0) { +#ifdef DIAGNOSTIC + if (add) + panic("nfs getcookie add at 0"); +#endif + return (&nfs_nullcookie); + } + pos--; + dp = np->n_cookies.lh_first; + if (!dp) { + if (add) { + MALLOC(dp, struct nfsdmap *, sizeof (struct nfsdmap), + M_NFSDIROFF, M_WAITOK); + dp->ndm_eocookie = 0; + LIST_INSERT_HEAD(&np->n_cookies, dp, ndm_list); + } else + return ((nfsuint64 *)0); + } + while (pos >= NFSNUMCOOKIES) { + pos -= NFSNUMCOOKIES; + if (dp->ndm_list.le_next) { + if (!add && dp->ndm_eocookie < NFSNUMCOOKIES && + pos >= dp->ndm_eocookie) + return ((nfsuint64 *)0); + dp = dp->ndm_list.le_next; + } else if (add) { + MALLOC(dp2, struct nfsdmap *, sizeof (struct nfsdmap), + M_NFSDIROFF, M_WAITOK); + dp2->ndm_eocookie = 0; + LIST_INSERT_AFTER(dp, dp2, ndm_list); + dp = dp2; + } else + return ((nfsuint64 *)0); + } + if (pos >= dp->ndm_eocookie) { + if (add) + dp->ndm_eocookie = pos + 1; + else + return ((nfsuint64 *)0); + } + return (&dp->ndm_cookies[pos]); +} + +/* + * Invalidate cached directory information, except for the actual directory + * blocks (which are invalidated separately). + * Done mainly to avoid the use of stale offset cookies. + */ +void +nfs_invaldir(vp) + register struct vnode *vp; +{ + register struct nfsnode *np = VTONFS(vp); + +#ifdef DIAGNOSTIC + if (vp->v_type != VDIR) + panic("nfs: invaldir not dir"); +#endif + np->n_direofoffset = 0; + np->n_cookieverf.nfsuquad[0] = 0; + np->n_cookieverf.nfsuquad[1] = 0; + if (np->n_cookies.lh_first) + np->n_cookies.lh_first->ndm_eocookie = 0; +} + +/* + * The write verifier has changed (probably due to a server reboot), so all + * B_NEEDCOMMIT blocks will have to be written again. Since they are on the + * dirty block list as B_DELWRI, all this takes is clearing the B_NEEDCOMMIT + * flag. Once done the new write verifier can be set for the mount point. + */ +void +nfs_clearcommit(mp) + struct mount *mp; +{ + register struct vnode *vp, *nvp; + register struct buf *bp, *nbp; + int s; + + s = splbio(); +loop: + for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) { + if (vp->v_mount != mp) /* Paranoia */ + goto loop; + nvp = vp->v_mntvnodes.le_next; + for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { + nbp = bp->b_vnbufs.le_next; + if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) + == (B_DELWRI | B_NEEDCOMMIT)) + bp->b_flags &= ~B_NEEDCOMMIT; + } + } + splx(s); +} + +/* + * Map errnos to NFS error numbers. For Version 3 also filter out error + * numbers not specified for the associated procedure. + */ +int +nfsrv_errmap(nd, err) + struct nfsrv_descript *nd; + register int err; +{ + register short *defaulterrp, *errp; + + if (nd->nd_flag & ND_NFSV3) { + if (nd->nd_procnum <= NFSPROC_COMMIT) { + errp = defaulterrp = nfsrv_v3errmap[nd->nd_procnum]; + while (*++errp) { + if (*errp == err) + return (err); + else if (*errp > err) + break; + } + return ((int)*defaulterrp); + } else + return (err & 0xffff); + } + if (err <= ELAST) + return ((int)nfsrv_v2errmap[err - 1]); + return (NFSERR_IO); +} + int nfsrv_vmio( struct vnode *vp) { vm_object_t object; diff --git a/sys/nfsserver/nfs_syscalls.c b/sys/nfsserver/nfs_syscalls.c index f7e159f..35a50f4 100644 --- a/sys/nfsserver/nfs_syscalls.c +++ b/sys/nfsserver/nfs_syscalls.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_syscalls.c 8.3 (Berkeley) 1/4/94 - * $Id: nfs_syscalls.c,v 1.5 1994/10/17 17:47:38 phk Exp $ + * $Id: nfs_syscalls.c,v 1.6 1995/05/30 08:12:45 rgrimes Exp $ */ #include <sys/param.h> @@ -61,9 +61,11 @@ #ifdef ISO #include <netiso/iso.h> #endif +#include <nfs/xdr_subs.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> +#include <nfs/nfsm_subs.h> #include <nfs/nfsrvcache.h> #include <nfs/nfsmount.h> #include <nfs/nfsnode.h> @@ -73,13 +75,14 @@ void nfsrv_zapsock __P((struct nfssvc_sock *)); /* Global defs. */ -extern u_long nfs_prog, nfs_vers; -extern int (*nfsrv_procs[NFS_NPROCS])(); +extern int (*nfsrv3_procs[NFS_NPROCS])(); extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; extern int nfs_numasync; extern time_t nqnfsstarttime; extern int nqsrv_writeslack; extern int nfsrtton; +extern struct nfsstats nfsstats; +extern int nfsrvw_procrastinate; struct nfssvc_sock *nfs_udpsock, *nfs_cltpsock; int nuidhash_max = NFS_MAXUIDHASH; static int nfs_numnfsd = 0; @@ -225,19 +228,25 @@ nfssvc(p, uap, retval) if (error) return (error); if ((uap->flag & NFSSVC_AUTHIN) && ((nfsd = nsd->nsd_nfsd)) && - (nfsd->nd_slp->ns_flag & SLP_VALID)) { - slp = nfsd->nd_slp; + (nfsd->nfsd_slp->ns_flag & SLP_VALID)) { + slp = nfsd->nfsd_slp; /* * First check to see if another nfsd has already * added this credential. */ - for (nuidp = NUIDHASH(slp, nsd->nsd_uid)->lh_first; + for (nuidp = NUIDHASH(slp,nsd->nsd_cr.cr_uid)->lh_first; nuidp != 0; nuidp = nuidp->nu_hash.le_next) { - if (nuidp->nu_uid == nsd->nsd_uid) + if (nuidp->nu_cr.cr_uid == nsd->nsd_cr.cr_uid && + (!nfsd->nfsd_nd->nd_nam2 || + netaddr_match(NU_NETFAM(nuidp), + &nuidp->nu_haddr, nfsd->nfsd_nd->nd_nam2))) break; } - if (!nuidp) { + if (nuidp) { + nfsrv_setcred(&nuidp->nu_cr,&nfsd->nfsd_nd->nd_cr); + nfsd->nfsd_nd->nd_flag |= ND_KERBFULL; + } else { /* * Nope, so we will. */ @@ -257,22 +266,53 @@ nfssvc(p, uap, retval) LIST_REMOVE(nuidp, nu_hash); TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru); + if (nuidp->nu_flag & NU_NAM) + m_freem(nuidp->nu_nam); } + nuidp->nu_flag = 0; nuidp->nu_cr = nsd->nsd_cr; if (nuidp->nu_cr.cr_ngroups > NGROUPS) - nuidp->nu_cr.cr_ngroups = NGROUPS; + nuidp->nu_cr.cr_ngroups = NGROUPS; nuidp->nu_cr.cr_ref = 1; - nuidp->nu_uid = nsd->nsd_uid; + nuidp->nu_timestamp = nsd->nsd_timestamp; + nuidp->nu_expire = time.tv_sec + nsd->nsd_ttl; + /* + * and save the session key in nu_key. + */ + bcopy(nsd->nsd_key, nuidp->nu_key, + sizeof (nsd->nsd_key)); + if (nfsd->nfsd_nd->nd_nam2) { + struct sockaddr_in *saddr; + + saddr = mtod(nfsd->nfsd_nd->nd_nam2, + struct sockaddr_in *); + switch (saddr->sin_family) { + case AF_INET: + nuidp->nu_flag |= NU_INETADDR; + nuidp->nu_inetaddr = + saddr->sin_addr.s_addr; + break; + case AF_ISO: + default: + nuidp->nu_flag |= NU_NAM; + nuidp->nu_nam = m_copym( + nfsd->nfsd_nd->nd_nam2, 0, + M_COPYALL, M_WAIT); + break; + }; + } TAILQ_INSERT_TAIL(&slp->ns_uidlruhead, nuidp, nu_lru); LIST_INSERT_HEAD(NUIDHASH(slp, nsd->nsd_uid), nuidp, nu_hash); - + nfsrv_setcred(&nuidp->nu_cr, + &nfsd->nfsd_nd->nd_cr); + nfsd->nfsd_nd->nd_flag |= ND_KERBFULL; } } } if ((uap->flag & NFSSVC_AUTHINFAIL) && (nfsd = nsd->nsd_nfsd)) - nfsd->nd_flag |= NFSD_AUTHFAIL; + nfsd->nfsd_flag |= NFSD_AUTHFAIL; error = nfssvc_nfsd(nsd, uap->argp, p); } if (error == EINTR || error == ERESTART) @@ -353,8 +393,6 @@ nfssvc_addsock(fp, mynam) slp = (struct nfssvc_sock *) malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK); bzero((caddr_t)slp, sizeof (struct nfssvc_sock)); - slp->ns_uidhashtbl = - hashinit(NUIDHASHSIZ, M_NFSSVC, &slp->ns_uidhash); TAILQ_INIT(&slp->ns_uidlruhead); TAILQ_INSERT_TAIL(&nfssvc_sockhead, slp, ns_chain); } @@ -381,44 +419,47 @@ nfssvc_nfsd(nsd, argp, p) caddr_t argp; struct proc *p; { - register struct mbuf *m, *nam2; + register struct mbuf *m; 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 nfsd *nfsd = nsd->nsd_nfsd; + struct nfsrv_descript *nd = NULL; + struct mbuf *mreq; struct nfsuid *uidp; - int error = 0, cacherep, s; - int sotype; + int error = 0, cacherep, s, sotype, writes_todo; + u_quad_t cur_usec; +#ifndef nolint + cacherep = RC_DOIT; + writes_todo = 0; +#endif s = splnet(); - if (nd == (struct nfsd *)0) { - nsd->nsd_nfsd = nd = (struct nfsd *) + if (nfsd == (struct nfsd *)0) { + nsd->nsd_nfsd = nfsd = (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; - TAILQ_INSERT_TAIL(&nfsd_head, nd, nd_chain); - nd->nd_nqlflag = NQL_NOVAL; + bzero((caddr_t)nfsd, sizeof (struct nfsd)); + nfsd->nfsd_procp = p; + TAILQ_INSERT_TAIL(&nfsd_head, nfsd, nfsd_chain); nfs_numnfsd++; } /* * Loop getting rpc requests until SIGKILL. */ for (;;) { - if ((nd->nd_flag & NFSD_REQINPROG) == 0) { - while (nd->nd_slp == (struct nfssvc_sock *)0 && + if ((nfsd->nfsd_flag & NFSD_REQINPROG) == 0) { + while (nfsd->nfsd_slp == (struct nfssvc_sock *)0 && (nfsd_head_flag & NFSD_CHECKSLP) == 0) { - nd->nd_flag |= NFSD_WAITING; + nfsd->nfsd_flag |= NFSD_WAITING; nfsd_waiting++; - error = tsleep((caddr_t)nd, PSOCK | PCATCH, "nfsd", 0); + error = tsleep((caddr_t)nfsd, PSOCK | PCATCH, + "nfsd", 0); nfsd_waiting--; if (error) goto done; } - if (nd->nd_slp == (struct nfssvc_sock *)0 && + if (nfsd->nfsd_slp == (struct nfssvc_sock *)0 && (nfsd_head_flag & NFSD_CHECKSLP) != 0) { for (slp = nfssvc_sockhead.tqh_first; slp != 0; slp = slp->ns_chain.tqe_next) { @@ -426,14 +467,14 @@ nfssvc_nfsd(nsd, argp, p) == (SLP_VALID | SLP_DOREC)) { slp->ns_flag &= ~SLP_DOREC; slp->ns_sref++; - nd->nd_slp = slp; + nfsd->nfsd_slp = slp; break; } } if (slp == 0) nfsd_head_flag &= ~NFSD_CHECKSLP; } - if ((slp = nd->nd_slp) == (struct nfssvc_sock *)0) + if ((slp = nfsd->nfsd_slp) == (struct nfssvc_sock *)0) continue; if (slp->ns_flag & SLP_VALID) { if (slp->ns_flag & SLP_DISCONN) @@ -446,86 +487,78 @@ nfssvc_nfsd(nsd, argp, p) M_WAIT); nfs_sndunlock(&slp->ns_solock); } - error = nfsrv_dorec(slp, nd); - nd->nd_flag |= NFSD_REQINPROG; + error = nfsrv_dorec(slp, nfsd, &nd); + cur_usec = (u_quad_t)time.tv_sec * 1000000 + + (u_quad_t)time.tv_usec; + if (error && slp->ns_tq.lh_first && + slp->ns_tq.lh_first->nd_time <= cur_usec) { + error = 0; + cacherep = RC_DOIT; + writes_todo = 1; + } else + writes_todo = 0; + nfsd->nfsd_flag |= NFSD_REQINPROG; } } else { error = 0; - slp = nd->nd_slp; + slp = nfsd->nfsd_slp; } if (error || (slp->ns_flag & SLP_VALID) == 0) { - nd->nd_slp = (struct nfssvc_sock *)0; - nd->nd_flag &= ~NFSD_REQINPROG; + if (nd) { + free((caddr_t)nd, M_NFSRVDESC); + nd = NULL; + } + nfsd->nfsd_slp = (struct nfssvc_sock *)0; + nfsd->nfsd_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. - */ - nam2 = nd->nd_nam; - if (nam2) { - 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; + if (nd) { + nd->nd_starttime = time; + if (nd->nd_nam2) + nd->nd_nam = nd->nd_nam2; + else + nd->nd_nam = slp->ns_nam; - nd->nd_flag &= ~NFSD_NEEDAUTH; - /* - * Check for a mapping already installed. - */ - for (uidp = NUIDHASH(slp, nd->nd_cr.cr_uid)->lh_first; - uidp != 0; uidp = uidp->nu_hash.le_next) { - if (uidp->nu_uid == nd->nd_cr.cr_uid) - break; - } - 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 to see if authorization is needed. + */ + if (nfsd->nfsd_flag & NFSD_NEEDAUTH) { + nfsd->nfsd_flag &= ~NFSD_NEEDAUTH; + nsd->nsd_haddr = mtod(nd->nd_nam, + struct sockaddr_in *)->sin_addr.s_addr; + nsd->nsd_authlen = nfsd->nfsd_authlen; + nsd->nsd_verflen = nfsd->nfsd_verflen; + if (!copyout(nfsd->nfsd_authstr,nsd->nsd_authstr, + nfsd->nfsd_authlen) && + !copyout(nfsd->nfsd_verfstr, nsd->nsd_verfstr, + nfsd->nfsd_verflen) && + !copyout((caddr_t)nsd, argp, sizeof (*nsd))) + return (ENEEDAUTH); + cacherep = RC_DROPIT; + } else + cacherep = nfsrv_getcache(nd, slp, &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) { + /* + * 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) + } + if (notstarted) { + if ((nd->nd_flag & ND_NQNFS) == 0) cacherep = RC_DROPIT; else if (nd->nd_procnum != NFSPROC_WRITE) { nd->nd_procnum = NFSPROC_NOOP; @@ -533,36 +566,42 @@ nfssvc_nfsd(nsd, argp, p) cacherep = RC_DOIT; } else modify_flag = 1; - } else if (nd->nd_flag & NFSD_AUTHFAIL) { - nd->nd_flag &= ~NFSD_AUTHFAIL; + } else if (nfsd->nfsd_flag & NFSD_AUTHFAIL) { + nfsd->nfsd_flag &= ~NFSD_AUTHFAIL; nd->nd_procnum = NFSPROC_NOOP; - nd->nd_repstat = NQNFS_AUTHERR; + nd->nd_repstat = (NFSERR_AUTHERR | AUTH_TOOWEAK); 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"); - } + /* + * Loop to get all the write rpc relies that have been + * gathered together. + */ + do { + switch (cacherep) { + case RC_DOIT: + if (writes_todo || (nd->nd_procnum == NFSPROC_WRITE && + nfsrvw_procrastinate > 0 && !notstarted)) + error = nfsrv_writegather(&nd, slp, + nfsd->nfsd_procp, &mreq); + else + error = (*(nfsrv3_procs[nd->nd_procnum]))(nd, + slp, nfsd->nfsd_procp, &mreq); + if (mreq == NULL) + break; if (error) { if (nd->nd_procnum != NQNFSPROC_VACATED) nfsstats.srv_errs++; - if (nam2) { - nfsrv_updatecache(nam2, nd, FALSE, mreq); - m_freem(nam2); - } + nfsrv_updatecache(nd, FALSE, mreq); + if (nd->nd_nam2) + m_freem(nd->nd_nam2); break; } nfsstats.srvrpccnt[nd->nd_procnum]++; - if (nam2) - nfsrv_updatecache(nam2, nd, TRUE, mreq); + nfsrv_updatecache(nd, TRUE, mreq); nd->nd_mrep = (struct mbuf *)0; - case RC_REPLY: + case RC_REPLY: m = mreq; siz = 0; while (m) { @@ -587,15 +626,15 @@ nfssvc_nfsd(nsd, argp, p) if (solockp) (void) nfs_sndlock(solockp, (struct nfsreq *)0); if (slp->ns_flag & SLP_VALID) - error = nfs_send(so, nam2, m, (struct nfsreq *)0); + error = nfs_send(so, nd->nd_nam2, m, NULL); else { error = EPIPE; m_freem(m); } if (nfsrtton) - nfsd_rt(&starttime, sotype, nd, nam, cacherep); - if (nam2) - MFREE(nam2, m); + nfsd_rt(sotype, nd, cacherep); + if (nd->nd_nam2) + MFREE(nd->nd_nam2, m); if (nd->nd_mrep) m_freem(nd->nd_mrep); if (error == EPIPE) @@ -603,29 +642,50 @@ nfssvc_nfsd(nsd, argp, p) if (solockp) nfs_sndunlock(solockp); if (error == EINTR || error == ERESTART) { + free((caddr_t)nd, M_NFSRVDESC); nfsrv_slpderef(slp); s = splnet(); goto done; } break; - case RC_DROPIT: + case RC_DROPIT: if (nfsrtton) - nfsd_rt(&starttime, sotype, nd, nam, cacherep); + nfsd_rt(sotype, nd, cacherep); m_freem(nd->nd_mrep); - m_freem(nam2); + m_freem(nd->nd_nam2); break; - }; + }; + if (nd) { + FREE((caddr_t)nd, M_NFSRVDESC); + nd = NULL; + } + + /* + * Check to see if there are outstanding writes that + * need to be serviced. + */ + cur_usec = (u_quad_t)time.tv_sec * 1000000 + + (u_quad_t)time.tv_usec; + s = splsoftclock(); + if (slp->ns_tq.lh_first && + slp->ns_tq.lh_first->nd_time <= cur_usec) { + cacherep = RC_DOIT; + writes_todo = 1; + } else + writes_todo = 0; + splx(s); + } while (writes_todo); s = splnet(); - if (nfsrv_dorec(slp, nd)) { - nd->nd_flag &= ~NFSD_REQINPROG; - nd->nd_slp = (struct nfssvc_sock *)0; + if (nfsrv_dorec(slp, nfsd, &nd)) { + nfsd->nfsd_flag &= ~NFSD_REQINPROG; + nfsd->nfsd_slp = NULL; nfsrv_slpderef(slp); } } done: - TAILQ_REMOVE(&nfsd_head, nd, nd_chain); + TAILQ_REMOVE(&nfsd_head, nfsd, nfsd_chain); splx(s); - free((caddr_t)nd, M_NFSD); + free((caddr_t)nfsd, M_NFSD); nsd->nsd_nfsd = (struct nfsd *)0; if (--nfs_numnfsd == 0) nfsrv_init(TRUE); /* Reinitialize everything */ @@ -641,9 +701,10 @@ int nfssvc_iod(p) struct proc *p; { - register struct buf *bp; + register struct buf *bp, *nbp; register int i, myiod; - int error = 0; + struct vnode *vp; + int error = 0, s; /* * Assign my position or return error if too many already running @@ -662,24 +723,53 @@ nfssvc_iod(p) * 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); - } + 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 do { + /* + * Look for a delayed write for the same vnode, so I can do + * it now. We must grab it before calling nfs_doio() to + * avoid any risk of the vnode getting vclean()'d while + * we are doing the write rpc. + */ + vp = bp->b_vp; + s = splbio(); + for (nbp = vp->v_dirtyblkhd.lh_first; nbp; + nbp = nbp->b_vnbufs.le_next) { + if ((nbp->b_flags & + (B_BUSY|B_DELWRI|B_NEEDCOMMIT|B_NOCACHE))!=B_DELWRI) + continue; + bremfree(nbp); + vfs_busy_pages(nbp, 1); + nbp->b_flags |= (B_BUSY|B_ASYNC); + break; + } + splx(s); + /* + * For the delayed write, do the first part of nfs_bwrite() + * up to, but not including nfs_strategy(). + */ + if (nbp) { + nbp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); + reassignbuf(nbp, nbp->b_vp); + nbp->b_vp->v_numoutput++; + } + (void) nfs_doio(bp, bp->b_wcred, (struct proc *)0); + } while (bp = nbp); + } + if (error) { + nfs_asyncdaemon[myiod] = 0; + nfs_numasync--; + return (error); + } } } @@ -695,9 +785,11 @@ nfsrv_zapsock(slp) register struct nfssvc_sock *slp; { register struct nfsuid *nuidp, *nnuidp; + register struct nfsrv_descript *nwp, *nnwp; struct socket *so; struct file *fp; struct mbuf *m; + int s; slp->ns_flag &= ~SLP_ALLFLAGS; fp = slp->ns_fp; @@ -716,8 +808,18 @@ nfsrv_zapsock(slp) nnuidp = nuidp->nu_lru.tqe_next; LIST_REMOVE(nuidp, nu_hash); TAILQ_REMOVE(&slp->ns_uidlruhead, nuidp, nu_lru); + if (nuidp->nu_flag & NU_NAM) + m_freem(nuidp->nu_nam); free((caddr_t)nuidp, M_NFSUID); } + s = splsoftclock(); + for (nwp = slp->ns_tq.lh_first; nwp; nwp = nnwp) { + nnwp = nwp->nd_tq.le_next; + LIST_REMOVE(nwp, nd_tq); + free((caddr_t)nwp, M_NFSRVDESC); + } + LIST_INIT(&slp->ns_tq); + splx(s); } } @@ -726,13 +828,15 @@ nfsrv_zapsock(slp) * on this mount point porpous out of the kernel and do it. */ int -nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len) +nfs_getauth(nmp, rep, cred, auth_str, auth_len, verf_str, verf_len, key) register struct nfsmount *nmp; struct nfsreq *rep; struct ucred *cred; - int *auth_type; char **auth_str; int *auth_len; + char *verf_str; + int *verf_len; + NFSKERBKEY_T key; /* return session key */ { int error = 0; @@ -748,6 +852,9 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len) } nmp->nm_flag &= ~(NFSMNT_WAITAUTH | NFSMNT_WANTAUTH); nmp->nm_authstr = *auth_str = (char *)malloc(RPCAUTH_MAXSIZ, M_TEMP, M_WAITOK); + nmp->nm_authlen = RPCAUTH_MAXSIZ; + nmp->nm_verfstr = verf_str; + nmp->nm_verflen = *verf_len; nmp->nm_authuid = cred->cr_uid; wakeup((caddr_t)&nmp->nm_authstr); @@ -766,8 +873,9 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len) if (error) free((caddr_t)*auth_str, M_TEMP); else { - *auth_type = nmp->nm_authtype; *auth_len = nmp->nm_authlen; + *verf_len = nmp->nm_verflen; + bcopy((caddr_t)nmp->nm_key, (caddr_t)key, sizeof (key)); } nmp->nm_flag &= ~NFSMNT_HASAUTH; nmp->nm_flag |= NFSMNT_WAITAUTH; @@ -779,6 +887,149 @@ nfs_getauth(nmp, rep, cred, auth_type, auth_str, auth_len) } /* + * Get a nickname authenticator and verifier. + */ +int +nfs_getnickauth(nmp, cred, auth_str, auth_len, verf_str, verf_len) + struct nfsmount *nmp; + struct ucred *cred; + char **auth_str; + int *auth_len; + char *verf_str; + int verf_len; +{ + register struct nfsuid *nuidp; + register u_long *nickp, *verfp; + struct timeval ktvin, ktvout; + NFSKERBKEYSCHED_T keys; /* stores key schedule */ + +#ifdef DIAGNOSTIC + if (verf_len < (4 * NFSX_UNSIGNED)) + panic("nfs_getnickauth verf too small"); +#endif + for (nuidp = NMUIDHASH(nmp, cred->cr_uid)->lh_first; + nuidp != 0; nuidp = nuidp->nu_hash.le_next) { + if (nuidp->nu_cr.cr_uid == cred->cr_uid) + break; + } + if (!nuidp || nuidp->nu_expire < time.tv_sec) + return (EACCES); + + /* + * Move to the end of the lru list (end of lru == most recently used). + */ + TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, nu_lru); + TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, nu_lru); + + nickp = (u_long *)malloc(2 * NFSX_UNSIGNED, M_TEMP, M_WAITOK); + *nickp++ = txdr_unsigned(RPCAKN_NICKNAME); + *nickp = txdr_unsigned(nuidp->nu_nickname); + *auth_str = (char *)nickp; + *auth_len = 2 * NFSX_UNSIGNED; + + /* + * Now we must encrypt the verifier and package it up. + */ + verfp = (u_long *)verf_str; + *verfp++ = txdr_unsigned(RPCAKN_NICKNAME); + if (time.tv_sec > nuidp->nu_timestamp.tv_sec || + (time.tv_sec == nuidp->nu_timestamp.tv_sec && + time.tv_usec > nuidp->nu_timestamp.tv_usec)) + nuidp->nu_timestamp = time; + else + nuidp->nu_timestamp.tv_usec++; + ktvin.tv_sec = txdr_unsigned(nuidp->nu_timestamp.tv_sec); + ktvin.tv_usec = txdr_unsigned(nuidp->nu_timestamp.tv_usec); + + /* + * Now encrypt the timestamp verifier in ecb mode using the session + * key. + */ +#ifdef NFSKERB + XXX +#endif + + *verfp++ = ktvout.tv_sec; + *verfp++ = ktvout.tv_usec; + *verfp = 0; + return (0); +} + +/* + * Save the current nickname in a hash list entry on the mount point. + */ +int +nfs_savenickauth(nmp, cred, len, key, mdp, dposp, mrep) + register struct nfsmount *nmp; + struct ucred *cred; + int len; + NFSKERBKEY_T key; + struct mbuf **mdp; + char **dposp; + struct mbuf *mrep; +{ + register struct nfsuid *nuidp; + register u_long *tl; + register long t1; + struct mbuf *md = *mdp; + struct timeval ktvin, ktvout; + u_long nick; + NFSKERBKEYSCHED_T keys; + char *dpos = *dposp, *cp2; + int deltasec, error = 0; + + if (len == (3 * NFSX_UNSIGNED)) { + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + ktvin.tv_sec = *tl++; + ktvin.tv_usec = *tl++; + nick = fxdr_unsigned(u_long, *tl); + + /* + * Decrypt the timestamp in ecb mode. + */ +#ifdef NFSKERB + XXX +#endif + ktvout.tv_sec = fxdr_unsigned(long, ktvout.tv_sec); + ktvout.tv_usec = fxdr_unsigned(long, ktvout.tv_usec); + deltasec = time.tv_sec - ktvout.tv_sec; + if (deltasec < 0) + deltasec = -deltasec; + /* + * If ok, add it to the hash list for the mount point. + */ + if (deltasec <= NFS_KERBCLOCKSKEW) { + if (nmp->nm_numuids < nuidhash_max) { + nmp->nm_numuids++; + nuidp = (struct nfsuid *) + malloc(sizeof (struct nfsuid), M_NFSUID, + M_WAITOK); + } else { + nuidp = nmp->nm_uidlruhead.tqh_first; + LIST_REMOVE(nuidp, nu_hash); + TAILQ_REMOVE(&nmp->nm_uidlruhead, nuidp, + nu_lru); + } + nuidp->nu_flag = 0; + nuidp->nu_cr.cr_uid = cred->cr_uid; + nuidp->nu_expire = time.tv_sec + NFS_KERBTTL; + nuidp->nu_timestamp = ktvout; + nuidp->nu_nickname = nick; + bcopy(key, nuidp->nu_key, sizeof (key)); + TAILQ_INSERT_TAIL(&nmp->nm_uidlruhead, nuidp, + nu_lru); + LIST_INSERT_HEAD(NMUIDHASH(nmp, cred->cr_uid), + nuidp, nu_hash); + } + } else + nfsm_adv(nfsm_rndup(len)); +nfsmout: + *mdp = md; + *dposp = dpos; + return (error); +} + +/* * Derefence a server socket structure. If it has no more references and * is no longer valid, you can throw it away. */ @@ -830,16 +1081,12 @@ nfsrv_init(terminating) nfs_udpsock = (struct nfssvc_sock *) malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK); bzero((caddr_t)nfs_udpsock, sizeof (struct nfssvc_sock)); - nfs_udpsock->ns_uidhashtbl = - hashinit(NUIDHASHSIZ, M_NFSSVC, &nfs_udpsock->ns_uidhash); TAILQ_INIT(&nfs_udpsock->ns_uidlruhead); TAILQ_INSERT_HEAD(&nfssvc_sockhead, nfs_udpsock, ns_chain); nfs_cltpsock = (struct nfssvc_sock *) malloc(sizeof (struct nfssvc_sock), M_NFSSVC, M_WAITOK); bzero((caddr_t)nfs_cltpsock, sizeof (struct nfssvc_sock)); - nfs_cltpsock->ns_uidhashtbl = - hashinit(NUIDHASHSIZ, M_NFSSVC, &nfs_cltpsock->ns_uidhash); TAILQ_INIT(&nfs_cltpsock->ns_uidlruhead); TAILQ_INSERT_TAIL(&nfssvc_sockhead, nfs_cltpsock, ns_chain); } @@ -848,11 +1095,9 @@ nfsrv_init(terminating) * Add entries to the server monitor log. */ static void -nfsd_rt(startp, sotype, nd, nam, cacherep) - struct timeval *startp; +nfsd_rt(sotype, nd, cacherep) int sotype; - register struct nfsd *nd; - struct mbuf *nam; + register struct nfsrv_descript *nd; int cacherep; { register struct drt *rt; @@ -866,15 +1111,17 @@ nfsd_rt(startp, sotype, nd, nam, cacherep) rt->flag = DRT_CACHEDROP; if (sotype == SOCK_STREAM) rt->flag |= DRT_TCP; - if (nd->nd_nqlflag != NQL_NOVAL) + if (nd->nd_flag & ND_NQNFS) rt->flag |= DRT_NQNFS; + else if (nd->nd_flag & ND_NFSV3) + rt->flag |= DRT_NFSV3; 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; + if (mtod(nd->nd_nam, struct sockaddr *)->sa_family == AF_INET) + rt->ipadr = mtod(nd->nd_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->ipadr = INADDR_ANY; + rt->resptime = ((time.tv_sec - nd->nd_starttime.tv_sec) * 1000000) + + (time.tv_usec - nd->nd_starttime.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 index 030a985..9e89c57 100644 --- a/sys/nfsserver/nfsm_subs.h +++ b/sys/nfsserver/nfsm_subs.h @@ -34,12 +34,13 @@ * SUCH DAMAGE. * * @(#)nfsm_subs.h 8.1 (Berkeley) 6/16/93 - * $Id: nfsm_subs.h,v 1.5 1994/10/17 17:47:42 phk Exp $ + * $Id: nfsm_subs.h,v 1.6 1995/05/30 08:12:51 rgrimes Exp $ */ #ifndef _NFS_NFSM_SUBS_H_ #define _NFS_NFSM_SUBS_H_ + /* * These macros do strange and peculiar things to mbuf chains for * the assistance of the nfs code. To attempt to use them for any @@ -90,49 +91,153 @@ extern struct mbuf *nfsm_reqh(); mb->m_len += (s); \ bpos += (s); } -#define nfsm_dissect(a,c,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 (t1 = nfsm_disct(&md, &dpos, (s), t1, &cp2)) { \ + error = t1; \ + m_freem(mrep); \ + goto nfsmout; \ } else { \ - error = nfsm_disct(&md, &dpos, (s), t1, &cp2); \ - if (error) { \ - m_freem(mrep); \ + (a) = (c)cp2; \ + } } + +#define nfsm_fhtom(v, v3) \ + { if (v3) { \ + t2 = nfsm_rndup(VTONFS(v)->n_fhsize) + NFSX_UNSIGNED; \ + if (t2 <= M_TRAILINGSPACE(mb)) { \ + nfsm_build(tl, u_long *, t2); \ + *tl++ = txdr_unsigned(VTONFS(v)->n_fhsize); \ + *(tl + ((t2>>2) - 2)) = 0; \ + bcopy((caddr_t)VTONFS(v)->n_fhp,(caddr_t)tl, \ + VTONFS(v)->n_fhsize); \ + } else if (t2 = nfsm_strtmbuf(&mb, &bpos, \ + (caddr_t)VTONFS(v)->n_fhp, VTONFS(v)->n_fhsize)) { \ + error = t2; \ + m_freem(mreq); \ goto nfsmout; \ - } else { \ - (a) = (c)cp2; \ } \ + } else { \ + nfsm_build(cp, caddr_t, NFSX_V2FH); \ + bcopy((caddr_t)VTONFS(v)->n_fhp, cp, NFSX_V2FH); \ } } -#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, v3) \ + { if (v3) { \ + nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3FH); \ + *tl++ = txdr_unsigned(NFSX_V3FH); \ + bcopy((caddr_t)(f), (caddr_t)tl, NFSX_V3FH); \ + } else { \ + nfsm_build(cp, caddr_t, NFSX_V2FH); \ + bcopy((caddr_t)(f), cp, NFSX_V2FH); \ + } } -#define nfsm_srvfhtom(f) \ - nfsm_build(cp,caddr_t,NFSX_FH); \ - bcopy((caddr_t)(f), cp, NFSX_FH) +#define nfsm_srvpostop_fh(f) \ + { nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED + NFSX_V3FH); \ + *tl++ = nfs_true; \ + *tl++ = txdr_unsigned(NFSX_V3FH); \ + bcopy((caddr_t)(f), (caddr_t)tl, NFSX_V3FH); \ + } -#define nfsm_mtofh(d,v) \ - { struct nfsnode *np; nfsv2fh_t *fhp; \ - nfsm_dissect(fhp,nfsv2fh_t *,NFSX_FH); \ - error = nfs_nget((d)->v_mount, fhp, &np); \ - if (error) { \ - m_freem(mrep); \ - goto nfsmout; \ +#define nfsm_mtofh(d, v, v3, f) \ + { struct nfsnode *ttnp; nfsfh_t *ttfhp; int ttfhsize; \ + if (v3) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + (f) = fxdr_unsigned(int, *tl); \ + } else \ + (f) = 1; \ + if (f) { \ + nfsm_getfh(ttfhp, ttfhsize, (v3)); \ + if (t1 = nfs_nget((d)->v_mount, ttfhp, ttfhsize, \ + &ttnp)) { \ + error = t1; \ + m_freem(mrep); \ + goto nfsmout; \ + } \ + (v) = NFSTOV(ttnp); \ + } \ + if (v3) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (f) \ + (f) = fxdr_unsigned(int, *tl); \ + else if (fxdr_unsigned(int, *tl)) \ + nfsm_adv(NFSX_V3FATTR); \ } \ - (v) = NFSTOV(np); \ - nfsm_loadattr(v, (struct vattr *)0); \ + if (f) \ + nfsm_loadattr((v), (struct vattr *)0); \ } -#define nfsm_loadattr(v,a) \ - { struct vnode *tvp = (v); \ - error = nfs_loadattrcache(&tvp, &md, &dpos, (a)); \ - if (error) { \ +#define nfsm_getfh(f, s, v3) \ + { if (v3) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (((s) = fxdr_unsigned(int, *tl)) <= 0 || \ + (s) > NFSX_V3FHMAX) { \ + m_freem(mrep); \ + error = EBADRPC; \ + goto nfsmout; \ + } \ + } else \ + (s) = NFSX_V2FH; \ + nfsm_dissect((f), nfsfh_t *, nfsm_rndup(s)); } + +#define nfsm_loadattr(v, a) \ + { struct vnode *ttvp = (v); \ + if (t1 = nfs_loadattrcache(&ttvp, &md, &dpos, (a))) { \ + error = t1; \ m_freem(mrep); \ goto nfsmout; \ } \ - (v) = tvp; } + (v) = ttvp; } + +#define nfsm_postop_attr(v, f) \ + { struct vnode *ttvp = (v); \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if ((f) = fxdr_unsigned(int, *tl)) { \ + if (t1 = nfs_loadattrcache(&ttvp, &md, &dpos, \ + (struct vattr *)0)) { \ + error = t1; \ + (f) = 0; \ + m_freem(mrep); \ + goto nfsmout; \ + } \ + (v) = ttvp; \ + } } + +/* Used as (f) for nfsm_wcc_data() */ +#define NFSV3_WCCRATTR 0 +#define NFSV3_WCCCHK 1 + +#define nfsm_wcc_data(v, f) \ + { int ttattrf, ttretf = 0; \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (*tl == nfs_true) { \ + nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED); \ + if (f) \ + ttretf = (VTONFS(v)->n_mtime == \ + fxdr_unsigned(u_long, *(tl + 2))); \ + } \ + nfsm_postop_attr((v), ttattrf); \ + if (f) { \ + (f) = ttretf; \ + } else { \ + (f) = ttattrf; \ + } } + +#define nfsm_v3sattr(s, a, u, g) \ + { (s)->sa_modetrue = nfs_true; \ + (s)->sa_mode = vtonfsv3_mode((a)->va_mode); \ + (s)->sa_uidtrue = nfs_true; \ + (s)->sa_uid = txdr_unsigned(u); \ + (s)->sa_gidtrue = nfs_true; \ + (s)->sa_gid = txdr_unsigned(g); \ + (s)->sa_sizefalse = nfs_false; \ + (s)->sa_atimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); \ + txdr_nfsv3time(&(a)->va_atime, &(s)->sa_atime); \ + (s)->sa_mtimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); \ + txdr_nfsv3time(&(a)->va_mtime, &(s)->sa_mtime); \ + } #define nfsm_strsiz(s,m) \ { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \ @@ -149,16 +254,27 @@ extern struct mbuf *nfsm_reqh(); nfsm_reply(0); \ } } +#define nfsm_srvnamesiz(s) \ + { nfsm_dissect(tl,u_long *,NFSX_UNSIGNED); \ + if (((s) = fxdr_unsigned(long,*tl)) > NFS_MAXNAMLEN) \ + error = NFSERR_NAMETOL; \ + if ((s) <= 0) \ + error = EBADRPC; \ + if (error) \ + nfsm_reply(0); \ + } + #define nfsm_mtouio(p,s) \ if ((s) > 0 && \ - (error = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \ + (t1 = nfsm_mbuftouio(&md,(p),(s),&dpos))) { \ + error = t1; \ m_freem(mrep); \ goto nfsmout; \ } #define nfsm_uiotom(p,s) \ - error = nfsm_uiotombuf((p),&mb,(s),&bpos); \ - if (error) { \ + if (t1 = nfsm_uiotombuf((p),&mb,(s),&bpos)) { \ + error = t1; \ m_freem(mreq); \ goto nfsmout; \ } @@ -172,10 +288,13 @@ extern struct mbuf *nfsm_reqh(); #define nfsm_rndup(a) (((a)+3)&(~0x3)) #define nfsm_request(v, t, p, c) \ - error = nfs_request((v), mreq, (t), (p), \ - (c), &mrep, &md, &dpos); \ - if (error) \ - goto nfsmout + if (error = nfs_request((v), mreq, (t), (p), \ + (c), &mrep, &md, &dpos)) { \ + if (error & NFSERR_RETERR) \ + error &= ~NFSERR_RETERR; \ + else \ + goto nfsmout; \ + } #define nfsm_strtom(a,s,m) \ if ((s) > (m)) { \ @@ -189,12 +308,10 @@ extern struct mbuf *nfsm_reqh(); *tl++ = txdr_unsigned(s); \ *(tl+((t2>>2)-2)) = 0; \ bcopy((caddr_t)(a), (caddr_t)tl, (s)); \ - } else { \ - error = nfsm_strtmbuf(&mb, &bpos, (a), (s)); \ - if (error) { \ - m_freem(mreq); \ - goto nfsmout; \ - } \ + } else if (t2 = nfsm_strtmbuf(&mb, &bpos, (a), (s))) { \ + error = t2; \ + m_freem(mreq); \ + goto nfsmout; \ } #define nfsm_srvdone \ @@ -204,33 +321,53 @@ extern struct mbuf *nfsm_reqh(); #define nfsm_reply(s) \ { \ nfsd->nd_repstat = error; \ - if (error) \ - (void) nfs_rephead(0, nfsd, error, cache, &frev, \ + if (error && !(nfsd->nd_flag & ND_NFSV3)) \ + (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \ mrq, &mb, &bpos); \ else \ - (void) nfs_rephead((s), nfsd, error, cache, &frev, \ + (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \ mrq, &mb, &bpos); \ m_freem(mrep); \ mreq = *mrq; \ - if (error) \ + if (error && (!(nfsd->nd_flag & ND_NFSV3) || \ + error == EBADRPC)) \ return(0); \ } +#define nfsm_writereply(s, v3) \ + { \ + nfsd->nd_repstat = error; \ + if (error && !(v3)) \ + (void) nfs_rephead(0, nfsd, slp, error, cache, &frev, \ + &mreq, &mb, &bpos); \ + else \ + (void) nfs_rephead((s), nfsd, slp, error, cache, &frev, \ + &mreq, &mb, &bpos); \ + } + #define nfsm_adv(s) \ - t1 = mtod(md, caddr_t)+md->m_len-dpos; \ + { t1 = mtod(md, caddr_t)+md->m_len-dpos; \ if (t1 >= (s)) { \ dpos += (s); \ - } else { \ - error = nfs_adv(&md, &dpos, (s), t1); \ - if (error) { \ - m_freem(mrep); \ - goto nfsmout; \ - } \ - } + } else if (t1 = nfs_adv(&md, &dpos, (s), t1)) { \ + error = 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) + { if (nfsd->nd_flag & ND_NFSV3) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (fxdr_unsigned(int, *tl) != NFSX_V3FH) { \ + error = EBADRPC; \ + nfsm_reply(0); \ + } \ + } \ + nfsm_dissect(tl, u_long *, NFSX_V3FH); \ + bcopy((caddr_t)tl, (caddr_t)(f), NFSX_V3FH); \ + if ((nfsd->nd_flag & ND_NFSV3) == 0) \ + nfsm_adv(NFSX_V2FH - NFSX_V3FH); \ + } #define nfsm_clget \ if (bp >= be) { \ @@ -246,41 +383,57 @@ extern struct mbuf *nfsm_reqh(); } \ 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); \ - txdr_nfstime(&vap->va_ctime, &fp->fa_nfsctime); \ - } 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); \ - } +#define nfsm_srvfillattr(a, f) \ + nfsm_srvfattr(nfsd, (a), (f)) + +#define nfsm_srvwcc_data(br, b, ar, a) \ + nfsm_srvwcc(nfsd, (br), (b), (ar), (a), &mb, &bpos) + +#define nfsm_srvpostop_attr(r, a) \ + nfsm_srvpostopattr(nfsd, (r), (a), &mb, &bpos) + +#define nfsm_srvsattr(a) \ + { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (*tl == nfs_true) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + (a)->va_mode = nfstov_mode(*tl); \ + } \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (*tl == nfs_true) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + (a)->va_uid = fxdr_unsigned(uid_t, *tl); \ + } \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (*tl == nfs_true) { \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + (a)->va_gid = fxdr_unsigned(gid_t, *tl); \ + } \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + if (*tl == nfs_true) { \ + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \ + fxdr_hyper(tl, &(a)->va_size); \ + } \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + switch (fxdr_unsigned(int, *tl)) { \ + case NFSV3SATTRTIME_TOCLIENT: \ + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \ + fxdr_nfsv3time(tl, &(a)->va_atime); \ + break; \ + case NFSV3SATTRTIME_TOSERVER: \ + (a)->va_atime.ts_sec = time.tv_sec; \ + (a)->va_atime.ts_nsec = time.tv_usec * 1000; \ + break; \ + }; \ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); \ + switch (fxdr_unsigned(int, *tl)) { \ + case NFSV3SATTRTIME_TOCLIENT: \ + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); \ + fxdr_nfsv3time(tl, &(a)->va_mtime); \ + break; \ + case NFSV3SATTRTIME_TOSERVER: \ + (a)->va_mtime.ts_sec = time.tv_sec; \ + (a)->va_mtime.ts_nsec = time.tv_usec * 1000; \ + break; \ + }; } #endif diff --git a/sys/nfsserver/nfsproto.h b/sys/nfsserver/nfsproto.h new file mode 100644 index 0000000..ac1a090 --- /dev/null +++ b/sys/nfsserver/nfsproto.h @@ -0,0 +1,441 @@ +/* + * 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. + * + * @(#)nfsproto.h 8.1 (Berkeley) 6/10/93 + * $Id: nfsproto.h,v 1.4 1994/08/21 06:50:13 paul Exp $ + */ + +#ifndef _NFS_NFSPROTO_H_ +#define _NFS_NFSPROTO_H_ + +/* + * nfs definitions as per the Version 2 and 3 specs + */ + +/* + * Constants as defined in the Sun NFS Version 2 and 3 specs. + * "NFS: Network File System Protocol Specification" RFC1094 + * and in the "NFS: Network File System Version 3 Protocol + * Specification" + */ + +#define NFS_PORT 2049 +#define NFS_PROG 100003 +#define NFS_VER2 2 +#define NFS_VER3 3 +#define NFS_V2MAXDATA 8192 +#define NFS_MAXDGRAMDATA 16384 +#define NFS_MAXDATA 32768 +#define NFS_MAXPATHLEN 1024 +#define NFS_MAXNAMLEN 255 +#define NFS_MAXPKTHDR 404 +#define NFS_MAXPACKET (NFS_MAXPKTHDR + NFS_MAXDATA) +#define NFS_MINPACKET 20 +#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */ + +/* Stat numbers for rpc returns (version 2 and 3) */ +#define NFS_OK 0 +#define NFSERR_PERM 1 +#define NFSERR_NOENT 2 +#define NFSERR_IO 5 +#define NFSERR_NXIO 6 +#define NFSERR_ACCES 13 +#define NFSERR_EXIST 17 +#define NFSERR_XDEV 18 /* Version 3 only */ +#define NFSERR_NODEV 19 +#define NFSERR_NOTDIR 20 +#define NFSERR_ISDIR 21 +#define NFSERR_INVAL 22 /* Version 3 only */ +#define NFSERR_FBIG 27 +#define NFSERR_NOSPC 28 +#define NFSERR_ROFS 30 +#define NFSERR_MLINK 31 /* Version 3 only */ +#define NFSERR_NAMETOL 63 +#define NFSERR_NOTEMPTY 66 +#define NFSERR_DQUOT 69 +#define NFSERR_STALE 70 +#define NFSERR_REMOTE 71 /* Version 3 only */ +#define NFSERR_WFLUSH 99 /* Version 2 only */ +#define NFSERR_BADHANDLE 10001 /* The rest Version 3 only */ +#define NFSERR_NOT_SYNC 10002 +#define NFSERR_BAD_COOKIE 10003 +#define NFSERR_NOTSUPP 10004 +#define NFSERR_TOOSMALL 10005 +#define NFSERR_SERVERFAULT 10006 +#define NFSERR_BADTYPE 10007 +#define NFSERR_JUKEBOX 10008 +#define NFSERR_TRYLATER NFSERR_JUKEBOX +#define NFSERR_STALEWRITEVERF 30001 /* Fake return for nfs_commit() */ + +#define NFSERR_RETVOID 0x20000000 /* Return void, not error */ +#define NFSERR_AUTHERR 0x40000000 /* Mark an authentication error */ +#define NFSERR_RETERR 0x80000000 /* Mark an error return for V3 */ + +/* Sizes in bytes of various nfs rpc components */ +#define NFSX_UNSIGNED 4 + +/* specific to NFS Version 2 */ +#define NFSX_V2FH 32 +#define NFSX_V2FATTR 68 +#define NFSX_V2SATTR 32 +#define NFSX_V2COOKIE 4 +#define NFSX_V2STATFS 20 + +/* specific to NFS Version 3 */ +#define NFSX_V3FH (sizeof (fhandle_t)) /* size this server uses */ +#define NFSX_V3FHMAX 64 /* max. allowed by protocol */ +#define NFSX_V3FATTR 84 +#define NFSX_V3SATTR 60 /* max. all fields filled in */ +#define NFSX_V3SRVSATTR (sizeof (struct nfsv3_sattr)) +#define NFSX_V3POSTOPATTR (NFSX_V3FATTR + NFSX_UNSIGNED) +#define NFSX_V3WCCDATA (NFSX_V3POSTOPATTR + 8 * NFSX_UNSIGNED) +#define NFSX_V3COOKIEVERF 8 +#define NFSX_V3WRITEVERF 8 +#define NFSX_V3CREATEVERF 8 +#define NFSX_V3STATFS 52 +#define NFSX_V3FSINFO 48 +#define NFSX_V3PATHCONF 24 + +/* variants for both versions */ +#define NFSX_FH(v3) ((v3) ? (NFSX_V3FHMAX + NFSX_UNSIGNED) : \ + NFSX_V2FH) +#define NFSX_SRVFH(v3) ((v3) ? NFSX_V3FH : NFSX_V2FH) +#define NFSX_FATTR(v3) ((v3) ? NFSX_V3FATTR : NFSX_V2FATTR) +#define NFSX_PREOPATTR(v3) ((v3) ? (7 * NFSX_UNSIGNED) : 0) +#define NFSX_POSTOPATTR(v3) ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : 0) +#define NFSX_POSTOPORFATTR(v3) ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : \ + NFSX_V2FATTR) +#define NFSX_WCCDATA(v3) ((v3) ? NFSX_V3WCCDATA : 0) +#define NFSX_WCCORFATTR(v3) ((v3) ? NFSX_V3WCCDATA : NFSX_V2FATTR) +#define NFSX_SATTR(v3) ((v3) ? NFSX_V3SATTR : NFSX_V2SATTR) +#define NFSX_COOKIEVERF(v3) ((v3) ? NFSX_V3COOKIEVERF : 0) +#define NFSX_WRITEVERF(v3) ((v3) ? NFSX_V3WRITEVERF : 0) +#define NFSX_READDIR(v3) ((v3) ? (5 * NFSX_UNSIGNED) : \ + (2 * NFSX_UNSIGNED)) +#define NFSX_STATFS(v3) ((v3) ? NFSX_V3STATFS : NFSX_V2STATFS) + +/* nfs rpc procedure numbers (before version mapping) */ +#define NFSPROC_NULL 0 +#define NFSPROC_GETATTR 1 +#define NFSPROC_SETATTR 2 +#define NFSPROC_LOOKUP 3 +#define NFSPROC_ACCESS 4 +#define NFSPROC_READLINK 5 +#define NFSPROC_READ 6 +#define NFSPROC_WRITE 7 +#define NFSPROC_CREATE 8 +#define NFSPROC_MKDIR 9 +#define NFSPROC_SYMLINK 10 +#define NFSPROC_MKNOD 11 +#define NFSPROC_REMOVE 12 +#define NFSPROC_RMDIR 13 +#define NFSPROC_RENAME 14 +#define NFSPROC_LINK 15 +#define NFSPROC_READDIR 16 +#define NFSPROC_READDIRPLUS 17 +#define NFSPROC_FSSTAT 18 +#define NFSPROC_FSINFO 19 +#define NFSPROC_PATHCONF 20 +#define NFSPROC_COMMIT 21 + +/* And leasing (nqnfs) procedure numbers (must be last) */ +#define NQNFSPROC_GETLEASE 22 +#define NQNFSPROC_VACATED 23 +#define NQNFSPROC_EVICTED 24 + +#define NFSPROC_NOOP 25 +#define NFS_NPROCS 26 + +/* Actual Version 2 procedure numbers */ +#define NFSV2PROC_NULL 0 +#define NFSV2PROC_GETATTR 1 +#define NFSV2PROC_SETATTR 2 +#define NFSV2PROC_NOOP 3 +#define NFSV2PROC_ROOT NFSV2PROC_NOOP /* Obsolete */ +#define NFSV2PROC_LOOKUP 4 +#define NFSV2PROC_READLINK 5 +#define NFSV2PROC_READ 6 +#define NFSV2PROC_WRITECACHE NFSV2PROC_NOOP /* Obsolete */ +#define NFSV2PROC_WRITE 8 +#define NFSV2PROC_CREATE 9 +#define NFSV2PROC_REMOVE 10 +#define NFSV2PROC_RENAME 11 +#define NFSV2PROC_LINK 12 +#define NFSV2PROC_SYMLINK 13 +#define NFSV2PROC_MKDIR 14 +#define NFSV2PROC_RMDIR 15 +#define NFSV2PROC_READDIR 16 +#define NFSV2PROC_STATFS 17 + +/* + * Constants used by the Version 3 protocol for various RPCs + */ +#define NFSV3SATTRTIME_DONTCHANGE 0 +#define NFSV3SATTRTIME_TOSERVER 1 +#define NFSV3SATTRTIME_TOCLIENT 2 + +#define NFSV3ACCESS_READ 0x01 +#define NFSV3ACCESS_LOOKUP 0x02 +#define NFSV3ACCESS_MODIFY 0x04 +#define NFSV3ACCESS_EXTEND 0x08 +#define NFSV3ACCESS_DELETE 0x10 +#define NFSV3ACCESS_EXECUTE 0x20 + +#define NFSV3WRITE_UNSTABLE 0 +#define NFSV3WRITE_DATASYNC 1 +#define NFSV3WRITE_FILESYNC 2 + +#define NFSV3CREATE_UNCHECKED 0 +#define NFSV3CREATE_GUARDED 1 +#define NFSV3CREATE_EXCLUSIVE 2 + +#define NFSV3FSINFO_LINK 0x01 +#define NFSV3FSINFO_SYMLINK 0x02 +#define NFSV3FSINFO_HOMOGENEOUS 0x08 +#define NFSV3FSINFO_CANSETTIME 0x10 + +/* Conversion macros */ +#define vtonfsv2_mode(t,m) \ + txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : \ + MAKEIMODE((t), (m))) +#define vtonfsv3_mode(m) txdr_unsigned((m) & 07777) +#define nfstov_mode(a) (fxdr_unsigned(u_short, (a))&07777) +#define vtonfsv2_type(a) txdr_unsigned(nfsv2_type[((long)(a))]) +#define vtonfsv3_type(a) txdr_unsigned(nfsv3_type[((long)(a))]) +#define nfsv2tov_type(a) nv2tov_type[fxdr_unsigned(u_long,(a))&0x7] +#define nfsv3tov_type(a) nv3tov_type[fxdr_unsigned(u_long,(a))&0x7] + +/* File types */ +typedef enum { NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, + NFSOCK=6, NFFIFO=7 } nfstype; + +/* Structs for common parts of the rpc's */ +/* + * File Handle (32 bytes for version 2), variable up to 64 for version 3. + * File Handles of up to NFS_SMALLFH in size are stored directly in the + * nfs node, whereas larger ones are malloc'd. (This never happens when + * NFS_SMALLFH is set to 64.) + * NFS_SMALLFH should be in the range of 32 to 64 and be divisible by 4. + */ +#ifndef NFS_SMALLFH +#define NFS_SMALLFH 64 +#endif +union nfsfh { + fhandle_t fh_generic; + u_char fh_bytes[NFS_SMALLFH]; +}; +typedef union nfsfh nfsfh_t; + +struct nfsv2_time { + u_long nfsv2_sec; + u_long nfsv2_usec; +}; +typedef struct nfsv2_time nfstime2; + +struct nfsv3_time { + u_long nfsv3_sec; + u_long nfsv3_nsec; +}; +typedef struct nfsv3_time nfstime3; + +/* + * Quads are defined as arrays of 2 longs to ensure dense packing for the + * protocol and to facilitate xdr conversion. + */ +struct nfs_uquad { + u_long nfsuquad[2]; +}; +typedef struct nfs_uquad nfsuint64; + +/* + * Used to convert between two u_longs and a u_quad_t. + */ +union nfs_quadconvert { + u_long lval[2]; + u_quad_t qval; +}; +typedef union nfs_quadconvert nfsquad_t; + +/* + * NFS Version 3 special file number. + */ +struct nfsv3_spec { + u_long specdata1; + u_long specdata2; +}; +typedef struct nfsv3_spec nfsv3spec; + +/* + * File attributes and setable attributes. These structures cover both + * NFS version 2 and the version 3 protocol. Note that the union is only + * used so that one pointer can refer to both variants. These structures + * go out on the wire and must be densely packed, so no quad data types + * are used. (all fields are longs or u_longs or structures of same) + * NB: You can't do sizeof(struct nfs_fattr), you must use the + * NFSX_FATTR(v3) macro. + */ +struct nfs_fattr { + u_long fa_type; + u_long fa_mode; + u_long fa_nlink; + u_long fa_uid; + u_long fa_gid; + union { + struct { + u_long nfsv2fa_size; + u_long nfsv2fa_blocksize; + u_long nfsv2fa_rdev; + u_long nfsv2fa_blocks; + u_long nfsv2fa_fsid; + u_long nfsv2fa_fileid; + nfstime2 nfsv2fa_atime; + nfstime2 nfsv2fa_mtime; + nfstime2 nfsv2fa_ctime; + } fa_nfsv2; + struct { + nfsuint64 nfsv3fa_size; + nfsuint64 nfsv3fa_used; + nfsv3spec nfsv3fa_rdev; + nfsuint64 nfsv3fa_fsid; + nfsuint64 nfsv3fa_fileid; + nfstime3 nfsv3fa_atime; + nfstime3 nfsv3fa_mtime; + nfstime3 nfsv3fa_ctime; + } fa_nfsv3; + } fa_un; +}; + +/* and some ugly defines for accessing union components */ +#define fa2_size fa_un.fa_nfsv2.nfsv2fa_size +#define fa2_blocksize fa_un.fa_nfsv2.nfsv2fa_blocksize +#define fa2_rdev fa_un.fa_nfsv2.nfsv2fa_rdev +#define fa2_blocks fa_un.fa_nfsv2.nfsv2fa_blocks +#define fa2_fsid fa_un.fa_nfsv2.nfsv2fa_fsid +#define fa2_fileid fa_un.fa_nfsv2.nfsv2fa_fileid +#define fa2_atime fa_un.fa_nfsv2.nfsv2fa_atime +#define fa2_mtime fa_un.fa_nfsv2.nfsv2fa_mtime +#define fa2_ctime fa_un.fa_nfsv2.nfsv2fa_ctime +#define fa3_size fa_un.fa_nfsv3.nfsv3fa_size +#define fa3_used fa_un.fa_nfsv3.nfsv3fa_used +#define fa3_rdev fa_un.fa_nfsv3.nfsv3fa_rdev +#define fa3_fsid fa_un.fa_nfsv3.nfsv3fa_fsid +#define fa3_fileid fa_un.fa_nfsv3.nfsv3fa_fileid +#define fa3_atime fa_un.fa_nfsv3.nfsv3fa_atime +#define fa3_mtime fa_un.fa_nfsv3.nfsv3fa_mtime +#define fa3_ctime fa_un.fa_nfsv3.nfsv3fa_ctime + +struct nfsv2_sattr { + u_long sa_mode; + u_long sa_uid; + u_long sa_gid; + u_long sa_size; + nfstime2 sa_atime; + nfstime2 sa_mtime; +}; + +/* + * NFS Version 3 sattr structure for the new node creation case. + */ +struct nfsv3_sattr { + u_long sa_modetrue; + u_long sa_mode; + u_long sa_uidtrue; + u_long sa_uid; + u_long sa_gidtrue; + u_long sa_gid; + u_long sa_sizefalse; + u_long sa_atimetype; + nfstime3 sa_atime; + u_long sa_mtimetype; + nfstime3 sa_mtime; +}; + +struct nfs_statfs { + union { + struct { + u_long nfsv2sf_tsize; + u_long nfsv2sf_bsize; + u_long nfsv2sf_blocks; + u_long nfsv2sf_bfree; + u_long nfsv2sf_bavail; + } sf_nfsv2; + struct { + nfsuint64 nfsv3sf_tbytes; + nfsuint64 nfsv3sf_fbytes; + nfsuint64 nfsv3sf_abytes; + nfsuint64 nfsv3sf_tfiles; + nfsuint64 nfsv3sf_ffiles; + nfsuint64 nfsv3sf_afiles; + u_long nfsv3sf_invarsec; + } sf_nfsv3; + } sf_un; +}; + +#define sf_tsize sf_un.sf_nfsv2.nfsv2sf_tsize +#define sf_bsize sf_un.sf_nfsv2.nfsv2sf_bsize +#define sf_blocks sf_un.sf_nfsv2.nfsv2sf_blocks +#define sf_bfree sf_un.sf_nfsv2.nfsv2sf_bfree +#define sf_bavail sf_un.sf_nfsv2.nfsv2sf_bavail +#define sf_tbytes sf_un.sf_nfsv3.nfsv3sf_tbytes +#define sf_fbytes sf_un.sf_nfsv3.nfsv3sf_fbytes +#define sf_abytes sf_un.sf_nfsv3.nfsv3sf_abytes +#define sf_tfiles sf_un.sf_nfsv3.nfsv3sf_tfiles +#define sf_ffiles sf_un.sf_nfsv3.nfsv3sf_ffiles +#define sf_afiles sf_un.sf_nfsv3.nfsv3sf_afiles +#define sf_invarsec sf_un.sf_nfsv3.nfsv3sf_invarsec + +struct nfsv3_fsinfo { + u_long fs_rtmax; + u_long fs_rtpref; + u_long fs_rtmult; + u_long fs_wtmax; + u_long fs_wtpref; + u_long fs_wtmult; + u_long fs_dtpref; + nfsuint64 fs_maxfilesize; + nfstime3 fs_timedelta; + u_long fs_properties; +}; + +struct nfsv3_pathconf { + u_long pc_linkmax; + u_long pc_namemax; + u_long pc_notrunc; + u_long pc_chownrestricted; + u_long pc_caseinsensitive; + u_long pc_casepreserving; +}; + +#endif diff --git a/sys/nfsserver/nfsrvcache.h b/sys/nfsserver/nfsrvcache.h index b367b9f..dc10bd7 100644 --- a/sys/nfsserver/nfsrvcache.h +++ b/sys/nfsserver/nfsrvcache.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfsrvcache.h 8.1 (Berkeley) 6/10/93 - * $Id: nfsrvcache.h,v 1.3 1994/08/21 06:50:13 paul Exp $ + * $Id: nfsrvcache.h,v 1.4 1994/10/17 17:47:44 phk Exp $ */ #ifndef _NFS_NFSRVCACHE_H_ @@ -44,7 +44,7 @@ * Definitions for the server recent request cache */ -#define NFSRVCACHESIZ 256 +#define NFSRVCACHESIZ 64 struct nfsrvcache { TAILQ_ENTRY(nfsrvcache) rc_lru; /* LRU chain */ diff --git a/sys/nfsserver/nfsrvstats.h b/sys/nfsserver/nfsrvstats.h index cbf80c6..e1a0f07 100644 --- a/sys/nfsserver/nfsrvstats.h +++ b/sys/nfsserver/nfsrvstats.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs.h 8.1 (Berkeley) 6/10/93 - * $Id: nfs.h,v 1.8 1994/11/02 00:11:00 wollman Exp $ + * $Id: nfs.h,v 1.9 1995/02/14 06:22:18 phk Exp $ */ #ifndef _NFS_NFS_H_ @@ -45,11 +45,12 @@ */ #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_TICKINTVL 5 /* Desired time for a tick (msec) */ +#define NFS_HZ (hz / nfs_ticks) /* Ticks/sec */ +#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 */ @@ -62,13 +63,81 @@ #endif #define NFS_WSIZE 8192 /* Def. write data size <= 8192 */ #define NFS_RSIZE 8192 /* Def. read data size <= 8192 */ +#define NFS_READDIRSIZE 8192 /* Def. readdir size */ #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 NFS_MAXASYNCDAEMON 20 /* Max. number async_daemons runable */ +#define NFS_MAXGATHERDELAY 100 /* Max. write gather delay (msec) */ +#ifndef NFS_GATHERDELAY +#define NFS_GATHERDELAY 10 /* Default write gather delay (msec) */ +#endif +#define NFS_DIRBLKSIZ 4096 /* Must be a multiple of DIRBLKSIZ */ + +/* + * Oddballs + */ #define NMOD(a) ((a) % nfs_asyncdaemons) +#define NFS_CMPFH(n, f, s) \ + ((n)->n_fhsize == (s) && !bcmp((caddr_t)(n)->n_fhp, (caddr_t)(f), (s))) +#define NFS_ISV3(v) (VFSTONFS((v)->v_mount)->nm_flag & NFSMNT_NFSV3) +#define NFS_SRVMAXDATA(n) \ + (((n)->nd_flag & ND_NFSV3) ? (((n)->nd_nam2) ? \ + NFS_MAXDGRAMDATA : NFS_MAXDATA) : NFS_V2MAXDATA) + +/* + * XXX + * sys/buf.h should be editted to change B_APPENDWRITE --> B_NEEDCOMMIT, but + * until then... + * Same goes for sys/malloc.h, which needs M_NFSDIROFF, + * M_NFSRVDESC and M_NFSBIGFH added. + * The VA_EXCLUSIVE flag should be added for va_vaflags and set for an + * exclusive create. + * The B_INVAFTERWRITE flag should be set to whatever is required by the + * buffer cache code to say "Invalidate the block after it is written back". + */ +#ifndef B_NEEDCOMMIT +#define B_NEEDCOMMIT B_APPENDWRITE +#endif +#ifndef M_NFSRVDESC +#define M_NFSRVDESC M_TEMP +#endif +#ifndef M_NFSDIROFF +#define M_NFSDIROFF M_TEMP +#endif +#ifndef M_NFSBIGFH +#define M_NFSBIGFH M_TEMP +#endif +#ifndef VA_EXCLUSIVE +#define VA_EXCLUSIVE 0 +#endif +#ifdef __FreeBSD__ +#define B_INVAFTERWRITE B_NOCACHE +#else +#define B_INVAFTERWRITE B_INVAL +#endif + +/* + * These ifdefs try to handle the differences between the various 4.4BSD-Lite + * based vfs interfaces. + * btw: NetBSD-current does have a VOP_LEASDE(), but I don't know how to + * differentiate between NetBSD-1.0 and NetBSD-current, so.. + * I also don't know about BSDi's 2.0 release. + */ +#if !defined(HAS_VOPLEASE) && !defined(__FreeBSD__) && !defined(__NetBSD__) +#define HAS_VOPLEASE 1 +#endif +#if !defined(HAS_VOPREVOKE) && !defined(__FreeBSD__) && !defined(__NetBSD__) +#define HAS_VOPREVOKE 1 +#endif + +/* + * The IO_METASYNC flag should be implemented for local file systems. + * (Until then, it is nothin at all.) + */ +#ifndef IO_METASYNC +#define IO_METASYNC 0 +#endif /* * Set the attribute timeout based on how recently the file has been modified. @@ -80,6 +149,20 @@ (time.tv_sec - (np)->n_mtime) / 10)) /* + * Expected allocation sizes for major data structures. If the actual size + * of the structure exceeds these sizes, then malloc() will be allocating + * almost twice the memory required. This is used in nfs_init() to warn + * the sysadmin that the size of a structure should be reduced. + * (These sizes are always a power of 2. If the kernel malloc() changes + * to one that does not allocate space in powers of 2 size, then this all + * becomes bunk!) + */ +#define NFS_NODEALLOC 256 +#define NFS_MNTALLOC 512 +#define NFS_SVCALLOC 256 +#define NFS_UIDALLOC 128 + +/* * Structures for the nfssvc(2) syscall. Not that anyone but nfsd and mount_nfs * should ever try and use it. */ @@ -95,7 +178,12 @@ struct nfsd_srvargs { 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) */ + u_char *nsd_authstr; /* Auth string (ret) */ + int nsd_verflen; /* and the verfier */ + u_char *nsd_verfstr; + struct timeval nsd_timestamp; /* timestamp from verifier */ + u_long nsd_ttl; /* credential ttl (sec) */ + NFSKERBKEY_T nsd_key; /* Session key */ }; struct nfsd_cargs { @@ -103,7 +191,10 @@ struct nfsd_cargs { uid_t ncd_authuid; /* Effective uid */ int ncd_authtype; /* Type of authenticator */ int ncd_authlen; /* Length of authenticator string */ - char *ncd_authstr; /* Authenticator string */ + u_char *ncd_authstr; /* Authenticator string */ + int ncd_verflen; /* and the verifier */ + u_char *ncd_verfstr; + NFSKERBKEY_T ncd_key; /* Session key */ }; /* @@ -142,6 +233,7 @@ struct nfsstats { int srvnqnfs_leases; int srvnqnfs_maxleases; int srvnqnfs_getleases; + int srvvop_writes; }; /* @@ -173,7 +265,7 @@ struct nfsstats { * such as SIGALRM will not expect file I/O system calls to be interrupted * by them and break. */ -#ifdef KERNEL +#if defined(KERNEL) || defined(_KERNEL) struct uio; struct buf; struct vattr; struct nameidata; /* XXX */ @@ -224,17 +316,29 @@ TAILQ_HEAD(, nfsreq) nfs_reqq; #define R_MUSTRESEND 0x40 /* Must resend request */ #define R_GETONEREP 0x80 /* Probe for one reply only */ -extern 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 +#ifndef NFS_UIDHASHSIZ +#define NFS_UIDHASHSIZ 29 /* Tune the size of nfssvc_sock with this */ +#endif #define NUIDHASH(sock, uid) \ - (&(sock)->ns_uidhashtbl[(uid) & (sock)->ns_uidhash]) + (&(sock)->ns_uidhashtbl[(uid) % NFS_UIDHASHSIZ]) +#ifndef NFS_WDELAYHASHSIZ +#define NFS_WDELAYHASHSIZ 16 /* and with this */ +#endif +#define NWDELAYHASH(sock, f) \ + (&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ]) +#ifndef NFS_MUIDHASHSIZ +#define NFS_MUIDHASHSIZ 67 /* Tune the size of nfsmount with this */ +#endif +#define NMUIDHASH(nmp, uid) \ + (&(nmp)->nm_uidhashtbl[(uid) % NFS_MUIDHASHSIZ]) +#define NFSNOHASH(fhsum) \ + (&nfsnodehashtbl[(fhsum) & nfsnodehash]) /* * Network address hash list element @@ -248,35 +352,41 @@ struct nfsuid { TAILQ_ENTRY(nfsuid) nu_lru; /* LRU chain */ LIST_ENTRY(nfsuid) nu_hash; /* Hash list */ 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 */ + int nu_expire; /* Expiry time (sec) */ + struct timeval nu_timestamp; /* Kerb. timestamp */ + u_long nu_nickname; /* Nickname on server */ + NFSKERBKEY_T nu_key; /* and session key */ }; #define nu_inetaddr nu_haddr.had_inetaddr #define nu_nam nu_haddr.had_nam /* Bits for nu_flag */ #define NU_INETADDR 0x1 +#define NU_NAM 0x2 +#define NU_NETFAM(u) (((u)->nu_flag & NU_INETADDR) ? AF_INET : AF_ISO) struct nfssvc_sock { TAILQ_ENTRY(nfssvc_sock) ns_chain; /* List of all nfssvc_sock's */ TAILQ_HEAD(, nfsuid) ns_uidlruhead; - LIST_HEAD(, nfsuid) *ns_uidhashtbl; - u_long ns_uidhash; - - 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; + struct mbuf *ns_frag; + int ns_flag; + int ns_solock; + int ns_cc; + int ns_reclen; int ns_numuids; + u_long ns_sref; + LIST_HEAD(, nfsrv_descript) ns_tq; /* Write gather lists */ + LIST_HEAD(, nfsuid) ns_uidhashtbl[NFS_UIDHASHSIZ]; + LIST_HEAD(nfsrvw_delayhash, nfsrv_descript) ns_wdelayhashtbl[NFS_WDELAYHASHSIZ]; }; /* Bits for "ns_flag" */ @@ -285,6 +395,7 @@ struct nfssvc_sock { #define SLP_NEEDQ 0x04 #define SLP_DISCONN 0x08 #define SLP_GETSTREAM 0x10 +#define SLP_LASTFRAG 0x20 #define SLP_ALLFLAGS 0xff TAILQ_HEAD(, nfssvc_sock) nfssvc_sockhead; @@ -296,73 +407,124 @@ int nfssvc_sockhead_flag; * One of these structures is allocated for each nfsd. */ struct nfsd { - TAILQ_ENTRY(nfsd) nd_chain; /* List of all nfsd's */ - 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 */ - u_long nd_duration; /* Lease duration */ - int nd_authlen; /* Authenticator len */ - u_char nd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */ - struct proc *nd_procp; /* Proc ptr */ + TAILQ_ENTRY(nfsd) nfsd_chain; /* List of all nfsd's */ + int nfsd_flag; /* NFSD_ flags */ + struct nfssvc_sock *nfsd_slp; /* Current socket */ + int nfsd_authlen; /* Authenticator len */ + u_char nfsd_authstr[RPCAUTH_MAXSIZ]; /* Authenticator data */ + int nfsd_verflen; /* and the Verifier */ + u_char nfsd_verfstr[RPCVERF_MAXSIZ]; + struct proc *nfsd_procp; /* Proc ptr */ + struct nfsrv_descript *nfsd_nd; /* Associated nfsrv_descript */ }; -/* Bits for "nd_flag" */ +/* Bits for "nfsd_flag" */ #define NFSD_WAITING 0x01 #define NFSD_REQINPROG 0x02 #define NFSD_NEEDAUTH 0x04 #define NFSD_AUTHFAIL 0x08 +/* + * This structure is used by the server for describing each request. + * Some fields are used only when write request gathering is performed. + */ +struct nfsrv_descript { + u_quad_t nd_time; /* Write deadline (usec) */ + off_t nd_off; /* Start byte offset */ + off_t nd_eoff; /* and end byte offset */ + LIST_ENTRY(nfsrv_descript) nd_hash; /* Hash list */ + LIST_ENTRY(nfsrv_descript) nd_tq; /* and timer list */ + LIST_HEAD(,nfsrv_descript) nd_coalesce; /* coalesced writes */ + struct mbuf *nd_mrep; /* Request mbuf list */ + struct mbuf *nd_md; /* Current dissect mbuf */ + struct mbuf *nd_mreq; /* Reply mbuf list */ + struct mbuf *nd_nam; /* and socket addr */ + struct mbuf *nd_nam2; /* return socket addr */ + caddr_t nd_dpos; /* Current dissect pos */ + int nd_procnum; /* RPC # */ + int nd_stable; /* storage type */ + int nd_flag; /* nd_flag */ + int nd_len; /* Length of this write */ + int nd_repstat; /* Reply status */ + u_long nd_retxid; /* Reply xid */ + u_long nd_duration; /* Lease duration */ + struct timeval nd_starttime; /* Time RPC initiated */ + fhandle_t nd_fh; /* File handle */ + struct ucred nd_cr; /* Credentials */ +}; + +/* Bits for "nd_flag" */ +#define ND_READ LEASE_READ +#define ND_WRITE LEASE_WRITE +#define ND_CHECK 0x04 +#define ND_LEASE (ND_READ | ND_WRITE | ND_CHECK) +#define ND_NFSV3 0x08 +#define ND_NQNFS 0x10 +#define ND_KERBNICK 0x20 +#define ND_KERBFULL 0x40 +#define ND_KERBAUTH (ND_KERBNICK | ND_KERBFULL) + TAILQ_HEAD(, nfsd) nfsd_head; int nfsd_head_flag; #define NFSD_CHECKSLP 0x01 +/* + * These macros compare nfsrv_descript structures. + */ +#define NFSW_CONTIG(o, n) \ + ((o)->nd_eoff >= (n)->nd_off && \ + !bcmp((caddr_t)&(o)->nd_fh, (caddr_t)&(n)->nd_fh, NFSX_V3FH)) + +#define NFSW_SAMECRED(o, n) \ + (((o)->nd_flag & ND_KERBAUTH) == ((n)->nd_flag & ND_KERBAUTH) && \ + !bcmp((caddr_t)&(o)->nd_cr, (caddr_t)&(n)->nd_cr, \ + sizeof (struct ucred))) + int nfs_reply __P((struct nfsreq *)); -int nfs_getreq __P((struct nfsd *,int)); +int nfs_getreq __P((struct nfsrv_descript *,struct nfsd *,int)); int nfs_send __P((struct socket *,struct mbuf *,struct mbuf *,struct nfsreq *)); -int nfs_rephead __P((int,struct nfsd *,int,int,u_quad_t *,struct mbuf **,struct mbuf **,caddr_t *)); +int nfs_rephead __P((int,struct nfsrv_descript *,struct nfssvc_sock *,int,int,u_quad_t *,struct mbuf **,struct mbuf **,caddr_t *)); int nfs_sndlock __P((int *,struct nfsreq *)); int nfs_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *)); int nfs_vinvalbuf __P((struct vnode *,int,struct ucred *,struct proc *,int)); int nfs_readrpc __P((struct vnode *,struct uio *,struct ucred *)); -int nfs_writerpc __P((struct vnode *,struct uio *,struct ucred *,int)); +int nfs_writerpc __P((struct vnode *,struct uio *,struct ucred *,int *,int *)); int nfs_readdirrpc __P((register struct vnode *,struct uio *,struct ucred *)); +int nfs_setattrrpc __P((struct vnode *,struct vattr *,struct ucred *,struct proc *)); int nfs_asyncio __P((struct buf *,struct ucred *)); int nfs_doio __P((struct buf *,struct ucred *,struct proc *)); int nfs_readlinkrpc __P((struct vnode *,struct uio *,struct ucred *)); int nfs_sigintr __P((struct nfsmount *,struct nfsreq *r,struct proc *)); -int nfs_readdirlookrpc __P((struct vnode *,register struct uio *,struct ucred *)); +int nfs_readdirplusrpc __P((struct vnode *,register struct uio *,struct ucred *)); int nfsm_disct __P((struct mbuf **,caddr_t *,int,int,caddr_t *)); -int nfsrv_fhtovp __P((fhandle_t *,int,struct vnode **,struct ucred *,struct nfssvc_sock *,struct mbuf *,int *)); +void nfsm_srvfattr __P((struct nfsrv_descript *,struct vattr *,struct nfs_fattr *)); +void nfsm_srvwcc __P((struct nfsrv_descript *,int,struct vattr *,int,struct vattr *,struct mbuf **,char **)); +void nfsm_srvpostopattr __P((struct nfsrv_descript *,int,struct vattr *,struct mbuf **,char **)); +int nfsrv_fhtovp __P((fhandle_t *,int,struct vnode **,struct ucred *,struct nfssvc_sock *,struct mbuf *,int *,int)); int nfsrv_access __P((struct vnode *,int,struct ucred *,int,struct proc *)); int netaddr_match __P((int,union nethostaddr *,struct mbuf *)); int nfs_request __P((struct vnode *,struct mbuf *,int,struct proc *,struct ucred *,struct mbuf **,struct mbuf **,caddr_t *)); int nfs_loadattrcache __P((struct vnode **,struct mbuf **,caddr_t *,struct vattr *)); -int nfs_namei __P((struct nameidata *,fhandle_t *,int,struct nfssvc_sock *,struct mbuf *,struct mbuf **,caddr_t *,struct proc *)); +int nfs_namei __P((struct nameidata *,fhandle_t *,int,struct nfssvc_sock *,struct mbuf *,struct mbuf **,caddr_t *,struct vnode **,struct proc *,int)); void nfsm_adj __P((struct mbuf *,int,int)); int nfsm_mbuftouio __P((struct mbuf **,struct uio *,int,caddr_t *)); void nfsrv_initcache __P((void)); int nfs_rcvlock __P((struct nfsreq *)); -int nfs_getauth __P((struct nfsmount *,struct nfsreq *,struct ucred *,int *,char **,int *)); +int nfs_getauth __P((struct nfsmount *,struct nfsreq *,struct ucred *,char **,int *,char *,int *,NFSKERBKEY_T)); +int nfs_getnickauth __P((struct nfsmount *,struct ucred *,char **,int *,char *,int)); +int nfs_savenickauth __P((struct nfsmount *,struct ucred *,int,NFSKERBKEY_T,struct mbuf **,char **,struct mbuf *)); int nfs_msg __P((struct proc *,char *,char *)); int nfs_adv __P((struct mbuf **,caddr_t *,int,int)); int nfsrv_getstream __P((struct nfssvc_sock *,int)); void nfs_nhinit __P((void)); void nfs_timer __P((void*)); -struct nfsnodehashhead * nfs_hash __P((nfsv2fh_t *)); +u_long nfs_hash __P((nfsfh_t *,int)); int nfssvc_iod __P((struct proc *)); int nfssvc_nfsd __P((struct nfsd_srvargs *,caddr_t,struct proc *)); int nfssvc_addsock __P((struct file *,struct mbuf *)); -int nfsrv_dorec __P((struct nfssvc_sock *,struct nfsd *)); -int nfsrv_getcache __P((struct mbuf *,struct nfsd *,struct mbuf **)); -void nfsrv_updatecache __P((struct mbuf *,struct nfsd *,int,struct mbuf *)); +int nfsrv_dorec __P((struct nfssvc_sock *,struct nfsd *,struct nfsrv_descript **)); +int nfsrv_getcache __P((struct nfsrv_descript *,struct nfssvc_sock *,struct mbuf **)); +void nfsrv_updatecache __P((struct nfsrv_descript *,int,struct mbuf *)); int mountnfs __P((struct nfs_args *,struct mount *,struct mbuf *,char *,char *,struct vnode **)); int nfs_connect __P((struct nfsmount *,struct nfsreq *)); int nfs_getattrcache __P((struct vnode *,struct vattr *)); @@ -370,9 +532,20 @@ int nfsm_strtmbuf __P((struct mbuf **,char **,char *,long)); int nfs_bioread __P((struct vnode *,struct uio *,int,struct ucred *)); int nfsm_uiotombuf __P((struct uio *,struct mbuf **,int,caddr_t *)); void nfsrv_init __P((int)); +void nfs_clearcommit __P((struct mount *)); +int nfsrv_errmap __P((struct nfsrv_descript *, int)); +void nfsrvw_coalesce __P((struct nfsrv_descript *,struct nfsrv_descript *)); +void nfsrvw_sort __P((gid_t [],int)); +void nfsrv_setcred __P((struct ucred *,struct ucred *)); +int nfs_flush __P((struct vnode *,struct ucred *,int,struct proc *,int)); +int nfs_writebp __P((struct buf *,int)); int nfsrv_vput __P(( struct vnode * )); int nfsrv_vrele __P(( struct vnode * )); int nfsrv_vmio __P(( struct vnode * )); +int nfsrv_writegather __P((struct nfsrv_descript **, struct nfssvc_sock *, + struct proc *, struct mbuf **)); +int nfs_fsinfo __P((struct nfsmount *, struct vnode *, struct ucred *, + struct proc *p)); #endif /* KERNEL */ |