* 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.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* From: @(#)nfs_subs.c 7.41 (Berkeley) 5/15/91
* 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 "vm/vnode_pager.h"
#endif /*PROTOTYPESDONE*/
#include "../ufs/quota.h"
#include "../ufs/inode.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_mismatch
, rpc_auth_unix
, rpc_msgaccepted
;
u_long nfs_vers
, nfs_prog
, nfs_true
, nfs_false
;
/* And other global data */
static u_long
*rpc_uidp
= (u_long
*)0;
static u_long nfs_xid
= 1;
static char *rpc_unixauth
;
enum vtype ntov_type
[7] = { VNON
, VREG
, VDIR
, VBLK
, VCHR
, VLNK
, VNON
};
extern struct proc
*nfs_iodwant
[NFS_MAXASYNCDAEMON
];
extern struct nfsreq nfsreqh
;
static char *nfs_unixauth();
* Maximum number of groups passed through to NFS server.
* According to RFC1057 it should be 16.
* For release 3.X systems, the maximum value is 8.
* For some other servers, the maximum value is 10.
* Create the header for an rpc request packet
* The function nfs_unixauth() creates a unix style authorization string
* and returns a ptr to it.
* The hsiz is the size of the rest of the nfs request header.
* (just used to decide if a cluster is a good idea)
* nb: Note that the prog, vers and procid args are already in xdr byte order
struct mbuf
*nfsm_reqh(prog
, vers
, procid
, cred
, hsiz
, bpos
, mb
, retxid
)
register struct mbuf
*mreq
, *m
;
asiz
= ((((cred
->cr_ngroups
- 1) > numgrps
) ? numgrps
:
(cred
->cr_ngroups
- 1)) << 2);
asiz
+= nfsm_rndup(hostnamelen
)+(9*NFSX_UNSIGNED
);
/* If we need a lot, alloc a cluster ?? */
if ((asiz
+hsiz
+RPC_SIZ
) > MHLEN
)
mreq
->m_len
= NFSMSIZ(mreq
);
* We do it now to avoid all sleeps after the call to nfs_unixauth()
while ((asiz
+RPC_SIZ
) > siz
) {
MGET(m
, M_WAIT
, MT_DATA
);
tl
= mtod(mreq
, u_long
*);
*tl
++ = *retxid
= txdr_unsigned(++nfs_xid
);
/* Now we can call nfs_unixauth() and copy it in */
bcopy(ap
, (caddr_t
)tl
, asiz
);
bcopy(ap
, (caddr_t
)tl
, siz
);
siz
= (asiz
> MLEN
) ? MLEN
: asiz
;
bcopy(ap
, mtod(m
, caddr_t
), siz
);
/* Finally, return values */
*bpos
= mtod(m
, caddr_t
)+m
->m_len
;
* 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
, len
;
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
;
MGET(mp
, M_WAIT
, MT_DATA
);
xfer
= (left
> mp
->m_len
) ? mp
->m_len
: left
;
if (uiop
->uio_iov
->iov_op
!= NULL
)
(*(uiop
->uio_iov
->iov_op
))
(uiocp
, mtod(mp
, caddr_t
), xfer
);
if (uiop
->uio_segflg
== UIO_SYSSPACE
)
bcopy(uiocp
, mtod(mp
, caddr_t
), xfer
);
copyin(uiocp
, mtod(mp
, caddr_t
), 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
> (len
-mp
->m_len
)) {
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
* This is used by the macros nfsm_disect and nfsm_disecton 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
= tl
= mtod(mp
, caddr_t
);
bcopy(*dposp
, tl
, 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
), tl
, 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
= NFSMSIZ(m2
)-m2
->m_len
;
tl
= ((u_long
*)(*bpos
));
*tl
++ = txdr_unsigned(siz
);
m2
->m_len
+= NFSX_UNSIGNED
;
bcopy(cp
, (caddr_t
) tl
, left
);
/* Loop arround 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_auth_unix
= txdr_unsigned(RPCAUTH_UNIX
);
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 reply list and start timer
nfsreqh
.r_prev
= nfsreqh
.r_next
= &nfsreqh
;
* Fill in the rest of the rpc_unixauth and return it
static char *nfs_unixauth(cr
)
register struct ucred
*cr
;
/* Maybe someday there should be a cache of AUTH_SHORT's */
if ((tl
= rpc_uidp
) == NULL
) {
i
= nfsm_rndup(hostnamelen
)+(25*NFSX_UNSIGNED
);
MALLOC(tl
, u_long
*, i
, M_TEMP
, M_WAITOK
);
rpc_unixauth
= (caddr_t
)tl
;
*tl
++ = txdr_unsigned(RPCAUTH_UNIX
);
tl
++; /* Fill in size later */
*tl
++ = txdr_unsigned(hostnamelen
);
i
= nfsm_rndup(hostnamelen
);
bcopy(hostname
, (caddr_t
)tl
, hostnamelen
);
*tl
++ = txdr_unsigned(cr
->cr_uid
);
*tl
++ = txdr_unsigned(cr
->cr_groups
[0]);
ngr
= ((cr
->cr_ngroups
- 1) > numgrps
) ? numgrps
: (cr
->cr_ngroups
- 1);
*tl
++ = txdr_unsigned(ngr
);
for (i
= 1; i
<= ngr
; i
++)
*tl
++ = txdr_unsigned(cr
->cr_groups
[i
]);
/* And add the AUTH_NULL */
i
= (((caddr_t
)tl
)-rpc_unixauth
)-12;
tl
= (u_long
*)(rpc_unixauth
+4);
* 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
;
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
;
type
= nfstov_type(fp
->fa_type
);
mode
= 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 (type
== VCHR
&& rdev
== 0xffffffff)
vp
->v_type
= type
= 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
)) {
* Reinitialize aliased node.
bcopy((caddr_t
)&VTONFS(vp
)->n_fh
,
(caddr_t
)&np
->n_fh
, NFSX_FH
);
insque(np
, nfs_hash(&np
->n_fh
));
np
->n_sillyrename
= (struct sillyrename
*)0;
* Discard unneeded vnode and update actual one
np
->n_mtime
= mtime
.tv_sec
;
vap
->va_mode
= (mode
& 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 ((time
.tv_sec
-np
->n_attrstamp
) < NFS_ATTRTIMEO
) {
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
;
nfsstats
.attrcache_misses
++;
* Set up nameidata for a namei() call and do it
nfs_namei(ndp
, fhp
, len
, 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
))
if (dp
->v_type
!= VDIR
) {
ndp
->ni_nameiop
|= (NOCROSSMOUNT
| REMOTE
);
* 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 set it to m_exroot
nfsrv_fhtovp(fhp
, lockflag
, vpp
, cred
)
register struct mount
*mp
;
if ((mp
= getvfs(&fhp
->fh_fsid
)) == NULL
)
if ((mp
->mnt_flag
& MNT_EXPORTED
) == 0)
if (VFS_FHTOVP(mp
, &fhp
->fh_fid
, vpp
))
cred
->cr_uid
= mp
->mnt_exroot
;
* These two functions implement nfs rpc compression.
* The algorithm is a trivial run length encoding of '\0' bytes. The high
* order nibble of hex "e" is or'd with the number of zeroes - 2 in four
* bits. (2 - 17 zeros) Any data byte with a high order nibble of hex "e"
* The compressed data is padded with 0x0 bytes to an even multiple of
* 4 bytes in length to avoid any weird long pointer alignments.
* If compression/uncompression is unsuccessful, the original mbuf list
* The first four bytes (the XID) are left uncompressed and the fifth
* byte is set to 0x1 for request and 0x2 for reply.
* An uncompressed RPC will always have the fifth byte == 0x0.
register u_char ch
, nextch
;
register u_char
*ip
, *op
;
register int ileft
, oleft
, noteof
;
register struct mbuf
*m
, *om
;
if (m
->m_pkthdr
.len
>= MINCLSIZE
)
MGETHDR(om
, M_WAIT
, MT_DATA
);
oleft
= M_TRAILINGSPACE(om
);
*((u_long
*)op
) = *((u_long
*)ip
);
} while (m
&& m
->m_len
== 0);
if (++i
== NFSC_MAX
|| nextch
!= '\0') {
if (NFSCRLE(i
) == (nextch
& 0xff)) {
if ((ch
& NFSCRL
) == NFSCRL
) {
if (olen
< m0
->m_pkthdr
.len
) {
retm
->m_pkthdr
.len
= olen
;
retm
->m_pkthdr
.rcvif
= (struct ifnet
*)0;
register u_char cp
, nextcp
, *ip
, *op
;
register struct mbuf
*m
, *om
;
int i
, j
, noteof
, clget
, ileft
, oleft
, olen
;
while (m
&& i
< MINCLSIZE
) {
MGET(om
, M_WAIT
, MT_DATA
);
oleft
= M_TRAILINGSPACE(om
);
*((u_long
*)op
) = *((u_long
*)ip
);
if ((cp
& NFSCRL
) == NFSCRL
) {
for (j
= 0; j
< i
; j
++) {