* 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 are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)nfs_subs.c 7.20 (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.
* 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
;
extern enum vtype v_type
[NFLNK
+1];
extern struct proc
*nfs_iodwant
[MAX_ASYNCDAEMON
];
extern struct map nfsmap
[NFS_MSIZ
];
static char *nfs_unixauth();
* Maximum number of groups passed through to NFS server.
* For release 3.X systems, the maximum value is 8.
* For release 4.X systems, 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
> numgrps
) ? numgrps
: cred
->cr_ngroups
)<<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
);
p
= mtod(mreq
, u_long
*);
*p
++ = *retxid
= txdr_unsigned(++nfs_xid
);
/* Now we can call nfs_unixauth() and copy it in */
bcopy(ap
, (caddr_t
)p
, asiz
);
bcopy(ap
, (caddr_t
)p
, 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 int xfer
, left
, len
;
register struct mbuf
*mp
;
register char *mbufcp
, *uiocp
;
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
;
* copies a uio scatter/gather list to an mbuf chain...
nfsm_uiotombuf(uiop
, mq
, siz
, bpos
)
register struct uio
*uiop
;
register struct mbuf
*mp
;
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
> MCLBYTES
) {
/* Iff update, you can overwrite, else must alloc new mbuf */
MGET(mp2
, M_WAIT
, MT_DATA
);
mp2
->m_next
= mp
->m_next
;
/* Alloc cluster iff we need it */
if (!M_HASCL(mp
) && siz
> NFSMSIZ(mp
)) {
*cp2
= p
= mtod(mp
, caddr_t
);
bcopy(*dposp
, p
, left
); /* Copy what was left */
/* Loop arround 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 with/without freeing mbufs
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
;
*p
++ = txdr_unsigned(siz
);
m2
->m_len
+= NFSX_UNSIGNED
;
bcopy(cp
, (caddr_t
) p
, left
);
/* Loop arround adding mbufs */
MGET(m1
, M_WAIT
, MT_DATA
);
*p
++ = txdr_unsigned(siz
);
m1
->m_len
-= NFSX_UNSIGNED
;
bcopy(cp
, (caddr_t
) p
, 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
< MAX_ASYNCDAEMON
; 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 */
rminit(nfsmap
, (long)NFS_MAPREG
, (long)1, "nfs mapreg", NFS_MSIZ
);
* 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 ((p
= rpc_uidp
) == NULL
) {
i
= nfsm_rndup(hostnamelen
)+(19*NFSX_UNSIGNED
);
MALLOC(p
, u_long
*, i
, M_TEMP
, M_WAITOK
);
rpc_unixauth
= (caddr_t
)p
;
*p
++ = txdr_unsigned(RPCAUTH_UNIX
);
p
++; /* Fill in size later */
*p
++ = txdr_unsigned(hostnamelen
);
i
= nfsm_rndup(hostnamelen
);
bcopy(hostname
, (caddr_t
)p
, hostnamelen
);
*p
++ = txdr_unsigned(cr
->cr_uid
);
*p
++ = txdr_unsigned(cr
->cr_groups
[0]);
ngr
= (cr
->cr_ngroups
> numgrps
) ? numgrps
: cr
->cr_ngroups
;
*p
++ = txdr_unsigned(ngr
);
for (i
= 0; i
< ngr
; i
++)
*p
++ = txdr_unsigned(cr
->cr_groups
[i
]);
/* And add the AUTH_NULL */
i
= (((caddr_t
)p
)-rpc_unixauth
)-12;
p
= (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
);
rdev
= fxdr_unsigned(dev_t
, 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 (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
, 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
= nfstov_mode(fp
->fa_mode
);
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
;
vap
->va_size1
= 0; /* OR -1 ?? */
vap
->va_blocksize
= fxdr_unsigned(long, fp
->fa_blocksize
);
vap
->va_bytes
= fxdr_unsigned(long, fp
->fa_blocks
) * vap
->va_blocksize
;
vap
->va_fsid
= vp
->v_mount
->m_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
;
else if (np
->n_size
> vap
->va_size
)
vap
->va_size
= np
->n_size
;
nfsstats
.attrcache_misses
++;
* nfs_namei - a liitle like namei(), but for one element only
* essentially look up file handle, fill in ndp and call VOP_LOOKUP()
nfs_namei(ndp
, fhp
, len
, mdp
, dposp
)
register struct nameidata
*ndp
;
register struct mbuf
*md
;
struct vnode
*dp
= (struct vnode
*)0;
ndp
->ni_vp
= ndp
->ni_dvp
= (struct vnode
*)0;
flag
= ndp
->ni_nameiop
& OPFLAG
;
wantparent
= ndp
->ni_nameiop
& (LOCKPARENT
| WANTPARENT
);
lockparent
= ndp
->ni_nameiop
& LOCKPARENT
;
docache
= (ndp
->ni_nameiop
& NOCACHE
) ^ NOCACHE
;
if (flag
== DELETE
|| wantparent
)
/* Fill in the nameidata and call lookup */
rem
= mtod(md
, caddr_t
)+md
->m_len
-cp
;
if (*cp
== '\0' || *cp
== '/')
if ((*cp
&0377) == ('/'|0200) || flag
!= DELETE
)
ndp
->ni_dent
.d_name
[i
++] = *cp
;
ndp
->ni_hash
+= (unsigned char)*cp
* i
;
len
= nfsm_rndup(len
)-len
;
ndp
->ni_dent
.d_namlen
= i
;
ndp
->ni_dent
.d_name
[i
] = '\0';
ndp
->ni_dirp
= ndp
->ni_ptr
= &ndp
->ni_dent
.d_name
[0];
ndp
->ni_next
= &ndp
->ni_dent
.d_name
[i
];
ndp
->ni_loopcnt
= 0; /* Not actually used for now */
ndp
->ni_isdotdot
= (i
== 2 &&
ndp
->ni_dent
.d_name
[1] == '.' && ndp
->ni_dent
.d_name
[0] == '.');
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &dp
, ndp
->ni_cred
))
if (dp
->v_type
!= VDIR
) {
* Must set current directory here to avoid confusion in namei()
ndp
->ni_rdir
= (struct vnode
*)0;
* If this vnode is the root of the mounted
* file system, then ignore it so can't get out
if (ndp
->ni_isdotdot
&& (dp
->v_flag
& VROOT
)) {
* We now have a segment name to search for, and a directory to search.
if (error
= VOP_LOOKUP(dp
, ndp
)) {
panic("leaf should be empty");
* If creating and at end of pathname, then can consider
* allowing file to be created.
if (ndp
->ni_dvp
->v_mount
->m_flag
& (M_RDONLY
| M_EXRDONLY
))
if (flag
== LOOKUP
|| flag
== DELETE
|| error
!= ENOENT
)
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory inode in ndp->ni_dvp.
return (0); /* should this be ENOENT? */
ndp
->ni_ptr
= ndp
->ni_next
;
* Check for read-only file systems
if (flag
== DELETE
|| flag
== RENAME
) {
* Disallow directory write attempts on read-only
if ((dp
->v_mount
->m_flag
& (M_RDONLY
|M_EXRDONLY
)) ||
(wantparent
&& (ndp
->ni_dvp
->v_mount
->m_flag
& (M_RDONLY
|M_EXRDONLY
)))) {
if ((ndp
->ni_nameiop
& LOCKLEAF
) == 0)
* 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
->m_flag
& M_EXPORTED
) == 0)
if (VFS_FHTOVP(mp
, &fhp
->fh_fid
, vpp
))
cred
->cr_uid
= mp
->m_exroot
;