diff options
author | dfr <dfr@FreeBSD.org> | 1995-06-27 11:07:30 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 1995-06-27 11:07:30 +0000 |
commit | 666343f7f055c064375d48bb9a608730d7145beb (patch) | |
tree | 372bad41f8c547f40d0826ed596c53dc772ab986 /sys/nfsclient | |
parent | 6da3ef32238f37b3b45cf709205fcff60bcbda7f (diff) | |
download | FreeBSD-src-666343f7f055c064375d48bb9a608730d7145beb.zip FreeBSD-src-666343f7f055c064375d48bb9a608730d7145beb.tar.gz |
Changes to support version 3 of the NFS protocol.
The version 2 support has been tested (client+server) against FreeBSD-2.0,
IRIX 5.3 and FreeBSD-current (using a loopback mount). The version 2 support
is stable AFAIK.
The version 3 support has been tested with a loopback mount and minimally
against an IRIX 5.3 server. It needs more testing and may have problems.
I have patched amd to support the new variable length filehandles although
it will still only use version 2 of the protocol.
Before booting a kernel with these changes, nfs clients will need to at least
build and install /usr/sbin/mount_nfs. Servers will need to build and
install /usr/sbin/mountd.
NFS diskless support is untested.
Obtained from: Rick Macklem <rick@snowhite.cis.uoguelph.ca>
Diffstat (limited to 'sys/nfsclient')
-rw-r--r-- | sys/nfsclient/nfs.h | 279 | ||||
-rw-r--r-- | sys/nfsclient/nfs_bio.c | 292 | ||||
-rw-r--r-- | sys/nfsclient/nfs_nfsiod.c | 571 | ||||
-rw-r--r-- | sys/nfsclient/nfs_node.c | 130 | ||||
-rw-r--r-- | sys/nfsclient/nfs_socket.c | 518 | ||||
-rw-r--r-- | sys/nfsclient/nfs_subs.c | 1035 | ||||
-rw-r--r-- | sys/nfsclient/nfs_vfsops.c | 244 | ||||
-rw-r--r-- | sys/nfsclient/nfs_vnops.c | 2318 | ||||
-rw-r--r-- | sys/nfsclient/nfsargs.h | 279 | ||||
-rw-r--r-- | sys/nfsclient/nfsdiskless.h | 34 | ||||
-rw-r--r-- | sys/nfsclient/nfsm_subs.h | 333 | ||||
-rw-r--r-- | sys/nfsclient/nfsmount.h | 15 | ||||
-rw-r--r-- | sys/nfsclient/nfsnode.h | 116 | ||||
-rw-r--r-- | sys/nfsclient/nfsstats.h | 279 |
14 files changed, 4661 insertions, 1782 deletions
diff --git a/sys/nfsclient/nfs.h b/sys/nfsclient/nfs.h index cbf80c6..e1a0f07 100644 --- a/sys/nfsclient/nfs.h +++ b/sys/nfsclient/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/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c index f929012..f9ae8b6 100644 --- a/sys/nfsclient/nfs_bio.c +++ b/sys/nfsclient/nfs_bio.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_bio.c 8.5 (Berkeley) 1/4/94 - * $Id: nfs_bio.c,v 1.13 1995/05/21 21:39:21 davidg Exp $ + * $Id: nfs_bio.c,v 1.14 1995/05/30 08:12:35 rgrimes Exp $ */ #include <sys/param.h> @@ -49,16 +49,30 @@ #include <vm/vm.h> -#include <nfs/nfsnode.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/nfsmount.h> #include <nfs/nqnfs.h> +#include <nfs/nfsnode.h> struct buf *nfs_getcacheblk(); extern struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; extern int nfs_numasync; +extern struct nfsstats nfsstats; + +/* + * Ifdefs for FreeBSD-current's merged VM/buffer cache. It is unfortunate + * that this isn't done inside getblk() and brelse() so these calls + * wouldn't need to be here. + */ +#ifdef B_VMIO +#define vnode_pager_uncache(vp) +#else +#define vfs_busy_pages(bp, f) +#define vfs_unbusy_pages(bp) +#define vfs_dirty_pages(bp) +#endif /* * Vnode op for read using bio @@ -72,29 +86,28 @@ nfs_bioread(vp, uio, ioflag, cred) struct ucred *cred; { register struct nfsnode *np = VTONFS(vp); - register int biosize, diff; + register int biosize, diff, i; struct buf *bp = 0, *rabp; struct vattr vattr; struct proc *p; - struct nfsmount *nmp; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); daddr_t lbn, rabn; int bufsize; int nra, error = 0, n = 0, on = 0, not_readin; + nfsquad_t tquad; -#ifdef lint - ioflag = ioflag; -#endif /* lint */ #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_READ) panic("nfs_read mode"); #endif if (uio->uio_resid == 0) return (0); - if (uio->uio_offset < 0 && vp->v_type != VDIR) + if (uio->uio_offset < 0) return (EINVAL); - nmp = VFSTONFS(vp->v_mount); - biosize = NFS_MAXDGRAMDATA; p = uio->uio_procp; + if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3) + (void)nfs_fsinfo(nmp, vp, cred, p); + biosize = nmp->nm_rsize; /* * For nfs, cache consistency can only be maintained approximately. * Although RFC1094 does not specify the criteria, the following is @@ -107,8 +120,6 @@ nfs_bioread(vp, uio, ioflag, cred) * server, so flush all of the file's data out of the cache. * Then force a getattr rpc to ensure that you have up to date * attributes. - * The mount flag NFSMNT_MYWRITE says "Assume that my writes are - * the ones changing the modify time. * NB: This implies that cache data can be read when up to * NFS_ATTRTIMEO seconds out of date. If you find that you need current * attributes this could be forced by setting n_attrstamp to 0 before @@ -116,14 +127,15 @@ nfs_bioread(vp, uio, ioflag, cred) */ if ((nmp->nm_flag & NFSMNT_NQNFS) == 0 && vp->v_type != VLNK) { if (np->n_flag & NMODIFIED) { - if ((nmp->nm_flag & NFSMNT_MYWRITE) == 0 || - vp->v_type != VREG) { + if (vp->v_type != VREG) { + if (vp->v_type != VDIR) + panic("nfs: bioread, not dir"); + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); } np->n_attrstamp = 0; - np->n_direofoffset = 0; error = VOP_GETATTR(vp, &vattr, cred, p); if (error) return (error); @@ -133,7 +145,8 @@ nfs_bioread(vp, uio, ioflag, cred) if (error) return (error); if (np->n_mtime != vattr.va_mtime.ts_sec) { - np->n_direofoffset = 0; + if (vp->v_type == VDIR) + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); @@ -147,27 +160,24 @@ nfs_bioread(vp, uio, ioflag, cred) * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { - if (NQNFS_CKINVALID(vp, np, NQL_READ)) { + if (NQNFS_CKINVALID(vp, np, ND_READ)) { do { - error = nqnfs_getlease(vp, NQL_READ, cred, p); + error = nqnfs_getlease(vp, ND_READ, cred, p); } while (error == NQNFS_EXPIRED); if (error) return (error); if (np->n_lrev != np->n_brev || (np->n_flag & NQNFSNONCACHE) || ((np->n_flag & NMODIFIED) && vp->v_type == VDIR)) { - if (vp->v_type == VDIR) { - np->n_direofoffset = 0; - cache_purge(vp); - } + if (vp->v_type == VDIR) + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); np->n_brev = np->n_lrev; } } else if (vp->v_type == VDIR && (np->n_flag & NMODIFIED)) { - np->n_direofoffset = 0; - cache_purge(vp); + nfs_invaldir(vp); error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); if (error) return (error); @@ -176,33 +186,27 @@ nfs_bioread(vp, uio, ioflag, cred) if (np->n_flag & NQNFSNONCACHE) { switch (vp->v_type) { case VREG: - error = nfs_readrpc(vp, uio, cred); - break; + return (nfs_readrpc(vp, uio, cred)); case VLNK: - error = nfs_readlinkrpc(vp, uio, cred); - break; + return (nfs_readlinkrpc(vp, uio, cred)); case VDIR: - error = nfs_readdirrpc(vp, uio, cred); break; default: printf(" NQNFSNONCACHE: type %x unexpected\n", vp->v_type); - break; }; - return (error); } switch (vp->v_type) { case VREG: nfsstats.biocache_reads++; lbn = uio->uio_offset / biosize; - on = uio->uio_offset & (biosize-1); + on = uio->uio_offset & (biosize - 1); not_readin = 1; /* * Start the read ahead(s), as required. */ - if (nfs_numasync > 0 && nmp->nm_readahead > 0 && - lbn == vp->v_lastr + 1) { + if (nfs_numasync > 0 && nmp->nm_readahead > 0) { for (nra = 0; nra < nmp->nm_readahead && (lbn + 1 + nra) * biosize < np->n_size; nra++) { rabn = lbn + 1 + nra; @@ -296,32 +300,55 @@ again: break; case VDIR: nfsstats.biocache_readdirs++; - lbn = (daddr_t)uio->uio_offset; + lbn = uio->uio_offset / NFS_DIRBLKSIZ; + on = uio->uio_offset & (NFS_DIRBLKSIZ - 1); bp = nfs_getcacheblk(vp, lbn, NFS_DIRBLKSIZ, p); if (!bp) - return (EINTR); - + return (EINTR); if ((bp->b_flags & B_CACHE) == 0) { - bp->b_flags |= B_READ; - vfs_busy_pages(bp, 0); - error = nfs_doio(bp, cred, p); - if (error) { - bp->b_flags |= B_ERROR; - brelse(bp); - return (error); + bp->b_flags |= B_READ; + vfs_busy_pages(bp, 0); + error = nfs_doio(bp, cred, p); + if (error) { + brelse(bp); + while (error == NFSERR_BAD_COOKIE) { + nfs_invaldir(vp); + error = nfs_vinvalbuf(vp, 0, cred, p, 1); + /* + * Yuck! The directory has been modified on the + * server. The only way to get the block is by + * reading from the beginning to get all the + * offset cookies. + */ + for (i = 0; i <= lbn && !error; i++) { + bp = nfs_getcacheblk(vp, i, NFS_DIRBLKSIZ, p); + if (!bp) + return (EINTR); + if ((bp->b_flags & B_DONE) == 0) { + bp->b_flags |= B_READ; + vfs_busy_pages(bp, 0); + error = nfs_doio(bp, cred, p); + if (error) + brelse(bp); + } + } } + if (error) + return (error); + } } /* * If not eof and read aheads are enabled, start one. * (You need the current block first, so that you have the - * directory offset cookie of the next block. + * directory offset cookie of the next block.) */ - rabn = bp->b_blkno; if (nfs_numasync > 0 && nmp->nm_readahead > 0 && - rabn != 0 && rabn != np->n_direofoffset && - !incore(vp, rabn)) { - rabp = nfs_getcacheblk(vp, rabn, NFS_DIRBLKSIZ, p); + (np->n_direofoffset == 0 || + (lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) && + !(np->n_flag & NQNFSNONCACHE) && + !incore(vp, lbn + 1)) { + rabp = nfs_getcacheblk(vp, lbn + 1, NFS_DIRBLKSIZ, p); if (rabp) { if ((rabp->b_flags & (B_CACHE|B_DELWRI)) == 0) { rabp->b_flags |= (B_READ | B_ASYNC); @@ -336,11 +363,10 @@ again: } } } - on = 0; - n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid); + n = min(uio->uio_resid, NFS_DIRBLKSIZ - bp->b_resid - on); break; default: - printf(" nfsbioread: type %x unexpected\n",vp->v_type); + printf(" nfs_bioread: type %x unexpected\n",vp->v_type); break; }; @@ -354,11 +380,11 @@ again: n = 0; break; case VDIR: - uio->uio_offset = bp->b_blkno; + if (np->n_flag & NQNFSNONCACHE) + bp->b_flags |= B_INVAL; break; default: - printf(" nfsbioread: type %x unexpected\n",vp->v_type); - break; + printf(" nfs_bioread: type %x unexpected\n",vp->v_type); } brelse(bp); } while (error == 0 && uio->uio_resid > 0 && n > 0); @@ -386,10 +412,10 @@ nfs_write(ap) int ioflag = ap->a_ioflag; struct buf *bp; struct vattr vattr; - struct nfsmount *nmp; - daddr_t lbn; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + daddr_t lbn, bn; int bufsize; - int n, on, error = 0; + int n, on, error = 0, iomode, must_commit; #ifdef DIAGNOSTIC if (uio->uio_rw != UIO_WRITE) @@ -403,6 +429,8 @@ nfs_write(ap) np->n_flag &= ~NWRITEERR; return (np->n_error); } + if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3) + (void)nfs_fsinfo(nmp, vp, cred, p); if (ioflag & (IO_APPEND | IO_SYNC)) { if (np->n_flag & NMODIFIED) { np->n_attrstamp = 0; @@ -418,7 +446,6 @@ nfs_write(ap) uio->uio_offset = np->n_size; } } - nmp = VFSTONFS(vp->v_mount); if (uio->uio_offset < 0) return (EINVAL); if (uio->uio_resid == 0) @@ -437,7 +464,7 @@ nfs_write(ap) * will be the same size within a filesystem. nfs_writerpc will * still use nm_wsize when sizing the rpc's. */ - biosize = NFS_MAXDGRAMDATA; + biosize = nmp->nm_rsize; do { /* @@ -445,12 +472,11 @@ nfs_write(ap) */ /* * Check for a valid write lease. - * If non-cachable, just do the rpc */ if ((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_WRITE)) { + NQNFS_CKINVALID(vp, np, ND_WRITE)) { do { - error = nqnfs_getlease(vp, NQL_WRITE, cred, p); + error = nqnfs_getlease(vp, ND_WRITE, cred, p); } while (error == NQNFS_EXPIRED); if (error) return (error); @@ -462,8 +488,13 @@ nfs_write(ap) np->n_brev = np->n_lrev; } } - if (np->n_flag & NQNFSNONCACHE) - return (nfs_writerpc(vp, uio, cred, ioflag)); + if ((np->n_flag & NQNFSNONCACHE) && uio->uio_iovcnt == 1) { + iomode = NFSV3WRITE_FILESYNC; + error = nfs_writerpc(vp, uio, cred, &iomode, &must_commit); + if (must_commit) + nfs_clearcommit(vp->v_mount); + return (error); + } nfsstats.biocache_writes++; lbn = uio->uio_offset / biosize; on = uio->uio_offset & (biosize-1); @@ -509,9 +540,9 @@ again: * In case getblk() and/or bwrite() delayed us. */ if ((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_WRITE)) { + NQNFS_CKINVALID(vp, np, ND_WRITE)) { do { - error = nqnfs_getlease(vp, NQL_WRITE, cred, p); + error = nqnfs_getlease(vp, ND_WRITE, cred, p); } while (error == NQNFS_EXPIRED); if (error) { brelse(bp); @@ -540,7 +571,6 @@ again: bp->b_dirtyoff = on; bp->b_dirtyend = on + n; } -#ifndef notdef if (bp->b_validend == 0 || bp->b_validend < bp->b_dirtyoff || bp->b_validoff > bp->b_dirtyend) { bp->b_validoff = bp->b_dirtyoff; @@ -549,13 +579,6 @@ again: bp->b_validoff = min(bp->b_validoff, bp->b_dirtyoff); bp->b_validend = max(bp->b_validend, bp->b_dirtyend); } -#else - bp->b_validoff = bp->b_dirtyoff; - bp->b_validend = bp->b_dirtyend; -#endif - if (ioflag & IO_APPEND) - bp->b_flags |= B_APPENDWRITE; - /* * If the lease is non-cachable or IO_SYNC do bwrite(). */ @@ -564,10 +587,16 @@ again: error = VOP_BWRITE(bp); if (error) return (error); + if (np->n_flag & NQNFSNONCACHE) { + error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1); + if (error) + return (error); + } } else if ((n + on) == biosize && (nmp->nm_flag & NFSMNT_NQNFS) == 0) { bp->b_proc = (struct proc *)0; - bawrite(bp); + bp->b_flags |= B_ASYNC; + (void)nfs_writebp(bp, 0); } else bdwrite(bp); } while (uio->uio_resid > 0 && n > 0); @@ -590,6 +619,7 @@ nfs_getcacheblk(vp, bn, size, p) { register struct buf *bp; struct nfsmount *nmp = VFSTONFS(vp->v_mount); + int biosize = nmp->nm_rsize; if (nmp->nm_flag & NFSMNT_INT) { bp = getblk(vp, bn, size, PCATCH, 0); @@ -602,7 +632,7 @@ nfs_getcacheblk(vp, bn, size, p) bp = getblk(vp, bn, size, 0, 0); if( vp->v_type == VREG) - bp->b_blkno = (bn * NFS_MAXDGRAMDATA) / DEV_BSIZE; + bp->b_blkno = (bn * biosize) / DEV_BSIZE; return (bp); } @@ -689,6 +719,7 @@ nfs_asyncio(bp, cred) bp->b_rcred = cred; } } else { + bp->b_flags |= B_WRITEINPROG; if (bp->b_wcred == NOCRED && cred != NOCRED) { crhold(cred); bp->b_wcred = cred; @@ -700,7 +731,25 @@ nfs_asyncio(bp, cred) wakeup((caddr_t)&nfs_iodwant[i]); return (0); } - return (EIO); + + /* + * If it is a read or a write already marked B_WRITEINPROG or B_NOCACHE + * return EIO so the process will call nfs_doio() and do it + * synchronously. + */ + if (bp->b_flags & (B_READ | B_WRITEINPROG | B_NOCACHE)) + return (EIO); + + /* + * Just turn the async write into a delayed write, instead of + * doing in synchronously. Hopefully, at least one of the nfsiods + * is currently doing a write for this file and will pick up the + * delayed writes before going back to sleep. + */ + bp->b_flags |= B_DELWRI; + reassignbuf(bp, bp->b_vp); + biodone(bp); + return (0); } /* @@ -717,9 +766,10 @@ nfs_doio(bp, cr, p) register struct vnode *vp; struct nfsnode *np; struct nfsmount *nmp; - int error = 0, diff, len; + int error = 0, diff, len, iomode, must_commit = 0; struct uio uio; struct iovec io; + nfsquad_t tquad; vp = bp->b_vp; np = VTONFS(vp); @@ -740,15 +790,18 @@ nfs_doio(bp, cr, p) io.iov_len = uiop->uio_resid = bp->b_bcount; /* mapping was done by vmapbuf() */ io.iov_base = bp->b_data; - uiop->uio_offset = bp->b_blkno * DEV_BSIZE; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; if (bp->b_flags & B_READ) { uiop->uio_rw = UIO_READ; nfsstats.read_physios++; error = nfs_readrpc(vp, uiop, cr); } else { + int com; + + iomode = NFSV3WRITE_DATASYNC; uiop->uio_rw = UIO_WRITE; nfsstats.write_physios++; - error = nfs_writerpc(vp, uiop, cr,0); + error = nfs_writerpc(vp, uiop, cr, &iomode, &com); } if (error) { bp->b_flags |= B_ERROR; @@ -760,7 +813,7 @@ nfs_doio(bp, cr, p) uiop->uio_rw = UIO_READ; switch (vp->v_type) { case VREG: - uiop->uio_offset = bp->b_blkno * DEV_BSIZE; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE; nfsstats.read_bios++; error = nfs_readrpc(vp, uiop, cr); if (!error) { @@ -773,7 +826,7 @@ nfs_doio(bp, cr, p) * Just zero fill the rest of the valid area. */ diff = bp->b_bcount - uiop->uio_resid; - len = np->n_size - (bp->b_blkno * DEV_BSIZE + len = np->n_size - (((u_quad_t)bp->b_blkno) * DEV_BSIZE + diff); if (len > 0) { len = min(len, uiop->uio_resid); @@ -786,31 +839,34 @@ nfs_doio(bp, cr, p) } if (p && (vp->v_flag & VTEXT) && (((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKINVALID(vp, np, NQL_READ) && + NQNFS_CKINVALID(vp, np, ND_READ) && np->n_lrev != np->n_brev) || (!(nmp->nm_flag & NFSMNT_NQNFS) && np->n_mtime != np->n_vattr.va_mtime.ts_sec))) { uprintf("Process killed due to text file modification\n"); psignal(p, SIGKILL); +#ifdef __NetBSD__ + p->p_holdcnt++; +#else p->p_flag |= P_NOSWAP; +#endif } break; case VLNK: - uiop->uio_offset = 0; + uiop->uio_offset = (off_t)0; nfsstats.readlink_bios++; error = nfs_readlinkrpc(vp, uiop, cr); break; case VDIR: - uiop->uio_offset = bp->b_lblkno; nfsstats.readdir_bios++; - if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) - error = nfs_readdirlookrpc(vp, uiop, cr); - else - error = nfs_readdirrpc(vp, uiop, cr); - /* - * Save offset cookie in b_blkno. - */ - bp->b_blkno = uiop->uio_offset; + uiop->uio_offset = ((u_quad_t)bp->b_lblkno) * NFS_DIRBLKSIZ; + if (nmp->nm_flag & NFSMNT_RDIRPLUS) { + error = nfs_readdirplusrpc(vp, uiop, cr); + if (error == NFSERR_NOTSUPP) + nmp->nm_flag &= ~NFSMNT_RDIRPLUS; + } + if ((nmp->nm_flag & NFSMNT_RDIRPLUS) == 0) + error = nfs_readdirrpc(vp, uiop, cr); break; default: printf("nfs_doio: type %x unexpected\n",vp->v_type); @@ -821,32 +877,44 @@ nfs_doio(bp, cr, p) bp->b_error = error; } } else { - if (((bp->b_blkno * DEV_BSIZE) + bp->b_dirtyend) > np->n_size) bp->b_dirtyend = np->n_size - (bp->b_blkno * DEV_BSIZE); if (bp->b_dirtyend > bp->b_dirtyoff) { io.iov_len = uiop->uio_resid = bp->b_dirtyend - - bp->b_dirtyoff; - uiop->uio_offset = (bp->b_blkno * DEV_BSIZE) - + bp->b_dirtyoff; + - bp->b_dirtyoff; + uiop->uio_offset = ((off_t)bp->b_blkno) * DEV_BSIZE + + bp->b_dirtyoff; io.iov_base = (char *)bp->b_data + bp->b_dirtyoff; uiop->uio_rw = UIO_WRITE; nfsstats.write_bios++; - if (bp->b_flags & B_APPENDWRITE) - error = nfs_writerpc(vp, uiop, cr, IO_APPEND); + if ((bp->b_flags & (B_ASYNC | B_NEEDCOMMIT | B_NOCACHE)) == B_ASYNC) + iomode = NFSV3WRITE_UNSTABLE; + else + iomode = NFSV3WRITE_FILESYNC; + bp->b_flags |= B_WRITEINPROG; + error = nfs_writerpc(vp, uiop, cr, &iomode, &must_commit); + if (!error && iomode == NFSV3WRITE_UNSTABLE) + bp->b_flags |= B_NEEDCOMMIT; else - error = nfs_writerpc(vp, uiop, cr, 0); - bp->b_flags &= ~(B_WRITEINPROG | B_APPENDWRITE); + bp->b_flags &= ~B_NEEDCOMMIT; + bp->b_flags &= ~B_WRITEINPROG; - /* - * For an interrupted write, the buffer is still valid and the - * write hasn't been pushed to the server yet, so we can't set - * B_ERROR and report the interruption by setting B_EINTR. For - * the B_ASYNC case, B_EINTR is not relevant, so the rpc attempt - * is essentially a noop. - */ - if (error == EINTR) { + /* + * For an interrupted write, the buffer is still valid + * and the write hasn't been pushed to the server yet, + * so we can't set B_ERROR and report the interruption + * by setting B_EINTR. For the B_ASYNC case, B_EINTR + * is not relevant, so the rpc attempt is essentially + * a noop. For the case of a V3 write rpc not being + * committed to stable storage, the block is still + * dirty and requires either a commit rpc or another + * write rpc with iomode == NFSV3WRITE_FILESYNC before + * the block is reused. This is indicated by setting + * the B_DELWRI and B_NEEDCOMMIT flags. + */ + if (error == EINTR + || (!error && (bp->b_flags & B_NEEDCOMMIT))) { bp->b_flags &= ~(B_INVAL|B_NOCACHE); bp->b_flags |= B_DELWRI; @@ -874,6 +942,8 @@ nfs_doio(bp, cr, p) } } bp->b_resid = uiop->uio_resid; + if (must_commit) + nfs_clearcommit(vp->v_mount); biodone(bp); return (error); } diff --git a/sys/nfsclient/nfs_nfsiod.c b/sys/nfsclient/nfs_nfsiod.c index f7e159f..35a50f4 100644 --- a/sys/nfsclient/nfs_nfsiod.c +++ b/sys/nfsclient/nfs_nfsiod.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/nfsclient/nfs_node.c b/sys/nfsclient/nfs_node.c index e5315f4..c002cea 100644 --- a/sys/nfsclient/nfs_node.c +++ b/sys/nfsclient/nfs_node.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_node.c 8.2 (Berkeley) 12/30/93 - * $Id: nfs_node.c,v 1.7 1994/10/17 17:47:33 phk Exp $ + * $Id: nfs_node.c,v 1.8 1995/03/16 18:15:36 bde Exp $ */ #include <sys/param.h> @@ -47,14 +47,12 @@ #include <sys/malloc.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/nfsnode.h> #include <nfs/nfsmount.h> #include <nfs/nqnfs.h> -#define NFSNOHASH(fhsum) \ - (&nfsnodehashtbl[(fhsum) & nfsnodehash]) LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl; u_long nfsnodehash; @@ -79,19 +77,20 @@ nfs_nhinit() /* * Compute an entry in the NFS hash table structure */ -struct nfsnodehashhead * -nfs_hash(fhp) - register nfsv2fh_t *fhp; +u_long +nfs_hash(fhp, fhsize) + register nfsfh_t *fhp; + int fhsize; { register u_char *fhpp; register u_long fhsum; - int i; + register int i; fhpp = &fhp->fh_bytes[0]; fhsum = 0; - for (i = 0; i < NFSX_FH; i++) + for (i = 0; i < fhsize; i++) fhsum += *fhpp++; - return (NFSNOHASH(fhsum)); + return (fhsum); } /* @@ -101,9 +100,10 @@ nfs_hash(fhp) * nfsnode structure is returned. */ int -nfs_nget(mntp, fhp, npp) +nfs_nget(mntp, fhp, fhsize, npp) struct mount *mntp; - register nfsv2fh_t *fhp; + register nfsfh_t *fhp; + int fhsize; struct nfsnode **npp; { register struct nfsnode *np; @@ -112,11 +112,11 @@ nfs_nget(mntp, fhp, npp) struct vnode *nvp; int error; - nhpp = nfs_hash(fhp); + nhpp = NFSNOHASH(nfs_hash(fhp, fhsize)); loop: for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) { - if (mntp != NFSTOV(np)->v_mount || - bcmp((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH)) + if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize || + bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) continue; vp = NFSTOV(np); if (vget(vp, 1)) @@ -131,27 +131,26 @@ loop: } vp = nvp; MALLOC(np, struct nfsnode *, sizeof *np, M_NFSNODE, M_WAITOK); + bzero((caddr_t)np, sizeof *np); vp->v_data = np; np->n_vnode = vp; /* * Insert the nfsnode in the hash queue for its new file handle */ - np->n_flag = 0; LIST_INSERT_HEAD(nhpp, np, n_hash); - bcopy((caddr_t)fhp, (caddr_t)&np->n_fh, NFSX_FH); - np->n_attrstamp = 0; - np->n_direofoffset = 0; - np->n_sillyrename = (struct sillyrename *)0; - np->n_size = 0; - np->n_mtime = 0; - np->n_lockf = 0; - if (VFSTONFS(mntp)->nm_flag & NFSMNT_NQNFS) { - np->n_brev = 0; - np->n_lrev = 0; - np->n_expiry = (time_t)0; - np->n_timer.cqe_next = (struct nfsnode *)0; - } + if (fhsize > NFS_SMALLFH) { + MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK); + } else + np->n_fhp = &np->n_fh; + bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize); + np->n_fhsize = fhsize; *npp = np; + + /* + * Lock the new nfsnode. + */ + VOP_LOCK(vp); + return (0); } @@ -168,7 +167,10 @@ nfs_inactive(ap) np = VTONFS(ap->a_vp); if (prtactive && ap->a_vp->v_usecount != 0) vprint("nfs_inactive: pushing active", ap->a_vp); - sp = np->n_sillyrename; + if (ap->a_vp->v_type != VDIR) + sp = np->n_sillyrename; + else + sp = (struct sillyrename *)0; np->n_sillyrename = (struct sillyrename *)0; if (sp) { /* @@ -178,9 +180,7 @@ nfs_inactive(ap) nfs_removeit(sp); crfree(sp->s_cred); vrele(sp->s_dvp); -#ifdef SILLYSEPARATE - free((caddr_t)sp, M_NFSREQ); -#endif + FREE((caddr_t)sp, M_NFSREQ); } np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT | NQNFSEVICTED | NQNFSNONCACHE | NQNFSWRITE); @@ -199,6 +199,7 @@ nfs_reclaim(ap) register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct nfsmount *nmp = VFSTONFS(vp->v_mount); + register struct nfsdmap *dp, *dp2; if (prtactive && vp->v_usecount != 0) vprint("nfs_reclaim: pushing active", vp); @@ -211,6 +212,24 @@ nfs_reclaim(ap) if ((nmp->nm_flag & NFSMNT_NQNFS) && np->n_timer.cqe_next != 0) { CIRCLEQ_REMOVE(&nmp->nm_timerhead, np, n_timer); } + + /* + * Free up any directory cookie structures and + * large file handle structures that might be associated with + * this nfs node. + */ + if (vp->v_type == VDIR) { + dp = np->n_cookies.lh_first; + while (dp) { + dp2 = dp; + dp = dp->ndm_list.le_next; + FREE((caddr_t)dp2, M_NFSDIROFF); + } + } + if (np->n_fhsize > NFS_SMALLFH) { + FREE((caddr_t)np->n_fhp, M_NFSBIGFH); + } + cache_purge(vp); FREE(vp->v_data, M_NFSNODE); vp->v_data = (void *)0; @@ -227,6 +246,7 @@ nfs_lock(ap) } */ *ap; { register struct vnode *vp = ap->a_vp; + struct nfsnode *np = VTONFS(vp); /* * Ugh, another place where interruptible mounts will get hung. @@ -239,6 +259,33 @@ nfs_lock(ap) } if (vp->v_tag == VT_NON) return (ENOENT); + +#if 0 + /* + * Only lock regular files. If a server crashed while we were + * holding a directory lock, we could easily end up sleeping + * until the server rebooted while holding a lock on the root. + * Locks are only needed for protecting critical sections in + * VMIO at the moment. + * New vnodes will have type VNON but they should be locked + * since they may become VREG. This is checked in loadattrcache + * and unwanted locks are released there. + */ + if (vp->v_type == VREG || vp->v_type == VNON) { + while (np->n_flag & NLOCKED) { + np->n_flag |= NWANTED; + (void) tsleep((caddr_t) np, PINOD, "nfslck2", 0); + /* + * If the vnode has transmuted into a VDIR while we + * were asleep, then skip the lock. + */ + if (vp->v_type != VREG && vp->v_type != VNON) + return (0); + } + np->n_flag |= NLOCKED; + } +#endif + return (0); } @@ -251,6 +298,20 @@ nfs_unlock(ap) struct vnode *a_vp; } */ *ap; { +#if 0 + struct vnode* vp = ap->a_vp; + struct nfsnode* np = VTONFS(vp); + + if (vp->v_type == VREG || vp->v_type == VNON) { + if (!(np->n_flag & NLOCKED)) + panic("nfs_unlock: nfsnode not locked"); + np->n_flag &= ~NLOCKED; + if (np->n_flag & NWANTED) { + np->n_flag &= ~NWANTED; + wakeup((caddr_t) np); + } + } +#endif return (0); } @@ -264,8 +325,7 @@ nfs_islocked(ap) struct vnode *a_vp; } */ *ap; { - - return (0); + return VTONFS(ap->a_vp)->n_flag & NLOCKED ? 1 : 0; } /* diff --git a/sys/nfsclient/nfs_socket.c b/sys/nfsclient/nfs_socket.c index 20253ef..8d15b24 100644 --- a/sys/nfsclient/nfs_socket.c +++ b/sys/nfsclient/nfs_socket.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/nfsclient/nfs_subs.c b/sys/nfsclient/nfs_subs.c index 1653c8b..350ba46 100644 --- a/sys/nfsclient/nfs_subs.c +++ b/sys/nfsclient/nfs_subs.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/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c index 2daa891..2c4070b 100644 --- a/sys/nfsclient/nfs_vfsops.c +++ b/sys/nfsclient/nfs_vfsops.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfs_vfsops.c 8.3 (Berkeley) 1/4/94 - * $Id: nfs_vfsops.c,v 1.14.2.1 1995/06/02 11:13:15 davidg Exp $ + * $Id: nfs_vfsops.c,v 1.15 1995/06/11 19:31:46 rgrimes Exp $ */ #include <sys/param.h> @@ -59,10 +59,10 @@ #include <netinet/in.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfsnode.h> -#include <nfs/nfsmount.h> #include <nfs/nfs.h> +#include <nfs/nfsmount.h> #include <nfs/xdr_subs.h> #include <nfs/nfsm_subs.h> #include <nfs/nfsdiskless.h> @@ -71,11 +71,15 @@ struct nfsstats nfsstats; static int nfs_sysctl(int *, u_int, void *, size_t *, void *, size_t, struct proc *); +extern int nfs_ticks; /* * nfs vfs operations. */ struct vfsops nfs_vfsops = { +#ifdef __NetBSD__ + MOUNT_NFS, +#endif nfs_mount, nfs_start, nfs_unmount, @@ -87,9 +91,13 @@ struct vfsops nfs_vfsops = { nfs_fhtovp, nfs_vptofh, nfs_init, +#ifdef __FreeBSD__ nfs_sysctl +#endif }; +#ifdef __FreeBSD__ VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, VFCF_NETWORK); +#endif /* * This structure must be filled in by a primary bootstrap or bootstrap @@ -99,16 +107,11 @@ VFS_SET(nfs_vfsops, nfs, MOUNT_NFS, VFCF_NETWORK); struct nfs_diskless nfs_diskless = { 0 }; int nfs_diskless_valid = 0; -extern u_long nfs_procids[NFS_NPROCS]; -extern u_long nfs_prog, nfs_vers; void nfs_disconnect __P((struct nfsmount *)); void nfsargs_ntoh __P((struct nfs_args *)); static struct mount *nfs_mountdiskless __P((char *, char *, int, struct sockaddr_in *, struct nfs_args *, register struct vnode **)); -#define TRUE 1 -#define FALSE 0 - /* * nfs statfs call */ @@ -119,40 +122,65 @@ nfs_statfs(mp, sbp, p) struct proc *p; { register struct vnode *vp; - register struct nfsv2_statfs *sfp; + register struct nfs_statfs *sfp; register caddr_t cp; - register long t1; + register u_long *tl; + register long t1, t2; caddr_t bpos, dpos, cp2; - int error = 0, isnq; + struct nfsmount *nmp = VFSTONFS(mp); + int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - struct nfsmount *nmp; struct ucred *cred; struct nfsnode *np; + u_quad_t tquad; - nmp = VFSTONFS(mp); - isnq = (nmp->nm_flag & NFSMNT_NQNFS); - error = nfs_nget(mp, &nmp->nm_fh, &np); +#ifndef nolint + sfp = (struct nfs_statfs *)0; +#endif + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); if (error) return (error); vp = NFSTOV(np); - nfsstats.rpccnt[NFSPROC_STATFS]++; cred = crget(); cred->cr_ngroups = 1; - nfsm_reqhead(vp, NFSPROC_STATFS, NFSX_FH); - nfsm_fhtom(vp); - nfsm_request(vp, NFSPROC_STATFS, p, cred); - nfsm_dissect(sfp, struct nfsv2_statfs *, NFSX_STATFS(isnq)); + if (v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0) + (void)nfs_fsinfo(nmp, vp, cred, p); + nfsstats.rpccnt[NFSPROC_FSSTAT]++; + nfsm_reqhead(vp, NFSPROC_FSSTAT, NFSX_FH(v3)); + nfsm_fhtom(vp, v3); + nfsm_request(vp, NFSPROC_FSSTAT, p, cred); + if (v3) + nfsm_postop_attr(vp, retattr); + if (!error) + nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); +#ifdef __NetBSD__ +#ifdef COMPAT_09 + sbp->f_type = 2; +#else + sbp->f_type = 0; +#endif +#else sbp->f_type = MOUNT_NFS; +#endif sbp->f_flags = nmp->nm_flag; - sbp->f_iosize = NFS_MAXDGRAMDATA; - sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); - sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); - sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); - sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); - if (isnq) { - sbp->f_files = fxdr_unsigned(long, sfp->sf_files); - sbp->f_ffree = fxdr_unsigned(long, sfp->sf_ffree); + sbp->f_iosize = min(nmp->nm_rsize, nmp->nm_wsize); + if (v3) { + sbp->f_bsize = NFS_FABLKSIZE; + fxdr_hyper(&sfp->sf_tbytes, &tquad); + sbp->f_blocks = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); + fxdr_hyper(&sfp->sf_fbytes, &tquad); + sbp->f_bfree = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); + fxdr_hyper(&sfp->sf_abytes, &tquad); + sbp->f_bavail = (long)(tquad / ((u_quad_t)NFS_FABLKSIZE)); + sbp->f_files = (fxdr_unsigned(long, sfp->sf_tfiles.nfsuquad[1]) + & 0x7fffffff); + sbp->f_ffree = (fxdr_unsigned(long, sfp->sf_ffiles.nfsuquad[1]) + & 0x7fffffff); } else { + sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize); + sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks); + sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree); + sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail); sbp->f_files = 0; sbp->f_ffree = 0; } @@ -161,12 +189,72 @@ nfs_statfs(mp, sbp, p) bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN); } nfsm_reqdone; - vrele(vp); + vput(vp); crfree(cred); return (error); } /* + * nfs version 3 fsinfo rpc call + */ +int +nfs_fsinfo(nmp, vp, cred, p) + register struct nfsmount *nmp; + register struct vnode *vp; + struct ucred *cred; + struct proc *p; +{ + register struct nfsv3_fsinfo *fsp; + register caddr_t cp; + register long t1, t2; + register u_long *tl, pref, max; + caddr_t bpos, dpos, cp2; + int error = 0, retattr; + struct mbuf *mreq, *mrep, *md, *mb, *mb2; + + nfsstats.rpccnt[NFSPROC_FSINFO]++; + nfsm_reqhead(vp, NFSPROC_FSINFO, NFSX_FH(1)); + nfsm_fhtom(vp, 1); + nfsm_request(vp, NFSPROC_FSINFO, p, cred); + nfsm_postop_attr(vp, retattr); + if (!error) { + nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO); + pref = fxdr_unsigned(u_long, fsp->fs_wtpref); + if (pref < nmp->nm_wsize) + nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) & + ~(NFS_FABLKSIZE - 1); + max = fxdr_unsigned(u_long, fsp->fs_wtmax); + if (max < nmp->nm_wsize) { + nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1); + if (nmp->nm_wsize == 0) + nmp->nm_wsize = max; + } + pref = fxdr_unsigned(u_long, fsp->fs_rtpref); + if (pref < nmp->nm_rsize) + nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) & + ~(NFS_FABLKSIZE - 1); + max = fxdr_unsigned(u_long, fsp->fs_rtmax); + if (max < nmp->nm_rsize) { + nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1); + if (nmp->nm_rsize == 0) + nmp->nm_rsize = max; + } + pref = fxdr_unsigned(u_long, fsp->fs_dtpref); + if (pref < nmp->nm_readdirsize) + nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) & + ~(NFS_DIRBLKSIZ - 1); + if (max < nmp->nm_readdirsize) { + nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1); + if (nmp->nm_readdirsize == 0) + nmp->nm_readdirsize = max; + } + nmp->nm_flag |= NFSMNT_GOTFSINFO; + } + nfsm_reqdone; + return (error); +} + +/* * Mount a remote root fs via. nfs. This depends on the info in the * nfs_diskless structure that has been filled in properly by some primary * bootstrap. @@ -268,7 +356,12 @@ nfs_mountroot() * Create a fake mount point just for the swap vnode so that the * swap file can be on a different server from the rootfs. */ - nd->swap_args.fh = (nfsv2fh_t *)nd->swap_fh; + nd->swap_args.fh = nd->swap_fh; + /* + * If using nfsv3_diskless, replace NFSX_V2FH with + * nd->swap_fhsize. + */ + nd->swap_args.fhsize = NFSX_V2FH; l = ntohl(nd->swap_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff, @@ -293,8 +386,12 @@ nfs_mountroot() /* * Create the rootfs mount point. */ - nd->root_args.fh = (nfsv2fh_t *)nd->root_fh; - l = ntohl(nd->root_saddr.sin_addr.s_addr); + nd->root_args.fh = nd->root_fh; + /* + * If using nfsv3_diskless, replace NFSX_V2FH with nd->root_fhsize. + */ + nd->root_args.fhsize = NFSX_V2FH; + l = ntohl(nd->swap_saddr.sin_addr.s_addr); sprintf(buf,"%ld.%ld.%ld.%ld:%s", (l >> 24) & 0xff, (l >> 16) & 0xff, (l >> 8) & 0xff, (l >> 0) & 0xff,nd->root_hostnam); @@ -304,7 +401,11 @@ nfs_mountroot() if (vfs_lock(mp)) panic("nfs_mountroot: vfs_lock"); +#if NetBSD >= 1994101 + CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list); +#else TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); +#endif mp->mnt_flag |= MNT_ROOTFS; mp->mnt_vnodecovered = NULLVP; vfs_unlock(mp); @@ -386,12 +487,12 @@ nfs_mount(mp, path, data, ndp, p) struct vnode *vp; char pth[MNAMELEN], hst[MNAMELEN]; u_int len; - nfsv2fh_t nfh; + u_char nfh[NFSX_V3FHMAX]; error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args)); if (error) return (error); - error = copyin((caddr_t)args.fh, (caddr_t)&nfh, sizeof (nfsv2fh_t)); + error = copyin((caddr_t)args.fh, (caddr_t)nfh, args.fhsize); if (error) return (error); error = copyinstr(path, pth, MNAMELEN-1, &len); @@ -406,7 +507,7 @@ nfs_mount(mp, path, data, ndp, p) error = sockargs(&nam, (caddr_t)args.addr, args.addrlen, MT_SONAME); if (error) return (error); - args.fh = &nfh; + args.fh = nfh; error = mountnfs(&args, mp, nam, pth, hst, &vp); return (error); } @@ -424,7 +525,7 @@ mountnfs(argp, mp, nam, pth, hst, vpp) { register struct nfsmount *nmp; struct nfsnode *np; - int error; + int error, maxio; if (mp->mnt_flag & MNT_UPDATE) { nmp = VFSTONFS(mp); @@ -435,16 +536,12 @@ mountnfs(argp, mp, nam, pth, hst, vpp) MALLOC(nmp, struct nfsmount *, sizeof (struct nfsmount), M_NFSMNT, M_WAITOK); bzero((caddr_t)nmp, sizeof (struct nfsmount)); + TAILQ_INIT(&nmp->nm_uidlruhead); mp->mnt_data = (qaddr_t)nmp; } getnewfsid(mp, MOUNT_NFS); nmp->nm_mountp = mp; nmp->nm_flag = argp->flags; - if ((nmp->nm_flag & (NFSMNT_NQNFS | NFSMNT_MYWRITE)) == - (NFSMNT_NQNFS | NFSMNT_MYWRITE)) { - error = EPERM; - goto bad; - } if (nmp->nm_flag & NFSMNT_NQNFS) /* * We have to set mnt_maxsymlink to a non-zero value so @@ -457,14 +554,24 @@ mountnfs(argp, mp, nam, pth, hst, vpp) nmp->nm_retry = NFS_RETRANS; nmp->nm_wsize = NFS_WSIZE; nmp->nm_rsize = NFS_RSIZE; + nmp->nm_readdirsize = NFS_READDIRSIZE; nmp->nm_numgrps = NFS_MAXGRPS; nmp->nm_readahead = NFS_DEFRAHEAD; nmp->nm_leaseterm = NQ_DEFLEASE; nmp->nm_deadthresh = NQ_DEADTHRESH; CIRCLEQ_INIT(&nmp->nm_timerhead); nmp->nm_inprog = NULLVP; - bcopy((caddr_t)argp->fh, (caddr_t)&nmp->nm_fh, sizeof(nfsv2fh_t)); + nmp->nm_fhsize = argp->fhsize; + bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); +#ifdef __NetBSD__ +#ifdef COMPAT_09 + mp->mnt_stat.f_type = 2; +#else + mp->mnt_stat.f_type = 0; +#endif +#else mp->mnt_stat.f_type = MOUNT_NFS; +#endif bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN); bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN); nmp->nm_nam = nam; @@ -483,29 +590,48 @@ mountnfs(argp, mp, nam, pth, hst, vpp) nmp->nm_retry = NFS_MAXREXMIT; } + if (argp->flags & NFSMNT_NFSV3) { + if (argp->sotype == SOCK_DGRAM) + maxio = NFS_MAXDGRAMDATA; + else + maxio = NFS_MAXDATA; + } else + maxio = NFS_V2MAXDATA; + if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) { nmp->nm_wsize = argp->wsize; /* Round down to multiple of blocksize */ - nmp->nm_wsize &= ~0x1ff; + nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1); if (nmp->nm_wsize <= 0) - nmp->nm_wsize = 512; - else if (nmp->nm_wsize > NFS_MAXDATA) - nmp->nm_wsize = NFS_MAXDATA; + nmp->nm_wsize = NFS_FABLKSIZE; } + if (nmp->nm_wsize > maxio) + nmp->nm_wsize = maxio; if (nmp->nm_wsize > MAXBSIZE) nmp->nm_wsize = MAXBSIZE; if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) { nmp->nm_rsize = argp->rsize; /* Round down to multiple of blocksize */ - nmp->nm_rsize &= ~0x1ff; + nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1); if (nmp->nm_rsize <= 0) - nmp->nm_rsize = 512; - else if (nmp->nm_rsize > NFS_MAXDATA) - nmp->nm_rsize = NFS_MAXDATA; + nmp->nm_rsize = NFS_FABLKSIZE; } + if (nmp->nm_rsize > maxio) + nmp->nm_rsize = maxio; if (nmp->nm_rsize > MAXBSIZE) nmp->nm_rsize = MAXBSIZE; + + if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) { + nmp->nm_readdirsize = argp->readdirsize; + /* Round down to multiple of blocksize */ + nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1); + if (nmp->nm_readdirsize < NFS_DIRBLKSIZ) + nmp->nm_readdirsize = NFS_DIRBLKSIZ; + } + if (nmp->nm_readdirsize > maxio) + nmp->nm_readdirsize = maxio; + if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 && argp->maxgrouplist <= NFS_MAXGRPS) nmp->nm_numgrps = argp->maxgrouplist; @@ -536,7 +662,7 @@ mountnfs(argp, mp, nam, pth, hst, vpp) * stuck on a dead server and we are holding a lock on the mount * point. */ - mp->mnt_stat.f_iosize = NFS_MAXDGRAMDATA; + mp->mnt_stat.f_iosize = min(nmp->nm_rsize, nmp->nm_wsize); /* * A reference count is needed on the nfsnode representing the * remote root. If this object is not persistent, then backward @@ -545,11 +671,16 @@ mountnfs(argp, mp, nam, pth, hst, vpp) * this problem, because one can identify root inodes by their * number == ROOTINO (2). */ - error = nfs_nget(mp, &nmp->nm_fh, &np); + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); if (error) goto bad; *vpp = NFSTOV(np); + /* + * Lose the lock but keep the ref. + */ + VOP_UNLOCK(*vpp); + return (0); bad: nfs_disconnect(nmp); @@ -592,7 +723,7 @@ nfs_unmount(mp, mntflags, p) * the remote root. See comment in mountnfs(). The VFS unmount() * has done vput on this vnode, otherwise we would get deadlock! */ - error = nfs_nget(mp, &nmp->nm_fh, &np); + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); if (error) return(error); vp = NFSTOV(np); @@ -622,9 +753,9 @@ nfs_unmount(mp, mntflags, p) nmp->nm_flag |= NFSMNT_DISMNT; /* - * There are two reference counts to get rid of here. + * There are two reference counts and one lock to get rid of here. */ - vrele(vp); + vput(vp); vrele(vp); vgone(vp); nfs_disconnect(nmp); @@ -649,10 +780,11 @@ nfs_root(mp, vpp) int error; nmp = VFSTONFS(mp); - error = nfs_nget(mp, &nmp->nm_fh, &np); + error = nfs_nget(mp, (nfsfh_t *)nmp->nm_fh, nmp->nm_fhsize, &np); if (error) return (error); vp = NFSTOV(np); + VOP_UNLOCK(vp); vp->v_type = VDIR; vp->v_flag = VROOT; *vpp = vp; diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c index 46484ea..769b0bb 100644 --- a/sys/nfsclient/nfs_vnops.c +++ b/sys/nfsclient/nfs_vnops.c @@ -34,17 +34,18 @@ * SUCH DAMAGE. * * @(#)nfs_vnops.c 8.5 (Berkeley) 2/13/94 - * $Id: nfs_vnops.c,v 1.14 1995/03/23 09:43:40 davidg Exp $ + * $Id: nfs_vnops.c,v 1.15 1995/05/30 08:12:49 rgrimes Exp $ */ /* - * vnode op calls for sun nfs version 2 + * vnode op calls for Sun NFS version 2 and 3 */ #include <sys/param.h> -#include <sys/proc.h> #include <sys/kernel.h> #include <sys/systm.h> +#include <sys/resourcevar.h> +#include <sys/proc.h> #include <sys/mount.h> #include <sys/buf.h> #include <sys/malloc.h> @@ -53,7 +54,9 @@ #include <sys/namei.h> #include <sys/vnode.h> #include <sys/dirent.h> +#include <sys/fcntl.h> #include <sys/lockf.h> +#include <ufs/ufs/dir.h> #include <vm/vm.h> @@ -61,7 +64,7 @@ #include <miscfs/fifofs/fifo.h> #include <nfs/rpcv2.h> -#include <nfs/nfsv2.h> +#include <nfs/nfsproto.h> #include <nfs/nfs.h> #include <nfs/nfsnode.h> #include <nfs/nfsmount.h> @@ -69,11 +72,24 @@ #include <nfs/nfsm_subs.h> #include <nfs/nqnfs.h> +#include <net/if.h> +#include <netinet/in.h> +#include <netinet/in_var.h> + /* Defs */ #define TRUE 1 #define FALSE 0 /* + * Ifdef for FreeBSD-current merged buffer cache. It is unfortunate that these + * calls are not in getblk() and brelse() so that they would not be necessary + * here. + */ +#ifndef B_VMIO +#define vfs_busy_pages(bp, f) +#endif + +/* * Global vfs data structures for nfs */ int (**nfsv2_vnodeop_p)(); @@ -89,8 +105,14 @@ struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfs_read }, /* read */ { &vop_write_desc, nfs_write }, /* write */ +#ifdef HAS_VOPLEASE + { &vop_lease_desc, nfs_lease_check }, /* lease */ +#endif { &vop_ioctl_desc, nfs_ioctl }, /* ioctl */ { &vop_select_desc, nfs_select }, /* select */ +#ifdef HAS_VOPREVOKE + { &vop_revoke_desc, nfs_revoke }, /* revoke */ +#endif { &vop_mmap_desc, nfs_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, nfs_seek }, /* seek */ @@ -119,12 +141,14 @@ struct vnodeopv_entry_desc nfsv2_vnodeop_entries[] = { { &vop_vfree_desc, nfs_vfree }, /* vfree */ { &vop_truncate_desc, nfs_truncate }, /* truncate */ { &vop_update_desc, nfs_update }, /* update */ - { &vop_bwrite_desc, vn_bwrite }, + { &vop_bwrite_desc, nfs_bwrite }, { (struct vnodeop_desc*)NULL, (int(*)())NULL } }; struct vnodeopv_desc nfsv2_vnodeop_opv_desc = { &nfsv2_vnodeop_p, nfsv2_vnodeop_entries }; +#ifdef __FreeBSD__ VNODEOP_SET(nfsv2_vnodeop_opv_desc); +#endif /* * Special device vnode ops @@ -142,8 +166,14 @@ struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsspec_read }, /* read */ { &vop_write_desc, nfsspec_write }, /* write */ +#ifdef HAS_VOPLEASE + { &vop_lease_desc, spec_lease_check }, /* lease */ +#endif { &vop_ioctl_desc, spec_ioctl }, /* ioctl */ { &vop_select_desc, spec_select }, /* select */ +#ifdef HAS_VOPREVOKE + { &vop_revoke_desc, spec_revoke }, /* revoke */ +#endif { &vop_mmap_desc, spec_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, spec_seek }, /* seek */ @@ -177,7 +207,9 @@ struct vnodeopv_entry_desc spec_nfsv2nodeop_entries[] = { }; struct vnodeopv_desc spec_nfsv2nodeop_opv_desc = { &spec_nfsv2nodeop_p, spec_nfsv2nodeop_entries }; +#ifdef __FreeBSD__ VNODEOP_SET(spec_nfsv2nodeop_opv_desc); +#endif int (**fifo_nfsv2nodeop_p)(); struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { @@ -192,8 +224,14 @@ struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { { &vop_setattr_desc, nfs_setattr }, /* setattr */ { &vop_read_desc, nfsfifo_read }, /* read */ { &vop_write_desc, nfsfifo_write }, /* write */ +#ifdef HAS_VOPLEASE + { &vop_lease_desc, fifo_lease_check }, /* lease */ +#endif { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */ { &vop_select_desc, fifo_select }, /* select */ +#ifdef HAS_VOPREVOKE + { &vop_revoke_desc, fifo_revoke }, /* revoke */ +#endif { &vop_mmap_desc, fifo_mmap }, /* mmap */ { &vop_fsync_desc, nfs_fsync }, /* fsync */ { &vop_seek_desc, fifo_seek }, /* seek */ @@ -227,15 +265,21 @@ struct vnodeopv_entry_desc fifo_nfsv2nodeop_entries[] = { }; struct vnodeopv_desc fifo_nfsv2nodeop_opv_desc = { &fifo_nfsv2nodeop_p, fifo_nfsv2nodeop_entries }; +#ifdef __FreeBSD__ VNODEOP_SET(fifo_nfsv2nodeop_opv_desc); +#endif void nqnfs_clientlease(); +int nfs_commit(); +int nfs_removerpc(); +int nfs_renamerpc(); /* * Global variables */ -extern u_long nfs_procids[NFS_NPROCS]; -extern u_long nfs_prog, nfs_vers, nfs_true, nfs_false; +extern u_long nfs_true, nfs_false; +extern struct nfsstats nfsstats; +extern nfstype nfsv3_type[9]; struct proc *nfs_iodwant[NFS_MAXASYNCDAEMON]; int nfs_numasync = 0; #define DIRHDSIZ (sizeof (struct dirent) - (MAXNAMLEN + 1)) @@ -261,9 +305,9 @@ nfs_null(vp, cred, procp) /* * nfs access vnode op. - * For nfs, just return ok. File accesses may fail later. - * For nqnfs, use the access rpc to check accessibility. If file modes are - * changed on the server, accesses might still fail later. + * For nfs version 2, just return ok. File accesses may fail later. + * For nfs version 3, use the access rpc to check accessibility. If file modes + * are changed on the server, accesses might still fail later. */ int nfs_access(ap) @@ -277,36 +321,56 @@ nfs_access(ap) register struct vnode *vp = ap->a_vp; register u_long *tl; register caddr_t cp; - caddr_t bpos, dpos; - int error = 0; + register int t1, t2; + caddr_t bpos, dpos, cp2; + int error = 0, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + u_long mode, rmode; + int v3 = NFS_ISV3(vp); /* - * For nqnfs, do an access rpc, otherwise you are stuck emulating + * For nfs v3, do an access rpc, otherwise you are stuck emulating * ufs_access() locally using the vattr. This may not be correct, * since the server may apply other access criteria such as * client uid-->server uid mapping that we do not know about, but * this is better than just returning anything that is lying about * in the cache. */ - if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { - nfsstats.rpccnt[NQNFSPROC_ACCESS]++; - nfsm_reqhead(vp, NQNFSPROC_ACCESS, NFSX_FH + 3 * NFSX_UNSIGNED); - nfsm_fhtom(vp); - nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + if (v3) { + nfsstats.rpccnt[NFSPROC_ACCESS]++; + nfsm_reqhead(vp, NFSPROC_ACCESS, NFSX_FH(v3) + NFSX_UNSIGNED); + nfsm_fhtom(vp, v3); + nfsm_build(tl, u_long *, NFSX_UNSIGNED); if (ap->a_mode & VREAD) - *tl++ = nfs_true; - else - *tl++ = nfs_false; - if (ap->a_mode & VWRITE) - *tl++ = nfs_true; - else - *tl++ = nfs_false; - if (ap->a_mode & VEXEC) - *tl = nfs_true; + mode = NFSV3ACCESS_READ; else - *tl = nfs_false; - nfsm_request(vp, NQNFSPROC_ACCESS, ap->a_p, ap->a_cred); + mode = 0; + if (vp->v_type == VDIR) { + if (ap->a_mode & VWRITE) + mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | + NFSV3ACCESS_DELETE); + if (ap->a_mode & VEXEC) + mode |= NFSV3ACCESS_LOOKUP; + } else { + if (ap->a_mode & VWRITE) + mode |= (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); + if (ap->a_mode & VEXEC) + mode |= NFSV3ACCESS_EXECUTE; + } + *tl = txdr_unsigned(mode); + nfsm_request(vp, NFSPROC_ACCESS, ap->a_p, ap->a_cred); + nfsm_postop_attr(vp, attrflag); + if (!error) { + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + rmode = fxdr_unsigned(u_long, *tl); + /* + * The NFS V3 spec does not clarify whether or not + * the returned access bits can be a superset of + * the ones requested, so... + */ + if ((rmode & mode) != mode) + error = EACCES; + } nfsm_reqdone; return (error); } else @@ -337,14 +401,17 @@ nfs_open(ap) int error; if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) +{ printf("open eacces vtyp=%d\n",vp->v_type); return (EACCES); +} /* * Get a valid lease. If cached data is stale, flush it. */ if (nmp->nm_flag & NFSMNT_NQNFS) { - if (NQNFS_CKINVALID(vp, np, NQL_READ)) { + if (NQNFS_CKINVALID(vp, np, ND_READ)) { do { - error = nqnfs_getlease(vp, NQL_READ, ap->a_cred, ap->a_p); + error = nqnfs_getlease(vp, ND_READ, ap->a_cred, + ap->a_p); } while (error == NQNFS_EXPIRED); if (error) return (error); @@ -353,6 +420,7 @@ nfs_open(ap) if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); + (void) vnode_pager_uncache(vp); np->n_brev = np->n_lrev; } } @@ -361,8 +429,10 @@ nfs_open(ap) if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); + /* (void) vnode_pager_uncache(vp); */ np->n_attrstamp = 0; - np->n_direofoffset = 0; + if (vp->v_type == VDIR) + np->n_direofoffset = 0; error = VOP_GETATTR(vp, &vattr, ap->a_cred, ap->a_p); if (error) return (error); @@ -372,10 +442,12 @@ nfs_open(ap) if (error) return (error); if (np->n_mtime != vattr.va_mtime.ts_sec) { - np->n_direofoffset = 0; + if (vp->v_type == VDIR) + np->n_direofoffset = 0; if ((error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1)) == EINTR) return (error); + /* (void) vnode_pager_uncache(vp); */ np->n_mtime = vattr.va_mtime.ts_sec; } } @@ -387,7 +459,33 @@ nfs_open(ap) /* * nfs close vnode op - * For reg files, invalidate any buffer cache entries. + * What an NFS client should do upon close after writing is a debatable issue. + * Most NFS clients push delayed writes to the server upon close, basically for + * two reasons: + * 1 - So that any write errors may be reported back to the client process + * doing the close system call. By far the two most likely errors are + * NFSERR_NOSPC and NFSERR_DQUOT to indicate space allocation failure. + * 2 - To put a worst case upper bound on cache inconsistency between + * multiple clients for the file. + * There is also a consistency problem for Version 2 of the protocol w.r.t. + * not being able to tell if other clients are writing a file concurrently, + * since there is no way of knowing if the changed modify time in the reply + * is only due to the write for this client. + * (NFS Version 3 provides weak cache consistency data in the reply that + * should be sufficient to detect and handle this case.) + * + * The current code does the following: + * for NFS Version 2 - play it safe and flush/invalidate all dirty buffers + * for NFS Version 3 - flush dirty buffers to the server but don't invalidate + * or commit them (this satisfies 1 and 2 except for the + * case where the server crashes after this close but + * before the commit RPC, which is felt to be "good + * enough". Changing the last argument to nfs_flush() to + * a 1 would force a commit operation, if it is felt a + * commit is necessary now. + * for NQNFS - do nothing now, since 2 is dealt with via leases and + * 1 should be dealt with via an fsync() system call for + * cases where write errors are important. */ /* ARGSUSED */ int @@ -407,7 +505,10 @@ nfs_close(ap) if (vp->v_type == VREG) { if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) == 0 && (np->n_flag & NMODIFIED)) { - error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); + if (NFS_ISV3(vp)) + error = nfs_flush(vp, ap->a_cred, MNT_WAIT, ap->a_p, 0); + else + error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); np->n_attrstamp = 0; } if (np->n_flag & NWRITEERR) { @@ -433,9 +534,12 @@ nfs_getattr(ap) register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register caddr_t cp; + register u_long *tl; + register int t1, t2; caddr_t bpos, dpos; int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int v3 = NFS_ISV3(vp); /* * Update local times for special files. @@ -448,10 +552,11 @@ nfs_getattr(ap) if (nfs_getattrcache(vp, ap->a_vap) == 0) return (0); nfsstats.rpccnt[NFSPROC_GETATTR]++; - nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH); - nfsm_fhtom(vp); + nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3)); + nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_GETATTR, ap->a_p, ap->a_cred); - nfsm_loadattr(vp, ap->a_vap); + if (!error) + nfsm_loadattr(vp, ap->a_vap); nfsm_reqdone; return (error); } @@ -469,92 +574,166 @@ nfs_setattr(ap) struct proc *a_p; } */ *ap; { - register struct nfsv2_sattr *sp; - register caddr_t cp; - register long t1; - caddr_t bpos, dpos, cp2; - u_long *tl; - int error = 0, isnq; - struct mbuf *mreq, *mrep, *md, *mb, *mb2; register struct vnode *vp = ap->a_vp; register struct nfsnode *np = VTONFS(vp); register struct vattr *vap = ap->a_vap; - u_quad_t frev, tsize = 0; + int error = 0; + u_quad_t tsize; +#ifndef nolint + tsize = (u_quad_t)0; +#endif if (vap->va_size != VNOVAL) { - switch (vp->v_type) { - case VDIR: - return (EISDIR); - case VCHR: - case VBLK: + switch (vp->v_type) { + case VDIR: + return (EISDIR); + case VCHR: + case VBLK: if (vap->va_mtime.ts_sec == VNOVAL && vap->va_atime.ts_sec == VNOVAL && vap->va_mode == (u_short)VNOVAL && - vap->va_uid == VNOVAL && - vap->va_gid == VNOVAL) + vap->va_uid == (uid_t)VNOVAL && + vap->va_gid == (gid_t)VNOVAL) return (0); - vap->va_size = VNOVAL; - break; - default: - if (np->n_flag & NMODIFIED) { - error = nfs_vinvalbuf(vp, - vap->va_size ? V_SAVE : 0, - ap->a_cred, ap->a_p, 1); - if (error) - return (error); - } - tsize = np->n_size; - np->n_size = np->n_vattr.va_size = vap->va_size; - vnode_pager_setsize(vp, (u_long)np->n_size); - } - } else if ((vap->va_mtime.ts_sec != VNOVAL || - vap->va_atime.ts_sec != VNOVAL) && (np->n_flag & NMODIFIED)) { - error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 1); - if (error == EINTR) - return (error); + vap->va_size = VNOVAL; + break; + default: + if (np->n_flag & NMODIFIED) { + if (vap->va_size == 0) + error = nfs_vinvalbuf(vp, 0, + ap->a_cred, ap->a_p, 1); + else + error = nfs_vinvalbuf(vp, V_SAVE, + ap->a_cred, ap->a_p, 1); + if (error) + return (error); + } + tsize = np->n_size; + np->n_size = np->n_vattr.va_size = vap->va_size; + vnode_pager_setsize(vp, (u_long)np->n_size); + }; + } else if ((vap->va_mtime.ts_sec != VNOVAL || + vap->va_atime.ts_sec != VNOVAL) && (np->n_flag & NMODIFIED) && + vp->v_type == VREG && + (error = nfs_vinvalbuf(vp, V_SAVE, ap->a_cred, + ap->a_p, 1)) == EINTR) + return (error); + error = nfs_setattrrpc(vp, vap, ap->a_cred, ap->a_p); + if (error) { + np->n_size = np->n_vattr.va_size = tsize; + vnode_pager_setsize(vp, (u_long)np->n_size); } + return (error); +} + +/* + * Do an nfs setattr rpc. + */ +int +nfs_setattrrpc(vp, vap, cred, procp) + register struct vnode *vp; + register struct vattr *vap; + struct ucred *cred; + struct proc *procp; +{ + register struct nfsv2_sattr *sp; + register caddr_t cp; + register long t1, t2; + caddr_t bpos, dpos, cp2; + u_long *tl; + int error = 0, wccflag = NFSV3_WCCRATTR; + struct mbuf *mreq, *mrep, *md, *mb, *mb2; + u_quad_t frev; + int v3 = NFS_ISV3(vp); + nfsstats.rpccnt[NFSPROC_SETATTR]++; - isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); - nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH+NFSX_SATTR(isnq)); - nfsm_fhtom(vp); - nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); - if (vap->va_mode == (u_short)-1) - sp->sa_mode = VNOVAL; - else - sp->sa_mode = vtonfs_mode(vp->v_type, vap->va_mode); - if (vap->va_uid == (uid_t)-1) - sp->sa_uid = VNOVAL; - else - sp->sa_uid = txdr_unsigned(vap->va_uid); - if (vap->va_gid == (gid_t)-1) - sp->sa_gid = VNOVAL; - else - sp->sa_gid = txdr_unsigned(vap->va_gid); - if (isnq) { - txdr_hyper(&vap->va_size, &sp->sa_nqsize); - txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); - txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); - sp->sa_nqflags = txdr_unsigned(vap->va_flags); - sp->sa_nqrdev = VNOVAL; + nfsm_reqhead(vp, NFSPROC_SETATTR, NFSX_FH(v3) + NFSX_SATTR(v3)); + nfsm_fhtom(vp, v3); + if (v3) { + if (vap->va_mode != (u_short)VNOVAL) { + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + *tl++ = nfs_true; + *tl = txdr_unsigned(vap->va_mode); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } + if (vap->va_uid != (uid_t)VNOVAL) { + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + *tl++ = nfs_true; + *tl = txdr_unsigned(vap->va_uid); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } + if (vap->va_gid != (gid_t)VNOVAL) { + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + *tl++ = nfs_true; + *tl = txdr_unsigned(vap->va_gid); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } + if (vap->va_size != VNOVAL) { + nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + *tl++ = nfs_true; + txdr_hyper(&vap->va_size, tl); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; + } + if (vap->va_atime.ts_sec != VNOVAL) { + if (vap->va_atime.ts_sec != time.tv_sec) { + nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); + txdr_nfsv3time(&vap->va_atime, tl); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); + } + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); + } + if (vap->va_mtime.ts_sec != VNOVAL) { + if (vap->va_mtime.ts_sec != time.tv_sec) { + nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT); + txdr_nfsv3time(&vap->va_atime, tl); + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV3SATTRTIME_TOSERVER); + } + } else { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV3SATTRTIME_DONTCHANGE); + } + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + *tl = nfs_false; } else { - sp->sa_nfssize = txdr_unsigned(vap->va_size); - txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); - txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); - } - nfsm_request(vp, NFSPROC_SETATTR, ap->a_p, ap->a_cred); - nfsm_loadattr(vp, (struct vattr *)0); - if ((VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKCACHABLE(vp, NQL_WRITE)) { - nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); - fxdr_hyper(tl, &frev); - if (frev > np->n_brev) - np->n_brev = frev; + nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + if (vap->va_mode == (u_short)VNOVAL) + sp->sa_mode = VNOVAL; + else + sp->sa_mode = vtonfsv2_mode(vp->v_type, vap->va_mode); + if (vap->va_uid == (uid_t)VNOVAL) + sp->sa_uid = VNOVAL; + else + sp->sa_uid = txdr_unsigned(vap->va_uid); + if (vap->va_gid == (gid_t)VNOVAL) + sp->sa_gid = VNOVAL; + else + sp->sa_gid = txdr_unsigned(vap->va_gid); + sp->sa_size = txdr_unsigned(vap->va_size); + txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); + txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } + nfsm_request(vp, NFSPROC_SETATTR, procp, cred); + if (v3) { + nfsm_wcc_data(vp, wccflag); + } else + nfsm_loadattr(vp, (struct vattr *)0); nfsm_reqdone; - if (error) { - np->n_size = np->n_vattr.va_size = tsize; - vnode_pager_setsize(vp, (u_long)np->n_size); - } return (error); } @@ -576,23 +755,20 @@ nfs_lookup(ap) register struct vnode *dvp = ap->a_dvp; register struct vnode **vpp = ap->a_vpp; register int flags = cnp->cn_flags; - register struct vnode *vdp; + register struct vnode *newvp; register u_long *tl; register caddr_t cp; register long t1, t2; struct nfsmount *nmp; caddr_t bpos, dpos, cp2; - time_t reqtime = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - struct vnode *newvp; long len; - nfsv2fh_t *fhp; + nfsfh_t *fhp; struct nfsnode *np; - int lockparent, wantparent, error = 0; - int nqlflag = 0, cachable = 0; - u_quad_t frev; + int lockparent, wantparent, error = 0, attrflag, fhsize; + int v3 = NFS_ISV3(dvp); - *vpp = NULL; + *vpp = NULLVP; if (dvp->v_type != VDIR) return (ENOTDIR); lockparent = flags & LOCKPARENT; @@ -603,161 +779,141 @@ nfs_lookup(ap) struct vattr vattr; int vpid; - vdp = *vpp; - vpid = vdp->v_id; + newvp = *vpp; + vpid = newvp->v_id; /* * See the comment starting `Step through' in ufs/ufs_lookup.c * for an explanation of the locking protocol */ - if (dvp == vdp) { - VREF(vdp); + if (dvp == newvp) { + VREF(newvp); error = 0; - } else - error = vget(vdp, 1); + } else if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp); + error = vget(newvp, 1); + if (!error && lockparent && (flags & ISLASTCN)) + error = VOP_LOCK(dvp); + } else { + error = vget(newvp, 1); + if (!lockparent || error || !(flags & ISLASTCN)) + VOP_UNLOCK(dvp); + } if (!error) { - if (vpid == vdp->v_id) { - if (nmp->nm_flag & NFSMNT_NQNFS) { - if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) == 0) { - nfsstats.lookupcache_hits++; - if (cnp->cn_nameiop != LOOKUP && - (flags & ISLASTCN)) - cnp->cn_flags |= SAVENAME; - return (0); - } else if (NQNFS_CKCACHABLE(dvp, NQL_READ)) { - if (np->n_lrev != np->n_brev || - (np->n_flag & NMODIFIED)) { - np->n_direofoffset = 0; - cache_purge(dvp); - error = nfs_vinvalbuf(dvp, 0, - cnp->cn_cred, cnp->cn_proc, - 1); - if (error == EINTR) - return (error); - np->n_brev = np->n_lrev; - } else { - nfsstats.lookupcache_hits++; - if (cnp->cn_nameiop != LOOKUP && - (flags & ISLASTCN)) - cnp->cn_flags |= SAVENAME; - return (0); - } - } - } else if (!VOP_GETATTR(vdp, &vattr, cnp->cn_cred, cnp->cn_proc) && - vattr.va_ctime.ts_sec == VTONFS(vdp)->n_ctime) { + if (vpid == newvp->v_id) { + if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, cnp->cn_proc) + && vattr.va_ctime.ts_sec == VTONFS(newvp)->n_ctime) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; return (0); } - cache_purge(vdp); + cache_purge(newvp); } - vrele(vdp); + vput(newvp); + if (lockparent && dvp != newvp && (flags & ISLASTCN)) + VOP_UNLOCK(dvp); } + error = VOP_LOCK(dvp); + if (error) + return (error); *vpp = NULLVP; } error = 0; + newvp = NULLVP; nfsstats.lookupcache_misses++; nfsstats.rpccnt[NFSPROC_LOOKUP]++; len = cnp->cn_namelen; - nfsm_reqhead(dvp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); - - /* - * For nqnfs optionally piggyback a getlease request for the name - * being looked up. - */ - if (nmp->nm_flag & NFSMNT_NQNFS) { - nfsm_build(tl, u_long *, NFSX_UNSIGNED); - if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && - ((cnp->cn_flags & MAKEENTRY) && - (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)))) - *tl = txdr_unsigned(nmp->nm_leaseterm); - else - *tl = 0; - } - nfsm_fhtom(dvp); + nfsm_reqhead(dvp, NFSPROC_LOOKUP, + NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); - reqtime = time.tv_sec; nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); -nfsmout: if (error) { - if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && - (flags & ISLASTCN) && error == ENOENT) - error = EJUSTRETURN; - if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) - cnp->cn_flags |= SAVENAME; - return (error); - } - if (nmp->nm_flag & NFSMNT_NQNFS) { - nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); - if (*tl) { - nqlflag = fxdr_unsigned(int, *tl); - nfsm_dissect(tl, u_long *, 4*NFSX_UNSIGNED); - cachable = fxdr_unsigned(int, *tl++); - reqtime += fxdr_unsigned(int, *tl++); - fxdr_hyper(tl, &frev); - } else - nqlflag = 0; + nfsm_postop_attr(dvp, attrflag); + m_freem(mrep); + goto nfsmout; } - nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); + nfsm_getfh(fhp, fhsize, v3); /* * Handle RENAME case... */ if (cnp->cn_nameiop == RENAME && wantparent && (flags & ISLASTCN)) { - if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { + if (NFS_CMPFH(np, fhp, fhsize)) { m_freem(mrep); return (EISDIR); } - error = nfs_nget(dvp->v_mount, fhp, &np); - if (error) { + if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } newvp = NFSTOV(np); - error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr*)0); - if (error) { - vrele(newvp); - m_freem(mrep); - return (error); - } + if (v3) { + nfsm_postop_attr(newvp, attrflag); + nfsm_postop_attr(dvp, attrflag); + } else + nfsm_loadattr(newvp, (struct vattr *)0); *vpp = newvp; m_freem(mrep); cnp->cn_flags |= SAVENAME; + if (!lockparent) + VOP_UNLOCK(dvp); return (0); } - if (!bcmp(np->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { + if (flags & ISDOTDOT) { + VOP_UNLOCK(dvp); + error = nfs_nget(dvp->v_mount, fhp, fhsize, &np); + if (error) { + VOP_LOCK(dvp); + return (error); + } + newvp = NFSTOV(np); + if (lockparent && (flags & ISLASTCN) && + (error = VOP_LOCK(dvp))) { + vput(newvp); + return (error); + } + } else if (NFS_CMPFH(np, fhp, fhsize)) { VREF(dvp); newvp = dvp; } else { - error = nfs_nget(dvp->v_mount, fhp, &np); - if (error) { + if (error = nfs_nget(dvp->v_mount, fhp, fhsize, &np)) { m_freem(mrep); return (error); } + if (!lockparent || !(flags & ISLASTCN)) + VOP_UNLOCK(dvp); newvp = NFSTOV(np); } - error = nfs_loadattrcache(&newvp, &md, &dpos, (struct vattr *)0); - if (error) { - vrele(newvp); - m_freem(mrep); - return (error); - } - m_freem(mrep); - *vpp = newvp; + if (v3) { + nfsm_postop_attr(newvp, attrflag); + nfsm_postop_attr(dvp, attrflag); + } else + nfsm_loadattr(newvp, (struct vattr *)0); if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { - if ((nmp->nm_flag & NFSMNT_NQNFS) == 0) - np->n_ctime = np->n_vattr.va_ctime.ts_sec; - else if (nqlflag && reqtime > time.tv_sec) - nqnfs_clientlease(nmp, np, nqlflag, cachable, reqtime, - frev); - cache_enter(dvp, *vpp, cnp); + np->n_ctime = np->n_vattr.va_ctime.ts_sec; + cache_enter(dvp, newvp, cnp); } - return (0); + *vpp = newvp; + nfsm_reqdone; + if (error) { + if (newvp != NULLVP) + vrele(newvp); + if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && + (flags & ISLASTCN) && error == ENOENT) { + if (!lockparent) + VOP_UNLOCK(dvp); + error = EJUSTRETURN; + } + if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) + cnp->cn_flags |= SAVENAME; + } + return (error); } /* @@ -810,18 +966,22 @@ nfs_readlinkrpc(vp, uiop, cred) { register u_long *tl; register caddr_t cp; - register long t1; + register long t1, t2; caddr_t bpos, dpos, cp2; - int error = 0; + int error = 0, len, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - long len; + int v3 = NFS_ISV3(vp); nfsstats.rpccnt[NFSPROC_READLINK]++; - nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH); - nfsm_fhtom(vp); + nfsm_reqhead(vp, NFSPROC_READLINK, NFSX_FH(v3)); + nfsm_fhtom(vp, v3); nfsm_request(vp, NFSPROC_READLINK, uiop->uio_procp, cred); - nfsm_strsiz(len, NFS_MAXPATHLEN); - nfsm_mtouio(uiop, len); + if (v3) + nfsm_postop_attr(vp, attrflag); + if (!error) { + nfsm_strsiz(len, NFS_MAXPATHLEN); + nfsm_mtouio(uiop, len); + } nfsm_reqdone; return (error); } @@ -838,25 +998,27 @@ nfs_readrpc(vp, uiop, cred) { register u_long *tl; register caddr_t cp; - register long t1; + register long t1, t2; caddr_t bpos, dpos, cp2; - int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct nfsmount *nmp; - long len, retlen, tsiz; + int error = 0, len, retlen, tsiz, eof, attrflag; + int v3 = NFS_ISV3(vp); +#ifndef nolint + eof = 0; +#endif nmp = VFSTONFS(vp->v_mount); tsiz = uiop->uio_resid; - if (uiop->uio_offset + tsiz > 0xffffffff && - (nmp->nm_flag & NFSMNT_NQNFS) == 0) + if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_READ]++; len = (tsiz > nmp->nm_rsize) ? nmp->nm_rsize : tsiz; - nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH+NFSX_UNSIGNED*3); - nfsm_fhtom(vp); - nfsm_build(tl, u_long *, NFSX_UNSIGNED*3); - if (nmp->nm_flag & NFSMNT_NQNFS) { + nfsm_reqhead(vp, NFSPROC_READ, NFSX_FH(v3) + NFSX_UNSIGNED * 3); + nfsm_fhtom(vp, v3); + nfsm_build(tl, u_long *, NFSX_UNSIGNED * 3); + if (v3) { txdr_hyper(&uiop->uio_offset, tl); *(tl + 2) = txdr_unsigned(len); } else { @@ -865,14 +1027,25 @@ nfs_readrpc(vp, uiop, cred) *tl = 0; } nfsm_request(vp, NFSPROC_READ, uiop->uio_procp, cred); - nfsm_loadattr(vp, (struct vattr *)0); + if (v3) { + nfsm_postop_attr(vp, attrflag); + if (error) { + m_freem(mrep); + goto nfsmout; + } + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + eof = fxdr_unsigned(int, *(tl + 1)); + } else + nfsm_loadattr(vp, (struct vattr *)0); nfsm_strsiz(retlen, nmp->nm_rsize); nfsm_mtouio(uiop, retlen); m_freem(mrep); - if (retlen < len) + tsiz -= retlen; + if (v3) { + if (eof || retlen == 0) + tsiz = 0; + } else if (retlen < len) tsiz = 0; - else - tsiz -= len; } nfsmout: return (error); @@ -882,150 +1055,227 @@ nfsmout: * nfs write call */ int -nfs_writerpc(vp, uiop, cred, ioflags) +nfs_writerpc(vp, uiop, cred, iomode, must_commit) register struct vnode *vp; - struct uio *uiop; + register struct uio *uiop; struct ucred *cred; - int ioflags; + int *iomode, *must_commit; { register u_long *tl; register caddr_t cp; - register long t1; + register int t1, t2, backup; caddr_t bpos, dpos, cp2; - int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - struct nfsmount *nmp; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *np = VTONFS(vp); u_quad_t frev; - long len, tsiz; + int error = 0, len, tsiz, wccflag = NFSV3_WCCRATTR, rlen, commit; + int v3 = NFS_ISV3(vp), committed = NFSV3WRITE_FILESYNC; - nmp = VFSTONFS(vp->v_mount); +#ifndef DIAGNOSTIC + if (uiop->uio_iovcnt != 1) + panic("nfs: writerpc iovcnt > 1"); +#endif + *must_commit = 0; tsiz = uiop->uio_resid; - if (uiop->uio_offset + tsiz > 0xffffffff && - (nmp->nm_flag & NFSMNT_NQNFS) == 0) + if (uiop->uio_offset + tsiz > 0xffffffff && !v3) return (EFBIG); while (tsiz > 0) { nfsstats.rpccnt[NFSPROC_WRITE]++; len = (tsiz > nmp->nm_wsize) ? nmp->nm_wsize : tsiz; nfsm_reqhead(vp, NFSPROC_WRITE, - NFSX_FH+NFSX_UNSIGNED*4+nfsm_rndup(len)); - nfsm_fhtom(vp); - nfsm_build(tl, u_long *, NFSX_UNSIGNED * 4); - if (nmp->nm_flag & NFSMNT_NQNFS) { + NFSX_FH(v3) + 5 * NFSX_UNSIGNED + nfsm_rndup(len)); + nfsm_fhtom(vp, v3); + if (v3) { + nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); txdr_hyper(&uiop->uio_offset, tl); tl += 2; -#ifdef notyet - if (ioflags & IO_APPEND) - *tl++ = txdr_unsigned(1); - else -#endif - *tl++ = 0; + *tl++ = txdr_unsigned(len); + *tl++ = txdr_unsigned(*iomode); } else { + nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *++tl = txdr_unsigned(uiop->uio_offset); tl += 2; } *tl = txdr_unsigned(len); nfsm_uiotom(uiop, len); nfsm_request(vp, NFSPROC_WRITE, uiop->uio_procp, cred); - nfsm_loadattr(vp, (struct vattr *)0); - if (nmp->nm_flag & NFSMNT_MYWRITE) - VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec; - else if ((nmp->nm_flag & NFSMNT_NQNFS) && - NQNFS_CKCACHABLE(vp, NQL_WRITE)) { - nfsm_dissect(tl, u_long *, 2*NFSX_UNSIGNED); - fxdr_hyper(tl, &frev); - if (frev > np->n_brev) - np->n_brev = frev; - } + if (v3) { + wccflag = NFSV3_WCCCHK; + nfsm_wcc_data(vp, wccflag); + if (!error) { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED + + NFSX_V3WRITEVERF); + rlen = fxdr_unsigned(int, *tl++); + if (rlen == 0) { + error = NFSERR_IO; + break; + } else if (rlen < len) { + backup = len - rlen; + uiop->uio_iov->iov_base -= backup; + uiop->uio_iov->iov_len += backup; + uiop->uio_offset -= backup; + uiop->uio_resid += backup; + len = rlen; + } + commit = fxdr_unsigned(int, *tl++); + + /* + * Return the lowest committment level + * obtained by any of the RPCs. + */ + if (committed == NFSV3WRITE_FILESYNC) + committed = commit; + else if (committed == NFSV3WRITE_DATASYNC && + commit == NFSV3WRITE_UNSTABLE) + committed = commit; + if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) { + bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, + NFSX_V3WRITEVERF); + nmp->nm_flag |= NFSMNT_HASWRITEVERF; + } else if (bcmp((caddr_t)tl, + (caddr_t)nmp->nm_verf, NFSX_V3WRITEVERF)) { + *must_commit = 1; + bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, + NFSX_V3WRITEVERF); + } + } + } else + nfsm_loadattr(vp, (struct vattr *)0); + if (wccflag) + VTONFS(vp)->n_mtime = VTONFS(vp)->n_vattr.va_mtime.ts_sec; m_freem(mrep); tsiz -= len; } nfsmout: + *iomode = committed; if (error) uiop->uio_resid = tsiz; return (error); } /* - * nfs mknod call - * This is a kludge. Use a create rpc but with the IFMT bits of the mode - * set to specify the file type and the size field for rdev. + * nfs mknod rpc + * For NFS v2 this is a kludge. Use a create rpc but with the IFMT bits of the + * mode set to specify the file type and the size field for rdev. */ -/* ARGSUSED */ int -nfs_mknod(ap) - struct vop_mknod_args /* { - struct vnode *a_dvp; - struct vnode **a_vpp; - struct componentname *a_cnp; - struct vattr *a_vap; - } */ *ap; +nfs_mknodrpc(dvp, vpp, cnp, vap) + register struct vnode *dvp; + register struct vnode **vpp; + register struct componentname *cnp; + register struct vattr *vap; { - register struct vnode *dvp = ap->a_dvp; - register struct vattr *vap = ap->a_vap; - register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; + register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; - struct vnode *newvp = 0; + struct vnode *newvp = (struct vnode *)0; + struct nfsnode *np = (struct nfsnode *)0; struct vattr vattr; char *cp2; caddr_t bpos, dpos; - int error = 0, isnq; + int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; u_long rdev; + int v3 = NFS_ISV3(dvp); - isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); if (vap->va_type == VCHR || vap->va_type == VBLK) rdev = txdr_unsigned(vap->va_rdev); - else if (vap->va_type == VFIFO) + else if (vap->va_type == VFIFO || vap->va_type == VSOCK) rdev = 0xffffffff; else { VOP_ABORTOP(dvp, cnp); vput(dvp); return (EOPNOTSUPP); } - error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc); - if (error) { + if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } - newvp = NULLVP; - nfsstats.rpccnt[NFSPROC_CREATE]++; - nfsm_reqhead(dvp, NFSPROC_CREATE, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); - nfsm_fhtom(dvp); + nfsstats.rpccnt[NFSPROC_MKNOD]++; + nfsm_reqhead(dvp, NFSPROC_MKNOD, NFSX_FH(v3) + 4 * NFSX_UNSIGNED + + + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); - sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); - sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); - sp->sa_gid = txdr_unsigned(vattr.va_gid); - if (isnq) { - sp->sa_nqrdev = rdev; - sp->sa_nqflags = 0; - txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); - txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); + if (v3) { + nfsm_build(tl, u_long *, NFSX_UNSIGNED + NFSX_V3SRVSATTR); + *tl++ = vtonfsv3_type(vap->va_type); + sp3 = (struct nfsv3_sattr *)tl; + nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); + if (vap->va_type == VCHR || vap->va_type == VBLK) { + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + *tl++ = txdr_unsigned(major(vap->va_rdev)); + *tl = txdr_unsigned(minor(vap->va_rdev)); + } } else { - sp->sa_nfssize = rdev; - txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); - txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); + nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); + sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); + sp->sa_gid = txdr_unsigned(vattr.va_gid); + sp->sa_size = rdev; + txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); + txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } - nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); - nfsm_mtofh(dvp, newvp); + nfsm_request(dvp, NFSPROC_MKNOD, cnp->cn_proc, cnp->cn_cred); + if (!error) { + nfsm_mtofh(dvp, newvp, v3, gotvp); + if (!gotvp) { + if (newvp) { + vput(newvp); + newvp = (struct vnode *)0; + } + error = nfs_lookitup(dvp, cnp->cn_nameptr, + cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); + if (!error) + newvp = NFSTOV(np); + } + } + if (v3) + nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; - if (!error && (cnp->cn_flags & MAKEENTRY)) - cache_enter(dvp, newvp, cnp); + if (error) { + if (newvp) + vput(newvp); + } else { + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, newvp, cnp); + *vpp = newvp; + } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; - vrele(dvp); - if (newvp != NULLVP) - vrele(newvp); + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; + vput(dvp); + return (error); +} + +/* + * nfs mknod vop + * just call nfs_mknodrpc() to do the work. + */ +/* ARGSUSED */ +int +nfs_mknod(ap) + struct vop_mknod_args /* { + struct vnode *a_dvp; + struct vnode **a_vpp; + struct componentname *a_cnp; + struct vattr *a_vap; + } */ *ap; +{ + struct vnode *newvp; + int error; + + error = nfs_mknodrpc(ap->a_dvp, &newvp, ap->a_cnp, ap->a_vap); + if (!error) + vput(newvp); return (error); } +static u_long create_verf; /* * nfs file create call */ @@ -1042,52 +1292,98 @@ nfs_create(ap) register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; + register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; + struct nfsnode *np = (struct nfsnode *)0; + struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; - int error = 0, isnq; + int error = 0, wccflag = NFSV3_WCCRATTR, gotvp = 0, fmode = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; + int v3 = NFS_ISV3(dvp); - error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc); - if (error) { + /* + * Oops, not for me.. + */ + if (vap->va_type == VSOCK) + return (nfs_mknodrpc(dvp, ap->a_vpp, cnp, vap)); + + if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } + if (vap->va_vaflags & VA_EXCLUSIVE) + fmode |= O_EXCL; +again: nfsstats.rpccnt[NFSPROC_CREATE]++; - isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); - nfsm_reqhead(dvp, NFSPROC_CREATE, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)+NFSX_SATTR(isnq)); - nfsm_fhtom(dvp); + nfsm_reqhead(dvp, NFSPROC_CREATE, NFSX_FH(v3) + 2 * NFSX_UNSIGNED + + nfsm_rndup(cnp->cn_namelen) + NFSX_SATTR(v3)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); - sp->sa_mode = vtonfs_mode(vap->va_type, vap->va_mode); - sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); - sp->sa_gid = txdr_unsigned(vattr.va_gid); - if (isnq) { - u_quad_t qval = 0; - - txdr_hyper(&qval, &sp->sa_nqsize); - sp->sa_nqflags = 0; - sp->sa_nqrdev = -1; - txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); - txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); + if (v3) { + nfsm_build(tl, u_long *, NFSX_UNSIGNED); + if (fmode & O_EXCL) { + *tl = txdr_unsigned(NFSV3CREATE_EXCLUSIVE); + nfsm_build(tl, u_long *, NFSX_V3CREATEVERF); + if (in_ifaddr) + *tl++ = IA_SIN(in_ifaddr)->sin_addr.s_addr; + else + *tl++ = create_verf; + *tl = ++create_verf; + } else { + *tl = txdr_unsigned(NFSV3CREATE_UNCHECKED); + nfsm_build(tl, u_long *, NFSX_V3SRVSATTR); + sp3 = (struct nfsv3_sattr *)tl; + nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); + } } else { - sp->sa_nfssize = 0; - txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); - txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); + nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + sp->sa_mode = vtonfsv2_mode(vap->va_type, vap->va_mode); + sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); + sp->sa_gid = txdr_unsigned(vattr.va_gid); + sp->sa_size = 0; + txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); + txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_CREATE, cnp->cn_proc, cnp->cn_cred); - nfsm_mtofh(dvp, *ap->a_vpp); + if (!error) { + nfsm_mtofh(dvp, newvp, v3, gotvp); + if (!gotvp) { + if (newvp) { + vput(newvp); + newvp = (struct vnode *)0; + } + error = nfs_lookitup(dvp, cnp->cn_nameptr, + cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc, &np); + if (!error) + newvp = NFSTOV(np); + } + } + if (v3) + nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; - if (!error && (cnp->cn_flags & MAKEENTRY)) - cache_enter(dvp, *ap->a_vpp, cnp); + if (error) { + if (v3 && (fmode & O_EXCL) && error == NFSERR_NOTSUPP) { + fmode &= ~O_EXCL; + goto again; + } + if (newvp) + vput(newvp); + } else if (v3 && (fmode & O_EXCL)) + error = nfs_setattrrpc(newvp, vap, cnp->cn_cred, cnp->cn_proc); + if (!error) { + if (cnp->cn_flags & MAKEENTRY) + cache_enter(dvp, newvp, cnp); + *ap->a_vpp = newvp; + } FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; - vrele(dvp); + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; + vput(dvp); return (error); } @@ -1122,22 +1418,17 @@ nfs_remove(ap) int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; + int v3 = NFS_ISV3(dvp); - if (vp->v_usecount > 1) { - if (!np->n_sillyrename) - error = nfs_sillyrename(dvp, vp, cnp); - else if (VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) - == 0 && vattr.va_nlink > 1) - /* - * If we already have a silly name but there are more - * than one links, just proceed with the NFS remove - * request, as the bits will remain available (modulo - * network races). This avoids silently ignoring the - * attempted removal of a non-silly entry. - */ - goto doit; - } else { - doit: +#ifndef DIAGNOSTIC + if ((cnp->cn_flags & HASBUF) == 0) + panic("nfs_remove: no name"); + if (vp->v_usecount < 1) + panic("nfs_remove: bad v_usecount"); +#endif + if (vp->v_usecount == 1 || (np->n_sillyrename && + VOP_GETATTR(vp, &vattr, cnp->cn_cred, cnp->cn_proc) == 0 && + vattr.va_nlink > 1)) { /* * Purge the name cache so that the chance of a lookup for * the name succeeding while the remove is in progress is @@ -1147,23 +1438,14 @@ nfs_remove(ap) */ cache_purge(vp); /* - * Throw away biocache buffers. Mainly to avoid - * unnecessary delayed writes. + * throw away biocache buffers, mainly to avoid + * unnecessary delayed writes later. */ error = nfs_vinvalbuf(vp, 0, cnp->cn_cred, cnp->cn_proc, 1); - if (error == EINTR) - return (error); /* Do the rpc */ - nfsstats.rpccnt[NFSPROC_REMOVE]++; - nfsm_reqhead(dvp, NFSPROC_REMOVE, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); - nfsm_fhtom(dvp); - nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_request(dvp, NFSPROC_REMOVE, cnp->cn_proc, cnp->cn_cred); - nfsm_reqdone; - FREE(cnp->cn_pnbuf, M_NAMEI); - VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; + if (error != EINTR) + error = nfs_removerpc(dvp, cnp->cn_nameptr, + cnp->cn_namelen, cnp->cn_cred, cnp->cn_proc); /* * Kludge City: If the first reply to the remove rpc is lost.. * the reply to the retransmitted request will be ENOENT @@ -1172,10 +1454,15 @@ nfs_remove(ap) */ if (error == ENOENT) error = 0; - } + } else if (!np->n_sillyrename) + error = nfs_sillyrename(dvp, vp, cnp); + FREE(cnp->cn_pnbuf, M_NAMEI); np->n_attrstamp = 0; - vrele(dvp); - vrele(vp); + vput(dvp); + if (vp == dvp) + vrele(vp); + else + vput(vp); return (error); } @@ -1186,22 +1473,42 @@ int nfs_removeit(sp) register struct sillyrename *sp; { + + return (nfs_removerpc(sp->s_dvp, sp->s_name, sp->s_namlen, sp->s_cred, + (struct proc *)0)); +} + +/* + * Nfs remove rpc, called from nfs_remove() and nfs_removeit(). + */ +int +nfs_removerpc(dvp, name, namelen, cred, proc) + register struct vnode *dvp; + char *name; + int namelen; + struct ucred *cred; + struct proc *proc; +{ register u_long *tl; register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int error = 0; + register long t1, t2; + caddr_t bpos, dpos, cp2; + int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_REMOVE]++; - nfsm_reqhead(sp->s_dvp, NFSPROC_REMOVE, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(sp->s_namlen)); - nfsm_fhtom(sp->s_dvp); - nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); - nfsm_request(sp->s_dvp, NFSPROC_REMOVE, NULL, sp->s_cred); + nfsm_reqhead(dvp, NFSPROC_REMOVE, + NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(namelen)); + nfsm_fhtom(dvp, v3); + nfsm_strtom(name, namelen, NFS_MAXNAMLEN); + nfsm_request(dvp, NFSPROC_REMOVE, proc, cred); + if (v3) + nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; - VTONFS(sp->s_dvp)->n_flag |= NMODIFIED; - VTONFS(sp->s_dvp)->n_attrstamp = 0; + VTONFS(dvp)->n_flag |= NMODIFIED; + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; return (error); } @@ -1225,13 +1532,13 @@ nfs_rename(ap) register struct vnode *tdvp = ap->a_tdvp; register struct componentname *tcnp = ap->a_tcnp; register struct componentname *fcnp = ap->a_fcnp; - register u_long *tl; - register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int error = 0; - struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int error; +#ifndef DIAGNOSTIC + if ((tcnp->cn_flags & HASBUF) == 0 || + (fcnp->cn_flags & HASBUF) == 0) + panic("nfs_rename: no name"); +#endif /* Check for cross-device rename */ if ((fvp->v_mount != tdvp->v_mount) || (tvp && (fvp->v_mount != tvp->v_mount))) { @@ -1239,21 +1546,20 @@ nfs_rename(ap) goto out; } + /* + * If the tvp exists and is in use, sillyrename it before doing the + * rename of the new file over it. + */ + if (tvp && tvp->v_usecount > 1 && !VTONFS(tvp)->n_sillyrename && + !nfs_sillyrename(tdvp, tvp, tcnp)) { + vrele(tvp); + tvp = NULL; + } + + error = nfs_renamerpc(fdvp, fcnp->cn_nameptr, fcnp->cn_namelen, + tdvp, tcnp->cn_nameptr, tcnp->cn_namelen, tcnp->cn_cred, + tcnp->cn_proc); - nfsstats.rpccnt[NFSPROC_RENAME]++; - nfsm_reqhead(fdvp, NFSPROC_RENAME, - (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(fcnp->cn_namelen)+ - nfsm_rndup(fcnp->cn_namelen)); /* or fcnp->cn_cred?*/ - nfsm_fhtom(fdvp); - nfsm_strtom(fcnp->cn_nameptr, fcnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_fhtom(tdvp); - nfsm_strtom(tcnp->cn_nameptr, tcnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_request(fdvp, NFSPROC_RENAME, tcnp->cn_proc, tcnp->cn_cred); - nfsm_reqdone; - VTONFS(fdvp)->n_flag |= NMODIFIED; - VTONFS(fdvp)->n_attrstamp = 0; - VTONFS(tdvp)->n_flag |= NMODIFIED; - VTONFS(tdvp)->n_attrstamp = 0; if (fvp->v_type == VDIR) { if (tvp != NULL && tvp->v_type == VDIR) cache_purge(tdvp); @@ -1285,26 +1591,52 @@ nfs_renameit(sdvp, scnp, sp) struct componentname *scnp; register struct sillyrename *sp; { + return (nfs_renamerpc(sdvp, scnp->cn_nameptr, scnp->cn_namelen, + sdvp, sp->s_name, sp->s_namlen, scnp->cn_cred, scnp->cn_proc)); +} + +/* + * Do an nfs rename rpc. Called from nfs_rename() and nfs_renameit(). + */ +int +nfs_renamerpc(fdvp, fnameptr, fnamelen, tdvp, tnameptr, tnamelen, cred, proc) + register struct vnode *fdvp; + char *fnameptr; + int fnamelen; + register struct vnode *tdvp; + char *tnameptr; + int tnamelen; + struct ucred *cred; + struct proc *proc; +{ register u_long *tl; register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int error = 0; + register long t1, t2; + caddr_t bpos, dpos, cp2; + int error = 0, fwccflag = NFSV3_WCCRATTR, twccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int v3 = NFS_ISV3(fdvp); nfsstats.rpccnt[NFSPROC_RENAME]++; - nfsm_reqhead(sdvp, NFSPROC_RENAME, - (NFSX_FH+NFSX_UNSIGNED)*2+nfsm_rndup(scnp->cn_namelen)+ - nfsm_rndup(sp->s_namlen)); - nfsm_fhtom(sdvp); - nfsm_strtom(scnp->cn_nameptr, scnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_fhtom(sdvp); - nfsm_strtom(sp->s_name, sp->s_namlen, NFS_MAXNAMLEN); - nfsm_request(sdvp, NFSPROC_RENAME, scnp->cn_proc, scnp->cn_cred); + nfsm_reqhead(fdvp, NFSPROC_RENAME, + (NFSX_FH(v3) + NFSX_UNSIGNED)*2 + nfsm_rndup(fnamelen) + + nfsm_rndup(tnamelen)); + nfsm_fhtom(fdvp, v3); + nfsm_strtom(fnameptr, fnamelen, NFS_MAXNAMLEN); + nfsm_fhtom(tdvp, v3); + nfsm_strtom(tnameptr, tnamelen, NFS_MAXNAMLEN); + nfsm_request(fdvp, NFSPROC_RENAME, proc, cred); + if (v3) { + nfsm_wcc_data(fdvp, fwccflag); + nfsm_wcc_data(tdvp, twccflag); + } nfsm_reqdone; - FREE(scnp->cn_pnbuf, M_NAMEI); - VTONFS(sdvp)->n_flag |= NMODIFIED; - VTONFS(sdvp)->n_attrstamp = 0; + VTONFS(fdvp)->n_flag |= NMODIFIED; + VTONFS(tdvp)->n_flag |= NMODIFIED; + if (!fwccflag) + VTONFS(fdvp)->n_attrstamp = 0; + if (!twccflag) + VTONFS(tdvp)->n_attrstamp = 0; return (error); } @@ -1319,22 +1651,32 @@ nfs_link(ap) struct componentname *a_cnp; } */ *ap; { +#if defined(__FreeBSD__) || defined(__NetBSD__) + /* + * Since the args are reversed in the VOP_LINK() calls, + * switch them back. Argh! + */ + register struct vnode *vp = ap->a_tdvp; + register struct vnode *tdvp = ap->a_vp; +#else register struct vnode *vp = ap->a_vp; register struct vnode *tdvp = ap->a_tdvp; +#endif register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int error = 0; + register long t1, t2; + caddr_t bpos, dpos, cp2; + int error = 0, wccflag = NFSV3_WCCRATTR, attrflag = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int v3 = NFS_ISV3(vp); if (vp->v_mount != tdvp->v_mount) { /*VOP_ABORTOP(vp, cnp);*/ if (tdvp == vp) - vrele(vp); + vrele(tdvp); else - vput(vp); + vput(tdvp); return (EXDEV); } @@ -1343,21 +1685,27 @@ nfs_link(ap) * doesn't get "out of sync" with the server. * XXX There should be a better way! */ - VOP_FSYNC(tdvp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc); + VOP_FSYNC(vp, cnp->cn_cred, MNT_WAIT, cnp->cn_proc); nfsstats.rpccnt[NFSPROC_LINK]++; - nfsm_reqhead(tdvp, NFSPROC_LINK, - NFSX_FH*2+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); - nfsm_fhtom(tdvp); - nfsm_fhtom(vp); + nfsm_reqhead(vp, NFSPROC_LINK, + NFSX_FH(v3)*2 + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); + nfsm_fhtom(vp, v3); + nfsm_fhtom(tdvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); - nfsm_request(tdvp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); + nfsm_request(vp, NFSPROC_LINK, cnp->cn_proc, cnp->cn_cred); + if (v3) { + nfsm_postop_attr(vp, attrflag); + nfsm_wcc_data(tdvp, wccflag); + } nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); - VTONFS(tdvp)->n_attrstamp = 0; - VTONFS(vp)->n_flag |= NMODIFIED; - VTONFS(vp)->n_attrstamp = 0; - vrele(vp); + VTONFS(tdvp)->n_flag |= NMODIFIED; + if (!attrflag) + VTONFS(vp)->n_attrstamp = 0; + if (!wccflag) + VTONFS(tdvp)->n_attrstamp = 0; + vput(tdvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ @@ -1369,7 +1717,6 @@ nfs_link(ap) /* * nfs symbolic link create call */ -/* start here */ int nfs_symlink(ap) struct vop_symlink_args /* { @@ -1384,43 +1731,51 @@ nfs_symlink(ap) register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; register struct nfsv2_sattr *sp; + register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int slen, error = 0, isnq; + register long t1, t2; + caddr_t bpos, dpos, cp2; + int slen, error = 0, wccflag = NFSV3_WCCRATTR, gotvp; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + struct vnode *newvp = (struct vnode *)0; + int v3 = NFS_ISV3(dvp); nfsstats.rpccnt[NFSPROC_SYMLINK]++; slen = strlen(ap->a_target); - isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); - nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH+2*NFSX_UNSIGNED+ - nfsm_rndup(cnp->cn_namelen)+nfsm_rndup(slen)+NFSX_SATTR(isnq)); - nfsm_fhtom(dvp); + nfsm_reqhead(dvp, NFSPROC_SYMLINK, NFSX_FH(v3) + 2*NFSX_UNSIGNED + + nfsm_rndup(cnp->cn_namelen) + nfsm_rndup(slen) + NFSX_SATTR(v3)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); + if (v3) { + nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); + nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, + cnp->cn_cred->cr_gid); + } nfsm_strtom(ap->a_target, slen, NFS_MAXPATHLEN); - nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); - sp->sa_mode = vtonfs_mode(VLNK, vap->va_mode); - sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); - sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); - if (isnq) { - quad_t qval = -1; - - txdr_hyper(&qval, &sp->sa_nqsize); - sp->sa_nqflags = 0; - txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); - txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); - } else { - sp->sa_nfssize = -1; - txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); - txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); + if (!v3) { + nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + sp->sa_mode = vtonfsv2_mode(VLNK, vap->va_mode); + sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); + sp->sa_gid = txdr_unsigned(cnp->cn_cred->cr_gid); + sp->sa_size = -1; + txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); + txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_SYMLINK, cnp->cn_proc, cnp->cn_cred); + if (v3) { + if (!error) + nfsm_mtofh(dvp, newvp, v3, gotvp); + nfsm_wcc_data(dvp, wccflag); + } nfsm_reqdone; + if (newvp) + vput(newvp); FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; - vrele(dvp); + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; + vput(dvp); /* * Kludge: Map EEXIST => 0 assuming that it is a reply to a retry. */ @@ -1444,76 +1799,78 @@ nfs_mkdir(ap) register struct vnode *dvp = ap->a_dvp; register struct vattr *vap = ap->a_vap; register struct componentname *cnp = ap->a_cnp; - register struct vnode **vpp = ap->a_vpp; register struct nfsv2_sattr *sp; + register struct nfsv3_sattr *sp3; register u_long *tl; register caddr_t cp; register long t1, t2; register int len; + struct nfsnode *np = (struct nfsnode *)0; + struct vnode *newvp = (struct vnode *)0; caddr_t bpos, dpos, cp2; - int error = 0, firsttry = 1, isnq; + nfsfh_t *fhp; + int error = 0, wccflag = NFSV3_WCCRATTR, attrflag; + int fhsize, gotvp = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; struct vattr vattr; + int v3 = NFS_ISV3(dvp); - error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc); - if (error) { + if (error = VOP_GETATTR(dvp, &vattr, cnp->cn_cred, cnp->cn_proc)) { VOP_ABORTOP(dvp, cnp); vput(dvp); return (error); } len = cnp->cn_namelen; - isnq = (VFSTONFS(dvp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_MKDIR]++; nfsm_reqhead(dvp, NFSPROC_MKDIR, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)+NFSX_SATTR(isnq)); - nfsm_fhtom(dvp); + NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len) + NFSX_SATTR(v3)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); - nfsm_build(sp, struct nfsv2_sattr *, NFSX_SATTR(isnq)); - sp->sa_mode = vtonfs_mode(VDIR, vap->va_mode); - sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); - sp->sa_gid = txdr_unsigned(vattr.va_gid); - if (isnq) { - quad_t qval = -1; - - txdr_hyper(&qval, &sp->sa_nqsize); - sp->sa_nqflags = 0; - txdr_nqtime(&vap->va_atime, &sp->sa_nqatime); - txdr_nqtime(&vap->va_mtime, &sp->sa_nqmtime); + if (v3) { + nfsm_build(sp3, struct nfsv3_sattr *, NFSX_V3SRVSATTR); + nfsm_v3sattr(sp3, vap, cnp->cn_cred->cr_uid, vattr.va_gid); } else { - sp->sa_nfssize = -1; - txdr_nfstime(&vap->va_atime, &sp->sa_nfsatime); - txdr_nfstime(&vap->va_mtime, &sp->sa_nfsmtime); + nfsm_build(sp, struct nfsv2_sattr *, NFSX_V2SATTR); + sp->sa_mode = vtonfsv2_mode(VDIR, vap->va_mode); + sp->sa_uid = txdr_unsigned(cnp->cn_cred->cr_uid); + sp->sa_gid = txdr_unsigned(vattr.va_gid); + sp->sa_size = -1; + txdr_nfsv2time(&vap->va_atime, &sp->sa_atime); + txdr_nfsv2time(&vap->va_mtime, &sp->sa_mtime); } nfsm_request(dvp, NFSPROC_MKDIR, cnp->cn_proc, cnp->cn_cred); - nfsm_mtofh(dvp, *vpp); + if (!error) + nfsm_mtofh(dvp, newvp, v3, gotvp); + if (v3) + nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; /* * Kludge: Map EEXIST => 0 assuming that you have a reply to a retry * if we can succeed in looking up the directory. - * "firsttry" is necessary since the macros may "goto nfsmout" which - * is above the if on errors. (Ugh) */ - if (error == EEXIST && firsttry) { - firsttry = 0; - error = 0; - nfsstats.rpccnt[NFSPROC_LOOKUP]++; - *vpp = NULL; - nfsm_reqhead(dvp, NFSPROC_LOOKUP, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); - nfsm_fhtom(dvp); - nfsm_strtom(cnp->cn_nameptr, len, NFS_MAXNAMLEN); - nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_proc, cnp->cn_cred); - nfsm_mtofh(dvp, *vpp); - if ((*vpp)->v_type != VDIR) { - vput(*vpp); - error = EEXIST; + if (error == EEXIST || (!error && !gotvp)) { + if (newvp) { + vrele(newvp); + newvp = (struct vnode *)0; + } + error = nfs_lookitup(dvp, cnp->cn_nameptr, len, cnp->cn_cred, + cnp->cn_proc, &np); + if (!error) { + newvp = NFSTOV(np); + if (newvp->v_type != VDIR) + error = EEXIST; } - m_freem(mrep); } + if (error) { + if (newvp) + vrele(newvp); + } else + *ap->a_vpp = newvp; FREE(cnp->cn_pnbuf, M_NAMEI); - vrele(dvp); + vput(dvp); return (error); } @@ -1533,31 +1890,35 @@ nfs_rmdir(ap) register struct componentname *cnp = ap->a_cnp; register u_long *tl; register caddr_t cp; - register long t2; - caddr_t bpos, dpos; - int error = 0; + register long t1, t2; + caddr_t bpos, dpos, cp2; + int error = 0, wccflag = NFSV3_WCCRATTR; struct mbuf *mreq, *mrep, *md, *mb, *mb2; + int v3 = NFS_ISV3(dvp); if (dvp == vp) { - vrele(dvp); + vput(dvp); vrele(dvp); FREE(cnp->cn_pnbuf, M_NAMEI); return (EINVAL); } nfsstats.rpccnt[NFSPROC_RMDIR]++; nfsm_reqhead(dvp, NFSPROC_RMDIR, - NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(cnp->cn_namelen)); - nfsm_fhtom(dvp); + NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(cnp->cn_namelen)); + nfsm_fhtom(dvp, v3); nfsm_strtom(cnp->cn_nameptr, cnp->cn_namelen, NFS_MAXNAMLEN); nfsm_request(dvp, NFSPROC_RMDIR, cnp->cn_proc, cnp->cn_cred); + if (v3) + nfsm_wcc_data(dvp, wccflag); nfsm_reqdone; FREE(cnp->cn_pnbuf, M_NAMEI); VTONFS(dvp)->n_flag |= NMODIFIED; - VTONFS(dvp)->n_attrstamp = 0; + if (!wccflag) + VTONFS(dvp)->n_attrstamp = 0; cache_purge(dvp); cache_purge(vp); - vrele(vp); - vrele(dvp); + vput(vp); + vput(dvp); /* * Kludge: Map ENOENT => 0 assuming that you have a reply to a retry. */ @@ -1568,9 +1929,6 @@ nfs_rmdir(ap) /* * nfs readdir call - * Although cookie is defined as opaque, I translate it to/from net byte - * order so that it looks more sensible. This appears consistent with the - * Ultrix implementation of NFS. */ int nfs_readdir(ap) @@ -1591,10 +1949,10 @@ nfs_readdir(ap) /* * First, check for hit on the EOF offset cache */ - if (uio->uio_offset != 0 && uio->uio_offset == np->n_direofoffset && + if (np->n_direofoffset > 0 && uio->uio_offset >= np->n_direofoffset && (np->n_flag & NMODIFIED) == 0) { if (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS) { - if (NQNFS_CKCACHABLE(vp, NQL_READ)) { + if (NQNFS_CKCACHABLE(vp, ND_READ)) { nfsstats.direofcache_hits++; return (0); } @@ -1622,90 +1980,148 @@ nfs_readdir(ap) */ int nfs_readdirrpc(vp, uiop, cred) - register struct vnode *vp; - struct uio *uiop; + struct vnode *vp; + register struct uio *uiop; struct ucred *cred; + { - register long len; - register struct dirent *dp = 0; + register int len, left; + register struct dirent *dp; register u_long *tl; register caddr_t cp; - register long t1; - long tlen, lastlen = 0; + register long t1, t2; + register nfsuint64 *cookiep; caddr_t bpos, dpos, cp2; - int error = 0; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - struct mbuf *md2; - caddr_t dpos2; - int siz; - int more_dirs = 1; - u_long off, savoff = 0; - struct dirent *savdp = 0; - struct nfsmount *nmp; - struct nfsnode *np = VTONFS(vp); - long tresid; + nfsuint64 cookie; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + struct nfsnode *dnp = VTONFS(vp); + nfsfh_t *fhp; + u_quad_t frev, fileno; + int error = 0, tlen, more_dirs = 1, blksiz = 0, bigenough = 1, i; + int cachable, attrflag, fhsize; + int v3 = NFS_ISV3(vp); + +#ifndef nolint + dp = (struct dirent *)0; +#endif +#ifndef DIAGNOSTIC + if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (NFS_DIRBLKSIZ - 1)) || + (uiop->uio_resid & (NFS_DIRBLKSIZ - 1))) + panic("nfs readdirrpc bad uio"); +#endif - nmp = VFSTONFS(vp->v_mount); - tresid = uiop->uio_resid; /* - * Loop around doing readdir rpc's of size uio_resid or nm_rsize, - * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. + * If there is no cookie, assume end of directory. + */ + cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); + if (cookiep) + cookie = *cookiep; + else + return (0); + /* + * Loop around doing readdir rpc's of size nm_readdirsize + * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ - while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { + while (more_dirs && bigenough) { nfsstats.rpccnt[NFSPROC_READDIR]++; - nfsm_reqhead(vp, NFSPROC_READDIR, - NFSX_FH + 2 * NFSX_UNSIGNED); - nfsm_fhtom(vp); - nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); - off = (u_long)uiop->uio_offset; - *tl++ = txdr_unsigned(off); - *tl = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? - nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); + nfsm_reqhead(vp, NFSPROC_READDIR, NFSX_FH(v3) + + NFSX_READDIR(v3)); + nfsm_fhtom(vp, v3); + if (v3) { + nfsm_build(tl, u_long *, 5 * NFSX_UNSIGNED); + *tl++ = cookie.nfsuquad[0]; + *tl++ = cookie.nfsuquad[1]; + *tl++ = dnp->n_cookieverf.nfsuquad[0]; + *tl++ = dnp->n_cookieverf.nfsuquad[1]; + } else { + nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); + *tl++ = cookie.nfsuquad[0]; + } + *tl = txdr_unsigned(nmp->nm_readdirsize); nfsm_request(vp, NFSPROC_READDIR, uiop->uio_procp, cred); - siz = 0; + if (v3) { + nfsm_postop_attr(vp, attrflag); + if (!error) { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + dnp->n_cookieverf.nfsuquad[0] = *tl++; + dnp->n_cookieverf.nfsuquad[1] = *tl; + } else { + m_freem(mrep); + goto nfsmout; + } + } nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); - /* Save the position so that we can do nfsm_mtouio() later */ - dpos2 = dpos; - md2 = md; - /* loop thru the dir entries, doctoring them to 4bsd form */ -#ifdef lint - dp = (struct dirent *)0; -#endif /* lint */ - while (more_dirs && siz < uiop->uio_resid) { - savoff = off; /* Hold onto offset and dp */ - savdp = dp; - nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); - dp = (struct dirent *)tl; - dp->d_fileno = fxdr_unsigned(u_long, *tl++); - len = fxdr_unsigned(int, *tl); + while (more_dirs && bigenough) { + if (v3) { + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + fxdr_hyper(tl, &fileno); + len = fxdr_unsigned(int, *(tl + 2)); + } else { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + fileno = fxdr_unsigned(u_quad_t, *tl++); + len = fxdr_unsigned(int, *tl); + } if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } - dp->d_namlen = (u_char)len; - dp->d_type = DT_UNKNOWN; - nfsm_adv(len); /* Point past name */ tlen = nfsm_rndup(len); - /* - * This should not be necessary, but some servers have - * broken XDR such that these bytes are not null filled. - */ - if (tlen != len) { - *dpos = '\0'; /* Null-terminate */ - nfsm_adv(tlen - len); - len = tlen; + if (tlen == len) + tlen += 4; /* To ensure null termination */ + left = DIRBLKSIZ - blksiz; + if ((tlen + DIRHDSIZ) > left) { + dp->d_reclen += left; + uiop->uio_iov->iov_base += left; + uiop->uio_iov->iov_len -= left; + uiop->uio_offset += left; + uiop->uio_resid -= left; + blksiz = 0; } - nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); - off = fxdr_unsigned(u_long, *tl); - *tl++ = 0; /* Ensures null termination of name */ + if ((tlen + DIRHDSIZ) > uiop->uio_resid) + bigenough = 0; + if (bigenough) { + dp = (struct dirent *)uiop->uio_iov->iov_base; + dp->d_fileno = (int)fileno; + dp->d_namlen = len; + dp->d_reclen = tlen + DIRHDSIZ; + dp->d_type = DT_UNKNOWN; + blksiz += dp->d_reclen; + if (blksiz == DIRBLKSIZ) + blksiz = 0; + uiop->uio_offset += DIRHDSIZ; + uiop->uio_resid -= DIRHDSIZ; + uiop->uio_iov->iov_base += DIRHDSIZ; + uiop->uio_iov->iov_len -= DIRHDSIZ; + nfsm_mtouio(uiop, len); + cp = uiop->uio_iov->iov_base; + tlen -= len; + *cp = '\0'; /* null terminate */ + uiop->uio_iov->iov_base += tlen; + uiop->uio_iov->iov_len -= tlen; + uiop->uio_offset += tlen; + uiop->uio_resid -= tlen; + } else + nfsm_adv(nfsm_rndup(len)); + if (v3) { + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + } else { + nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); + } + if (bigenough) { + cookie.nfsuquad[0] = *tl++; + if (v3) + cookie.nfsuquad[1] = *tl++; + } else if (v3) + tl += 2; + else + tl++; more_dirs = fxdr_unsigned(int, *tl); - dp->d_reclen = len + 4 * NFSX_UNSIGNED; - siz += dp->d_reclen; } /* * If at end of rpc data, get the eof boolean @@ -1713,189 +2129,224 @@ nfs_readdirrpc(vp, uiop, cred) if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); - - /* - * If at EOF, cache directory offset - */ - if (!more_dirs) - np->n_direofoffset = off; - } - /* - * If there is too much to fit in the data buffer, use savoff and - * savdp to trim off the last record. - * --> we are not at eof - */ - if (siz > uiop->uio_resid) { - off = savoff; - siz -= dp->d_reclen; - dp = savdp; - more_dirs = 0; /* Paranoia */ } - if (siz > 0) { - lastlen = dp->d_reclen; - md = md2; - dpos = dpos2; - nfsm_mtouio(uiop, siz); - uiop->uio_offset = (off_t)off; - } else - more_dirs = 0; /* Ugh, never happens, but in case.. */ m_freem(mrep); } /* - * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ + * Fill last record, iff any, out to a multiple of DIRBLKSIZ * by increasing d_reclen for the last record. */ - if (uiop->uio_resid < tresid) { - len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); - if (len > 0) { - dp = (struct dirent *) - (uiop->uio_iov->iov_base - lastlen); - dp->d_reclen += len; - uiop->uio_iov->iov_base += len; - uiop->uio_iov->iov_len -= len; - uiop->uio_resid -= len; - } + if (blksiz > 0) { + left = DIRBLKSIZ - blksiz; + dp->d_reclen += left; + uiop->uio_iov->iov_base += left; + uiop->uio_iov->iov_len -= left; + uiop->uio_offset += left; + uiop->uio_resid -= left; + } + + /* + * We are now either at the end of the directory or have filled the + * block. + */ + if (bigenough) + dnp->n_direofoffset = uiop->uio_offset; + else { + if (uiop->uio_resid > 0) + printf("EEK! readdirrpc resid > 0\n"); + cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); + *cookiep = cookie; } nfsmout: return (error); } /* - * Nqnfs readdir_and_lookup RPC. Used in place of nfs_readdirrpc(). + * NFS V3 readdir plus RPC. Used in place of nfs_readdirrpc(). */ int -nfs_readdirlookrpc(vp, uiop, cred) +nfs_readdirplusrpc(vp, uiop, cred) struct vnode *vp; register struct uio *uiop; struct ucred *cred; { - register int len; - register struct dirent *dp = 0; + register int len, left; + register struct dirent *dp; register u_long *tl; register caddr_t cp; - register long t1; - caddr_t bpos, dpos, cp2; - struct mbuf *mreq, *mrep, *md, *mb, *mb2; + register long t1, t2; + register struct vnode *newvp; + register nfsuint64 *cookiep; + caddr_t bpos, dpos, cp2, dpossav1, dpossav2; + struct mbuf *mreq, *mrep, *md, *mb, *mb2, *mdsav1, *mdsav2; struct nameidata nami, *ndp = &nami; struct componentname *cnp = &ndp->ni_cnd; - u_long off, endoff = 0, fileno; - time_t reqtime, ltime = 0; - struct nfsmount *nmp; - struct nfsnode *np; - struct vnode *newvp; - nfsv2fh_t *fhp; - u_quad_t frev; - int error = 0, tlen, more_dirs = 1, tresid, doit, bigenough, i; - int cachable = 0; - - if (uiop->uio_iovcnt != 1) - panic("nfs rdirlook"); - nmp = VFSTONFS(vp->v_mount); - tresid = uiop->uio_resid; + nfsuint64 cookie; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + struct nfsnode *dnp = VTONFS(vp), *np; + nfsfh_t *fhp; + u_quad_t frev, fileno; + int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; + int cachable, attrflag, fhsize; + +#ifndef nolint + dp = (struct dirent *)0; +#endif +#ifndef DIAGNOSTIC + if (uiop->uio_iovcnt != 1 || (uiop->uio_offset & (DIRBLKSIZ - 1)) || + (uiop->uio_resid & (DIRBLKSIZ - 1))) + panic("nfs readdirplusrpc bad uio"); +#endif ndp->ni_dvp = vp; newvp = NULLVP; + /* - * Loop around doing readdir rpc's of size uio_resid or nm_rsize, - * whichever is smaller, truncated to a multiple of NFS_DIRBLKSIZ. + * If there is no cookie, assume end of directory. + */ + cookiep = nfs_getcookie(dnp, uiop->uio_offset, 0); + if (cookiep) + cookie = *cookiep; + else + return (0); + /* + * Loop around doing readdir rpc's of size nm_readdirsize + * truncated to a multiple of DIRBLKSIZ. * The stopping criteria is EOF or buffer full. */ - while (more_dirs && uiop->uio_resid >= NFS_DIRBLKSIZ) { - nfsstats.rpccnt[NQNFSPROC_READDIRLOOK]++; - nfsm_reqhead(vp, NQNFSPROC_READDIRLOOK, - NFSX_FH + 3 * NFSX_UNSIGNED); - nfsm_fhtom(vp); - nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); - off = (u_long)uiop->uio_offset; - *tl++ = txdr_unsigned(off); - *tl++ = txdr_unsigned(((uiop->uio_resid > nmp->nm_rsize) ? - nmp->nm_rsize : uiop->uio_resid) & ~(NFS_DIRBLKSIZ-1)); - if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) - *tl = txdr_unsigned(nmp->nm_leaseterm); - else - *tl = 0; - reqtime = time.tv_sec; - nfsm_request(vp, NQNFSPROC_READDIRLOOK, uiop->uio_procp, cred); - nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + while (more_dirs && bigenough) { + nfsstats.rpccnt[NFSPROC_READDIRPLUS]++; + nfsm_reqhead(vp, NFSPROC_READDIRPLUS, + NFSX_FH(1) + 6 * NFSX_UNSIGNED); + nfsm_fhtom(vp, 1); + nfsm_build(tl, u_long *, 6 * NFSX_UNSIGNED); + *tl++ = cookie.nfsuquad[0]; + *tl++ = cookie.nfsuquad[1]; + *tl++ = dnp->n_cookieverf.nfsuquad[0]; + *tl++ = dnp->n_cookieverf.nfsuquad[1]; + *tl++ = txdr_unsigned(nmp->nm_readdirsize); + *tl = txdr_unsigned(nmp->nm_rsize); + nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_procp, cred); + nfsm_postop_attr(vp, attrflag); + if (error) { + m_freem(mrep); + goto nfsmout; + } + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + dnp->n_cookieverf.nfsuquad[0] = *tl++; + dnp->n_cookieverf.nfsuquad[1] = *tl++; more_dirs = fxdr_unsigned(int, *tl); /* loop thru the dir entries, doctoring them to 4bsd form */ - bigenough = 1; while (more_dirs && bigenough) { - doit = 1; - nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED); - if (nmp->nm_flag & NFSMNT_NQLOOKLEASE) { - cachable = fxdr_unsigned(int, *tl++); - ltime = reqtime + fxdr_unsigned(int, *tl++); - fxdr_hyper(tl, &frev); - } - nfsm_dissect(fhp, nfsv2fh_t *, NFSX_FH); - if (!bcmp(VTONFS(vp)->n_fh.fh_bytes, (caddr_t)fhp, NFSX_FH)) { - VREF(vp); - newvp = vp; - np = VTONFS(vp); - } else { - error = nfs_nget(vp->v_mount, fhp, &np); - if (error) - doit = 0; - newvp = NFSTOV(np); - } - error = nfs_loadattrcache(&newvp, &md, &dpos, - (struct vattr *)0); - if (error) - doit = 0; - nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); - fileno = fxdr_unsigned(u_long, *tl++); - len = fxdr_unsigned(int, *tl); + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + fxdr_hyper(tl, &fileno); + len = fxdr_unsigned(int, *(tl + 2)); if (len <= 0 || len > NFS_MAXNAMLEN) { error = EBADRPC; m_freem(mrep); goto nfsmout; } - tlen = (len + 4) & ~0x3; + tlen = nfsm_rndup(len); + if (tlen == len) + tlen += 4; /* To ensure null termination*/ + left = DIRBLKSIZ - blksiz; + if ((tlen + DIRHDSIZ) > left) { + dp->d_reclen += left; + uiop->uio_iov->iov_base += left; + uiop->uio_iov->iov_len -= left; + uiop->uio_offset += left; + uiop->uio_resid -= left; + blksiz = 0; + } if ((tlen + DIRHDSIZ) > uiop->uio_resid) bigenough = 0; - if (bigenough && doit) { + if (bigenough) { dp = (struct dirent *)uiop->uio_iov->iov_base; - dp->d_fileno = fileno; + dp->d_fileno = (int)fileno; dp->d_namlen = len; dp->d_reclen = tlen + DIRHDSIZ; - dp->d_type = - IFTODT(VTTOIF(np->n_vattr.va_type)); + dp->d_type = DT_UNKNOWN; + blksiz += dp->d_reclen; + if (blksiz == DIRBLKSIZ) + blksiz = 0; + uiop->uio_offset += DIRHDSIZ; uiop->uio_resid -= DIRHDSIZ; uiop->uio_iov->iov_base += DIRHDSIZ; uiop->uio_iov->iov_len -= DIRHDSIZ; cnp->cn_nameptr = uiop->uio_iov->iov_base; cnp->cn_namelen = len; - ndp->ni_vp = newvp; nfsm_mtouio(uiop, len); cp = uiop->uio_iov->iov_base; tlen -= len; - for (i = 0; i < tlen; i++) - *cp++ = '\0'; + *cp = '\0'; uiop->uio_iov->iov_base += tlen; uiop->uio_iov->iov_len -= tlen; + uiop->uio_offset += tlen; uiop->uio_resid -= tlen; + } else + nfsm_adv(nfsm_rndup(len)); + nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); + if (bigenough) { + cookie.nfsuquad[0] = *tl++; + cookie.nfsuquad[1] = *tl++; + } else + tl += 2; + + /* + * Since the attributes are before the file handle + * (sigh), we must skip over the attributes and then + * come back and get them. + */ + attrflag = fxdr_unsigned(int, *tl); + if (attrflag) { + dpossav1 = dpos; + mdsav1 = md; + nfsm_adv(NFSX_V3FATTR); + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + doit = fxdr_unsigned(int, *tl); + if (doit) { + nfsm_getfh(fhp, fhsize, 1); + if (NFS_CMPFH(dnp, fhp, fhsize)) { + VREF(vp); + newvp = vp; + np = dnp; + } else { + if (error = nfs_nget(vp->v_mount, fhp, + fhsize, &np)) + doit = 0; + else + newvp = NFSTOV(np); + } + } + if (doit) { + dpossav2 = dpos; + dpos = dpossav1; + mdsav2 = md; + md = mdsav1; + nfsm_loadattr(newvp, (struct vattr *)0); + dpos = dpossav2; + md = mdsav2; + dp->d_type = + IFTODT(VTTOIF(np->n_vattr.va_type)); + ndp->ni_vp = newvp; cnp->cn_hash = 0; - for (cp = cnp->cn_nameptr, i = 1; i <= len; i++, cp++) - cnp->cn_hash += (unsigned char)*cp * i; - if ((nmp->nm_flag & NFSMNT_NQLOOKLEASE) && - ltime > time.tv_sec) - nqnfs_clientlease(nmp, np, NQL_READ, - cachable, ltime, frev); + for (cp = cnp->cn_nameptr, i = 1; i <= len; + i++, cp++) + cnp->cn_hash += (unsigned char)*cp * i; if (cnp->cn_namelen <= NCHNAMLEN) cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); + } } else { - nfsm_adv(nfsm_rndup(len)); + /* Just skip over the file handle */ + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); + i = fxdr_unsigned(int, *tl); + nfsm_adv(nfsm_rndup(i)); } if (newvp != NULLVP) { - vrele(newvp); - newvp = NULLVP; + vrele(newvp); + newvp = NULLVP; } - nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); - if (bigenough) - endoff = off = fxdr_unsigned(u_long, *tl++); - else - endoff = fxdr_unsigned(u_long, *tl++); + nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = fxdr_unsigned(int, *tl); } /* @@ -1904,35 +2355,42 @@ nfs_readdirlookrpc(vp, uiop, cred) if (!more_dirs) { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); more_dirs = (fxdr_unsigned(int, *tl) == 0); - - /* - * If at EOF, cache directory offset - */ - if (!more_dirs) - VTONFS(vp)->n_direofoffset = endoff; } - if (uiop->uio_resid < tresid) - uiop->uio_offset = (off_t)off; - else - more_dirs = 0; m_freem(mrep); } /* * Fill last record, iff any, out to a multiple of NFS_DIRBLKSIZ * by increasing d_reclen for the last record. */ - if (uiop->uio_resid < tresid) { - len = uiop->uio_resid & (NFS_DIRBLKSIZ - 1); - if (len > 0) { - dp->d_reclen += len; - uiop->uio_iov->iov_base += len; - uiop->uio_iov->iov_len -= len; - uiop->uio_resid -= len; - } + if (blksiz > 0) { + left = DIRBLKSIZ - blksiz; + dp->d_reclen += left; + uiop->uio_iov->iov_base += left; + uiop->uio_iov->iov_len -= left; + uiop->uio_offset += left; + uiop->uio_resid -= left; + } + + /* + * We are now either at the end of the directory or have filled the + * block. + */ + if (bigenough) + dnp->n_direofoffset = uiop->uio_offset; + else { + if (uiop->uio_resid > 0) + printf("EEK! readdirplusrpc resid > 0\n"); + cookiep = nfs_getcookie(dnp, uiop->uio_offset, 1); + *cookiep = cookie; } nfsmout: - if (newvp != NULLVP) - vrele(newvp); + if (newvp != NULLVP) { + if (newvp == vp) + vrele(newvp); + else + vput(newvp); + newvp = NULLVP; + } return (error); } static char hextoasc[] = "0123456789abcdef"; @@ -1950,19 +2408,19 @@ nfs_sillyrename(dvp, vp, cnp) struct vnode *dvp, *vp; struct componentname *cnp; { - register struct nfsnode *np; register struct sillyrename *sp; + struct nfsnode *np; int error; short pid; cache_purge(dvp); np = VTONFS(vp); -#ifdef SILLYSEPARATE +#ifndef DIAGNOSTIC + if (vp->v_type == VDIR) + panic("nfs: sillyrename dir"); +#endif MALLOC(sp, struct sillyrename *, sizeof (struct sillyrename), M_NFSREQ, M_WAITOK); -#else - sp = &np->n_silly; -#endif sp->s_cred = crdup(cnp->cn_cred); sp->s_dvp = dvp; VREF(dvp); @@ -1977,65 +2435,149 @@ nfs_sillyrename(dvp, vp, cnp) sp->s_name[5] = hextoasc[(pid >> 12) & 0xf]; /* Try lookitups until we get one that isn't there */ - while (nfs_lookitup(sp, (nfsv2fh_t *)0, cnp->cn_proc) == 0) { + while (nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, + cnp->cn_proc, (struct nfsnode **)0) == 0) { sp->s_name[4]++; if (sp->s_name[4] > 'z') { error = EINVAL; goto bad; } } - error = nfs_renameit(dvp, cnp, sp); - if (error) + if (error = nfs_renameit(dvp, cnp, sp)) goto bad; - nfs_lookitup(sp, &np->n_fh, cnp->cn_proc); + error = nfs_lookitup(dvp, sp->s_name, sp->s_namlen, sp->s_cred, + cnp->cn_proc, &np); np->n_sillyrename = sp; return (0); bad: vrele(sp->s_dvp); crfree(sp->s_cred); -#ifdef SILLYSEPARATE free((caddr_t)sp, M_NFSREQ); -#endif return (error); } /* - * Look up a file name for silly rename stuff. - * Just like nfs_lookup() except that it doesn't load returned values - * into the nfsnode table. - * If fhp != NULL it copies the returned file handle out + * Look up a file name and optionally either update the file handle or + * allocate an nfsnode, depending on the value of npp. + * npp == NULL --> just do the lookup + * *npp == NULL --> allocate a new nfsnode and make sure attributes are + * handled too + * *npp != NULL --> update the file handle in the vnode */ int -nfs_lookitup(sp, fhp, procp) - register struct sillyrename *sp; - nfsv2fh_t *fhp; +nfs_lookitup(dvp, name, len, cred, procp, npp) + register struct vnode *dvp; + char *name; + int len; + struct ucred *cred; struct proc *procp; + struct nfsnode **npp; { - register struct vnode *vp = sp->s_dvp; register u_long *tl; register caddr_t cp; register long t1, t2; + struct vnode *newvp = (struct vnode *)0; + struct nfsnode *np, *dnp = VTONFS(dvp); caddr_t bpos, dpos, cp2; - int error = 0, isnq; + int error = 0, fhlen, attrflag; struct mbuf *mreq, *mrep, *md, *mb, *mb2; - long len; + nfsfh_t *nfhp; + int v3 = NFS_ISV3(dvp); - isnq = (VFSTONFS(vp->v_mount)->nm_flag & NFSMNT_NQNFS); nfsstats.rpccnt[NFSPROC_LOOKUP]++; - len = sp->s_namlen; - nfsm_reqhead(vp, NFSPROC_LOOKUP, NFSX_FH+NFSX_UNSIGNED+nfsm_rndup(len)); - if (isnq) { - nfsm_build(tl, u_long *, NFSX_UNSIGNED); - *tl = 0; + nfsm_reqhead(dvp, NFSPROC_LOOKUP, + NFSX_FH(v3) + NFSX_UNSIGNED + nfsm_rndup(len)); + nfsm_fhtom(dvp, v3); + nfsm_strtom(name, len, NFS_MAXNAMLEN); + nfsm_request(dvp, NFSPROC_LOOKUP, procp, cred); + if (npp && !error) { + nfsm_getfh(nfhp, fhlen, v3); + if (*npp) { + np = *npp; + if (np->n_fhsize > NFS_SMALLFH && fhlen <= NFS_SMALLFH) { + free((caddr_t)np->n_fhp, M_NFSBIGFH); + np->n_fhp = &np->n_fh; + } else if (np->n_fhsize <= NFS_SMALLFH && fhlen>NFS_SMALLFH) + np->n_fhp =(nfsfh_t *)malloc(fhlen,M_NFSBIGFH,M_WAITOK); + bcopy((caddr_t)nfhp, (caddr_t)np->n_fhp, fhlen); + np->n_fhsize = fhlen; + newvp = NFSTOV(np); + } else if (NFS_CMPFH(dnp, nfhp, fhlen)) { + VREF(dvp); + newvp = dvp; + } else { + error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np); + if (error) { + m_freem(mrep); + return (error); + } + newvp = NFSTOV(np); + } + if (v3) { + nfsm_postop_attr(newvp, attrflag); + if (!attrflag && *npp == NULL) { + m_freem(mrep); + if (newvp == dvp) + vrele(newvp); + else + vput(newvp); + return (ENOENT); + } + } else + nfsm_loadattr(newvp, (struct vattr *)0); } - nfsm_fhtom(vp); - nfsm_strtom(sp->s_name, len, NFS_MAXNAMLEN); - nfsm_request(vp, NFSPROC_LOOKUP, procp, sp->s_cred); - if (fhp != NULL) { - if (isnq) - nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); - nfsm_dissect(cp, caddr_t, NFSX_FH); - bcopy(cp, (caddr_t)fhp, NFSX_FH); + nfsm_reqdone; + if (npp && *npp == NULL) { + if (error) { + if (newvp) + if (newvp == dvp) + vrele(newvp); + else + vput(newvp); + } else + *npp = np; + } + return (error); +} + +/* + * Nfs Version 3 commit rpc + */ +int +nfs_commit(vp, offset, cnt, cred, procp) + register struct vnode *vp; + u_quad_t offset; + int cnt; + struct ucred *cred; + struct proc *procp; +{ + register caddr_t cp; + register u_long *tl; + register int t1, t2; + register struct nfsmount *nmp = VFSTONFS(vp->v_mount); + caddr_t bpos, dpos, cp2; + int error = 0, wccflag = NFSV3_WCCRATTR; + struct mbuf *mreq, *mrep, *md, *mb, *mb2; + + if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0) + return (0); + nfsstats.rpccnt[NFSPROC_COMMIT]++; + nfsm_reqhead(vp, NFSPROC_COMMIT, NFSX_FH(1)); + nfsm_fhtom(vp, 1); + nfsm_build(tl, u_long *, 3 * NFSX_UNSIGNED); + txdr_hyper(&offset, tl); + tl += 2; + *tl = txdr_unsigned(cnt); + nfsm_request(vp, NFSPROC_COMMIT, procp, cred); + nfsm_wcc_data(vp, wccflag); + if (!error) { + nfsm_dissect(tl, u_long *, NFSX_V3WRITEVERF); + if (bcmp((caddr_t)nmp->nm_verf, (caddr_t)tl, + NFSX_V3WRITEVERF)) { + bcopy((caddr_t)tl, (caddr_t)nmp->nm_verf, + NFSX_V3WRITEVERF); + error = NFSERR_STALEWRITEVERF; + } } nfsm_reqdone; return (error); @@ -2044,9 +2586,7 @@ nfs_lookitup(sp, fhp, procp) /* * Kludge City.. * - make nfs_bmap() essentially a no-op that does no translation - * - do nfs_strategy() by faking physical I/O with nfs_readrpc/nfs_writerpc - * after mapping the physical addresses into Kernel Virtual space in the - * nfsiobuf area. + * - do nfs_strategy() by doing I/O with nfs_readrpc/nfs_writerpc * (Maybe I could use the process's page mapping, but I was concerned that * Kernel Write might not be enabled and also figured copyout() would do * a lot more work than bcopy() and also it currently happens in the @@ -2088,8 +2628,8 @@ nfs_strategy(ap) struct proc *p; int error = 0; - if ((bp->b_flags & (B_PHYS|B_ASYNC)) == (B_PHYS|B_ASYNC)) - panic("nfs physio/async"); + if (bp->b_flags & B_PHYS) + panic("nfs physio"); if (bp->b_flags & B_ASYNC) p = (struct proc *)0; else @@ -2129,9 +2669,7 @@ nfs_mmap(ap) } /* - * Flush all the blocks associated with a vnode. - * Walk through the buffer pool and push any dirty pages - * associated with the vnode. + * fsync vnode op. Just call nfs_flush() with commit == 1. */ /* ARGSUSED */ int @@ -2144,29 +2682,124 @@ nfs_fsync(ap) struct proc * a_p; } */ *ap; { - register struct vnode *vp = ap->a_vp; + + return (nfs_flush(ap->a_vp, ap->a_cred, ap->a_waitfor, ap->a_p, 1)); +} + +/* + * Flush all the blocks associated with a vnode. + * Walk through the buffer pool and push any dirty pages + * associated with the vnode. + */ +int +nfs_flush(vp, cred, waitfor, p, commit) + register struct vnode *vp; + struct ucred *cred; + int waitfor; + struct proc *p; + int commit; +{ register struct nfsnode *np = VTONFS(vp); register struct buf *bp; + register int i; struct buf *nbp; - struct nfsmount *nmp; - int s, error = 0, slptimeo = 0, slpflag = 0; + struct nfsmount *nmp = VFSTONFS(vp->v_mount); + int s, error = 0, slptimeo = 0, slpflag = 0, retv, bvecpos; + int passone = 1; + u_quad_t off = (u_quad_t)-1, endoff = 0, toff; +#ifndef NFS_COMMITBVECSIZ +#define NFS_COMMITBVECSIZ 20 +#endif + struct buf *bvec[NFS_COMMITBVECSIZ]; - nmp = VFSTONFS(vp->v_mount); if (nmp->nm_flag & NFSMNT_INT) slpflag = PCATCH; + if (!commit) + passone = 0; + /* + * A b_flags == (B_DELWRI | B_NEEDCOMMIT) block has been written to the + * server, but nas not been committed to stable storage on the server + * yet. On the first pass, the byte range is worked out and the commit + * rpc is done. On the second pass, nfs_writebp() is called to do the + * job. + */ +again: + bvecpos = 0; + if (NFS_ISV3(vp) && commit) { + s = splbio(); + for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { + nbp = bp->b_vnbufs.le_next; + if (bvecpos >= NFS_COMMITBVECSIZ) + break; + if ((bp->b_flags & (B_BUSY | B_DELWRI | B_NEEDCOMMIT)) + != (B_DELWRI | B_NEEDCOMMIT)) + continue; + bremfree(bp); + bp->b_flags |= (B_BUSY | B_WRITEINPROG); + vfs_busy_pages(bp, 1); + /* + * A list of these buffers is kept so that the + * second loop knows which buffers have actually + * been committed. This is necessary, since there + * may be a race between the commit rpc and new + * uncommitted writes on the file. + */ + bvec[bvecpos++] = bp; + toff = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + + bp->b_dirtyoff; + if (toff < off) + off = toff; + toff += (u_quad_t)(bp->b_dirtyend - bp->b_dirtyoff); + if (toff > endoff) + endoff = toff; + } + splx(s); + } + if (bvecpos > 0) { + /* + * Commit data on the server, as required. + */ + retv = nfs_commit(vp, off, (int)(endoff - off), cred, p); + if (retv == NFSERR_STALEWRITEVERF) + nfs_clearcommit(vp->v_mount); + /* + * Now, either mark the blocks I/O done or mark the + * blocks dirty, depending on whether the commit + * succeeded. + */ + for (i = 0; i < bvecpos; i++) { + bp = bvec[i]; + bp->b_flags &= ~(B_NEEDCOMMIT | B_WRITEINPROG); + if (retv) { + brelse(bp); + vfs_unbusy_pages(bp); + } else { + vp->v_numoutput++; + bp->b_flags |= B_ASYNC; + bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); + bp->b_dirtyoff = bp->b_dirtyend = 0; + reassignbuf(bp, vp); + biodone(bp); + } + } + } + + /* + * Start/do any write(s) that are required. + */ loop: s = splbio(); for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if (bp->b_flags & B_BUSY) { - if (ap->a_waitfor != MNT_WAIT) + if (waitfor != MNT_WAIT || passone) continue; bp->b_flags |= B_WANTED; error = tsleep((caddr_t)bp, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); splx(s); if (error) { - if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) + if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; @@ -2177,21 +2810,29 @@ loop: } if ((bp->b_flags & B_DELWRI) == 0) panic("nfs_fsync: not dirty"); + if ((passone || !commit) && (bp->b_flags & B_NEEDCOMMIT)) + continue; bremfree(bp); - bp->b_flags |= B_BUSY; + if (passone || !commit) + bp->b_flags |= (B_BUSY|B_ASYNC); + else + bp->b_flags |= (B_BUSY|B_ASYNC|B_WRITEINPROG|B_NEEDCOMMIT); splx(s); - bp->b_flags |= B_ASYNC; VOP_BWRITE(bp); goto loop; } splx(s); - if (ap->a_waitfor == MNT_WAIT) { + if (passone) { + passone = 0; + goto again; + } + if (waitfor == MNT_WAIT) { while (vp->v_numoutput) { vp->v_flag |= VBWAIT; error = tsleep((caddr_t)&vp->v_numoutput, slpflag | (PRIBIO + 1), "nfsfsync", slptimeo); if (error) { - if (nfs_sigintr(nmp, (struct nfsreq *)0, ap->a_p)) + if (nfs_sigintr(nmp, (struct nfsreq *)0, p)) return (EINTR); if (slpflag == PCATCH) { slpflag = 0; @@ -2199,7 +2840,7 @@ loop: } } } - if (vp->v_dirtyblkhd.lh_first) { + if (vp->v_dirtyblkhd.lh_first && commit) { goto loop; } } @@ -2207,14 +2848,15 @@ loop: error = np->n_error; np->n_flag &= ~NWRITEERR; } + np->n_flag &= ~NMODIFIED; return (error); } /* * Return POSIX pathconf information applicable to nfs. * - * Currently the NFS protocol does not support getting such - * information from the remote server. + * The NFS V2 protocol doesn't support this, so just return EINVAL + * for V2. */ /* ARGSUSED */ int @@ -2243,6 +2885,7 @@ nfs_advlock(ap) int a_flags; } */ *ap; { +#ifdef __FreeBSD__ register struct nfsnode *np = VTONFS(ap->a_vp); /* @@ -2251,6 +2894,9 @@ nfs_advlock(ap) * that this is a local lock. */ return (lf_advlock(ap, &(np->n_lockf), np->n_size)); +#else + return (EOPNOTSUPP); +#endif } /* @@ -2363,6 +3009,81 @@ nfs_update(ap) } /* + * Just call nfs_writebp() with the force argument set to 1. + */ +int +nfs_bwrite(ap) + struct vop_bwrite_args /* { + struct vnode *a_bp; + } */ *ap; +{ + + return (nfs_writebp(ap->a_bp, 1)); +} + +/* + * This is a clone of vn_bwrite(), except that B_WRITEINPROG isn't set unless + * the force flag is one and it also handles the B_NEEDCOMMIT flag. + */ +int +nfs_writebp(bp, force) + register struct buf *bp; + int force; +{ + register int oldflags = bp->b_flags, retv = 1; + off_t off; + + if(!(bp->b_flags & B_BUSY)) + panic("bwrite: buffer is not busy???"); + + bp->b_flags &= ~(B_READ|B_DONE|B_ERROR|B_DELWRI); + + if ((oldflags & (B_ASYNC|B_DELWRI)) == (B_ASYNC|B_DELWRI)) { + reassignbuf(bp, bp->b_vp); + } + + bp->b_vp->v_numoutput++; + curproc->p_stats->p_ru.ru_oublock++; + + /* + * If B_NEEDCOMMIT is set, a commit rpc may do the trick. If not + * an actual write will have to be scheduled via. VOP_STRATEGY(). + * If B_WRITEINPROG is already set, then push it with a write anyhow. + */ + if (oldflags & (B_NEEDCOMMIT | B_WRITEINPROG) == B_NEEDCOMMIT) { + off = ((u_quad_t)bp->b_blkno) * DEV_BSIZE + bp->b_dirtyoff; + bp->b_flags |= B_WRITEINPROG; + retv = nfs_commit(bp->b_vp, off, bp->b_dirtyend-bp->b_dirtyoff, + bp->b_wcred, bp->b_proc); + bp->b_flags &= ~B_WRITEINPROG; + if (!retv) { + bp->b_dirtyoff = bp->b_dirtyend = 0; + bp->b_flags &= ~B_NEEDCOMMIT; + biodone(bp); + } else if (retv == NFSERR_STALEWRITEVERF) + nfs_clearcommit(bp->b_vp->v_mount); + } + if (retv) { + if (force) + bp->b_flags |= B_WRITEINPROG; + vfs_busy_pages(bp, 1); + VOP_STRATEGY(bp); + } + + if( (oldflags & B_ASYNC) == 0) { + int rtval = biowait(bp); + + if (oldflags & B_DELWRI) { + reassignbuf(bp, bp->b_vp); + } + brelse(bp); + return (rtval); + } + + return (0); +} + +/* * nfs special file access vnode op. * Essentially just get vattr and then imitate iaccess() since the device is * local to the client. @@ -2409,7 +3130,8 @@ nfsspec_access(ap) found: ; } - return ((vap->va_mode & mode) == mode ? 0 : EACCES); + error = (vap->va_mode & mode) == mode ? 0 : EACCES; + return (error); } /* @@ -2430,7 +3152,8 @@ nfsspec_read(ap) * Set access flag. */ np->n_flag |= NACC; - np->n_atim = time; + np->n_atim.ts_sec = time.tv_sec; + np->n_atim.ts_nsec = time.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_read), ap)); } @@ -2452,7 +3175,8 @@ nfsspec_write(ap) * Set update flag. */ np->n_flag |= NUPD; - np->n_mtim = time; + np->n_mtim.ts_sec = time.tv_sec; + np->n_mtim.ts_nsec = time.tv_usec * 1000; return (VOCALL(spec_vnodeop_p, VOFFSET(vop_write), ap)); } @@ -2479,16 +3203,10 @@ nfsspec_close(ap) if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); - if (np->n_flag & NACC) { - vattr.va_atime.ts_sec = np->n_atim.tv_sec; - vattr.va_atime.ts_nsec = - np->n_atim.tv_usec * 1000; - } - if (np->n_flag & NUPD) { - vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; - vattr.va_mtime.ts_nsec = - np->n_mtim.tv_usec * 1000; - } + if (np->n_flag & NACC) + vattr.va_atime = np->n_atim; + if (np->n_flag & NUPD) + vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } @@ -2513,7 +3231,8 @@ nfsfifo_read(ap) * Set access flag. */ np->n_flag |= NACC; - np->n_atim = time; + np->n_atim.ts_sec = time.tv_sec; + np->n_atim.ts_nsec = time.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap)); } @@ -2535,7 +3254,8 @@ nfsfifo_write(ap) * Set update flag. */ np->n_flag |= NUPD; - np->n_mtim = time; + np->n_mtim.ts_sec = time.tv_sec; + np->n_mtim.ts_nsec = time.tv_usec * 1000; return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap)); } @@ -2558,24 +3278,22 @@ nfsfifo_close(ap) struct vattr vattr; if (np->n_flag & (NACC | NUPD)) { - if (np->n_flag & NACC) - np->n_atim = time; - if (np->n_flag & NUPD) - np->n_mtim = time; + if (np->n_flag & NACC) { + np->n_atim.ts_sec = time.tv_sec; + np->n_atim.ts_nsec = time.tv_usec * 1000; + } + if (np->n_flag & NUPD) { + np->n_mtim.ts_sec = time.tv_sec; + np->n_mtim.ts_nsec = time.tv_usec * 1000; + } np->n_flag |= NCHG; if (vp->v_usecount == 1 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { VATTR_NULL(&vattr); - if (np->n_flag & NACC) { - vattr.va_atime.ts_sec = np->n_atim.tv_sec; - vattr.va_atime.ts_nsec = - np->n_atim.tv_usec * 1000; - } - if (np->n_flag & NUPD) { - vattr.va_mtime.ts_sec = np->n_mtim.tv_sec; - vattr.va_mtime.ts_nsec = - np->n_mtim.tv_usec * 1000; - } + if (np->n_flag & NACC) + vattr.va_atime = np->n_atim; + if (np->n_flag & NUPD) + vattr.va_mtime = np->n_mtim; (void)VOP_SETATTR(vp, &vattr, ap->a_cred, ap->a_p); } } diff --git a/sys/nfsclient/nfsargs.h b/sys/nfsclient/nfsargs.h index cbf80c6..e1a0f07 100644 --- a/sys/nfsclient/nfsargs.h +++ b/sys/nfsclient/nfsargs.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/nfsclient/nfsdiskless.h b/sys/nfsclient/nfsdiskless.h index 5df85e0..f7371dc 100644 --- a/sys/nfsclient/nfsdiskless.h +++ b/sys/nfsclient/nfsdiskless.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfsdiskless.h 8.1 (Berkeley) 6/10/93 - * $Id: nfsdiskless.h,v 1.3 1994/08/05 09:28:49 davidg Exp $ + * $Id: nfsdiskless.h,v 1.4 1994/08/21 06:50:09 paul Exp $ */ #ifndef _NFS_NFSDISKLESS_H_ @@ -52,17 +52,45 @@ * NB: All fields are stored in net byte order to avoid hassles with * client/server byte ordering differences. */ + +/* + * I have defined a new structure that can handle an NFS Version 3 file handle + * but the kernel still expects the old Version 2 one to be provided. The + * changes required in nfs_vfsops.c for using the new are documented there in + * comments. (I felt that breaking network booting code by changing this + * structure would not be prudent at this time, since almost all servers are + * still Version 2 anyhow.) + */ +struct nfsv3_diskless { + struct ifaliasreq myif; /* Default interface */ + struct sockaddr_in mygateway; /* Default gateway */ + struct nfs_args swap_args; /* Mount args for swap file */ + int swap_fhsize; /* Size of file handle */ + u_char swap_fh[NFSX_V3FHMAX]; /* Swap file's file handle */ + struct sockaddr_in swap_saddr; /* Address of swap server */ + char swap_hostnam[MNAMELEN]; /* Host name for mount pt */ + int swap_nblks; /* Size of server swap file */ + struct ucred swap_ucred; /* Swap credentials */ + struct nfs_args root_args; /* Mount args for root fs */ + int root_fhsize; /* Size of root file handle */ + u_char root_fh[NFSX_V3FHMAX]; /* File handle of root dir */ + struct sockaddr_in root_saddr; /* Address of root server */ + char root_hostnam[MNAMELEN]; /* Host name for mount pt */ + long root_time; /* Timestamp of root fs */ + char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */ +}; + struct nfs_diskless { struct ifaliasreq myif; /* Default interface */ struct sockaddr_in mygateway; /* Default gateway */ struct nfs_args swap_args; /* Mount args for swap file */ - u_char swap_fh[NFS_FHSIZE]; /* Swap file's file handle */ + u_char swap_fh[NFSX_V2FH]; /* Swap file's file handle */ struct sockaddr_in swap_saddr; /* Address of swap server */ char swap_hostnam[MNAMELEN]; /* Host name for mount pt */ int swap_nblks; /* Size of server swap file */ struct ucred swap_ucred; /* Swap credentials */ struct nfs_args root_args; /* Mount args for root fs */ - u_char root_fh[NFS_FHSIZE]; /* File handle of root dir */ + u_char root_fh[NFSX_V2FH]; /* File handle of root dir */ struct sockaddr_in root_saddr; /* Address of root server */ char root_hostnam[MNAMELEN]; /* Host name for mount pt */ long root_time; /* Timestamp of root fs */ diff --git a/sys/nfsclient/nfsm_subs.h b/sys/nfsclient/nfsm_subs.h index 030a985..9e89c57 100644 --- a/sys/nfsclient/nfsm_subs.h +++ b/sys/nfsclient/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/nfsclient/nfsmount.h b/sys/nfsclient/nfsmount.h index 99a1b48..6ac33f7 100644 --- a/sys/nfsclient/nfsmount.h +++ b/sys/nfsclient/nfsmount.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * @(#)nfsmount.h 8.1 (Berkeley) 6/10/93 - * $Id: nfsmount.h,v 1.3 1994/08/21 06:50:10 paul Exp $ + * $Id: nfsmount.h,v 1.4 1994/10/17 17:47:43 phk Exp $ */ #ifndef _NFS_NFSMOUNT_H_ @@ -49,7 +49,8 @@ struct nfsmount { int nm_flag; /* Flags for soft/hard... */ struct mount *nm_mountp; /* Vfs structure for this filesystem */ int nm_numgrps; /* Max. size of groupslist */ - nfsv2fh_t nm_fh; /* File handle of root dir */ + u_char nm_fh[NFSX_V3FHMAX]; /* File handle of root dir */ + int nm_fhsize; /* Size of root file handle */ struct socket *nm_so; /* Rpc socket */ int nm_sotype; /* Type of socket */ int nm_soproto; /* and protocol */ @@ -65,6 +66,7 @@ struct nfsmount { int nm_deadthresh; /* Threshold of timeouts-->dead server*/ int nm_rsize; /* Max size of read rpc */ int nm_wsize; /* Max size of write rpc */ + int nm_readdirsize; /* Size of a readdir rpc */ int nm_readahead; /* Num. of blocks to readahead */ int nm_leaseterm; /* Term (sec) for NQNFS lease */ CIRCLEQ_HEAD(, nfsnode) nm_timerhead; /* Head of lease timer queue */ @@ -73,9 +75,16 @@ struct nfsmount { int nm_authtype; /* Authenticator type */ int nm_authlen; /* and length */ char *nm_authstr; /* Authenticator string */ + char *nm_verfstr; /* and the verifier */ + int nm_verflen; + u_char nm_verf[NFSX_V3WRITEVERF]; /* V3 write verifier */ + NFSKERBKEY_T nm_key; /* and the session key */ + int nm_numuids; /* Number of nfsuid mappings */ + TAILQ_HEAD(, nfsuid) nm_uidlruhead; /* Lists of nfsuid mappings */ + LIST_HEAD(, nfsuid) nm_uidhashtbl[NFS_MUIDHASHSIZ]; }; -#ifdef KERNEL +#if defined(KERNEL) || defined(_KERNEL) /* * Convert mount ptr to nfsmount ptr. */ diff --git a/sys/nfsclient/nfsnode.h b/sys/nfsclient/nfsnode.h index dc9162b..18dbde2 100644 --- a/sys/nfsclient/nfsnode.h +++ b/sys/nfsclient/nfsnode.h @@ -34,12 +34,16 @@ * SUCH DAMAGE. * * @(#)nfsnode.h 8.4 (Berkeley) 2/13/94 - * $Id: nfsnode.h,v 1.8 1994/10/17 17:47:44 phk Exp $ + * $Id: nfsnode.h,v 1.9 1995/03/16 18:15:42 bde Exp $ */ #ifndef _NFS_NFSNODE_H_ #define _NFS_NFSNODE_H_ +#ifndef _NFS_NFS_H_ +#include <nfs/nfs.h> +#endif + /* * Silly rename structure that hangs off the nfsnode until the name * can be removed by nfs_inactive() @@ -52,36 +56,73 @@ struct sillyrename { }; /* + * This structure is used to save the logical directory offset to + * NFS cookie mappings. + * The mappings are stored in a list headed + * by n_cookies, as required. + * There is one mapping for each NFS_DIRBLKSIZ bytes of directory information + * stored in increasing logical offset byte order. + */ +#define NFSNUMCOOKIES 31 + +struct nfsdmap { + LIST_ENTRY(nfsdmap) ndm_list; + int ndm_eocookie; + nfsuint64 ndm_cookies[NFSNUMCOOKIES]; +}; + +/* * The nfsnode is the nfs equivalent to ufs's inode. Any similarity * is purely coincidental. * There is a unique nfsnode allocated for each active file, * each current directory, each mounted-on file, text file, and the root. * An nfsnode is 'named' by its file handle. (nget/nfs_node.c) + * If this structure exceeds 256 bytes (it is currently 256 using 4.4BSD-Lite + * type definitions), file handles of > 32 bytes should probably be split out + * into a separate MALLOC()'d data structure. (Reduce the size of nfsfh_t by + * changing the definition in sys/mount.h of NFS_SMALLFH.) + * NB: Hopefully the current order of the fields is such that everything will + * be well aligned and, therefore, tightly packed. */ - struct nfsnode { - LIST_ENTRY(nfsnode) n_hash; /* Hash chain */ - CIRCLEQ_ENTRY(nfsnode) n_timer; /* Nqnfs timer chain */ - nfsv2fh_t n_fh; /* NFS File Handle */ - long n_flag; /* Flag for locking.. */ - struct vnode *n_vnode; /* vnode associated with this node */ - struct vattr n_vattr; /* Vnode attribute cache */ - time_t n_attrstamp; /* Time stamp for cached attributes */ - struct sillyrename *n_sillyrename; /* Ptr to silly rename struct */ - u_quad_t n_size; /* Current size of file */ - struct lockf *n_lockf; /* Locking record of file */ - int n_error; /* Save write error value */ - u_long n_direofoffset; /* Dir. EOF offset cache */ - time_t n_mtime; /* Prev modify time. */ - time_t n_ctime; /* Prev create time. */ - u_quad_t n_brev; /* Modify rev when cached */ - u_quad_t n_lrev; /* Modify rev for lease */ - time_t n_expiry; /* Lease expiry time */ - struct sillyrename n_silly; /* Silly rename struct */ - struct timeval n_atim; /* Special file times */ - struct timeval n_mtim; + LIST_ENTRY(nfsnode) n_hash; /* Hash chain */ + CIRCLEQ_ENTRY(nfsnode) n_timer; /* Nqnfs timer chain */ + u_quad_t n_size; /* Current size of file */ + u_quad_t n_brev; /* Modify rev when cached */ + u_quad_t n_lrev; /* Modify rev for lease */ + struct vattr n_vattr; /* Vnode attribute cache */ + time_t n_attrstamp; /* Attr. cache timestamp */ + time_t n_mtime; /* Prev modify time. */ + time_t n_ctime; /* Prev create time. */ + time_t n_expiry; /* Lease expiry time */ + nfsfh_t *n_fhp; /* NFS File Handle */ + struct vnode *n_vnode; /* associated vnode */ + struct lockf *n_lockf; /* Locking record of file */ + int n_error; /* Save write error value */ + union { + struct timespec nf_atim; /* Special file times */ + nfsuint64 nd_cookieverf; /* Cookie verifier (dir only) */ + } n_un1; + union { + struct timespec nf_mtim; + off_t nd_direof; /* Dir. EOF offset cache */ + } n_un2; + union { + struct sillyrename *nf_silly; /* Ptr to silly rename struct */ + LIST_HEAD(, nfsdmap) nd_cook; /* cookies */ + } n_un3; + short n_fhsize; /* size in bytes, of fh */ + short n_flag; /* Flag for locking.. */ + nfsfh_t n_fh; /* Small File Handle */ }; +#define n_atim n_un1.nf_atim +#define n_mtim n_un2.nf_mtim +#define n_sillyrename n_un3.nf_silly +#define n_cookieverf n_un1.nd_cookieverf +#define n_direofoffset n_un2.nd_direof +#define n_cookies n_un3.nd_cook + /* * Flags for n_flag */ @@ -95,6 +136,8 @@ struct nfsnode { #define NACC 0x0100 /* Special file accessed */ #define NUPD 0x0200 /* Special file updated */ #define NCHG 0x0400 /* Special file times changed */ +#define NLOCKED 0x0800 /* node is locked */ +#define NWANTED 0x0100 /* someone wants to lock */ /* * Convert between nfsnode pointers and vnode pointers @@ -107,7 +150,7 @@ struct nfsnode { */ TAILQ_HEAD(, buf) nfs_bufq; -#ifdef KERNEL +#if defined(KERNEL) || defined(_KERNEL) extern int (**fifo_nfsv2nodeop_p)(); extern int (**nfsv2_vnodeop_p)(); extern int (**spec_nfsv2nodeop_p)(); @@ -128,12 +171,25 @@ int nfs_getattr __P((struct vop_getattr_args *)); int nfs_setattr __P((struct vop_setattr_args *)); int nfs_read __P((struct vop_read_args *)); int nfs_write __P((struct vop_write_args *)); +#ifdef HAS_VOPLEASE +#define nfs_lease_check ((int (*) __P((struct vop_lease_args *)))nullop) +#define nqnfs_vop_lease_check lease_check +#else +#ifdef __FreeBSD__ +#define nqnfs_lease_check nfs_lease_check +#else +#define nqnfs_lease_check lease_check +#endif +#endif int nfsspec_read __P((struct vop_read_args *)); int nfsspec_write __P((struct vop_write_args *)); int nfsfifo_read __P((struct vop_read_args *)); int nfsfifo_write __P((struct vop_write_args *)); #define nfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))enoioctl) #define nfs_select ((int (*) __P((struct vop_select_args *)))seltrue) +#ifdef HAS_VOPREVOKE +#define nfs_revoke vop_revoke +#endif int nfs_mmap __P((struct vop_mmap_args *)); int nfs_fsync __P((struct vop_fsync_args *)); #define nfs_seek ((int (*) __P((struct vop_seek_args *)))nullop) @@ -157,6 +213,7 @@ int nfs_islocked __P((struct vop_islocked_args *)); int nfs_pathconf __P((struct vop_pathconf_args *)); int nfs_advlock __P((struct vop_advlock_args *)); int nfs_blkatoff __P((struct vop_blkatoff_args *)); +int nfs_bwrite __P((struct vop_bwrite_args *)); int nfs_vget __P((struct mount *, ino_t, struct vnode **)); int nfs_valloc __P((struct vop_valloc_args *)); #define nfs_reallocblks \ @@ -164,13 +221,20 @@ int nfs_valloc __P((struct vop_valloc_args *)); int nfs_vfree __P((struct vop_vfree_args *)); int nfs_truncate __P((struct vop_truncate_args *)); int nfs_update __P((struct vop_update_args *)); -int nfs_bwrite __P((struct vop_bwrite_args *)); /* other stuff */ int nfs_removeit __P((struct sillyrename *)); -int nfs_nget __P((struct mount *,nfsv2fh_t *,struct nfsnode **)); -int nfs_lookitup __P((struct sillyrename *,nfsv2fh_t *,struct proc *)); +int nfs_nget __P((struct mount *,nfsfh_t *,int,struct nfsnode **)); +int nfs_lookitup __P((struct vnode *,char *,int,struct ucred *,struct proc *,struct nfsnode **)); int nfs_sillyrename __P((struct vnode *,struct vnode *,struct componentname *)); +nfsuint64 *nfs_getcookie __P((struct nfsnode *, off_t, int)); +void nfs_invaldir __P((struct vnode *)); + +#ifdef __FreeBSD__ +#define nqnfs_lease_updatetime nfs_lease_updatetime +#else +#define nqnfs_lease_updatetime lease_updatetime +#endif #endif /* KERNEL */ diff --git a/sys/nfsclient/nfsstats.h b/sys/nfsclient/nfsstats.h index cbf80c6..e1a0f07 100644 --- a/sys/nfsclient/nfsstats.h +++ b/sys/nfsclient/nfsstats.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 */ |