* 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_serv.c 7.50 (Berkeley) %G%
* nfs version 2 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
#include <ufs/ufs/quota.h> /* XXX - for ufid */
#include <ufs/ufs/inode.h> /* XXX - for ufid */
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
extern u_long nfs_procids
[NFS_NPROCS
];
extern u_long nfs_xdrneg1
;
extern u_long nfs_false
, nfs_true
;
nfstype nfs_type
[9] = { NFNON
, NFREG
, NFDIR
, NFBLK
, NFCHR
, NFLNK
, NFNON
,
nfsrv_getattr(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct nfsv2_fattr
*fp
;
register struct vattr
*vap
= &va
;
int error
= 0, rdonly
, cache
;
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_READ
);
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
nfsrv_setattr(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct vattr
*vap
= &va
;
register struct nfsv2_sattr
*sp
;
register struct nfsv2_fattr
*fp
;
int error
= 0, rdonly
, cache
, duration2
, cache2
;
struct mbuf
*mb
, *mb2
, *mreq
;
nfsm_dissect(sp
, struct nfsv2_sattr
*, NFSX_SATTR
);
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_WRITE
);
if (error
= nfsrv_access(vp
, VWRITE
, cred
, rdonly
, nfsd
->nd_procp
))
* 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_long
, sp
->sa_size
);
* The usec field of sa_atime is overloaded with the va_flags field
* for 4.4BSD clients. Hopefully other clients always set both the
* sec and usec fields to -1 when not setting the atime.
if (sp
->sa_atime
.tv_sec
!= nfs_xdrneg1
) {
vap
->va_atime
.tv_sec
= fxdr_unsigned(long, sp
->sa_atime
.tv_sec
);
vap
->va_atime
.tv_usec
= 0;
if (sp
->sa_atime
.tv_usec
!= nfs_xdrneg1
)
vap
->va_flags
= fxdr_unsigned(u_long
, sp
->sa_atime
.tv_usec
);
if (sp
->sa_mtime
.tv_sec
!= nfs_xdrneg1
)
fxdr_time(&sp
->sa_mtime
, &vap
->va_mtime
);
if (error
= VOP_SETATTR(vp
, vap
, cred
, nfsd
->nd_procp
)) {
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_reply(NFSX_FATTR
+ 2*NFSX_UNSIGNED
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
if (nfsd
->nd_nqlflag
!= NQL_NOVAL
) {
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
nfsrv_lookup(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct nfsv2_fattr
*fp
;
int error
= 0, lflag
= 0, rdonly
, cache
, duration2
, cache2
, len
;
struct mbuf
*mb
, *mb2
, *mreq
;
struct vattr va
, *vap
= &va
;
if (nfsd
->nd_nqlflag
!= NQL_NOVAL
) {
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
lflag
= fxdr_unsigned(int, *tl
);
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
duration2
= fxdr_unsigned(int, *tl
);
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= LOOKUP
;
nd
.ni_cnd
.cn_flags
= LOCKLEAF
| SAVESTART
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
nqsrv_getl(nd
.ni_startdir
, NQL_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
;
if (error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
)) {
(void) nqsrv_getlease(vp
, &duration2
, lflag
, nfsd
,
nam
, &cache2
, &frev2
, cred
);
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_reply(NFSX_FH
+ NFSX_FATTR
+ 5*NFSX_UNSIGNED
);
if (nfsd
->nd_nqlflag
!= NQL_NOVAL
) {
nfsm_build(tl
, u_long
*, 5*NFSX_UNSIGNED
);
*tl
++ = txdr_unsigned(lflag
);
*tl
++ = txdr_unsigned(cache2
);
*tl
++ = txdr_unsigned(duration2
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
nfsrv_readlink(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
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
;
struct mbuf
*mb
, *mb2
, *mp2
, *mp3
, *mreq
;
struct uio io
, *uiop
= &io
;
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
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
)) {
if (vp
->v_type
!= VLNK
) {
nqsrv_getl(vp
, NQL_READ
);
error
= VOP_READLINK(vp
, uiop
, cred
);
nfsm_reply(NFSX_UNSIGNED
);
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
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct iovec
*iv
;
register struct nfsv2_fattr
*fp
;
int error
= 0, rdonly
, cache
, i
, cnt
, len
, left
, siz
, tlen
;
struct mbuf
*mb
, *mb2
, *mreq
;
struct uio io
, *uiop
= &io
;
struct vattr va
, *vap
= &va
;
nfsm_dissect(tl
, u_long
*, NFSX_UNSIGNED
);
off
= fxdr_unsigned(off_t
, *tl
);
nfsm_srvstrsiz(cnt
, NFS_MAXDATA
);
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_READ
);
if (error
= nfsrv_access(vp
, VREAD
| VEXEC
, cred
, rdonly
, nfsd
->nd_procp
)) {
if (error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
)) {
else if ((off
+ cnt
) > vap
->va_size
)
cnt
= nfsm_rndup(vap
->va_size
- off
);
nfsm_reply(NFSX_FATTR
+NFSX_UNSIGNED
+nfsm_rndup(cnt
));
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
nfsm_build(tl
, u_long
*, NFSX_UNSIGNED
);
* Generate the mbuf list with the uio_iov ref. to it.
MALLOC(iv
, struct iovec
*,
((NFS_MAXDATA
+MLEN
-1)/MLEN
) * sizeof (struct iovec
),
siz
= MIN(M_TRAILINGSPACE(m
), left
);
MGET(m
, M_WAIT
, MT_DATA
);
uiop
->uio_segflg
= UIO_SYSSPACE
;
error
= VOP_READ(vp
, uiop
, IO_NODELOCKED
, cred
);
FREE((caddr_t
)iv2
, M_TEMP
);
if (error
|| (error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
))) {
if (cnt
!= tlen
|| tlen
!= len
)
nfsm_adj(mb
, cnt
-tlen
, tlen
-len
);
*tl
= txdr_unsigned(len
);
nfsrv_write(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct iovec
*ivp
;
register struct mbuf
*mp
;
register struct nfsv2_fattr
*fp
;
struct iovec iv
[NFS_MAXIOVEC
];
register struct vattr
*vap
= &va
;
int error
= 0, rdonly
, cache
, siz
, len
, xfer
;
struct mbuf
*mb
, *mb2
, *mreq
;
struct uio io
, *uiop
= &io
;
nfsm_dissect(tl
, u_long
*, 4*NFSX_UNSIGNED
);
off
= fxdr_unsigned(off_t
, *++tl
);
len
= fxdr_unsigned(long, *tl
);
if (len
> NFS_MAXDATA
|| len
<= 0) {
if (dpos
== (mtod(md
, caddr_t
)+md
->m_len
)) {
siz
= dpos
-mtod(mp
, caddr_t
);
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_WRITE
);
if (error
= nfsrv_access(vp
, VWRITE
, cred
, rdonly
, nfsd
->nd_procp
)) {
uiop
->uio_rw
= UIO_WRITE
;
uiop
->uio_segflg
= UIO_SYSSPACE
;
uiop
->uio_procp
= (struct proc
*)0;
* Do up to NFS_MAXIOVEC mbufs of write each iteration of the
while (len
> 0 && uiop
->uio_resid
== 0) {
while (len
> 0 && uiop
->uio_iovcnt
< NFS_MAXIOVEC
&& mp
!= NULL
) {
ivp
->iov_base
= mtod(mp
, caddr_t
);
ivp
->iov_len
= xfer
= len
;
ivp
->iov_len
= xfer
= mp
->m_len
;
if (M_HASCL(mp
) && (((u_long
)ivp
->iov_base
) & CLOFSET
) == 0)
ivp
->iov_op
= NULL
; /* what should it be ?? */
if (len
> 0 && mp
== NULL
) {
if (error
= VOP_WRITE(vp
, uiop
, IO_SYNC
| IO_NODELOCKED
,
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
if (nfsd
->nd_nqlflag
!= NQL_NOVAL
) {
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
txdr_hyper(&vap
->va_filerev
, tl
);
* now does a truncate to 0 length via. setattr if it already exists
nfsrv_create(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct nfsv2_fattr
*fp
;
register struct vattr
*vap
= &va
;
int error
= 0, rdev
, cache
, len
;
struct mbuf
*mb
, *mb2
, *mreq
;
nd
.ni_cnd
.cn_nameiop
= 0;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
| SAVESTART
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
nfsm_dissect(tl
, u_long
*, NFSX_SATTR
);
* Iff doesn't exist, create it
* otherwise just truncate to 0 length
* should I set the mode too ??
vap
->va_type
= IFTOVT(fxdr_unsigned(u_long
, *tl
));
if (vap
->va_type
== VNON
)
vap
->va_mode
= nfstov_mode(*tl
);
rdev
= fxdr_unsigned(long, *(tl
+3));
if (vap
->va_type
== VREG
|| vap
->va_type
== VSOCK
) {
nqsrv_getl(nd
.ni_dvp
, NQL_WRITE
);
if (error
= VOP_CREATE(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
))
FREE(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
} else if (vap
->va_type
== VCHR
|| vap
->va_type
== VBLK
||
if (vap
->va_type
== VCHR
&& rdev
== 0xffffffff)
if (vap
->va_type
== VFIFO
) {
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
} else if (error
= suser(cred
, (u_short
*)0)) {
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
vap
->va_rdev
= (dev_t
)rdev
;
nqsrv_getl(nd
.ni_dvp
, NQL_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
= nfsd
->nd_procp
;
nd
.ni_cnd
.cn_cred
= nfsd
->nd_procp
->p_ucred
;
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
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
nqsrv_getl(vp
, NQL_WRITE
);
if (error
= VOP_SETATTR(vp
, vap
, cred
, nfsd
->nd_procp
)) {
bzero((caddr_t
)fhp
, sizeof(nfh
));
fhp
->fh_fsid
= vp
->v_mount
->mnt_stat
.f_fsid
;
if (error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
)) {
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_reply(NFSX_FH
+NFSX_FATTR
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
if (nd
.ni_cnd
.cn_nameiop
|| nd
.ni_cnd
.cn_flags
)
p
->p_spare
[1]--, vrele(nd
.ni_startdir
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
free(nd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
nfsrv_remove(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
int error
= 0, cache
, len
;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= DELETE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
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
, NQL_WRITE
);
nqsrv_getl(vp
, NQL_WRITE
);
error
= VOP_REMOVE(nd
.ni_dvp
, nd
.ni_vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
nfsrv_rename(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
int error
= 0, rdonly
, cache
, len
, len2
;
struct nameidata fromnd
, tond
;
struct vnode
*fvp
, *tvp
, *tdvp
;
fromnd
.ni_cnd
.cn_nameiop
= 0;
tond
.ni_cnd
.cn_nameiop
= 0;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
* 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
;
if (error
= nfs_namei(&fromnd
, ffhp
, len
, nfsd
->nd_slp
, nam
, &md
,
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
;
if (error
= nfs_namei(&tond
, tfhp
, len2
, nfsd
->nd_slp
, nam
, &md
,
&dpos
, nfsd
->nd_procp
)) {
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
, NQL_WRITE
);
nqsrv_getl(tdvp
, NQL_WRITE
);
nqsrv_getl(tvp
, NQL_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
);
vrele(fromnd
.ni_startdir
);
FREE(fromnd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (tond
.ni_cnd
.cn_nameiop
|| tond
.ni_cnd
.cn_flags
) {
FREE(tond
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
if (fromnd
.ni_cnd
.cn_nameiop
|| fromnd
.ni_cnd
.cn_flags
) {
vrele(fromnd
.ni_startdir
);
FREE(fromnd
.ni_cnd
.cn_pnbuf
, M_NAMEI
);
VOP_ABORTOP(fromnd
.ni_dvp
, &fromnd
.ni_cnd
);
nfsrv_link(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
int error
= 0, rdonly
, cache
, len
;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
if (error
= nfsrv_fhtovp(fhp
, FALSE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
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
;
if (error
= nfs_namei(&nd
, dfhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
if (vp
->v_mount
!= xp
->v_mount
)
nqsrv_getl(vp
, NQL_WRITE
);
nqsrv_getl(xp
, NQL_WRITE
);
error
= VOP_LINK(nd
.ni_dvp
, vp
, &nd
.ni_cnd
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
* nfs symbolic link service
nfsrv_symlink(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct vattr
*vap
= &va
;
int error
= 0, rdonly
, cache
, len
, len2
;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
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_SATTR
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
vap
->va_mode
= fxdr_unsigned(u_short
, sp
->sa_mode
);
nqsrv_getl(nd
.ni_dvp
, NQL_WRITE
);
error
= VOP_SYMLINK(nd
.ni_dvp
, &nd
.ni_vp
, &nd
.ni_cnd
, vap
, pathcp
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_mkdir(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct vattr
*vap
= &va
;
register struct nfsv2_fattr
*fp
;
int error
= 0, rdonly
, cache
, len
;
struct mbuf
*mb
, *mb2
, *mreq
;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= CREATE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
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
, NQL_WRITE
);
if (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
;
if (error
= VFS_VPTOFH(vp
, &fhp
->fh_fid
)) {
error
= VOP_GETATTR(vp
, vap
, cred
, nfsd
->nd_procp
);
nfsm_reply(NFSX_FH
+NFSX_FATTR
);
nfsm_build(fp
, struct nfsv2_fattr
*, NFSX_FATTR
);
VOP_ABORTOP(nd
.ni_dvp
, &nd
.ni_cnd
);
if (nd
.ni_dvp
== nd
.ni_vp
)
nfsrv_rmdir(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
int error
= 0, rdonly
, cache
, len
;
nfsm_srvstrsiz(len
, NFS_MAXNAMLEN
);
nd
.ni_cnd
.cn_cred
= cred
;
nd
.ni_cnd
.cn_nameiop
= DELETE
;
nd
.ni_cnd
.cn_flags
= LOCKPARENT
| LOCKLEAF
;
if (error
= nfs_namei(&nd
, fhp
, len
, nfsd
->nd_slp
, nam
, &md
, &dpos
,
if (vp
->v_type
!= VDIR
) {
* The root of a mounted filesystem cannot be deleted.
nqsrv_getl(nd
.ni_dvp
, NQL_WRITE
);
nqsrv_getl(vp
, NQL_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
)
* - 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 nqnfsrv_readdirlook() 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.
struct nfsv2_fattr fl_fattr
;
nfsrv_readdir(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
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;
int siz
, cnt
, fullsiz
, eofflag
, rdonly
, cache
;
nfsm_dissect(tl
, u_long
*, 2*NFSX_UNSIGNED
);
toff
= fxdr_unsigned(off_t
, *tl
++);
off
= (toff
& ~(NFS_DIRBLKSIZ
-1));
on
= (toff
& (NFS_DIRBLKSIZ
-1));
cnt
= fxdr_unsigned(int, *tl
);
siz
= ((cnt
+NFS_DIRBLKSIZ
-1) & ~(NFS_DIRBLKSIZ
-1));
if (cnt
> NFS_MAXREADDIR
)
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_READ
);
if (error
= nfsrv_access(vp
, VEXEC
, cred
, rdonly
, nfsd
->nd_procp
)) {
MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
io
.uio_segflg
= UIO_SYSSPACE
;
io
.uio_procp
= (struct proc
*)0;
error
= VOP_READDIR(vp
, &io
, cred
, &eofflag
);
free((caddr_t
)rbuf
, M_TEMP
);
* If nothing read, return eof
nfsm_reply(2*NFSX_UNSIGNED
);
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
FREE((caddr_t
)rbuf
, M_TEMP
);
* Check for degenerate cases of nothing useful read.
dp
= (struct dirent
*)cpos
;
while (cpos
< cend
&& dp
->d_fileno
== 0) {
dp
= (struct dirent
*)cpos
;
dp
= (struct dirent
*)cpos
;
len
= 3*NFSX_UNSIGNED
; /* paranoia, probably can be 0 */
be
= bp
+ M_TRAILINGSPACE(mp
);
/* Loop through the records and build reply */
rem
= nfsm_rndup(nlen
)-nlen
;
len
+= (4*NFSX_UNSIGNED
+ nlen
+ rem
);
* 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(toff
);
dp
= (struct dirent
*)cpos
;
mp
->m_len
= bp
- mtod(mp
, caddr_t
);
nqnfsrv_readdirlook(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct mbuf
*mp
;
register struct dirent
*dp
;
struct mbuf
*mb
, *mb2
, *mreq
, *mp2
;
char *cpos
, *cend
, *cp2
, *rbuf
;
struct ufid
*ufp
= (struct ufid
*)&fl
.fl_nfh
.fh_generic
.fh_fid
;
struct vattr va
, *vap
= &va
;
int len
, nlen
, rem
, xfer
, tsiz
, i
, error
= 0, duration2
, cache2
;
int siz
, cnt
, fullsiz
, eofflag
, rdonly
, cache
;
nfsm_dissect(tl
, u_long
*, 3*NFSX_UNSIGNED
);
toff
= fxdr_unsigned(off_t
, *tl
++);
off
= (toff
& ~(NFS_DIRBLKSIZ
-1));
on
= (toff
& (NFS_DIRBLKSIZ
-1));
cnt
= fxdr_unsigned(int, *tl
++);
duration2
= fxdr_unsigned(int, *tl
);
siz
= ((cnt
+NFS_DIRBLKSIZ
-1) & ~(NFS_DIRBLKSIZ
-1));
if (cnt
> NFS_MAXREADDIR
)
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
nqsrv_getl(vp
, NQL_READ
);
if (error
= nfsrv_access(vp
, VEXEC
, cred
, rdonly
, nfsd
->nd_procp
)) {
MALLOC(rbuf
, caddr_t
, siz
, M_TEMP
, M_WAITOK
);
io
.uio_segflg
= UIO_SYSSPACE
;
io
.uio_procp
= (struct proc
*)0;
error
= VOP_READDIR(vp
, &io
, cred
, &eofflag
);
free((caddr_t
)rbuf
, M_TEMP
);
* If nothing read, return eof
nfsm_reply(2*NFSX_UNSIGNED
);
nfsm_build(tl
, u_long
*, 2*NFSX_UNSIGNED
);
FREE((caddr_t
)rbuf
, M_TEMP
);
* Check for degenerate cases of nothing useful read.
dp
= (struct dirent
*)cpos
;
while (cpos
< cend
&& dp
->d_fileno
== 0) {
dp
= (struct dirent
*)cpos
;
dp
= (struct dirent
*)cpos
;
len
= 3*NFSX_UNSIGNED
; /* paranoia, probably can be 0 */
be
= bp
+ M_TRAILINGSPACE(mp
);
/* Loop through the records and build reply */
rem
= nfsm_rndup(nlen
)-nlen
;
* For readdir_and_lookup get the vnode using
bzero((caddr_t
)&fl
.fl_nfh
, sizeof (nfsv2fh_t
));
ufp
->ufid_len
= sizeof (struct ufid
);
ufp
->ufid_ino
= dp
->d_fileno
;
fl
.fl_nfh
.fh_generic
.fh_fsid
= mntp
->mnt_stat
.f_fsid
;
if (VFS_FHTOVP(mntp
, (struct fid
*)ufp
, 1, &nvp
))
(void) nqsrv_getlease(nvp
, &duration2
, NQL_READ
, nfsd
,
nam
, &cache2
, &frev2
, cred
);
fl
.fl_duration
= txdr_unsigned(duration2
);
fl
.fl_cachable
= txdr_unsigned(cache2
);
txdr_hyper(&frev2
, &fl
.fl_frev
);
if (VOP_GETATTR(nvp
, vap
, cred
, nfsd
->nd_procp
)) {
len
+= (4*NFSX_UNSIGNED
+ nlen
+ rem
+ NFSX_FH
* Build the directory record xdr from
* For readdir_and_lookup copy the stuff out.
xfer
= sizeof (struct flrep
);
*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(toff
);
dp
= (struct dirent
*)cpos
;
mp
->m_len
= bp
- mtod(mp
, caddr_t
);
nfsrv_statfs(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
register struct statfs
*sf
;
register struct nfsv2_statfs
*sfp
;
int error
= 0, rdonly
, cache
;
struct mbuf
*mb
, *mb2
, *mreq
;
if (error
= nfsrv_fhtovp(fhp
, TRUE
, &vp
, cred
, nfsd
->nd_slp
, nam
, &rdonly
))
error
= VFS_STATFS(vp
->v_mount
, sf
, nfsd
->nd_procp
);
nfsm_build(sfp
, struct nfsv2_statfs
*, NFSX_STATFS
);
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
);
* Null operation, used by clients to ping server
nfsrv_null(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
int error
= VNOVAL
, cache
;
* No operation, used for obsolete procedures
nfsrv_noop(nfsd
, mrep
, md
, dpos
, cred
, nam
, mrq
)
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
)