* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* %sccs.include.redist.c%
* @(#)nfs_subs.c 7.45 (Berkeley) %G%
* These functions support the macros and help fiddle mbuf chains for
* the nfs op functions. They do things like create the rpc header and
* copy data between mbuf chains and uio lists.
#include "ufs/ufs/quota.h"
#include "ufs/ufs/inode.h"
#include "ufs/ufs/ufsmount.h"
* 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 rpc_call
, rpc_vers
, rpc_reply
, rpc_msgdenied
, rpc_autherr
,
rpc_mismatch
, rpc_auth_unix
, rpc_msgaccepted
, rpc_rejectedcred
,
u_long nfs_vers
, nfs_prog
, nfs_true
, nfs_false
;
/* And other global data */
static u_long nfs_xid
= 0;
enum vtype ntov_type
[7] = { VNON
, VREG
, VDIR
, VBLK
, VCHR
, VLNK
, VNON
};
extern struct proc
*nfs_iodwant
[NFS_MAXASYNCDAEMON
];
extern struct nfsreq nfsreqh
;
extern int nqnfs_piggy
[NFS_NPROCS
];
extern struct nfsrtt nfsrtt
;
extern union nqsrvthead nqthead
;
extern union nqsrvthead nqfhead
[NQLCHSZ
];
extern time_t nqnfsstarttime
;
extern u_long nqnfs_prog
, nqnfs_vers
;
extern int nqsrv_clockskew
;
extern int nqsrv_writeslack
;
extern int nqsrv_maxlease
;
* Create the header for an rpc request packet
* The hsiz is the size of the rest of the nfs request header.
* (just used to decide if a cluster is a good idea)
nfsm_reqh(vp
, procid
, hsiz
, bposp
)
register struct mbuf
*mb
;
MGET(mb
, M_WAIT
, MT_DATA
);
bpos
= mtod(mb
, caddr_t
);
* For NQNFS, add lease request.
nmp
= VFSTONFS(vp
->v_mount
);
if (nmp
->nm_flag
& NFSMNT_NQNFS
) {
nqflag
= NQNFS_NEEDLEASE(vp
, procid
);
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(nqflag
);
*tl
= txdr_unsigned(nmp
->nm_leaseterm
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
/* Finally, return values */
* Build the RPC header and fill in the authorization info.
* The authorization string argument is only used when the credentials
* come from outside of the kernel.
* Returns the head of the mbuf list.
nfsm_rpchead(cr
, nqnfs
, procid
, auth_type
, auth_len
, auth_str
, mrest
,
register struct ucred
*cr
;
register struct mbuf
*mb
;
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
) {
} else if ((authsiz
+ 10*NFSX_UNSIGNED
) < MHLEN
) {
MH_ALIGN(mb
, authsiz
+ 10*NFSX_UNSIGNED
);
MH_ALIGN(mb
, 8*NFSX_UNSIGNED
);
bpos
= mtod(mb
, caddr_t
);
nfsm_build(tl
, u_long
*, 8*NFSX_UNSIGNED
);
*tl
++ = *xidp
= txdr_unsigned(nfs_xid
);
*tl
++ = txdr_unsigned(NQNFS_PROG
);
*tl
++ = txdr_unsigned(NQNFS_VER1
);
*tl
++ = txdr_unsigned(NFS_PROG
);
*tl
++ = txdr_unsigned(NFS_VER2
);
*tl
++ = txdr_unsigned(procid
);
* And then the authorization cred.
*tl
++ = txdr_unsigned(auth_type
);
*tl
= txdr_unsigned(authsiz
);
nfsm_build(tl
, u_long
*, auth_len
);
*tl
++ = 0; /* stamp ?? */
*tl
++ = 0; /* NULL hostname */
*tl
++ = txdr_unsigned(cr
->cr_uid
);
*tl
++ = txdr_unsigned(cr
->cr_groups
[0]);
grpsiz
= (auth_len
>> 2) - 5;
*tl
++ = txdr_unsigned(grpsiz
);
for (i
= 1; i
<= grpsiz
; i
++)
*tl
++ = txdr_unsigned(cr
->cr_groups
[i
]);
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(cr
->cr_uid
);
*tl
= txdr_unsigned(auth_len
);
if (M_TRAILINGSPACE(mb
) == 0) {
MGET(mb2
, M_WAIT
, MT_DATA
);
bpos
= mtod(mb
, caddr_t
);
i
= MIN(siz
, M_TRAILINGSPACE(mb
));
bcopy(auth_str
, bpos
, i
);
if ((siz
= nfsm_rndup(auth_len
) - auth_len
) > 0) {
for (i
= 0; i
< siz
; i
++)
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(RPCAUTH_NULL
);
mreq
->m_pkthdr
.len
= authsiz
+ 10*NFSX_UNSIGNED
+ mrest_len
;
mreq
->m_pkthdr
.rcvif
= (struct ifnet
*)0;
* copies mbuf chain to the uio scatter/gather list
nfsm_mbuftouio(mrep
, uiop
, siz
, dpos
)
register struct uio
*uiop
;
register char *mbufcp
, *uiocp
;
register int xfer
, left
, len
;
register struct mbuf
*mp
;
len
= mtod(mp
, caddr_t
)+mp
->m_len
-mbufcp
;
rem
= nfsm_rndup(siz
)-siz
;
if (uiop
->uio_iovcnt
<= 0 || uiop
->uio_iov
== NULL
)
left
= uiop
->uio_iov
->iov_len
;
uiocp
= uiop
->uio_iov
->iov_base
;
mbufcp
= mtod(mp
, caddr_t
);
xfer
= (left
> len
) ? len
: left
;
if (uiop
->uio_iov
->iov_op
!= NULL
)
(*(uiop
->uio_iov
->iov_op
))
if (uiop
->uio_segflg
== UIO_SYSSPACE
)
bcopy(mbufcp
, uiocp
, xfer
);
copyout(mbufcp
, uiocp
, xfer
);
uiop
->uio_offset
+= xfer
;
if (uiop
->uio_iov
->iov_len
<= siz
) {
uiop
->uio_iov
->iov_base
+= uiosiz
;
uiop
->uio_iov
->iov_len
-= uiosiz
;
error
= nfs_adv(mrep
, dpos
, rem
, len
);
* copies a uio scatter/gather list to an mbuf chain...
nfsm_uiotombuf(uiop
, mq
, siz
, bpos
)
register struct uio
*uiop
;
register struct mbuf
*mp
, *mp2
;
register int xfer
, left
, mlen
;
if (siz
> MLEN
) /* or should it >= MCLBYTES ?? */
rem
= nfsm_rndup(siz
)-siz
;
if (uiop
->uio_iovcnt
<= 0 || uiop
->uio_iov
== NULL
)
left
= uiop
->uio_iov
->iov_len
;
uiocp
= uiop
->uio_iov
->iov_base
;
mlen
= M_TRAILINGSPACE(mp
);
MGET(mp
, M_WAIT
, MT_DATA
);
mlen
= M_TRAILINGSPACE(mp
);
xfer
= (left
> mlen
) ? mlen
: left
;
if (uiop
->uio_iov
->iov_op
!= NULL
)
(*(uiop
->uio_iov
->iov_op
))
(uiocp
, mtod(mp
, caddr_t
)+mp
->m_len
, xfer
);
if (uiop
->uio_segflg
== UIO_SYSSPACE
)
bcopy(uiocp
, mtod(mp
, caddr_t
)+mp
->m_len
, xfer
);
copyin(uiocp
, mtod(mp
, caddr_t
)+mp
->m_len
, xfer
);
uiop
->uio_offset
+= xfer
;
if (uiop
->uio_iov
->iov_len
<= siz
) {
uiop
->uio_iov
->iov_base
+= uiosiz
;
uiop
->uio_iov
->iov_len
-= uiosiz
;
if (rem
> M_TRAILINGSPACE(mp
)) {
MGET(mp
, M_WAIT
, MT_DATA
);
cp
= mtod(mp
, caddr_t
)+mp
->m_len
;
for (left
= 0; left
< rem
; left
++)
*bpos
= mtod(mp
, caddr_t
)+mp
->m_len
;
* Help break down an mbuf chain by setting the first siz bytes contiguous
* pointed to by returned val.
* If Updateflg == True we can overwrite the first part of the mbuf data
* (in this case it can never sleep, so it can be called from interrupt level)
* it may however block when Updateflg == False
* This is used by the macros nfsm_dissect and nfsm_dissecton for tough
* cases. (The macros use the vars. dpos and dpos2)
nfsm_disct(mdp
, dposp
, siz
, left
, updateflg
, cp2
)
register struct mbuf
*mp
, *mp2
;
*dposp
= mtod(mp
, caddr_t
);
} else if (mp
->m_next
== NULL
) {
} else if (siz
> MHLEN
) {
/* Iff update, you can overwrite, else must alloc new mbuf */
MGET(mp2
, M_WAIT
, MT_DATA
);
mp2
->m_next
= mp
->m_next
;
*cp2
= p
= mtod(mp
, caddr_t
);
bcopy(*dposp
, p
, left
); /* Copy what was left */
/* Loop around copying up the siz2 bytes */
xfer
= (siz2
> mp2
->m_len
) ? mp2
->m_len
: siz2
;
bcopy(mtod(mp2
, caddr_t
), p
, xfer
);
*dposp
= mtod(mp2
, caddr_t
);
* Advance the position in the mbuf chain.
nfs_adv(mdp
, dposp
, offs
, left
)
*dposp
= mtod(m
, caddr_t
)+offs
;
* Copy a string into mbufs for the hard cases...
nfsm_strtmbuf(mb
, bpos
, cp
, siz
)
register struct mbuf
*m1
, *m2
;
long left
, xfer
, len
, tlen
;
left
= M_TRAILINGSPACE(m2
);
tl
= ((u_long
*)(*bpos
));
*tl
++ = txdr_unsigned(siz
);
m2
->m_len
+= NFSX_UNSIGNED
;
bcopy(cp
, (caddr_t
) tl
, left
);
/* Loop around adding mbufs */
MGET(m1
, M_WAIT
, MT_DATA
);
*tl
++ = txdr_unsigned(siz
);
m1
->m_len
-= NFSX_UNSIGNED
;
bcopy(cp
, (caddr_t
) tl
, xfer
);
*bpos
= mtod(m1
, caddr_t
)+m1
->m_len
;
* Called once to initialize data structures...
rpc_vers
= txdr_unsigned(RPC_VER2
);
rpc_call
= txdr_unsigned(RPC_CALL
);
rpc_reply
= txdr_unsigned(RPC_REPLY
);
rpc_msgdenied
= txdr_unsigned(RPC_MSGDENIED
);
rpc_msgaccepted
= txdr_unsigned(RPC_MSGACCEPTED
);
rpc_mismatch
= txdr_unsigned(RPC_MISMATCH
);
rpc_autherr
= txdr_unsigned(RPC_AUTHERR
);
rpc_rejectedcred
= txdr_unsigned(AUTH_REJECTCRED
);
rpc_auth_unix
= txdr_unsigned(RPCAUTH_UNIX
);
rpc_auth_kerb
= txdr_unsigned(RPCAUTH_NQNFS
);
nfs_vers
= txdr_unsigned(NFS_VER2
);
nfs_prog
= txdr_unsigned(NFS_PROG
);
nfs_true
= txdr_unsigned(TRUE
);
nfs_false
= txdr_unsigned(FALSE
);
/* Loop thru nfs procids */
for (i
= 0; i
< NFS_NPROCS
; i
++)
nfs_procids
[i
] = txdr_unsigned(i
);
/* Ensure async daemons disabled */
for (i
= 0; i
< NFS_MAXASYNCDAEMON
; i
++)
nfs_iodwant
[i
] = (struct proc
*)0;
nfs_xdrneg1
= txdr_unsigned(-1);
nfs_nhinit(); /* Init the nfsnode table */
nfsrv_initcache(); /* Init the server request cache */
* Initialize the nqnfs server stuff.
if (nqnfsstarttime
== 0) {
nqnfsstarttime
= boottime
.tv_sec
+ nqsrv_maxlease
+ nqsrv_clockskew
+ nqsrv_writeslack
;
NQLOADNOVRAM(nqnfsstarttime
);
nqnfs_prog
= txdr_unsigned(NQNFS_PROG
);
nqnfs_vers
= txdr_unsigned(NQNFS_VER1
);
nqthead
.th_head
[0] = &nqthead
;
nqthead
.th_head
[1] = &nqthead
;
for (i
= 0; i
< NQLCHSZ
; i
++) {
* Initialize reply list and start timer
nfsreqh
.r_prev
= nfsreqh
.r_next
= &nfsreqh
;
* Attribute cache routines.
* nfs_loadattrcache() - loads or updates the cache contents from attributes
* that are on the mbuf list
* nfs_getattrcache() - returns valid attributes if found in cache, returns
* Load the attribute cache (that lives in the nfsnode entry) with
* the values on the mbuf list and
* copy the attributes to *vaper
nfs_loadattrcache(vpp
, mdp
, dposp
, vaper
)
register struct vnode
*vp
= *vpp
;
register struct vattr
*vap
;
register struct nfsv2_fattr
*fp
;
extern struct vnodeops spec_nfsv2nodeops
, spec_vnodeops
;
register struct nfsnode
*np
;
t1
= (mtod(md
, caddr_t
) + md
->m_len
) - dpos
;
if (error
= nfsm_disct(&md
, &dpos
, NFSX_FATTR
, t1
, TRUE
, &cp2
))
fp
= (struct nfsv2_fattr
*)cp2
;
vtyp
= nfstov_type(fp
->fa_type
);
vmode
= fxdr_unsigned(u_short
, fp
->fa_mode
);
rdev
= fxdr_unsigned(long, fp
->fa_rdev
);
fxdr_time(&fp
->fa_mtime
, &mtime
);
* If v_type == VNON it is a new node, so fill in the v_type,
* n_mtime fields. Check to see if it represents a special
* device, and if so, check for a possible alias. Once the
* correct vnode has been obtained, fill in the rest of the
if (vp
->v_type
== VNON
) {
if (vtyp
== VCHR
&& rdev
== 0xffffffff)
vp
->v_type
= vtyp
= VFIFO
;
if (vp
->v_type
== VFIFO
) {
extern struct vnodeops fifo_nfsv2nodeops
;
vp
->v_op
= &fifo_nfsv2nodeops
;
if (vp
->v_type
== VCHR
|| vp
->v_type
== VBLK
) {
vp
->v_op
= &spec_nfsv2nodeops
;
if (nvp
= checkalias(vp
, (dev_t
)rdev
, vp
->v_mount
)) {
* Discard unneeded vnode, but save its nfsnode.
nvp
->v_data
= vp
->v_data
;
vp
->v_op
= &spec_vnodeops
;
* Reinitialize aliased node.
insque(np
, nfs_hash(&np
->n_fh
));
if ((VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) == 0)
np
->n_mtime
= mtime
.tv_sec
;
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_size
= fxdr_unsigned(u_long
, fp
->fa_size
);
if ((np
->n_flag
& NMODIFIED
) == 0 || vap
->va_size
> np
->n_size
) {
np
->n_size
= vap
->va_size
;
vnode_pager_setsize(vp
, np
->n_size
);
vap
->va_blocksize
= fxdr_unsigned(long, fp
->fa_blocksize
);
vap
->va_rdev
= (dev_t
)rdev
;
vap
->va_bytes
= fxdr_unsigned(long, fp
->fa_blocks
) * NFS_FABLKSIZE
;
vap
->va_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
.val
[0];
vap
->va_fileid
= fxdr_unsigned(long, fp
->fa_fileid
);
vap
->va_atime
.tv_sec
= fxdr_unsigned(long, fp
->fa_atime
.tv_sec
);
vap
->va_atime
.tv_usec
= 0;
vap
->va_flags
= fxdr_unsigned(u_long
, fp
->fa_atime
.tv_usec
);
vap
->va_ctime
.tv_sec
= fxdr_unsigned(long, fp
->fa_ctime
.tv_sec
);
vap
->va_ctime
.tv_usec
= 0;
vap
->va_gen
= fxdr_unsigned(u_long
, fp
->fa_ctime
.tv_usec
);
np
->n_attrstamp
= time
.tv_sec
;
bcopy((caddr_t
)vap
, (caddr_t
)vaper
, sizeof(*vap
));
if ((np
->n_flag
& NMODIFIED
) && (np
->n_size
> vap
->va_size
))
vaper
->va_size
= np
->n_size
;
* If the cache is valid, copy contents to *vap and return 0
* otherwise return an error
nfs_getattrcache(vp
, vap
)
register struct vnode
*vp
;
register struct nfsnode
*np
;
if (VFSTONFS(vp
->v_mount
)->nm_flag
& NFSMNT_NQNFS
) {
if (!NQNFS_CKCACHABLE(vp
, NQL_READ
) || np
->n_attrstamp
== 0) {
nfsstats
.attrcache_misses
++;
} else if ((time
.tv_sec
- np
->n_attrstamp
) >= NFS_ATTRTIMEO
) {
nfsstats
.attrcache_misses
++;
nfsstats
.attrcache_hits
++;
bcopy((caddr_t
)&np
->n_vattr
,(caddr_t
)vap
,sizeof(struct vattr
));
if ((np
->n_flag
& NMODIFIED
) == 0) {
np
->n_size
= vap
->va_size
;
vnode_pager_setsize(vp
, np
->n_size
);
} else if (np
->n_size
> vap
->va_size
)
vap
->va_size
= np
->n_size
;
* Set up nameidata for a lookup() call and do it
nfs_namei(ndp
, fhp
, len
, slp
, nam
, mdp
, dposp
, p
)
register struct nameidata
*ndp
;
register struct mbuf
*md
;
register char *fromcp
, *tocp
;
flag
= ndp
->ni_nameiop
& OPMASK
;
MALLOC(ndp
->ni_pnbuf
, char *, len
+ 1, M_NAMEI
, M_WAITOK
);
* Copy the name from the mbuf list to ndp->ni_pnbuf
* and set the various ndp fields appropriately.
rem
= mtod(md
, caddr_t
) + md
->m_len
- fromcp
;
for (i
= 0; i
< len
; i
++) {
fromcp
= mtod(md
, caddr_t
);
if (*fromcp
== '\0' || *fromcp
== '/') {
if ((*fromcp
&0377) == ('/'|0200) || flag
!= DELETE
) {
ndp
->ni_hash
+= (unsigned char)*fromcp
;
len
= nfsm_rndup(len
)-len
;
else if (error
= nfs_adv(mdp
, dposp
, len
, rem
))
ndp
->ni_pathlen
= tocp
- ndp
->ni_pnbuf
;
ndp
->ni_ptr
= ndp
->ni_pnbuf
;
* Extract and set starting directory.
if (error
= nfsrv_fhtovp(fhp
, FALSE
, &dp
, ndp
->ni_cred
, slp
, nam
, &rdonly
))
if (dp
->v_type
!= VDIR
) {
ndp
->ni_nameiop
|= (NOCROSSMOUNT
| RDONLY
);
ndp
->ni_nameiop
|= NOCROSSMOUNT
;
* And call lookup() to do the real work
if (error
= lookup(ndp
, p
))
* Check for encountering a symbolic link
if ((ndp
->ni_nameiop
& LOCKPARENT
) && ndp
->ni_pathlen
== 1)
* Check for saved name request
if (ndp
->ni_nameiop
& (SAVENAME
| SAVESTART
)) {
ndp
->ni_nameiop
|= HASBUF
;
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
* A fiddled version of m_adj() that ensures null fill to a long
* boundary and only trims off the back end
* Trim from tail. Scan the mbuf chain,
* calculating its length and finding the last mbuf.
* If the adjustment only affects this mbuf, then just
* adjust and return. Otherwise, rescan and truncate
* after the remaining size.
if (m
->m_next
== (struct mbuf
*)0)
cp
= mtod(m
, caddr_t
)+m
->m_len
-nul
;
for (i
= 0; i
< nul
; i
++)
* Correct length for chain is "count".
* Find the mbuf with last data, adjust its length,
* and toss data from remaining mbufs on chain.
for (m
= mp
; m
; m
= m
->m_next
) {
cp
= mtod(m
, caddr_t
)+m
->m_len
-nul
;
for (i
= 0; i
< nul
; i
++)
* nfsrv_fhtovp() - convert a fh to a vnode ptr (optionally locked)
* - look up fsid in mount list (if not found ret error)
* - check that it is exported
* - get vp by calling VFS_FHTOVP() macro
* - if not lockflag unlock it with VOP_UNLOCK()
* - if cred->cr_uid == 0 or MNT_EXPORTANON set it to neth_anon
nfsrv_fhtovp(fhp
, lockflag
, vpp
, cred
, slp
, nam
, rdonlyp
)
register struct mount
*mp
;
register struct netaddrhash
*np
;
register struct ufsmount
*ump
;
register struct nfsuid
*uidp
;
*vpp
= (struct vnode
*)0;
if ((mp
= getvfs(&fhp
->fh_fsid
)) == NULL
)
if ((mp
->mnt_flag
& MNT_EXPORTED
) == 0)
* Get the export permission structure for this <mp, client> tuple.
* First search for a network match.
np
= ump
->um_netaddr
[NETMASK_HASH
];
if (nfs_netaddr_match(np
->neth_family
, &np
->neth_haddr
,
* If not found, try for an address match.
if (np
== (struct netaddrhash
*)0) {
saddr
= mtod(nam
, struct sockaddr
*);
np
= ump
->um_netaddr
[NETADDRHASH(saddr
)];
if (nfs_netaddr_match(np
->neth_family
, &np
->neth_haddr
,
(struct netaddrhash
*)0, nam
))
np
= (struct netaddrhash
*)0;
if (np
== (struct netaddrhash
*)0) {
* If no address match, use the default if it exists.
if ((mp
->mnt_flag
& MNT_DEFEXPORTED
) == 0)
np
= &ump
->um_defexported
;
* Check/setup credentials.
if (np
->neth_exflags
& MNT_EXKERB
) {
uidp
= slp
->ns_uidh
[NUIDHASH(cred
->cr_uid
)];
if (uidp
->nu_uid
== cred
->cr_uid
)
} else if (cred
->cr_uid
== 0 || (np
->neth_exflags
& MNT_EXPORTANON
))
if (error
= VFS_FHTOVP(mp
, &fhp
->fh_fid
, 0, vpp
))
if (np
->neth_exflags
& MNT_EXRDONLY
)