* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* Rick Macklem at The University of Guelph.
* %sccs.include.redist.c%
* @(#)nfs_serv.c 8.6 (Berkeley) %G%
* nfs version 2 and 3 server calls to vnode ops
* - these routines generally have 3 phases
* 1 - break down and validate rpc request in mbuf list
* 2 - do the vnode ops for the request
* (surprisingly ?? many are very similar to syscalls in vfs_syscalls.c)
* 3 - build the rpc reply in an mbuf list
* - do not mix the phases, since the nfsm_?? macros can return failures
* on a bad rpc or similar and do not do any vrele() or vput()'s
* - the nfsm_reply() macro generates an nfs rpc reply with the nfs
* error number iff error != 0 whereas
* returning an error from the server function implies a fatal error
* such as a badly constructed rpc request that should be dropped without
* For Version 3, nfsm_reply() does not return for the error case, since
* most version 3 rpcs return more than the status for error cases.
#include <sys/socketvar.h>
#include <nfs/nfsproto.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
extern u_long nfs_xdrneg1
;
extern u_long nfs_false
, nfs_true
;
extern enum vtype nv3tov_type
[8];
extern struct nfsstats nfsstats
;
nfstype nfsv2_type
[9] = { NFNON
, NFREG
, NFDIR
, NFBLK
, NFCHR
, NFLNK
, NFNON
,
nfstype nfsv3_type
[9] = { NFNON
, NFREG
, NFDIR
, NFBLK
, NFCHR
, NFLNK
, NFSOCK
,
int nfsrvw_procrastinate
= NFS_GATHERDELAY
* 1000;
nfsrv3_access(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, rdonly
, cache
, getret
;
struct mbuf
*mb
, *mreq
, *mb2
;
struct vattr vattr
, *vap
= &vattr
;
u_long testmode
, nfsmode
;
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(1, (struct vattr
*)0);
nfsmode
= fxdr_unsigned(u_long
, *tl
);
if ((nfsmode
& NFSV3ACCESS_READ
) &&
nfsrv_access(vp
, VREAD
, cred
, rdonly
, procp
))
nfsmode
&= ~NFSV3ACCESS_READ
;
testmode
= (NFSV3ACCESS_MODIFY
| NFSV3ACCESS_EXTEND
|
testmode
= (NFSV3ACCESS_MODIFY
| NFSV3ACCESS_EXTEND
);
if ((nfsmode
& testmode
) &&
nfsrv_access(vp
, VWRITE
, cred
, rdonly
, procp
))
testmode
= NFSV3ACCESS_LOOKUP
;
testmode
= NFSV3ACCESS_EXECUTE
;
if ((nfsmode
& testmode
) &&
nfsrv_access(vp
, VEXEC
, cred
, rdonly
, procp
))
getret
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, vap
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
*tl
= txdr_unsigned(nfsmode
);
nfsrv_getattr(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfs_fattr
*fp
;
register struct vattr
*vap
= &va
;
int error
= 0, rdonly
, cache
;
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_FATTR(nfsd
->nd_flag
& ND_NFSV3
));
nfsm_build(fp
, struct nfs_fattr
*, NFSX_FATTR(nfsd
->nd_flag
& ND_NFSV3
));
nfsm_srvfillattr(vap
, fp
);
nfsrv_setattr(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct vattr
*vap
= &va
;
register struct nfsv2_sattr
*sp
;
register struct nfs_fattr
*fp
;
int error
= 0, rdonly
, cache
, preat_ret
= 1, postat_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
), gcheck
= 0;
struct mbuf
*mb
, *mb2
, *mreq
;
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
gcheck
= fxdr_unsigned(int, *tl
);
nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
fxdr_nfsv3time(tl
, &guard
);
nfsm_dissect(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
* There is a bug in the Sun client that puts 0xffff in the mode
* field of sattr when it should put in 0xffffffff. The u_short
* --> check the low order 2 bytes for 0xffff
if ((fxdr_unsigned(int, sp
->sa_mode
) & 0xffff) != 0xffff)
vap
->va_mode
= nfstov_mode(sp
->sa_mode
);
if (sp
->sa_uid
!= nfs_xdrneg1
)
vap
->va_uid
= fxdr_unsigned(uid_t
, sp
->sa_uid
);
if (sp
->sa_gid
!= nfs_xdrneg1
)
vap
->va_gid
= fxdr_unsigned(gid_t
, sp
->sa_gid
);
if (sp
->sa_size
!= nfs_xdrneg1
)
vap
->va_size
= fxdr_unsigned(u_quad_t
, sp
->sa_size
);
if (sp
->sa_atime
.nfsv2_sec
!= nfs_xdrneg1
) {
fxdr_nfsv2time(&sp
->sa_atime
, &vap
->va_atime
);
fxdr_unsigned(long, sp
->sa_atime
.nfsv2_sec
);
vap
->va_atime
.ts_nsec
= 0;
if (sp
->sa_mtime
.nfsv2_sec
!= nfs_xdrneg1
)
fxdr_nfsv2time(&sp
->sa_mtime
, &vap
->va_mtime
);
* Now that we have all the fields, lets do it.
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvwcc_data(preat_ret
, &preat
, postat_ret
, vap
);
nqsrv_getl(vp
, ND_WRITE
);
error
= preat_ret
= VOP_GETATTR(vp
, &preat
, cred
, procp
);
(preat
.va_ctime
.ts_sec
!= guard
.ts_sec
||
preat
.va_ctime
.ts_nsec
!= guard
.ts_nsec
))
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(preat_ret
, &preat
, postat_ret
, vap
);
* If the size is being changed write acces is required, otherwise
* just check for a read only file system.
if (vap
->va_size
== ((u_quad_t
)((quad_t
) -1))) {
if (rdonly
|| (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)) {
if (vp
->v_type
== VDIR
) {
} else if (error
= nfsrv_access(vp
, VWRITE
, cred
, rdonly
,
error
= VOP_SETATTR(vp
, vap
, cred
, procp
);
postat_ret
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_WCCORFATTR(v3
));
nfsm_srvwcc_data(preat_ret
, &preat
, postat_ret
, vap
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(vap
, fp
);
nfsrv_lookup(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfs_fattr
*fp
;
int error
= 0, cache
, len
, dirattr_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mb2
, *mreq
;
struct vattr va
, dirattr
, *vap
= &va
;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
nd
.ni_cnd
.cn_flags
= LOCKLEAF
| SAVESTART
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirattr_ret
= VOP_GETATTR(dirp
, &dirattr
, cred
,
nfsm_reply(NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(dirattr_ret
, &dirattr
);
nqsrv_getl(nd
.ni_startdir
, ND_READ
);
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
);
error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_SRVFH(v3
) + NFSX_POSTOPORFATTR(v3
) + NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(dirattr_ret
, &dirattr
);
nfsm_srvpostop_attr(0, vap
);
nfsm_srvpostop_attr(dirattr_ret
, &dirattr
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(vap
, fp
);
nfsrv_readlink(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
struct iovec iv
[(NFS_MAXPATHLEN
+MLEN
-1)/MLEN
];
register struct iovec
*ivp
= iv
;
register struct mbuf
*mp
;
int error
= 0, rdonly
, cache
, i
, tlen
, len
, getret
;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mb2
, *mp2
, *mp3
, *mreq
;
struct uio io
, *uiop
= &io
;
mp2
= mp3
= (struct mbuf
*)0;
while (len
< NFS_MAXPATHLEN
) {
MGET(mp
, M_WAIT
, MT_DATA
);
if ((len
+mp
->m_len
) > NFS_MAXPATHLEN
) {
mp
->m_len
= NFS_MAXPATHLEN
-len
;
ivp
->iov_base
= mtod(mp
, caddr_t
);
ivp
->iov_len
= mp
->m_len
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
uiop
->uio_procp
= (struct proc
*)0;
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvpostop_attr(1, (struct vattr
*)0);
if (vp
->v_type
!= VLNK
) {
error
= VOP_READLINK(vp
, uiop
, cred
);
getret
= VOP_GETATTR(vp
, &attr
, cred
, procp
);
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &attr
);
if (uiop
->uio_resid
> 0) {
nfsm_adj(mp3
, NFS_MAXPATHLEN
-tlen
, tlen
-len
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
*tl
= txdr_unsigned(len
);
nfsrv_read(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct iovec
*iv
;
register struct nfs_fattr
*fp
;
int error
= 0, rdonly
, cache
, cnt
, len
, left
, siz
, tlen
, getret
;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
), reqlen
;
struct mbuf
*mb
, *mb2
, *mreq
;
struct uio io
, *uiop
= &io
;
struct vattr va
, *vap
= &va
;
nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
off
= (off_t
)fxdr_unsigned(u_long
, *tl
);
nfsm_srvstrsiz(reqlen
, NFS_SRVMAXDATA(nfsd
));
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvpostop_attr(1, (struct vattr
*)0);
if (vp
->v_type
!= VREG
) {
error
= (vp
->v_type
== VDIR
) ? EISDIR
: EACCES
;
if (error
= nfsrv_access(vp
, VREAD
, cred
, rdonly
, procp
))
error
= nfsrv_access(vp
, VEXEC
, cred
, rdonly
, procp
);
getret
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(getret
, vap
);
else if ((off
+ reqlen
) > vap
->va_size
)
cnt
= nfsm_rndup(vap
->va_size
- off
);
nfsm_reply(NFSX_POSTOPORFATTR(v3
) + 3 * NFSX_UNSIGNED
+nfsm_rndup(cnt
));
nfsm_build(tl
, u_long
*, NFSX_V3FATTR
+ 4 * NFSX_UNSIGNED
);
fp
= (struct nfs_fattr
*)tl
;
tl
+= (NFSX_V3FATTR
/ sizeof (u_long
));
nfsm_build(tl
, u_long
*, NFSX_V2FATTR
+ NFSX_UNSIGNED
);
fp
= (struct nfs_fattr
*)tl
;
tl
+= (NFSX_V2FATTR
/ sizeof (u_long
));
* Generate the mbuf list with the uio_iov ref. to it.
siz
= min(M_TRAILINGSPACE(m
), left
);
MGET(m
, M_WAIT
, MT_DATA
);
MALLOC(iv
, struct iovec
*, i
* sizeof (struct iovec
),
uiop
->uio_iov
= iv2
= iv
;
siz
= min(M_TRAILINGSPACE(m
), left
);
iv
->iov_base
= mtod(m
, caddr_t
) + m
->m_len
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
error
= VOP_READ(vp
, uiop
, IO_NODELOCKED
, cred
);
FREE((caddr_t
)iv2
, M_TEMP
);
if (error
|| (getret
= VOP_GETATTR(vp
, vap
, cred
, procp
))) {
nfsm_reply(NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(getret
, vap
);
nfsm_srvfillattr(vap
, fp
);
if (cnt
!= tlen
|| tlen
!= len
)
nfsm_adj(mb
, cnt
- tlen
, tlen
- len
);
*tl
++ = txdr_unsigned(len
);
*tl
= txdr_unsigned(len
);
nfsrv_write(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct iovec
*ivp
;
register struct mbuf
*mp
;
register struct nfs_fattr
*fp
;
register struct vattr
*vap
= &va
;
int error
= 0, rdonly
, cache
, siz
, len
, xfer
, forat_ret
= 1;
int ioflags
, aftat_ret
= 1, retlen
, zeroing
, adjust
;
int stable
= NFSV3WRITE_FILESYNC
;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mb2
, *mreq
;
struct uio io
, *uiop
= &io
;
nfsm_dissect(tl
, u_long
*, 5 * NFSX_UNSIGNED
);
stable
= fxdr_unsigned(int, *tl
++);
nfsm_dissect(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
off
= (off_t
)fxdr_unsigned(u_long
, *++tl
);
retlen
= len
= fxdr_unsigned(long, *tl
);
* For NFS Version 2, it is not obvious what a write of zero length
* should do, but I might as well be consistent with Version 3,
* which is to return ok so long as there are no permission problems.
adjust
= dpos
- mtod(mp
, caddr_t
);
if (mp
->m_len
> 0 && adjust
> 0)
else if (mp
->m_len
> 0) {
if (len
> NFS_MAXDATA
|| len
< 0 || i
< len
) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, vap
);
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, vap
);
forat_ret
= VOP_GETATTR(vp
, &forat
, cred
, procp
);
if (vp
->v_type
!= VREG
) {
error
= (vp
->v_type
== VDIR
) ? EISDIR
: EACCES
;
nqsrv_getl(vp
, ND_WRITE
);
error
= nfsrv_access(vp
, VWRITE
, cred
, rdonly
, procp
);
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, vap
);
MALLOC(ivp
, struct iovec
*, cnt
* sizeof (struct iovec
), M_TEMP
,
uiop
->uio_iov
= iv
= ivp
;
ivp
->iov_base
= mtod(mp
, caddr_t
);
ivp
->iov_len
= mp
->m_len
;
* The IO_METASYNC flag indicates that all metadata (and not just
* enough to ensure data integrity) mus be written to stable storage
* (IO_METASYNC is not yet implemented in 4.4BSD-Lite.)
if (stable
== NFSV3WRITE_UNSTABLE
)
else if (stable
== NFSV3WRITE_DATASYNC
)
ioflags
= (IO_SYNC
| IO_NODELOCKED
);
ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
uiop
->uio_rw
= UIO_WRITE
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
uiop
->uio_procp
= (struct proc
*)0;
error
= VOP_WRITE(vp
, uiop
, ioflags
, cred
);
nfsstats
.srvvop_writes
++;
FREE((caddr_t
)iv
, M_TEMP
);
aftat_ret
= VOP_GETATTR(vp
, vap
, cred
, procp
);
nfsm_reply(NFSX_PREOPATTR(v3
) + NFSX_POSTOPORFATTR(v3
) +
2 * NFSX_UNSIGNED
+ NFSX_WRITEVERF(v3
));
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, vap
);
nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(retlen
);
if (stable
== NFSV3WRITE_UNSTABLE
)
*tl
++ = txdr_unsigned(stable
);
*tl
++ = txdr_unsigned(NFSV3WRITE_FILESYNC
);
* Actually, there is no need to txdr these fields,
* but it may make the values more human readable,
* for debugging purposes.
*tl
++ = txdr_unsigned(boottime
.tv_sec
);
*tl
= txdr_unsigned(boottime
.tv_usec
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(vap
, fp
);
* NFS write service with write gathering support. Called when
* nfsrvw_procrastinate > 0.
* See: Chet Juszczak, "Improving the Write Performance of an NFS Server",
* in Proc. of the Winter 1994 Usenix Conference, pg. 247-259, San Franscisco,
nfsrv_writegather(ndp
, slp
, procp
, mrq
)
struct nfsrv_descript
**ndp
;
register struct iovec
*ivp
;
register struct mbuf
*mp
;
register struct nfsrv_descript
*wp
, *nfsd
, *owp
, *swp
;
register struct nfs_fattr
*fp
;
struct nfsrvw_delayhash
*wpp
;
int error
= 0, rdonly
, cache
, len
, forat_ret
= 1;
int ioflags
, aftat_ret
= 1, s
, adjust
, v3
, zeroing
;
struct mbuf
*mb
, *mb2
, *mreq
, *mrep
, *md
;
struct uio io
, *uiop
= &io
;
v3
= (nfsd
->nd_flag
& ND_NFSV3
);
LIST_INIT(&nfsd
->nd_coalesce
);
nfsd
->nd_stable
= NFSV3WRITE_FILESYNC
;
cur_usec
= (u_quad_t
)time
.tv_sec
* 1000000 + (u_quad_t
)time
.tv_usec
;
nfsd
->nd_time
= cur_usec
+ nfsrvw_procrastinate
;
* Now, get the write header..
nfsm_srvmtofh(&nfsd
->nd_fh
);
nfsm_dissect(tl
, u_long
*, 5 * NFSX_UNSIGNED
);
fxdr_hyper(tl
, &nfsd
->nd_off
);
nfsd
->nd_stable
= fxdr_unsigned(int, *tl
++);
nfsm_dissect(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
nfsd
->nd_off
= (off_t
)fxdr_unsigned(u_long
, *++tl
);
len
= fxdr_unsigned(long, *tl
);
nfsd
->nd_eoff
= nfsd
->nd_off
+ len
;
* Trim the header out of the mbuf list and trim off any trailing
* junk so that the mbuf list has only the write data.
adjust
= dpos
- mtod(mp
, caddr_t
);
if (mp
->m_len
> 0 && adjust
> 0)
if (len
> NFS_MAXDATA
|| len
< 0 || i
< len
) {
nfsm_writereply(2 * NFSX_UNSIGNED
, v3
);
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, &va
);
* Add this entry to the hash and time queues.
wp
= slp
->ns_tq
.lh_first
;
while (wp
&& wp
->nd_time
< nfsd
->nd_time
) {
LIST_INSERT_AFTER(owp
, nfsd
, nd_tq
);
LIST_INSERT_HEAD(&slp
->ns_tq
, nfsd
, nd_tq
);
wpp
= NWDELAYHASH(slp
, nfsd
->nd_fh
.fh_fid
.fid_data
);
bcmp((caddr_t
)&nfsd
->nd_fh
,(caddr_t
)&wp
->nd_fh
,NFSX_V3FH
)) {
wp
= wp
->nd_hash
.le_next
;
while (wp
&& wp
->nd_off
< nfsd
->nd_off
&&
!bcmp((caddr_t
)&nfsd
->nd_fh
,(caddr_t
)&wp
->nd_fh
,NFSX_V3FH
)) {
wp
= wp
->nd_hash
.le_next
;
LIST_INSERT_AFTER(owp
, nfsd
, nd_hash
);
* Search the hash list for overlapping entries and
for(; nfsd
&& NFSW_CONTIG(owp
, nfsd
); nfsd
= wp
) {
wp
= nfsd
->nd_hash
.le_next
;
if (NFSW_SAMECRED(owp
, nfsd
))
nfsrvw_coalesce(owp
, nfsd
);
LIST_INSERT_HEAD(wpp
, nfsd
, nd_hash
);
* Now, do VOP_WRITE()s for any one(s) that need to be done now
* and generate the associated reply mbuf list(s).
cur_usec
= (u_quad_t
)time
.tv_sec
* 1000000 + (u_quad_t
)time
.tv_usec
;
for (nfsd
= slp
->ns_tq
.lh_first
; nfsd
; nfsd
= owp
) {
owp
= nfsd
->nd_tq
.le_next
;
if (nfsd
->nd_time
> cur_usec
)
LIST_REMOVE(nfsd
, nd_tq
);
LIST_REMOVE(nfsd
, nd_hash
);
v3
= (nfsd
->nd_flag
& ND_NFSV3
);
forat_ret
= aftat_ret
= 1;
error
= nfsrv_fhtovp(&nfsd
->nd_fh
, 1, &vp
, cred
, slp
,
nfsd
->nd_nam
, &rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
));
forat_ret
= VOP_GETATTR(vp
, &forat
, cred
, procp
);
if (vp
->v_type
!= VREG
) {
error
= (vp
->v_type
== VDIR
) ? EISDIR
: EACCES
;
nqsrv_getl(vp
, ND_WRITE
);
error
= nfsrv_access(vp
, VWRITE
, cred
, rdonly
, procp
);
if (nfsd
->nd_stable
== NFSV3WRITE_UNSTABLE
)
else if (nfsd
->nd_stable
== NFSV3WRITE_DATASYNC
)
ioflags
= (IO_SYNC
| IO_NODELOCKED
);
ioflags
= (IO_METASYNC
| IO_SYNC
| IO_NODELOCKED
);
uiop
->uio_rw
= UIO_WRITE
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
uiop
->uio_procp
= (struct proc
*)0;
uiop
->uio_offset
= nfsd
->nd_off
;
uiop
->uio_resid
= nfsd
->nd_eoff
- nfsd
->nd_off
;
if (uiop
->uio_resid
> 0) {
MALLOC(iov
, struct iovec
*, i
* sizeof (struct iovec
),
uiop
->uio_iov
= ivp
= iov
;
ivp
->iov_base
= mtod(mp
, caddr_t
);
ivp
->iov_len
= mp
->m_len
;
error
= VOP_WRITE(vp
, uiop
, ioflags
, cred
);
nfsstats
.srvvop_writes
++;
FREE((caddr_t
)iov
, M_TEMP
);
aftat_ret
= VOP_GETATTR(vp
, &va
, cred
, procp
);
* Loop around generating replies for all write rpcs that have
nfsm_writereply(NFSX_WCCDATA(v3
), v3
);
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, &va
);
nfsm_writereply(NFSX_PREOPATTR(v3
) +
NFSX_POSTOPORFATTR(v3
) + 2 * NFSX_UNSIGNED
+
nfsm_srvwcc_data(forat_ret
, &forat
, aftat_ret
, &va
);
nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(nfsd
->nd_len
);
*tl
++ = txdr_unsigned(swp
->nd_stable
);
* Actually, there is no need to txdr these fields,
* but it may make the values more human readable,
* for debugging purposes.
*tl
++ = txdr_unsigned(boottime
.tv_sec
);
*tl
= txdr_unsigned(boottime
.tv_usec
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(&va
, fp
);
panic("nfsrv_write: nd_mrep not free");
* Done. Put it at the head of the timer queue so that
* the final phase can return the reply.
LIST_INSERT_HEAD(&slp
->ns_tq
, nfsd
, nd_tq
);
nfsd
= swp
->nd_coalesce
.lh_first
;
LIST_REMOVE(nfsd
, nd_tq
);
LIST_INSERT_HEAD(&slp
->ns_tq
, swp
, nd_tq
);
* Search for a reply to return.
for (nfsd
= slp
->ns_tq
.lh_first
; nfsd
; nfsd
= nfsd
->nd_tq
.le_next
)
LIST_REMOVE(nfsd
, nd_tq
);
* Coalesce the write request nfsd into owp. To do this we must:
* - remove nfsd from the queues
* - merge nfsd->nd_mrep into owp->nd_mrep
* - update the nd_eoff and nd_stable for owp
* - put nfsd on owp's nd_coalesce list
* NB: Must be called at splsoftclock().
nfsrvw_coalesce(owp
, nfsd
)
register struct nfsrv_descript
*owp
;
register struct nfsrv_descript
*nfsd
;
register struct mbuf
*mp
;
LIST_REMOVE(nfsd
, nd_hash
);
LIST_REMOVE(nfsd
, nd_tq
);
if (owp
->nd_eoff
< nfsd
->nd_eoff
) {
overlap
= owp
->nd_eoff
- nfsd
->nd_off
;
panic("nfsrv_coalesce: bad off");
m_adj(nfsd
->nd_mrep
, overlap
);
mp
->m_next
= nfsd
->nd_mrep
;
owp
->nd_eoff
= nfsd
->nd_eoff
;
if (nfsd
->nd_stable
== NFSV3WRITE_FILESYNC
)
owp
->nd_stable
= NFSV3WRITE_FILESYNC
;
else if (nfsd
->nd_stable
== NFSV3WRITE_DATASYNC
&&
owp
->nd_stable
== NFSV3WRITE_UNSTABLE
)
owp
->nd_stable
= NFSV3WRITE_DATASYNC
;
LIST_INSERT_HEAD(&owp
->nd_coalesce
, nfsd
, nd_tq
);
* Sort the group list in increasing numerical order.
* (Insertion sort by Chris Torek, who was grossed out by the bubble sort
for (i
= 1; i
< num
; i
++) {
/* find correct slot for value v, moving others up */
for (j
= i
; --j
>= 0 && v
< list
[j
];)
* copy credentials making sure that the result can be compared with bcmp().
nfsrv_setcred(incred
, outcred
)
register struct ucred
*incred
, *outcred
;
bzero((caddr_t
)outcred
, sizeof (struct ucred
));
outcred
->cr_uid
= incred
->cr_uid
;
outcred
->cr_ngroups
= incred
->cr_ngroups
;
for (i
= 0; i
< incred
->cr_ngroups
; i
++)
outcred
->cr_groups
[i
] = incred
->cr_groups
[i
];
nfsrvw_sort(outcred
->cr_groups
, outcred
->cr_ngroups
);
* now does a truncate to 0 length via. setattr if it already exists
nfsrv_create(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfs_fattr
*fp
;
struct vattr va
, dirfor
, diraft
;
register struct vattr
*vap
= &va
;
register struct nfsv2_sattr
*sp
;
int error
= 0, rdev
, cache
, len
, tsize
, dirfor_ret
= 1, diraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
), how
, exclusive_flag
= 0;
struct mbuf
*mb
, *mb2
, *mreq
;
struct vnode
*vp
, *dirp
= (struct vnode
*)0;
u_char cverf
[NFSX_V3CREATEVERF
];
nd
.ni_cnd
.cn_nameiop
= 0;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
| SAVESTART
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
dirp
= (struct vnode
*)0;
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
how
= fxdr_unsigned(int, *tl
);
case NFSV3CREATE_GUARDED
:
case NFSV3CREATE_UNCHECKED
:
case NFSV3CREATE_EXCLUSIVE
:
nfsm_dissect(cp
, caddr_t
, NFSX_V3CREATEVERF
);
bcopy(cp
, cverf
, NFSX_V3CREATEVERF
);
nfsm_dissect(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
vap
->va_type
= IFTOVT(fxdr_unsigned(u_long
, sp
->sa_mode
));
if (vap
->va_type
== VNON
)
vap
->va_mode
= nfstov_mode(sp
->sa_mode
);
tsize
= fxdr_unsigned(long, sp
->sa_size
);
vap
->va_size
= (u_quad_t
)tsize
;
rdev
= fxdr_unsigned(long, sp
->sa_size
);
* Iff doesn't exist, create it
* otherwise just truncate to 0 length
* should I set the mode too ??
if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
error
= VOP_CREATE(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
);
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
bcopy(cverf
, (caddr_t
)&vap
->va_atime
,
error
= VOP_SETATTR(nd
.ni_vp
, vap
, cred
,
} else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
if (vap
->va_type
== VCHR
&& rdev
== 0xffffffff)
if (error
= suser(cred
, (u_short
*)0)) {
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
vap
->va_rdev
= (dev_t
)rdev
;
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
if (error
= VOP_MKNOD(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
)) {
nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
nd
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| SAVESTART
);
nd
.ni_cnd
.cn_proc
= procp
;
nd
.ni_cnd
.cn_cred
= cred
;
if (error
= lookup(&nd
)) {
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (nd
.ni_cnd
.cn_flags
& ISSYMLINK
) {
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (vap
->va_size
!= -1) {
error
= nfsrv_access(vp
, VWRITE
, cred
,
(nd
.ni_cnd
.cn_flags
& RDONLY
), procp
);
nqsrv_getl(vp
, ND_WRITE
);
error
= VOP_SETATTR(vp
, vap
, cred
,
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
);
error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
if (exclusive_flag
&& !error
&&
bcmp(cverf
, (caddr_t
)&vap
->va_atime
, NFSX_V3CREATEVERF
))
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_SRVFH(v3
) + NFSX_FATTR(v3
) + NFSX_WCCDATA(v3
));
nfsm_srvpostop_attr(0, vap
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(vap
, fp
);
if (nd
.ni_cnd
.cn_nameiop
) {
p
->p_spare
[1]--, vrele(nd
.ni_startdir
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_mknod(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfs_fattr
*fp
;
struct vattr va
, dirfor
, diraft
;
register struct vattr
*vap
= &va
;
int error
= 0, cache
, len
, tsize
, dirfor_ret
= 1, diraft_ret
= 1;
struct mbuf
*mb
, *mb2
, *mreq
;
struct vnode
*vp
, *dirp
= (struct vnode
*)0;
nd
.ni_cnd
.cn_nameiop
= 0;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
| SAVESTART
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
, procp
);
nfsm_reply(NFSX_WCCDATA(1));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
vtyp
= nfsv3tov_type(*tl
);
if (vtyp
!= VCHR
&& vtyp
!= VBLK
&& vtyp
!= VSOCK
&& vtyp
!= VFIFO
) {
free((caddr_t
)nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (vtyp
== VCHR
|| vtyp
== VBLK
) {
nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
major
= fxdr_unsigned(u_long
, *tl
++);
minor
= fxdr_unsigned(u_long
, *tl
);
vap
->va_rdev
= makedev(major
, minor
);
* Iff doesn't exist, create it.
free((caddr_t
)nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
error
= VOP_CREATE(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
);
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (error
= suser(cred
, (u_short
*)0)) {
free((caddr_t
)nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
if (error
= VOP_MKNOD(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
)) {
nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
nd
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| SAVESTART
);
nd
.ni_cnd
.cn_proc
= procp
;
nd
.ni_cnd
.cn_cred
= procp
->p_ucred
;
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (nd
.ni_cnd
.cn_flags
& ISSYMLINK
) {
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
);
error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1));
nfsm_srvpostop_attr(0, vap
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
if (nd
.ni_cnd
.cn_nameiop
) {
free((caddr_t
)nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_remove(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, cache
, len
, dirfor_ret
= 1, diraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mreq
, *mb2
;
struct vattr dirfor
, diraft
;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= DELETE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
if (vp
->v_type
== VDIR
&&
(error
= suser(cred
, (u_short
*)0)))
* The root of a mounted filesystem cannot be deleted.
if (vp
->v_flag
& VROOT
) {
(void) vnode_pager_uncache(vp
);
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
nqsrv_getl(vp
, ND_WRITE
);
error
= VOP_REMOVE(nd
.ni_dvp
, nd
.ni_vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsrv_rename(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, cache
, len
, len2
, fdirfor_ret
= 1, fdiraft_ret
= 1;
int tdirfor_ret
= 1, tdiraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mreq
, *mb2
;
struct nameidata fromnd
, tond
;
struct vnode
*fvp
, *tvp
, *tdvp
, *fdirp
= (struct vnode
*)0;
struct vnode
*tdirp
= (struct vnode
*)0;
struct vattr fdirfor
, fdiraft
, tdirfor
, tdiraft
;
fromnd
.ni_cnd
.cn_nameiop
= 0;
tond
.ni_cnd
.cn_nameiop
= 0;
* Remember our original uid so that we can reset cr_uid before
* the second nfs_namei() call, in case it is remapped.
saved_uid
= cred
->cr_uid
;
fromnd
.ni_cnd
.cn_cred
= cred
;
fromnd
.ni_cnd
.cn_nameiop
= DELETE
;
fromnd
.ni_cnd
.cn_flags
= WANTPARENT
| SAVESTART
;
error
= nfs_namei(&fromnd
, ffhp
, len
, slp
, nam
, &md
,
&dpos
, &fdirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
fdirfor_ret
= VOP_GETATTR(fdirp
, &fdirfor
, cred
,
fdirp
= (struct vnode
*)0;
nfsm_reply(2 * NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(fdirfor_ret
, &fdirfor
, fdiraft_ret
, &fdiraft
);
nfsm_srvwcc_data(tdirfor_ret
, &tdirfor
, tdiraft_ret
, &tdiraft
);
nfsm_strsiz(len2
, NFS_MAXNAMLEN
);
cred
->cr_uid
= saved_uid
;
tond
.ni_cnd
.cn_cred
= cred
;
tond
.ni_cnd
.cn_nameiop
= RENAME
;
tond
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
| NOCACHE
| SAVESTART
;
error
= nfs_namei(&tond
, tfhp
, len2
, slp
, nam
, &md
,
&dpos
, &tdirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
tdirfor_ret
= VOP_GETATTR(tdirp
, &tdirfor
, cred
,
tdirp
= (struct vnode
*)0;
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
if (fvp
->v_type
== VDIR
&& tvp
->v_type
!= VDIR
) {
} else if (fvp
->v_type
!= VDIR
&& tvp
->v_type
== VDIR
) {
if (tvp
->v_type
== VDIR
&& tvp
->v_mountedhere
) {
if (fvp
->v_type
== VDIR
&& fvp
->v_mountedhere
) {
if (fvp
->v_mount
!= tdvp
->v_mount
) {
* If source is the same as the destination (that is the
* same vnode with the same name in the same directory),
* then there is nothing to do.
if (fvp
== tvp
&& fromnd
.ni_dvp
== tdvp
&&
fromnd
.ni_cnd
.cn_namelen
== tond
.ni_cnd
.cn_namelen
&&
!bcmp(fromnd
.ni_cnd
.cn_nameptr
, tond
.ni_cnd
.cn_nameptr
,
fromnd
.ni_cnd
.cn_namelen
))
nqsrv_getl(fromnd
.ni_dvp
, ND_WRITE
);
nqsrv_getl(tdvp
, ND_WRITE
);
nqsrv_getl(tvp
, ND_WRITE
);
error
= VOP_RENAME(fromnd
.ni_dvp
, fromnd
.ni_vp
, &fromnd
.ni_cnd
,
tond
.ni_dvp
, tond
.ni_vp
, &tond
.ni_cnd
);
VOP_ABORTOP(tond
.ni_dvp
, &tond
.ni_cnd
);
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
FREE(tond
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
fdiraft_ret
= VOP_GETATTR(fdirp
, &fdiraft
, cred
, procp
);
tdiraft_ret
= VOP_GETATTR(tdirp
, &tdiraft
, cred
, procp
);
vrele(fromnd
.ni_startdir
);
FREE(fromnd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
nfsm_reply(2 * NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(fdirfor_ret
, &fdirfor
, fdiraft_ret
, &fdiraft
);
nfsm_srvwcc_data(tdirfor_ret
, &tdirfor
, tdiraft_ret
, &tdiraft
);
if (tond
.ni_cnd
.cn_nameiop
) {
FREE(tond
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (fromnd
.ni_cnd
.cn_nameiop
) {
vrele(fromnd
.ni_startdir
);
FREE(fromnd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
nfsrv_link(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, rdonly
, cache
, len
, dirfor_ret
= 1, diraft_ret
= 1;
int getret
= 1, v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mreq
, *mb2
;
struct vnode
*vp
, *xp
, *dirp
= (struct vnode
*)0;
struct vattr dirfor
, diraft
, at
;
if (error
= nfsrv_fhtovp(fhp
, FALSE
, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_WCCDATA(v3
));
nfsm_srvpostop_attr(getret
, &at
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
if (vp
->v_type
== VDIR
&& (error
= suser(cred
, (u_short
*)0)))
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
;
error
= nfs_namei(&nd
, dfhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
dirp
= (struct vnode
*)0;
if (vp
->v_mount
!= xp
->v_mount
)
nqsrv_getl(vp
, ND_WRITE
);
nqsrv_getl(xp
, ND_WRITE
);
error
= VOP_LINK(vp
, nd
.ni_dvp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_WCCDATA(v3
));
nfsm_srvpostop_attr(getret
, &at
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
* nfs symbolic link service
nfsrv_symlink(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
struct vattr va
, dirfor
, diraft
;
register struct vattr
*vap
= &va
;
char *bpos
, *cp
, *pathcp
= (char *)0, *cp2
;
int error
= 0, cache
, len
, len2
, dirfor_ret
= 1, diraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mreq
, *mb2
;
struct vnode
*dirp
= (struct vnode
*)0;
nd
.ni_cnd
.cn_nameiop
= 0;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| SAVESTART
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
dirp
= (struct vnode
*)0;
nfsm_strsiz(len2
, NFS_MAXPATHLEN
);
MALLOC(pathcp
, caddr_t
, len2
+ 1, M_TEMP
, M_WAITOK
);
io
.uio_segflg
= UIO_SYSSPACE
;
io
.uio_procp
= (struct proc
*)0;
nfsm_dissect(sp
, struct nfsv2_sattr
*, NFSX_V2SATTR
);
vap
->va_mode
= fxdr_unsigned(u_short
, sp
->sa_mode
);
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
error
= VOP_SYMLINK(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
, pathcp
);
nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
nd
.ni_cnd
.cn_flags
&= ~(LOCKPARENT
| SAVESTART
| FOLLOW
);
nd
.ni_cnd
.cn_flags
|= (NOFOLLOW
| LOCKLEAF
);
nd
.ni_cnd
.cn_proc
= procp
;
nd
.ni_cnd
.cn_cred
= cred
;
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= nd
.ni_vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(nd
.ni_vp
, &fhp
->fh_fid
);
error
= VOP_GETATTR(nd
.ni_vp
, vap
, cred
,
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_SRVFH(v3
) + NFSX_POSTOPATTR(v3
) + NFSX_WCCDATA(v3
));
nfsm_srvpostop_attr(0, vap
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
if (nd
.ni_cnd
.cn_nameiop
) {
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_mkdir(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
struct vattr va
, dirfor
, diraft
;
register struct vattr
*vap
= &va
;
register struct nfs_fattr
*fp
;
int error
= 0, cache
, len
, dirfor_ret
= 1, diraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mb2
, *mreq
;
struct vnode
*vp
, *dirp
= (struct vnode
*)0;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
dirp
= (struct vnode
*)0;
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
vap
->va_mode
= nfstov_mode(*tl
++);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
error
= VOP_MKDIR(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
);
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
);
error
= VOP_GETATTR(vp
, vap
, cred
, procp
);
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_SRVFH(v3
) + NFSX_POSTOPATTR(v3
) + NFSX_WCCDATA(v3
));
nfsm_srvpostop_attr(0, vap
);
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
nfsm_build(fp
, struct nfs_fattr
*, NFSX_V2FATTR
);
nfsm_srvfillattr(vap
, fp
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_rmdir(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, cache
, len
, dirfor_ret
= 1, diraft_ret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mreq
, *mb2
;
struct vnode
*vp
, *dirp
= (struct vnode
*)0;
struct vattr dirfor
, diraft
;
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= DELETE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
error
= nfs_namei(&nd
, fhp
, len
, slp
, nam
, &md
, &dpos
,
&dirp
, procp
, (nfsd
->nd_flag
& ND_KERBAUTH
));
dirfor_ret
= VOP_GETATTR(dirp
, &dirfor
, cred
,
dirp
= (struct vnode
*)0;
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
if (vp
->v_type
!= VDIR
) {
* The root of a mounted filesystem cannot be deleted.
nqsrv_getl(nd
.ni_dvp
, ND_WRITE
);
nqsrv_getl(vp
, ND_WRITE
);
error
= VOP_RMDIR(nd
.ni_dvp
, nd
.ni_vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
diraft_ret
= VOP_GETATTR(dirp
, &diraft
, cred
, procp
);
nfsm_reply(NFSX_WCCDATA(v3
));
nfsm_srvwcc_data(dirfor_ret
, &dirfor
, diraft_ret
, &diraft
);
* - mallocs what it thinks is enough to read
* count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR
* - loops around building the reply
* if the output generated exceeds count break out of loop
* The nfsm_clget macro is used here so that the reply will be packed
* tightly in mbuf clusters.
* - it only knows that it has encountered eof when the VOP_READDIR()
* - as such one readdir rpc will return eof false although you are there
* and then the next will return eof
* - it trims out records with d_fileno == 0
* this doesn't matter for Unix clients, but they might confuse clients
* NB: It is tempting to set eof to true if the VOP_READDIR() reads less
* than requested, but this may not apply to all filesystems. For
* example, client NFS does not { although it is never remote mounted
* The alternate call nfsrv_readdirplus() does lookups as well.
* PS: The NFS protocol spec. does not clarify what the "count" byte
* argument is a count of.. just name strings and file id's or the
* entire reply rpc or ...
* I tried just file name and id sizes and it confused the Sun client,
* so I am using the full rpc size now. The "paranoia.." comment refers
* to including the status longwords that are not a part of the dir.
* "entry" structures, but are in the rpc.
u_long fl_fattr
[NFSX_V3FATTR
/ sizeof (u_long
)];
u_long fl_nfh
[NFSX_V3FH
/ sizeof (u_long
)];
nfsrv_readdir(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct mbuf
*mp
;
register struct dirent
*dp
;
struct mbuf
*mb
, *mb2
, *mreq
, *mp2
;
char *cpos
, *cend
, *cp2
, *rbuf
;
int len
, nlen
, rem
, xfer
, tsiz
, i
, error
= 0, getret
= 1;
int siz
, cnt
, fullsiz
, eofflag
, rdonly
, cache
, ncookies
;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
u_quad_t frev
, off
, toff
, verf
;
u_long
*cookies
= NULL
, *cookiep
;
nfsm_dissect(tl
, u_long
*, 5 * NFSX_UNSIGNED
);
nfsm_dissect(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
toff
= fxdr_unsigned(u_quad_t
, *tl
++);
cnt
= fxdr_unsigned(int, *tl
);
siz
= ((cnt
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
xfer
= NFS_SRVMAXDATA(nfsd
);
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &at
);
error
= getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
if (!error
&& toff
&& verf
!= at
.va_filerev
)
error
= NFSERR_BAD_COOKIE
;
error
= nfsrv_access(vp
, VEXEC
, cred
, rdonly
, procp
);
nfsm_reply(NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(getret
, &at
);
MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
io
.uio_offset
= (off_t
)off
;
io
.uio_segflg
= UIO_SYSSPACE
;
io
.uio_procp
= (struct proc
*)0;
free((caddr_t
)cookies
, M_TEMP
);
error
= VOP_READDIR(vp
, &io
, cred
, &eofflag
, &ncookies
, &cookies
);
off
= (off_t
)io
.uio_offset
;
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
free((caddr_t
)rbuf
, M_TEMP
);
free((caddr_t
)cookies
, M_TEMP
);
nfsm_reply(NFSX_POSTOPATTR(v3
));
nfsm_srvpostop_attr(getret
, &at
);
* If nothing read, return eof
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_COOKIEVERF(v3
) +
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
txdr_hyper(&at
.va_filerev
, tl
);
nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
FREE((caddr_t
)rbuf
, M_TEMP
);
FREE((caddr_t
)cookies
, M_TEMP
);
* Check for degenerate cases of nothing useful read.
dp
= (struct dirent
*)cpos
;
while (dp
->d_fileno
== 0 && cpos
< cend
&& ncookies
> 0) {
dp
= (struct dirent
*)cpos
;
if (cpos
>= cend
|| ncookies
== 0) {
len
= 3 * NFSX_UNSIGNED
; /* paranoia, probably can be 0 */
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_COOKIEVERF(v3
) + siz
);
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
txdr_hyper(&at
.va_filerev
, tl
);
be
= bp
+ M_TRAILINGSPACE(mp
);
/* Loop through the records and build reply */
while (cpos
< cend
&& ncookies
> 0) {
rem
= nfsm_rndup(nlen
)-nlen
;
len
+= (4 * NFSX_UNSIGNED
+ nlen
+ rem
);
len
+= 2 * NFSX_UNSIGNED
;
* Build the directory record xdr from
*tl
= txdr_unsigned(dp
->d_fileno
);
*tl
= txdr_unsigned(nlen
);
/* And loop around copying the name */
/* And null pad to a long boundary */
for (i
= 0; i
< rem
; i
++)
/* Finish off the record */
*tl
= txdr_unsigned(*cookiep
);
dp
= (struct dirent
*)cpos
;
mp
->m_len
= bp
- mtod(mp
, caddr_t
);
FREE((caddr_t
)rbuf
, M_TEMP
);
FREE((caddr_t
)cookies
, M_TEMP
);
nfsrv_readdirplus(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct mbuf
*mp
;
register struct dirent
*dp
;
struct mbuf
*mb
, *mb2
, *mreq
, *mp2
;
char *cpos
, *cend
, *cp2
, *rbuf
;
fhandle_t
*fhp
, *nfhp
= (fhandle_t
*)fl
.fl_nfh
;
struct vattr va
, at
, *vap
= &va
;
int len
, nlen
, rem
, xfer
, tsiz
, i
, error
= 0, getret
= 1;
int siz
, cnt
, fullsiz
, eofflag
, rdonly
, cache
, dirlen
, ncookies
;
u_quad_t frev
, off
, toff
, verf
;
u_long
*cookies
= NULL
, *cookiep
;
nfsm_dissect(tl
, u_long
*, 6 * NFSX_UNSIGNED
);
siz
= fxdr_unsigned(int, *tl
++);
cnt
= fxdr_unsigned(int, *tl
);
siz
= ((siz
+ DIRBLKSIZ
- 1) & ~(DIRBLKSIZ
- 1));
xfer
= NFS_SRVMAXDATA(nfsd
);
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &at
);
error
= getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
if (!error
&& toff
&& verf
!= at
.va_filerev
)
error
= NFSERR_BAD_COOKIE
;
error
= nfsrv_access(vp
, VEXEC
, cred
, rdonly
, procp
);
nfsm_reply(NFSX_V3POSTOPATTR
);
nfsm_srvpostop_attr(getret
, &at
);
MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
io
.uio_offset
= (off_t
)off
;
io
.uio_segflg
= UIO_SYSSPACE
;
io
.uio_procp
= (struct proc
*)0;
free((caddr_t
)cookies
, M_TEMP
);
error
= VOP_READDIR(vp
, &io
, cred
, &eofflag
, &ncookies
, &cookies
);
off
= (u_quad_t
)io
.uio_offset
;
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
free((caddr_t
)cookies
, M_TEMP
);
free((caddr_t
)rbuf
, M_TEMP
);
nfsm_reply(NFSX_V3POSTOPATTR
);
nfsm_srvpostop_attr(getret
, &at
);
* If nothing read, return eof
nfsm_reply(NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(tl
, u_long
*, 4 * NFSX_UNSIGNED
);
txdr_hyper(&at
.va_filerev
, tl
);
FREE((caddr_t
)cookies
, M_TEMP
);
FREE((caddr_t
)rbuf
, M_TEMP
);
* Check for degenerate cases of nothing useful read.
dp
= (struct dirent
*)cpos
;
while (dp
->d_fileno
== 0 && cpos
< cend
&& ncookies
> 0) {
dp
= (struct dirent
*)cpos
;
if (cpos
>= cend
|| ncookies
== 0) {
dirlen
= len
= NFSX_V3POSTOPATTR
+ NFSX_V3COOKIEVERF
+ 2 * NFSX_UNSIGNED
;
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(tl
, u_long
*, 2 * NFSX_UNSIGNED
);
txdr_hyper(&at
.va_filerev
, tl
);
be
= bp
+ M_TRAILINGSPACE(mp
);
/* Loop through the records and build reply */
while (cpos
< cend
&& ncookies
> 0) {
rem
= nfsm_rndup(nlen
)-nlen
;
* For readdir_and_lookup get the vnode using
if (VFS_VGET(vp
->v_mount
, dp
->d_fileno
, &nvp
))
bzero((caddr_t
)nfhp
, NFSX_V3FH
);
nvp
->v_mount
->mnt_stat
.f_fsid
;
if (VFS_VPTOFH(nvp
, &nfhp
->fh_fid
)) {
if (VOP_GETATTR(nvp
, vap
, cred
, procp
)) {
* If either the dircount or maxcount will be
* exceeded, get out now. Both of these lengths
* are calculated conservatively, including all
len
+= (7 * NFSX_UNSIGNED
+ nlen
+ rem
+ NFSX_V3FH
+
dirlen
+= (6 * NFSX_UNSIGNED
+ nlen
+ rem
);
if (len
> cnt
|| dirlen
> fullsiz
) {
* Build the directory record xdr from
fp
= (struct nfs_fattr
*)&fl
.fl_fattr
;
nfsm_srvfillattr(vap
, fp
);
fl
.fl_fhsize
= txdr_unsigned(NFSX_V3FH
);
fl
.fl_postopok
= nfs_true
;
fl
.fl_off
.nfsuquad
[0] = 0;
fl
.fl_off
.nfsuquad
[1] = txdr_unsigned(*cookiep
);
*tl
= txdr_unsigned(dp
->d_fileno
);
*tl
= txdr_unsigned(nlen
);
/* And loop around copying the name */
/* And null pad to a long boundary */
for (i
= 0; i
< rem
; i
++)
* Now copy the flrep structure out.
xfer
= sizeof (struct flrep
);
dp
= (struct dirent
*)cpos
;
mp
->m_len
= bp
- mtod(mp
, caddr_t
);
FREE((caddr_t
)cookies
, M_TEMP
);
FREE((caddr_t
)rbuf
, M_TEMP
);
nfsrv_commit(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= 0, rdonly
, for_ret
= 1, aft_ret
= 1, cnt
, cache
;
struct mbuf
*mb
, *mb2
, *mreq
;
nfsm_dissect(tl
, u_long
*, 3 * NFSX_UNSIGNED
);
* XXX At this time VOP_FSYNC() does not accept offset and byte
* count parameters, so these arguments are useless (someday maybe).
cnt
= fxdr_unsigned(int, *tl
);
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(2 * NFSX_UNSIGNED
);
nfsm_srvwcc_data(for_ret
, &bfor
, aft_ret
, &aft
);
for_ret
= VOP_GETATTR(vp
, &bfor
, cred
, procp
);
error
= VOP_FSYNC(vp
, cred
, MNT_WAIT
, procp
);
aft_ret
= VOP_GETATTR(vp
, &aft
, cred
, procp
);
nfsm_reply(NFSX_V3WCCDATA
+ NFSX_V3WRITEVERF
);
nfsm_srvwcc_data(for_ret
, &bfor
, aft_ret
, &aft
);
nfsm_build(tl
, u_long
*, NFSX_V3WRITEVERF
);
*tl
++ = txdr_unsigned(boottime
.tv_sec
);
*tl
= txdr_unsigned(boottime
.tv_usec
);
nfsrv_statfs(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct statfs
*sf
;
register struct nfs_statfs
*sfp
;
int error
= 0, rdonly
, cache
, getret
= 1;
int v3
= (nfsd
->nd_flag
& ND_NFSV3
);
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &at
);
error
= VFS_STATFS(vp
->v_mount
, sf
, procp
);
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
nfsm_reply(NFSX_POSTOPATTR(v3
) + NFSX_STATFS(v3
));
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(sfp
, struct nfs_statfs
*, NFSX_STATFS(v3
));
tval
= (u_quad_t
)sf
->f_blocks
;
tval
*= (u_quad_t
)sf
->f_bsize
;
txdr_hyper(&tval
, &sfp
->sf_tbytes
);
tval
= (u_quad_t
)sf
->f_bfree
;
tval
*= (u_quad_t
)sf
->f_bsize
;
txdr_hyper(&tval
, &sfp
->sf_fbytes
);
tval
= (u_quad_t
)sf
->f_bavail
;
tval
*= (u_quad_t
)sf
->f_bsize
;
txdr_hyper(&tval
, &sfp
->sf_abytes
);
sfp
->sf_tfiles
.nfsuquad
[0] = 0;
sfp
->sf_tfiles
.nfsuquad
[1] = txdr_unsigned(sf
->f_files
);
sfp
->sf_ffiles
.nfsuquad
[0] = 0;
sfp
->sf_ffiles
.nfsuquad
[1] = txdr_unsigned(sf
->f_ffree
);
sfp
->sf_afiles
.nfsuquad
[0] = 0;
sfp
->sf_afiles
.nfsuquad
[1] = txdr_unsigned(sf
->f_ffree
);
sfp
->sf_tsize
= txdr_unsigned(NFS_MAXDGRAMDATA
);
sfp
->sf_bsize
= txdr_unsigned(sf
->f_bsize
);
sfp
->sf_blocks
= txdr_unsigned(sf
->f_blocks
);
sfp
->sf_bfree
= txdr_unsigned(sf
->f_bfree
);
sfp
->sf_bavail
= txdr_unsigned(sf
->f_bavail
);
nfsrv_fsinfo(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfsv3_fsinfo
*sip
;
int error
= 0, rdonly
, cache
, getret
= 1, pref
;
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &at
);
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
nfsm_reply(NFSX_V3POSTOPATTR
+ NFSX_V3FSINFO
);
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(sip
, struct nfsv3_fsinfo
*, NFSX_V3FSINFO
);
* There should be file system VFS OP(s) to get this information.
if (slp
->ns_so
->so_type
== SOCK_DGRAM
)
sip
->fs_rtmax
= txdr_unsigned(NFS_MAXDATA
);
sip
->fs_rtpref
= txdr_unsigned(pref
);
sip
->fs_rtmult
= txdr_unsigned(NFS_FABLKSIZE
);
sip
->fs_wtmax
= txdr_unsigned(NFS_MAXDATA
);
sip
->fs_wtpref
= txdr_unsigned(pref
);
sip
->fs_wtmult
= txdr_unsigned(NFS_FABLKSIZE
);
sip
->fs_dtpref
= txdr_unsigned(pref
);
sip
->fs_maxfilesize
.nfsuquad
[0] = 0xffffffff;
sip
->fs_maxfilesize
.nfsuquad
[1] = 0xffffffff;
sip
->fs_timedelta
.nfsv3_sec
= 0;
sip
->fs_timedelta
.nfsv3_nsec
= txdr_unsigned(1);
sip
->fs_properties
= txdr_unsigned(NFSV3FSINFO_LINK
|
NFSV3FSINFO_SYMLINK
| NFSV3FSINFO_HOMOGENEOUS
|
nfsrv_pathconf(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
register struct nfsv3_pathconf
*pc
;
int error
= 0, rdonly
, cache
, getret
= 1, linkmax
, namemax
;
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, 1, &vp
, cred
, slp
, nam
,
&rdonly
, (nfsd
->nd_flag
& ND_KERBAUTH
))) {
nfsm_reply(NFSX_UNSIGNED
);
nfsm_srvpostop_attr(getret
, &at
);
error
= VOP_PATHCONF(vp
, _PC_LINK_MAX
, &linkmax
);
error
= VOP_PATHCONF(vp
, _PC_NAME_MAX
, &namemax
);
error
= VOP_PATHCONF(vp
, _PC_CHOWN_RESTRICTED
, &chownres
);
error
= VOP_PATHCONF(vp
, _PC_NO_TRUNC
, ¬runc
);
getret
= VOP_GETATTR(vp
, &at
, cred
, procp
);
nfsm_reply(NFSX_V3POSTOPATTR
+ NFSX_V3PATHCONF
);
nfsm_srvpostop_attr(getret
, &at
);
nfsm_build(pc
, struct nfsv3_pathconf
*, NFSX_V3PATHCONF
);
pc
->pc_linkmax
= txdr_unsigned(linkmax
);
pc
->pc_namemax
= txdr_unsigned(namemax
);
pc
->pc_notrunc
= txdr_unsigned(notrunc
);
pc
->pc_chownrestricted
= txdr_unsigned(chownres
);
* These should probably be supported by VOP_PATHCONF(), but
* until msdosfs is exportable (why would you want to?), the
* Unix defaults should be ok.
pc
->pc_caseinsensitive
= nfs_false
;
pc
->pc_casepreserving
= nfs_true
;
* Null operation, used by clients to ping server
nfsrv_null(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
int error
= NFSERR_RETVOID
, cache
;
* No operation, used for obsolete procedures
nfsrv_noop(nfsd
, slp
, procp
, mrq
)
struct nfsrv_descript
*nfsd
;
struct mbuf
*mrep
= nfsd
->nd_mrep
, *md
= nfsd
->nd_md
;
struct mbuf
*nam
= nfsd
->nd_nam
;
caddr_t dpos
= nfsd
->nd_dpos
;
struct ucred
*cred
= &nfsd
->nd_cr
;
error
= nfsd
->nd_repstat
;
* Perform access checking for vnodes obtained from file handles that would
* refer to files already opened by a Unix client. You cannot just use
* vn_writechk() and VOP_ACCESS() for two reasons.
* 1 - You must check for exported rdonly as well as MNT_RDONLY for the write case
* 2 - The owner is to be given access irrespective of mode bits so that
* processes that chmod after opening a file don't break. I don't like
* this because it opens a security hole, but since the nfs server opens
* a security hole the size of a barn door anyhow, what the heck.
nfsrv_access(vp
, flags
, cred
, rdonly
, p
)
register struct vnode
*vp
;
register struct ucred
*cred
;
/* Just vn_writechk() changed to check rdonly */
* Disallow write attempts on read-only file systems;
* unless the file is a socket or a block or character
* device resident on the file system.
if (rdonly
|| (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)) {
case VREG
: case VDIR
: case VLNK
:
* If there's shared text associated with
* the inode, try to free it up once. If
* we fail, we can't allow writing.
if ((vp
->v_flag
& VTEXT
) && !vnode_pager_uncache(vp
))
if (error
= VOP_GETATTR(vp
, &vattr
, cred
, p
))
if ((error
= VOP_ACCESS(vp
, flags
, cred
, p
)) &&
cred
->cr_uid
!= vattr
.va_uid
)