#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kauth.h>
#include <sys/unistd.h>
#include <sys/malloc.h>
#include <sys/vnode.h>
#include <sys/mount_internal.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/kpi_mbuf.h>
#include <sys/dirent.h>
#include <sys/stat.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/ubc.h>
#include <sys/vnode_internal.h>
#include <sys/uio_internal.h>
#include <libkern/OSAtomic.h>
#include <sys/vm.h>
#include <sys/vmparam.h>
#include <nfs/nfsproto.h>
#include <nfs/rpcv2.h>
#include <nfs/nfs.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK,
NFFIFO, NFNON };
#ifndef NFS_NOSERVER
nfstype nfsv2_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFNON,
NFCHR, NFNON };
extern u_long nfs_xdrneg1;
extern u_long nfs_false, nfs_true;
extern enum vtype nv3tov_type[8];
extern struct nfsstats nfsstats;
int nfsrvw_procrastinate = NFS_GATHERDELAY * 1000;
int nfsrvw_procrastinate_v3 = 0;
int nfs_async = 0;
#ifdef notyet
SYSCTL_INT(_vfs_nfs, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, "");
#endif
static int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int);
static void nfsrvw_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
#define THREAD_SAFE_FS(VP) \
((VP)->v_mount ? (VP)->v_mount->mnt_vtable->vfc_threadsafe : 0)
int
nfsrv3_access(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
vnode_t vp;
struct nfs_filehandle nfh;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, getret;
char *cp2;
mbuf_t mb, mreq, mb2;
struct vnode_attr vattr, *vap = &vattr;
u_long nfsmode;
kauth_action_t testaction;
struct vfs_context context;
struct nfs_export *nx;
struct nfs_export_options *nxo;
nfsm_srvmtofh(&nfh);
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
nfsmode = fxdr_unsigned(u_long, *tl);
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (nfsmode & NFSV3ACCESS_READ) {
if (vnode_isdir(vp)) {
testaction =
KAUTH_VNODE_LIST_DIRECTORY |
KAUTH_VNODE_READ_EXTATTRIBUTES;
} else {
testaction =
KAUTH_VNODE_READ_DATA |
KAUTH_VNODE_READ_EXTATTRIBUTES;
}
if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
nfsmode &= ~NFSV3ACCESS_READ;
}
if ((nfsmode & NFSV3ACCESS_LOOKUP) &&
(!vnode_isdir(vp) ||
nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, &context, nxo, 0)))
nfsmode &= ~NFSV3ACCESS_LOOKUP;
if (nfsmode & NFSV3ACCESS_MODIFY) {
if (vnode_isdir(vp)) {
testaction =
KAUTH_VNODE_ADD_FILE |
KAUTH_VNODE_ADD_SUBDIRECTORY |
KAUTH_VNODE_DELETE_CHILD;
} else {
testaction =
KAUTH_VNODE_WRITE_DATA;
}
if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
nfsmode &= ~NFSV3ACCESS_MODIFY;
}
if (nfsmode & NFSV3ACCESS_EXTEND) {
if (vnode_isdir(vp)) {
testaction =
KAUTH_VNODE_ADD_FILE |
KAUTH_VNODE_ADD_SUBDIRECTORY;
} else {
testaction =
KAUTH_VNODE_WRITE_DATA |
KAUTH_VNODE_APPEND_DATA;
}
if (nfsrv_authorize(vp, NULL, testaction, &context, nxo, 0))
nfsmode &= ~NFSV3ACCESS_EXTEND;
}
if ((nfsmode & NFSV3ACCESS_EXECUTE) &&
(vnode_isdir(vp) ||
nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context, nxo, 0)))
nfsmode &= ~NFSV3ACCESS_EXECUTE;
nfsm_srv_vattr_init(vap, 1);
getret = vnode_getattr(vp, vap, &context);
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(1) + NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, vap);
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(nfsmode);
nfsmout:
return (error);
}
int
nfsrv_getattr(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct nfs_fattr *fp;
struct vnode_attr va;
struct vnode_attr *vap = &va;
vnode_t vp;
struct nfs_filehandle nfh;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0;
char *cp2;
mbuf_t mb, mb2, mreq;
struct vfs_context context;
struct nfs_export *nx;
struct nfs_export_options *nxo;
int v3 = (nfsd->nd_flag & ND_NFSV3);
nfsm_srvmtofh(&nfh);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(0);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(0);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
nfsm_srv_vattr_init(vap, v3);
error = vnode_getattr(vp, vap, &context);
vnode_put(vp);
nfsm_reply(NFSX_FATTR(v3));
if (error)
return (0);
nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(v3));
nfsm_srvfillattr(vap, fp);
nfsmout:
return (error);
}
int
nfsrv_setattr(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vnode_attr preat;
struct vnode_attr postat;
struct vnode_attr va;
struct vnode_attr *vap = &va;
struct nfsv2_sattr *sp;
struct nfs_fattr *fp;
vnode_t vp;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, preat_ret = 1, postat_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0;
char *cp2;
mbuf_t mb, mb2, mreq;
struct timespec guard;
struct vfs_context context;
kauth_action_t action;
uid_t saved_uid;
nfsm_srvmtofh(&nfh);
VATTR_INIT(vap);
if (v3) {
nfsm_srvsattr(vap);
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
gcheck = fxdr_unsigned(int, *tl);
if (gcheck) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
fxdr_nfsv3time(tl, &guard);
}
} else {
nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
if ((fxdr_unsigned(int, sp->sa_mode) & 0xffff) != 0xffff)
VATTR_SET(vap, va_mode, nfstov_mode(sp->sa_mode));
if (sp->sa_uid != nfs_xdrneg1)
VATTR_SET(vap, va_uid, fxdr_unsigned(uid_t, sp->sa_uid));
if (sp->sa_gid != nfs_xdrneg1)
VATTR_SET(vap, va_gid, fxdr_unsigned(gid_t, sp->sa_gid));
if (sp->sa_size != nfs_xdrneg1)
VATTR_SET(vap, va_data_size, fxdr_unsigned(u_quad_t, sp->sa_size));
if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) {
fxdr_nfsv2time(&sp->sa_atime, &vap->va_access_time);
VATTR_SET_ACTIVE(vap, va_access_time);
}
if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) {
fxdr_nfsv2time(&sp->sa_mtime, &vap->va_modify_time);
VATTR_SET_ACTIVE(vap, va_modify_time);
}
}
saved_uid = kauth_cred_getuid(nfsd->nd_cr);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (v3) {
nfsm_srv_pre_vattr_init(&preat, v3);
error = preat_ret = vnode_getattr(vp, &preat, &context);
if (!error && gcheck && VATTR_IS_SUPPORTED(&preat, va_change_time) &&
(preat.va_change_time.tv_sec != guard.tv_sec ||
preat.va_change_time.tv_nsec != guard.tv_nsec))
error = NFSERR_NOT_SYNC;
if (!preat_ret && !VATTR_ALL_SUPPORTED(&preat))
preat_ret = 1;
if (error) {
vnode_put(vp);
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
return (0);
}
}
if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
int ismember;
VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
}
if (((error = vnode_authattr(vp, vap, &action, &context))) ||
((error = nfsrv_authorize(vp, NULL, action, &context, nxo, 0))))
goto out;
error = vnode_setattr(vp, vap, &context);
nfsm_srv_vattr_init(&postat, v3);
postat_ret = vnode_getattr(vp, &postat, &context);
if (!error)
error = postat_ret;
out:
vnode_put(vp);
nfsm_reply(NFSX_WCCORFATTR(v3));
if (v3) {
nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat);
return (0);
} else {
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(&postat, fp);
}
nfsmout:
return (error);
}
int
nfsrv_lookup(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct nfs_fattr *fp;
struct nameidata nd, *ndp = &nd;
#ifdef WEBNFS_ENABLED
struct nameidata ind;
#endif
vnode_t vp, dirp = NULL;
struct nfs_filehandle dnfh, nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
caddr_t cp;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, len, dirattr_ret = 1, isdotdot;
int v3 = (nfsd->nd_flag & ND_NFSV3), pubflag;
char *cp2;
mbuf_t mb, mb2, mreq;
struct vnode_attr va, dirattr, *vap = &va;
struct vfs_context context;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
nfsm_srvmtofh(&dnfh);
nfsm_srvnamesiz(len, v3);
pubflag = nfs_ispublicfh(&dnfh);
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags = LOCKLEAF;
error = nfsm_path_mbuftond(&md, &dpos, v3, pubflag, &len, &nd);
isdotdot = ((len == 2) && (nd.ni_cnd.cn_pnbuf[0] == '.') && (nd.ni_cnd.cn_pnbuf[1] == '.'));
if (!error)
error = nfs_namei(nfsd, &context, &nd, &dnfh, nam, pubflag, &dirp, &nx, &nxo);
#ifdef WEBNFS_ENABLED
if (!error && pubflag) {
if (vnode_vtype(nd.ni_vp) == VDIR && nfs_pub.np_index != NULL) {
ind = nd;
ind.ni_pathlen = strlen(nfs_pub.np_index);
ind.ni_cnd.cn_nameptr = ind.ni_cnd.cn_pnbuf =
nfs_pub.np_index;
ind.ni_startdir = nd.ni_vp;
ind.ni_usedvp = nd.ni_vp;
if (!(error = lookup(&ind))) {
if (dirp)
vnode_put(dirp);
dirp = nd.ni_vp;
vnode_put(nd.ni_startdir);
ndp = &ind;
} else
error = 0;
}
if (!error && vnode_mount(ndp->ni_vp) != nfs_pub.np_mount) {
vnode_put(nd.ni_vp);
nameidone(&nd);
error = EPERM;
}
}
#endif
if (dirp) {
if (v3) {
nfsm_srv_vattr_init(&dirattr, v3);
dirattr_ret = vnode_getattr(dirp, &dirattr, &context);
}
vnode_put(dirp);
}
if (error) {
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(dirattr_ret, &dirattr);
return (0);
}
nameidone(&nd);
vp = ndp->ni_vp;
error = nfsrv_vptofh(nx, !v3, (isdotdot ? &dnfh : NULL), vp, &context, &nfh);
if (!error) {
nfsm_srv_vattr_init(vap, v3);
error = vnode_getattr(vp, vap, &context);
}
vnode_put(vp);
nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3));
if (error) {
nfsm_srvpostop_attr(dirattr_ret, &dirattr);
return (0);
}
nfsm_srvfhtom(&nfh, v3);
if (v3) {
nfsm_srvpostop_attr(0, vap);
nfsm_srvpostop_attr(dirattr_ret, &dirattr);
} else {
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(vap, fp);
}
nfsmout:
return (error);
}
int
nfsrv_readlink(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
mbuf_t mp;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, i, tlen, len, getret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mb2, mp2, mp3, mreq;
vnode_t vp;
struct vnode_attr attr;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t uiop = NULL;
char uio_buf[ UIO_SIZEOF(4) ];
char *uio_bufp = &uio_buf[0];
int uio_buflen = UIO_SIZEOF(4);
int mblen;
struct vfs_context context;
nfsm_srvmtofh(&nfh);
len = 0;
i = 0;
mp2 = mp3 = NULL;
vp = NULL;
while (len < NFS_MAXPATHLEN) {
mp = NULL;
if ((error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &mp)))
goto out;
mblen = mbuf_maxlen(mp);
mbuf_setlen(mp, mblen);
if (len == 0)
mp3 = mp2 = mp;
else {
if ((error = mbuf_setnext(mp2, mp))) {
mbuf_free(mp);
goto out;
}
mp2 = mp;
}
if ((len + mblen) > NFS_MAXPATHLEN) {
mbuf_setlen(mp, NFS_MAXPATHLEN - len);
len = NFS_MAXPATHLEN;
} else
len += mblen;
i++;
}
if (i > 4) {
uio_buflen = UIO_SIZEOF(i);
MALLOC(uio_bufp, char*, uio_buflen, M_TEMP, M_WAITOK);
if (!uio_bufp) {
error = ENOMEM;
mbuf_freem(mp3);
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
}
uiop = uio_createwithbuffer(i, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
if (!uiop) {
error = ENOMEM;
mbuf_freem(mp3);
if (uio_bufp != &uio_buf[0]) {
FREE(uio_bufp, M_TEMP);
uio_bufp = &uio_buf[0];
}
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
mp = mp3;
while (mp) {
uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
mp = mbuf_next(mp);
}
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
mbuf_freem(mp3);
if (uio_bufp != &uio_buf[0]) {
FREE(uio_bufp, M_TEMP);
uio_bufp = &uio_buf[0];
}
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
mbuf_freem(mp3);
if (uio_bufp != &uio_buf[0]) {
FREE(uio_bufp, M_TEMP);
uio_bufp = &uio_buf[0];
}
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
if (vnode_vtype(vp) != VLNK) {
if (v3)
error = EINVAL;
else
error = ENXIO;
goto out;
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context, nxo, 0)))
goto out;
error = VNOP_READLINK(vp, uiop, &context);
out:
if (vp) {
if (v3) {
nfsm_srv_vattr_init(&attr, v3);
getret = vnode_getattr(vp, &attr, &context);
}
vnode_put(vp);
}
if (error) {
mbuf_freem(mp3);
mp3 = NULL;
}
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_UNSIGNED);
if (v3) {
nfsm_srvpostop_attr(getret, &attr);
if (error) {
if (uio_bufp != &uio_buf[0])
FREE(uio_bufp, M_TEMP);
return (0);
}
}
if (!error) {
if (uiop && (uio_resid(uiop) > 0)) {
len -= uio_resid(uiop);
tlen = nfsm_rndup(len);
nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len);
}
nfsm_build(tl, u_long *, NFSX_UNSIGNED);
*tl = txdr_unsigned(len);
mbuf_setnext(mb, mp3);
}
nfsmout:
if (uio_bufp != &uio_buf[0])
FREE(uio_bufp, M_TEMP);
return (error);
}
int
nfsrv_read(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
mbuf_t m;
struct nfs_fattr *fp;
u_long *tl;
long t1;
int i;
caddr_t bpos;
int error = 0, count, len, left, siz, tlen, getret;
int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen, maxlen;
char *cp2;
mbuf_t mb, mb2, mreq;
mbuf_t m2;
vnode_t vp;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t uiop = NULL;
char *uio_bufp = NULL;
struct vnode_attr va, *vap = &va;
off_t off;
char uio_buf[ UIO_SIZEOF(0) ];
struct vfs_context context;
nfsm_srvmtofh(&nfh);
if (v3) {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
fxdr_hyper(tl, &off);
} else {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
off = (off_t)fxdr_unsigned(u_long, *tl);
}
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
reqlen = fxdr_unsigned(u_long, *tl);
maxlen = NFS_SRVMAXDATA(nfsd);
if (reqlen > maxlen)
reqlen = maxlen;
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(1, NULL);
return (0);
}
if (vnode_vtype(vp) != VREG) {
if (v3)
error = EINVAL;
else
error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (!error) {
if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context, nxo, 1)))
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context, nxo, 1);
}
nfsm_srv_vattr_init(vap, v3);
getret = vnode_getattr(vp, vap, &context);
if (!error)
error = getret;
if (error) {
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, vap);
return (0);
}
if ((u_quad_t)off >= vap->va_data_size)
count = 0;
else if (((u_quad_t)off + reqlen) > vap->va_data_size)
count = nfsm_rndup(vap->va_data_size - off);
else
count = reqlen;
nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(count));
if (v3) {
nfsm_build(tl, u_long *, NFSX_V3FATTR + 4 * NFSX_UNSIGNED);
*tl++ = nfs_true;
fp = (struct nfs_fattr *)tl;
tl += (NFSX_V3FATTR / sizeof (u_long));
} else {
nfsm_build(tl, u_long *, NFSX_V2FATTR + NFSX_UNSIGNED);
fp = (struct nfs_fattr *)tl;
tl += (NFSX_V2FATTR / sizeof (u_long));
}
len = left = count;
if (count > 0) {
i = 0;
m = m2 = mb;
while (left > 0) {
siz = min(mbuf_trailingspace(m), left);
if (siz > 0) {
left -= siz;
i++;
}
if (left > 0) {
m = NULL;
if ((error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &m)))
goto errorexit;
mbuf_setnext(m2, m);
m2 = m;
}
}
MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
if (!uio_bufp) {
error = ENOMEM;
goto errorexit;
}
uiop = uio_createwithbuffer(i, off, UIO_SYSSPACE, UIO_READ,
uio_bufp, UIO_SIZEOF(i));
if (!uiop) {
error = ENOMEM;
goto errorexit;
}
m = mb;
left = count;
i = 0;
while (left > 0) {
if (m == NULL)
panic("nfsrv_read iov");
siz = min(mbuf_trailingspace(m), left);
if (siz > 0) {
tlen = mbuf_len(m);
uio_addiov(uiop, CAST_USER_ADDR_T((char *)mbuf_data(m) + tlen), siz);
mbuf_setlen(m, tlen + siz);
left -= siz;
i++;
}
m = mbuf_next(m);
}
error = VNOP_READ(vp, uiop, IO_NODELOCKED, &context);
off = uio_offset(uiop);
errorexit:
nfsm_srv_vattr_init(vap, v3);
if (error || (getret = vnode_getattr(vp, vap, &context))) {
if (!error)
error = getret;
mbuf_freem(mreq);
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, vap);
if (uio_bufp != NULL) {
FREE(uio_bufp, M_TEMP);
}
return (0);
}
} else {
uiop = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
if (!uiop) {
error = ENOMEM;
goto errorexit;
}
}
vnode_put(vp);
nfsm_srvfillattr(vap, fp);
len -= uio_resid(uiop);
tlen = nfsm_rndup(len);
if (count != tlen || tlen != len)
nfsm_adj(mb, count - tlen, tlen - len);
if (v3) {
*tl++ = txdr_unsigned(len);
if (len < reqlen)
*tl++ = nfs_true;
else
*tl++ = nfs_false;
}
*tl = txdr_unsigned(len);
nfsmout:
if (uio_bufp != NULL) {
FREE(uio_bufp, M_TEMP);
}
return (error);
}
int
nfsrv_write(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
int i, count;
mbuf_t mp;
struct nfs_fattr *fp;
struct vnode_attr va, forat;
struct vnode_attr *vap = &va;
u_long *tl;
long t1;
caddr_t bpos, tpos;
int error = 0, len, forat_ret = 1;
int ioflags, aftat_ret = 1, retlen, zeroing, adjust, tlen;
int stable = NFSV3WRITE_FILESYNC;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t uiop;
off_t off;
char *uio_bufp = NULL;
struct vfs_context context;
if (mrep == NULL) {
*mrq = NULL;
return (0);
}
nfsm_srvmtofh(&nfh);
if (v3) {
nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
fxdr_hyper(tl, &off);
tl += 3;
stable = fxdr_unsigned(int, *tl++);
} else {
nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
off = (off_t)fxdr_unsigned(u_long, *++tl);
tl += 2;
if (nfs_async)
stable = NFSV3WRITE_UNSTABLE;
}
retlen = len = fxdr_unsigned(long, *tl);
count = i = 0;
if (len > 0) {
zeroing = 1;
mp = mrep;
while (mp) {
if (mp == md) {
zeroing = 0;
tpos = mbuf_data(mp);
tlen = mbuf_len(mp);
adjust = dpos - tpos;
tlen -= adjust;
mbuf_setlen(mp, tlen);
if (tlen > 0 && adjust > 0) {
tpos += adjust;
if ((error = mbuf_setdata(mp, tpos, tlen))) {
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
}
}
if (zeroing)
mbuf_setlen(mp, 0);
else if ((tlen = mbuf_len(mp)) > 0) {
i += tlen;
if (i > len) {
mbuf_setlen(mp, tlen - (i - len));
zeroing = 1;
}
if (mbuf_len(mp) > 0)
count++;
}
mp = mbuf_next(mp);
}
}
if (len > NFS_MAXDATA || len < 0 || i < len) {
error = EIO;
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (v3) {
nfsm_srv_pre_vattr_init(&forat, v3);
forat_ret = vnode_getattr(vp, &forat, &context);
}
if (vnode_vtype(vp) != VREG) {
if (v3)
error = EINVAL;
else
error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
}
if (!error) {
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1);
}
if (error) {
vnode_put(vp);
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
if (len > 0) {
MALLOC(uio_bufp, char *, UIO_SIZEOF(count), M_TEMP, M_WAITOK);
if (!uio_bufp) {
error = ENOMEM;
vnode_put(vp);
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
return (0);
}
uiop = uio_createwithbuffer(count, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(count));
if (!uiop) {
error = ENOMEM;
vnode_put(vp);
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
if (uio_bufp != NULL) {
FREE(uio_bufp, M_TEMP);
}
return (0);
}
mp = mrep;
while (mp) {
if ((tlen = mbuf_len(mp)) > 0)
uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), tlen);
mp = mbuf_next(mp);
}
if (stable == NFSV3WRITE_UNSTABLE)
ioflags = IO_NODELOCKED;
else if (stable == NFSV3WRITE_DATASYNC)
ioflags = (IO_SYNC | IO_NODELOCKED);
else
ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
error = VNOP_WRITE(vp, uiop, ioflags, &context);
OSAddAtomic(1, (SInt32*)(SInt32*)&nfsstats.srvvop_writes);
}
nfsm_srv_vattr_init(vap, v3);
aftat_ret = vnode_getattr(vp, vap, &context);
vnode_put(vp);
if (!error)
error = aftat_ret;
nfsm_reply(NFSX_PREOPATTR(v3) + NFSX_POSTOPORFATTR(v3) +
2 * NFSX_UNSIGNED + NFSX_WRITEVERF(v3));
if (v3) {
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap);
if (error) {
if (uio_bufp != NULL) {
FREE(uio_bufp, M_TEMP);
}
return (0);
}
nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
*tl++ = txdr_unsigned(retlen);
if (stable == NFSV3WRITE_UNSTABLE && !nfs_async)
*tl++ = txdr_unsigned(stable);
else
*tl++ = txdr_unsigned(NFSV3WRITE_FILESYNC);
*tl++ = txdr_unsigned(boottime_sec());
*tl = txdr_unsigned(0);
} else {
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(vap, fp);
}
nfsmout:
if (uio_bufp != NULL) {
FREE(uio_bufp, M_TEMP);
}
return (error);
}
int
nfsrv_writegather(ndp, slp, procp, mrq)
struct nfsrv_descript **ndp;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mp;
struct nfsrv_descript *wp, *nfsd, *owp, *swp;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct nfs_fattr *fp;
int i;
struct nfsrvw_delayhash *wpp;
kauth_cred_t cred;
struct vnode_attr va, forat;
u_long *tl;
long t1;
caddr_t bpos, dpos, tpos;
int error = 0, len, forat_ret = 1;
int ioflags, aftat_ret = 1, adjust, v3, zeroing, tlen;
char *cp2;
mbuf_t mb, mb2, mreq, mrep, md;
vnode_t vp;
uio_t uiop = NULL;
char *uio_bufp = NULL;
u_quad_t cur_usec;
struct timeval now;
struct vfs_context context;
context.vc_proc = procp;
#ifndef nolint
i = 0;
len = 0;
#endif
*mrq = NULL;
if (*ndp) {
nfsd = *ndp;
*ndp = NULL;
mrep = nfsd->nd_mrep;
md = nfsd->nd_md;
dpos = nfsd->nd_dpos;
cred = nfsd->nd_cr;
context.vc_ucred = cred;
v3 = (nfsd->nd_flag & ND_NFSV3);
LIST_INIT(&nfsd->nd_coalesce);
nfsd->nd_mreq = NULL;
nfsd->nd_stable = NFSV3WRITE_FILESYNC;
microuptime(&now);
cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec;
nfsd->nd_time = cur_usec +
(v3 ? nfsrvw_procrastinate_v3 : nfsrvw_procrastinate);
nfsm_srvmtofh(&nfsd->nd_fh);
if (v3) {
nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
fxdr_hyper(tl, &nfsd->nd_off);
tl += 3;
nfsd->nd_stable = fxdr_unsigned(int, *tl++);
} else {
nfsm_dissect(tl, u_long *, 4 * NFSX_UNSIGNED);
nfsd->nd_off = (off_t)fxdr_unsigned(u_long, *++tl);
tl += 2;
if (nfs_async)
nfsd->nd_stable = NFSV3WRITE_UNSTABLE;
}
len = fxdr_unsigned(long, *tl);
nfsd->nd_len = len;
nfsd->nd_eoff = nfsd->nd_off + len;
zeroing = 1;
i = 0;
mp = mrep;
while (mp) {
if (mp == md) {
zeroing = 0;
tpos = mbuf_data(mp);
tlen = mbuf_len(mp);
adjust = dpos - tpos;
tlen -= adjust;
mbuf_setlen(mp, tlen);
if (tlen > 0 && adjust > 0) {
tpos += adjust;
if ((error = mbuf_setdata(mp, tpos, tlen)))
goto nfsmout;
}
}
if (zeroing)
mbuf_setlen(mp, 0);
else {
tlen = mbuf_len(mp);
i += tlen;
if (i > len) {
mbuf_setlen(mp, tlen - (i - len));
zeroing = 1;
}
}
mp = mbuf_next(mp);
}
if (len > NFS_MAXDATA || len < 0 || i < len) {
nfsmout:
mbuf_freem(mrep);
mrep = NULL;
error = EIO;
nfsm_writereply(2 * NFSX_UNSIGNED, v3);
if (v3)
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
nfsd->nd_mreq = mreq;
nfsd->nd_mrep = NULL;
nfsd->nd_time = 1;
}
lck_mtx_lock(&slp->ns_wgmutex);
owp = NULL;
wp = slp->ns_tq.lh_first;
while (wp && wp->nd_time < nfsd->nd_time) {
owp = wp;
wp = wp->nd_tq.le_next;
}
if (owp) {
LIST_INSERT_AFTER(owp, nfsd, nd_tq);
} else {
LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
}
if (nfsd->nd_mrep) {
wpp = NWDELAYHASH(slp, nfsd->nd_fh.nfh_fid);
owp = NULL;
wp = wpp->lh_first;
while (wp && !nfsrv_fhmatch(&nfsd->nd_fh, &wp->nd_fh)) {
owp = wp;
wp = wp->nd_hash.le_next;
}
while (wp && (wp->nd_off < nfsd->nd_off) &&
nfsrv_fhmatch(&nfsd->nd_fh, &wp->nd_fh)) {
owp = wp;
wp = wp->nd_hash.le_next;
}
if (owp) {
LIST_INSERT_AFTER(owp, nfsd, nd_hash);
for(; nfsd && NFSW_CONTIG(owp, nfsd); nfsd = wp) {
wp = nfsd->nd_hash.le_next;
if (NFSW_SAMECRED(owp, nfsd))
nfsrvw_coalesce(owp, nfsd);
}
} else {
LIST_INSERT_HEAD(wpp, nfsd, nd_hash);
}
}
} else {
lck_mtx_lock(&slp->ns_wgmutex);
}
loop1:
microuptime(&now);
cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec;
for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = owp) {
owp = nfsd->nd_tq.le_next;
if (nfsd->nd_time > cur_usec)
break;
if (nfsd->nd_mreq)
continue;
LIST_REMOVE(nfsd, nd_tq);
LIST_REMOVE(nfsd, nd_hash);
mrep = nfsd->nd_mrep;
nfsd->nd_mrep = NULL;
v3 = (nfsd->nd_flag & ND_NFSV3);
forat_ret = aftat_ret = 1;
error = nfsrv_fhtovp(&nfsd->nd_fh, nfsd->nd_nam, TRUE, &vp, &nx, &nxo);
if (!error) {
error = nfsrv_credcheck(nfsd, nx, nxo);
if (error)
vnode_put(vp);
}
cred = nfsd->nd_cr;
context.vc_ucred = cred;
if (!error) {
if (v3) {
nfsm_srv_pre_vattr_init(&forat, v3);
forat_ret = vnode_getattr(vp, &forat, &context);
}
if (vnode_vtype(vp) != VREG) {
if (v3)
error = EINVAL;
else
error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES;
}
} else
vp = NULL;
if (!error) {
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1);
}
if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE)
ioflags = IO_NODELOCKED;
else if (nfsd->nd_stable == NFSV3WRITE_DATASYNC)
ioflags = (IO_SYNC | IO_NODELOCKED);
else
ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
if (!error && ((nfsd->nd_eoff - nfsd->nd_off) > 0)) {
mp = mrep;
i = 0;
while (mp) {
if (mbuf_len(mp) > 0)
i++;
mp = mbuf_next(mp);
}
MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
if (uio_bufp)
uiop = uio_createwithbuffer(i, nfsd->nd_off, UIO_SYSSPACE,
UIO_WRITE, uio_bufp, UIO_SIZEOF(i));
if (!uio_bufp || !uiop)
error = ENOMEM;
if (!error) {
mp = mrep;
while (mp) {
if ((tlen = mbuf_len(mp)) > 0)
uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), tlen);
mp = mbuf_next(mp);
}
error = VNOP_WRITE(vp, uiop, ioflags, &context);
OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
}
if (uio_bufp) {
FREE(uio_bufp, M_TEMP);
uio_bufp = NULL;
}
}
mbuf_freem(mrep);
mrep = NULL;
if (vp) {
nfsm_srv_pre_vattr_init(&va, v3);
aftat_ret = vnode_getattr(vp, &va, &context);
vnode_put(vp);
}
swp = nfsd;
do {
if (error) {
nfsm_writereply(NFSX_WCCDATA(v3), v3);
if (v3) {
nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, &va);
}
} else {
nfsm_writereply(NFSX_PREOPATTR(v3) +
NFSX_POSTOPORFATTR(v3) + 2 * NFSX_UNSIGNED +
NFSX_WRITEVERF(v3), v3);
if (v3) {
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);
*tl++ = txdr_unsigned(boottime_sec());
*tl = txdr_unsigned(0);
} else {
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(&va, fp);
}
}
nfsd->nd_mreq = mreq;
if (nfsd->nd_mrep)
panic("nfsrv_write: nd_mrep not free");
if (nfsd != swp) {
nfsd->nd_time = 1;
LIST_INSERT_HEAD(&slp->ns_tq, nfsd, nd_tq);
}
nfsd = swp->nd_coalesce.lh_first;
if (nfsd) {
LIST_REMOVE(nfsd, nd_tq);
}
} while (nfsd);
swp->nd_time = 1;
LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq);
goto loop1;
}
for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next)
if (nfsd->nd_mreq) {
LIST_REMOVE(nfsd, nd_tq);
*mrq = nfsd->nd_mreq;
*ndp = nfsd;
break;
}
slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0;
lck_mtx_unlock(&slp->ns_wgmutex);
return (0);
}
static void
nfsrvw_coalesce(
struct nfsrv_descript *owp,
struct nfsrv_descript *nfsd)
{
int overlap, error;
mbuf_t mp, mpnext;
struct nfsrv_descript *p;
LIST_REMOVE(nfsd, nd_hash);
LIST_REMOVE(nfsd, nd_tq);
if (owp->nd_eoff < nfsd->nd_eoff) {
overlap = owp->nd_eoff - nfsd->nd_off;
if (overlap < 0)
panic("nfsrv_coalesce: bad off");
if (overlap > 0)
mbuf_adj(nfsd->nd_mrep, overlap);
mp = owp->nd_mrep;
while ((mpnext = mbuf_next(mp)))
mp = mpnext;
error = mbuf_setnext(mp, nfsd->nd_mrep);
if (error)
panic("nfsrvw_coalesce: mbuf_setnext failed: %d", error);
owp->nd_eoff = nfsd->nd_eoff;
} else {
mbuf_freem(nfsd->nd_mrep);
}
nfsd->nd_mrep = NULL;
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);
for (p = nfsd->nd_coalesce.lh_first; p;
p = nfsd->nd_coalesce.lh_first) {
LIST_REMOVE(p, nd_tq);
LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq);
}
}
void
nfsrvw_sort(list, num)
gid_t *list;
int num;
{
int i, j;
gid_t v;
for (i = 1; i < num; i++) {
v = list[i];
for (j = i; --j >= 0 && v < list[j];)
list[j + 1] = list[j];
list[j + 1] = v;
}
}
void
nfsrv_setcred(kauth_cred_t incred, kauth_cred_t outcred)
{
int i;
bzero((caddr_t)outcred, sizeof (*outcred));
outcred->cr_ref = 1;
outcred->cr_uid = kauth_cred_getuid(incred);
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);
}
int
nfsrv_create(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct nfs_fattr *fp;
struct vnode_attr dirfor, diraft, postat;
struct vnode_attr va;
struct vnode_attr *vap = &va;
struct nfsv2_sattr *sp;
u_long *tl;
struct nameidata nd;
caddr_t cp;
long t1;
caddr_t bpos;
int error = 0, rdev, len, tsize, dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3), how, exclusive_flag = 0;
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp, dvp, dirp = NULL;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
u_quad_t tempsize;
u_char cverf[NFSX_V3CREATEVERF];
struct vfs_context context;
uid_t saved_uid;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
saved_uid = kauth_cred_getuid(nfsd->nd_cr);
#ifndef nolint
rdev = 0;
#endif
nd.ni_cnd.cn_nameiop = 0;
vp = dvp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, v3);
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (error) {
nd.ni_cnd.cn_nameiop = 0;
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
if (dirp)
vnode_put(dirp);
return (0);
}
dvp = nd.ni_dvp;
vp = nd.ni_vp;
VATTR_INIT(vap);
if (v3) {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
how = fxdr_unsigned(int, *tl);
switch (how) {
case NFSV3CREATE_GUARDED:
if (vp) {
error = EEXIST;
break;
}
case NFSV3CREATE_UNCHECKED:
nfsm_srvsattr(vap);
break;
case NFSV3CREATE_EXCLUSIVE:
nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF);
bcopy(cp, cverf, NFSX_V3CREATEVERF);
exclusive_flag = 1;
if (vp == NULL)
VATTR_SET(vap, va_mode, 0);
break;
};
VATTR_SET(vap, va_type, VREG);
} else {
enum vtype v_type;
nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
v_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode));
if (v_type == VNON)
v_type = VREG;
VATTR_SET(vap, va_type, v_type);
VATTR_SET(vap, va_mode, nfstov_mode(sp->sa_mode));
switch (v_type) {
case VREG:
tsize = fxdr_unsigned(long, sp->sa_size);
if (tsize != -1)
VATTR_SET(vap, va_data_size, (u_quad_t)tsize);
break;
case VCHR:
case VBLK:
case VFIFO:
rdev = fxdr_unsigned(long, sp->sa_size);
break;
default:
break;
};
}
if (vp == NULL) {
kauth_acl_t xacl = NULL;
if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
int ismember;
VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
}
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
if (!error) {
error = kauth_acl_inherit(dvp,
NULL,
&xacl,
0 ,
&context);
if (!error && xacl != NULL)
VATTR_SET(vap, va_acl, xacl);
}
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
if (!error) {
error = vnode_authattr_new(dvp, vap, 0, &context);
if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
VATTR_CLEAR_ACTIVE(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
error = vnode_authattr_new(dvp, vap, 0, &context);
}
}
if (vap->va_type == VREG || vap->va_type == VSOCK) {
if (!error)
error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context);
if (!error && !VATTR_ALL_SUPPORTED(vap))
error = vnode_setattr_fallback(vp, vap, &context);
if (xacl != NULL)
kauth_acl_free(xacl);
if (!error) {
if (exclusive_flag) {
exclusive_flag = 0;
VATTR_INIT(vap);
bcopy(cverf, (caddr_t)&vap->va_access_time,
NFSX_V3CREATEVERF);
VATTR_SET_ACTIVE(vap, va_access_time);
error = vnode_setattr(vp, vap, &context);
}
}
} else if (vap->va_type == VCHR || vap->va_type == VBLK ||
vap->va_type == VFIFO) {
if (vap->va_type == VCHR && rdev == (int)0xffffffff)
VATTR_SET(vap, va_type, VFIFO);
if (vap->va_type != VFIFO &&
(error = suser(nfsd->nd_cr, (u_short *)0))) {
nfsm_reply(0);
} else
VATTR_SET(vap, va_rdev, (dev_t)rdev);
error = VNOP_MKNOD(dvp, &vp, &nd.ni_cnd, vap, &context);
if (xacl != NULL)
kauth_acl_free(xacl);
if (error) {
nfsm_reply(0);
}
if (vp) {
vnode_recycle(vp);
vnode_put(vp);
vp = NULL;
}
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags &= ~LOCKPARENT;
nd.ni_cnd.cn_context = &context;
nd.ni_startdir = dvp;
nd.ni_usedvp = dvp;
error = lookup(&nd);
if (!error) {
if (nd.ni_cnd.cn_flags & ISSYMLINK)
error = EINVAL;
vp = nd.ni_vp;
}
if (error)
nfsm_reply(0);
} else {
error = ENXIO;
}
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
vnode_put(dvp);
} else {
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
vnode_put(dvp);
if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) {
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA,
&context, nxo, 0);
if (!error) {
tempsize = vap->va_data_size;
VATTR_INIT(vap);
VATTR_SET(vap, va_data_size, tempsize);
error = vnode_setattr(vp, vap, &context);
}
}
}
if (!error) {
error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
if (!error) {
nfsm_srv_vattr_init(&postat, v3);
error = vnode_getattr(vp, &postat, &context);
}
}
if (vp)
vnode_put(vp);
if (v3) {
if (exclusive_flag && !error &&
bcmp(cverf, (caddr_t)&postat.va_access_time, NFSX_V3CREATEVERF))
error = EEXIST;
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
dirp = NULL;
}
nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
if (!error) {
nfsm_srvpostop_fh(&nfh);
nfsm_srvpostop_attr(0, &postat);
}
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
} else {
nfsm_srvfhtom(&nfh, v3);
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(&postat, fp);
}
return (0);
nfsmout:
if (nd.ni_cnd.cn_nameiop) {
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
}
if (dirp)
vnode_put(dirp);
return (error);
}
int
nfsrv_mknod(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vnode_attr dirfor, diraft, postat;
struct vnode_attr va;
struct vnode_attr *vap = &va;
u_long *tl;
struct nameidata nd;
long t1;
caddr_t bpos;
int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
u_long major, minor;
enum vtype vtyp;
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp, dvp, dirp = NULL;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context hacked_context;
struct vfs_context context;
uid_t saved_uid;
kauth_acl_t xacl = NULL;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
hacked_context.vc_proc = procp;
hacked_context.vc_ucred = proc_ucred(procp);
saved_uid = kauth_cred_getuid(nfsd->nd_cr);
vp = dvp = NULL;
nd.ni_cnd.cn_nameiop = 0;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, 1);
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_path_mbuftond(&md, &dpos, 1, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
nfsm_srv_pre_vattr_init(&dirfor, 1);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
}
if (error) {
nd.ni_cnd.cn_nameiop = 0;
nfsm_reply(NFSX_WCCDATA(1));
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
if (dirp)
vnode_put(dirp);
return (0);
}
dvp = nd.ni_dvp;
vp = nd.ni_vp;
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
vtyp = nfsv3tov_type(*tl);
if (vtyp != VCHR && vtyp != VBLK && vtyp != VSOCK && vtyp != VFIFO) {
error = NFSERR_BADTYPE;
goto out;
}
VATTR_INIT(vap);
nfsm_srvsattr(vap);
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);
VATTR_SET(vap, va_rdev, makedev(major, minor));
}
if (vp) {
error = EEXIST;
goto out;
}
VATTR_SET(vap, va_type, vtyp);
if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
int ismember;
VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
}
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
if (!error) {
error = kauth_acl_inherit(dvp,
NULL,
&xacl,
0 ,
&context);
if (!error && xacl != NULL)
VATTR_SET(vap, va_acl, xacl);
}
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
if (!error) {
error = vnode_authattr_new(dvp, vap, 0, &context);
if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
VATTR_CLEAR_ACTIVE(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
error = vnode_authattr_new(dvp, vap, 0, &context);
}
}
if (vtyp == VSOCK) {
error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context);
if (!error && !VATTR_ALL_SUPPORTED(vap))
error = vnode_setattr_fallback(vp, vap, &context);
} else {
if (vtyp != VFIFO && (error = suser(nfsd->nd_cr, (u_short *)0))) {
goto out1;
}
if ((error = VNOP_MKNOD(dvp, &vp, &nd.ni_cnd, vap, &context))) {
goto out1;
}
if (vp) {
vnode_recycle(vp);
vnode_put(vp);
vp = NULL;
}
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags &= ~LOCKPARENT;
nd.ni_cnd.cn_context = &hacked_context;
nd.ni_startdir = dvp;
nd.ni_usedvp = dvp;
error = lookup(&nd);
if (!error) {
vp = nd.ni_vp;
if (nd.ni_cnd.cn_flags & ISSYMLINK)
error = EINVAL;
}
}
out1:
if (xacl != NULL)
kauth_acl_free(xacl);
out:
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
vnode_put(dvp);
if (!error) {
error = nfsrv_vptofh(nx, 0, NULL, vp, &context, &nfh);
if (!error) {
nfsm_srv_vattr_init(&postat, 1);
error = vnode_getattr(vp, &postat, &context);
}
}
if (vp)
vnode_put(vp);
nfsm_srv_vattr_init(&diraft, 1);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
dirp = NULL;
nfsm_reply(NFSX_SRVFH(1, &nfh) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1));
if (!error) {
nfsm_srvpostop_fh(&nfh);
nfsm_srvpostop_attr(0, &postat);
}
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
nfsmout:
if (nd.ni_cnd.cn_nameiop) {
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
}
if (dirp)
vnode_put(dirp);
return (error);
}
int
nfsrv_remove(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct nameidata nd;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mreq;
vnode_t vp, dvp, dirp = NULL;
struct vnode_attr dirfor, diraft;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context context;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
dvp = vp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, v3);
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (!error) {
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vnode_vtype(vp) == VDIR)
error = EPERM;
else if (vnode_isvroot(vp))
error = EBUSY;
else
error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, &context, nxo, 0);
if (!error)
error = VNOP_REMOVE(dvp, vp, &nd.ni_cnd, 0, &context);
nameidone(&nd);
vnode_put(vp);
vnode_put(dvp);
}
if (dirp) {
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
}
nfsm_reply(NFSX_WCCDATA(v3));
if (v3) {
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
nfsmout:
return (error);
}
int
nfsrv_rename(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
kauth_cred_t saved_cred = NULL;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, fromlen, tolen;
int fdirfor_ret = 1, fdiraft_ret = 1;
int tdirfor_ret = 1, tdiraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2, *frompath = NULL, *topath = NULL;
mbuf_t mb, mreq;
struct nameidata fromnd, tond;
vnode_t fvp, tvp, tdvp, fdvp, fdirp = NULL;
vnode_t tdirp = NULL;
struct vnode_attr fdirfor, fdiraft, tdirfor, tdiraft;
struct nfs_filehandle fnfh, tnfh;
struct nfs_export *fnx, *tnx;
struct nfs_export_options *fnxo, *tnxo;
enum vtype fvtype, tvtype;
int holding_mntlock;
mount_t locked_mp;
struct vfs_context context;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
#ifndef nolint
fvp = (vnode_t)0;
#endif
holding_mntlock = 0;
fvp = tvp = NULL;
fdvp = tdvp = NULL;
locked_mp = NULL;
nfsm_srvmtofh(&fnfh);
nfsm_srvnamesiz(fromlen, v3);
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &fromlen, &fromnd);
if (error) {
nfsm_reply(0);
return (0);
}
frompath = fromnd.ni_cnd.cn_pnbuf;
nfsm_srvmtofh(&tnfh);
nfsm_strsiz(tolen, NFS_MAXNAMLEN, v3);
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &tolen, &tond);
if (error) {
nfsm_reply(0);
FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
return (0);
}
topath = tond.ni_cnd.cn_pnbuf;
saved_cred = nfsd->nd_cr;
kauth_cred_ref(saved_cred);
retry:
fromnd.ni_cnd.cn_nameiop = DELETE;
fromnd.ni_cnd.cn_flags = WANTPARENT;
fromnd.ni_cnd.cn_pnbuf = frompath;
frompath = NULL;
fromnd.ni_cnd.cn_pnlen = MAXPATHLEN;
fromnd.ni_cnd.cn_flags |= HASBUF;
error = nfs_namei(nfsd, &context, &fromnd, &fnfh, nam, FALSE, &fdirp, &fnx, &fnxo);
if (error)
goto out;
fdvp = fromnd.ni_dvp;
fvp = fromnd.ni_vp;
if (fdirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&fdirfor, v3);
fdirfor_ret = vnode_getattr(fdirp, &fdirfor, &context);
} else {
vnode_put(fdirp);
fdirp = NULL;
}
}
fvtype = vnode_vtype(fvp);
if (nfsd->nd_cr != saved_cred) {
kauth_cred_rele(nfsd->nd_cr);
nfsd->nd_cr = saved_cred;
kauth_cred_ref(nfsd->nd_cr);
}
tond.ni_cnd.cn_nameiop = RENAME;
tond.ni_cnd.cn_flags = WANTPARENT;
tond.ni_cnd.cn_pnbuf = topath;
topath = NULL;
tond.ni_cnd.cn_pnlen = MAXPATHLEN;
tond.ni_cnd.cn_flags |= HASBUF;
if (fvtype == VDIR)
tond.ni_cnd.cn_flags |= WILLBEDIR;
error = nfs_namei(nfsd, &context, &tond, &tnfh, nam, FALSE, &tdirp, &tnx, &tnxo);
if (error) {
if (error == EISDIR && fvtype == VDIR) {
if (v3)
error = EINVAL;
else
error = ENOTEMPTY;
}
goto out;
}
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
if (tdirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&tdirfor, v3);
tdirfor_ret = vnode_getattr(tdirp, &tdirfor, &context);
} else {
vnode_put(tdirp);
tdirp = NULL;
}
}
if (tvp != NULL) {
tvtype = vnode_vtype(tvp);
if (fvtype == VDIR && tvtype != VDIR) {
if (v3)
error = EEXIST;
else
error = EISDIR;
goto out;
} else if (fvtype != VDIR && tvtype == VDIR) {
if (v3)
error = EEXIST;
else
error = ENOTDIR;
goto out;
}
if (tvtype == VDIR && vnode_mountedhere(tvp)) {
if (v3)
error = EXDEV;
else
error = ENOTEMPTY;
goto out;
}
}
if (fvp == tdvp) {
if (v3)
error = EINVAL;
else
error = ENOTEMPTY;
goto out;
}
{
int moving = 0;
error = 0;
if ((tvp != NULL) && vnode_isdir(tvp)) {
if (tvp != fdvp)
moving = 1;
} else if (tdvp != fdvp) {
moving = 1;
}
if (moving) {
if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, &context, fnxo, 0)) != 0)
goto auth_exit;
if ((error = nfsrv_authorize(((tvp != NULL) && vnode_isdir(tvp)) ? tvp : tdvp,
NULL,
vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
&context, tnxo, 0)) != 0)
goto auth_exit;
} else {
if ((error = nfsrv_authorize(fdvp, NULL,
vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE,
&context, fnxo, 0)) != 0)
goto auth_exit;
}
if ((tvp != NULL) && !vnode_isdir(tvp) &&
((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, &context, tnxo, 0)) != 0))
goto auth_exit;
auth_exit:
if (error != 0)
goto out;
}
if ((vnode_mount(fvp) != vnode_mount(tdvp)) ||
(tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) {
if (v3)
error = EXDEV;
else
error = ENOTEMPTY;
goto out;
}
if (tdvp->v_parent == fvp) {
if (v3)
error = EXDEV;
else
error = ENOTEMPTY;
goto out;
}
if (fvtype == VDIR && vnode_mountedhere(fvp)) {
if (v3)
error = EXDEV;
else
error = ENOTEMPTY;
goto out;
}
if ((fvp == tvp) && (fdvp == tdvp)) {
if (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)) {
goto out;
}
}
if (holding_mntlock && vnode_mount(fvp) != locked_mp) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
holding_mntlock = 0;
}
if (tdvp != fdvp && fvtype == VDIR) {
if (!holding_mntlock) {
locked_mp = vnode_mount(fvp);
mount_ref(locked_mp, 0);
MALLOC_ZONE(topath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (topath)
bcopy(tond.ni_cnd.cn_pnbuf, topath, tolen + 1);
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
MALLOC_ZONE(frompath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (frompath)
bcopy(fromnd.ni_cnd.cn_pnbuf, frompath, fromlen + 1);
nameidone(&fromnd);
vnode_put(fvp);
vnode_put(fdvp);
if (fdirp) {
vnode_put(fdirp);
fdirp = NULL;
}
if (tdirp) {
vnode_put(tdirp);
tdirp = NULL;
}
mount_lock_renames(locked_mp);
holding_mntlock = 1;
fvp = tvp = NULL;
fdvp = tdvp = NULL;
fdirfor_ret = tdirfor_ret = 1;
if (!topath || !frompath) {
error = ENOMEM;
goto out;
}
goto retry;
}
} else {
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
holding_mntlock = 0;
}
}
char *oname;
vnode_t oparent;
oname = fvp->v_name;
oparent = fvp->v_parent;
error = VNOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd,
tond.ni_dvp, tond.ni_vp, &tond.ni_cnd, &context);
if (oname == fvp->v_name && oparent == fvp->v_parent) {
int update_flags;
update_flags = VNODE_UPDATE_NAME;
if (fdvp != tdvp)
update_flags |= VNODE_UPDATE_PARENT;
vnode_update_identity(fvp, tdvp, tond.ni_cnd.cn_nameptr, tond.ni_cnd.cn_namelen, tond.ni_cnd.cn_hash, update_flags);
}
out:
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
holding_mntlock = 0;
}
if (tdvp) {
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
tdvp = NULL;
}
if (fdvp) {
nameidone(&fromnd);
if (fvp)
vnode_put(fvp);
vnode_put(fdvp);
fdvp = NULL;
}
if (fdirp) {
nfsm_srv_vattr_init(&fdiraft, v3);
fdiraft_ret = vnode_getattr(fdirp, &fdiraft, &context);
vnode_put(fdirp);
fdirp = NULL;
}
if (tdirp) {
nfsm_srv_vattr_init(&tdiraft, v3);
tdiraft_ret = vnode_getattr(tdirp, &tdiraft, &context);
vnode_put(tdirp);
tdirp = NULL;
}
nfsm_reply(2 * NFSX_WCCDATA(v3));
if (v3) {
nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
}
if (frompath)
FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
if (topath)
FREE_ZONE(topath, MAXPATHLEN, M_NAMEI);
if (saved_cred)
kauth_cred_rele(saved_cred);
return (0);
nfsmout:
if (holding_mntlock) {
mount_unlock_renames(locked_mp);
mount_drop(locked_mp, 0);
}
if (tdvp) {
nameidone(&tond);
if (tvp)
vnode_put(tvp);
vnode_put(tdvp);
}
if (fdvp) {
nameidone(&fromnd);
if (fvp)
vnode_put(fvp);
vnode_put(fdvp);
}
if (fdirp)
vnode_put(fdirp);
if (tdirp)
vnode_put(tdirp);
if (frompath)
FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI);
if (topath)
FREE_ZONE(topath, MAXPATHLEN, M_NAMEI);
if (saved_cred)
kauth_cred_rele(saved_cred);
return (error);
}
int
nfsrv_link(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct nameidata nd;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, len, dirfor_ret = 1, diraft_ret = 1;
int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mreq;
vnode_t vp, xp, dvp, dirp = NULL;
struct vnode_attr dirfor, diraft, at;
struct nfs_filehandle nfh, dnfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context context;
vp = xp = dvp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvmtofh(&dnfh);
nfsm_srvnamesiz(len, v3);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
nfsm_srvpostop_attr(getret, &at);
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
nfsm_srvpostop_attr(getret, &at);
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
if (vnode_vtype(vp) == VDIR) {
error = EPERM;
goto out1;
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, &context, nxo, 0)) != 0)
goto out1;
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &dnfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (error)
goto out1;
dvp = nd.ni_dvp;
xp = nd.ni_vp;
if (xp != NULL)
error = EEXIST;
else if (vnode_mount(vp) != vnode_mount(dvp))
error = EXDEV;
else
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
if (!error)
error = VNOP_LINK(vp, dvp, &nd.ni_cnd, &context);
nameidone(&nd);
if (xp)
vnode_put(xp);
vnode_put(dvp);
out1:
if (v3) {
nfsm_srv_vattr_init(&at, v3);
getret = vnode_getattr(vp, &at, &context);
}
if (dirp) {
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
}
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
nfsm_srvpostop_attr(getret, &at);
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
nfsmout:
return (error);
}
int
nfsrv_symlink(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vnode_attr dirfor, diraft, postat;
struct nameidata nd;
struct vnode_attr va;
struct vnode_attr *vap = &va;
u_long *tl;
long t1;
struct nfsv2_sattr *sp;
char *bpos, *linkdata = NULL, *cp2;
int error = 0, len, linkdatalen;
int dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
mbuf_t mb, mreq, mb2;
vnode_t vp, dvp, dirp = NULL;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t auio;
char uio_buf[ UIO_SIZEOF(1) ];
struct vfs_context context;
uid_t saved_uid;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
saved_uid = kauth_cred_getuid(nfsd->nd_cr);
nd.ni_cnd.cn_nameiop = 0;
vp = dvp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, v3);
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (error) {
nd.ni_cnd.cn_nameiop = 0;
goto out1;
}
dvp = nd.ni_dvp;
vp = nd.ni_vp;
VATTR_INIT(vap);
if (v3)
nfsm_srvsattr(vap);
nfsm_strsiz(linkdatalen, NFS_MAXPATHLEN, v3);
MALLOC(linkdata, caddr_t, linkdatalen + 1, M_TEMP, M_WAITOK);
if (!linkdata) {
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
vnode_put(nd.ni_dvp);
vnode_put(nd.ni_vp);
error = ENOMEM;
goto out;
}
auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
if (!auio) {
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
vnode_put(nd.ni_dvp);
vnode_put(nd.ni_vp);
error = ENOMEM;
goto out;
}
uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen);
nfsm_mtouio(auio, linkdatalen);
if (!v3) {
nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
VATTR_SET(vap, va_mode, fxdr_unsigned(u_short, sp->sa_mode));
}
*(linkdata + linkdatalen) = '\0';
if (vp) {
error = EEXIST;
goto out;
}
if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
int ismember;
VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
}
VATTR_SET(vap, va_type, VLNK);
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0);
if (!error) {
error = vnode_authattr_new(dvp, vap, 0, &context);
if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
VATTR_CLEAR_ACTIVE(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
error = vnode_authattr_new(dvp, vap, 0, &context);
}
}
if (!error)
error = VNOP_SYMLINK(dvp, &vp, &nd.ni_cnd, vap, linkdata, &context);
if (!error && v3) {
if (vp == NULL) {
nd.ni_cnd.cn_nameiop = LOOKUP;
nd.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
nd.ni_cnd.cn_context = &context;
nd.ni_startdir = dvp;
nd.ni_usedvp = dvp;
error = lookup(&nd);
if (!error)
vp = nd.ni_vp;
}
if (!error) {
error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
if (!error) {
nfsm_srv_vattr_init(&postat, v3);
error = vnode_getattr(vp, &postat, &context);
}
}
}
out:
nameidone(&nd);
nd.ni_cnd.cn_nameiop = 0;
if (vp)
vnode_put(vp);
vnode_put(dvp);
out1:
if (linkdata)
FREE(linkdata, M_TEMP);
if (dirp) {
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
}
nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
if (!error) {
nfsm_srvpostop_fh(&nfh);
nfsm_srvpostop_attr(0, &postat);
}
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
}
return (0);
nfsmout:
if (nd.ni_cnd.cn_nameiop) {
nameidone(&nd);
if (vp)
vnode_put(vp);
vnode_put(dvp);
}
if (dirp)
vnode_put(dirp);
if (linkdata)
FREE(linkdata, M_TEMP);
return (error);
}
int
nfsrv_mkdir(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vnode_attr dirfor, diraft, postat;
struct vnode_attr va;
struct vnode_attr *vap = &va;
struct nfs_fattr *fp;
struct nameidata nd;
caddr_t cp;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, len;
int dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp, dvp, dirp = NULL;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context context;
uid_t saved_uid;
kauth_acl_t xacl = NULL;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
saved_uid = kauth_cred_getuid(nfsd->nd_cr);
nd.ni_cnd.cn_nameiop = 0;
vp = dvp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, v3);
nd.ni_cnd.cn_nameiop = CREATE;
nd.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (error) {
nd.ni_cnd.cn_nameiop = 0;
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
if (dirp)
vnode_put(dirp);
return (0);
}
dvp = nd.ni_dvp;
vp = nd.ni_vp;
VATTR_INIT(vap);
if (v3) {
nfsm_srvsattr(vap);
} else {
nfsm_dissect(tl, u_long *, NFSX_UNSIGNED);
VATTR_SET(vap, va_mode, nfstov_mode(*tl++));
}
VATTR_SET(vap, va_type, VDIR);
if (vp != NULL) {
nameidone(&nd);
vnode_put(dvp);
vnode_put(vp);
error = EEXIST;
goto out;
}
if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nfsd->nd_cr) != saved_uid)) {
int ismember;
VATTR_SET(vap, va_uid, kauth_cred_getuid(nfsd->nd_cr));
if (kauth_cred_ismember_gid(nfsd->nd_cr, vap->va_gid, &ismember) || !ismember)
VATTR_SET(vap, va_gid, kauth_cred_getgid(nfsd->nd_cr));
}
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, &context, nxo, 0);
if (!error) {
error = kauth_acl_inherit(dvp,
NULL,
&xacl,
1,
&context);
if (!error && xacl != NULL)
VATTR_SET(vap, va_acl, xacl);
}
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
if (!error) {
error = vnode_authattr_new(dvp, vap, 0, &context);
if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
VATTR_CLEAR_ACTIVE(vap, va_uid);
VATTR_CLEAR_ACTIVE(vap, va_gid);
error = vnode_authattr_new(dvp, vap, 0, &context);
}
}
if (!error)
error = VNOP_MKDIR(dvp, &vp, &nd.ni_cnd, vap, &context);
if (!error && !VATTR_ALL_SUPPORTED(vap))
error = vnode_setattr_fallback(vp, vap, &context);
if (xacl != NULL)
kauth_acl_free(xacl);
if (!error) {
error = nfsrv_vptofh(nx, !v3, NULL, vp, &context, &nfh);
if (!error) {
nfsm_srv_vattr_init(&postat, v3);
error = vnode_getattr(vp, &postat, &context);
}
vnode_put(vp);
vp = NULL;
}
nameidone(&nd);
vnode_put(dvp);
out:
nd.ni_cnd.cn_nameiop = 0;
if (dirp) {
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
}
nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3));
if (v3) {
if (!error) {
nfsm_srvpostop_fh(&nfh);
nfsm_srvpostop_attr(0, &postat);
}
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
} else {
nfsm_srvfhtom(&nfh, v3);
nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR);
nfsm_srvfillattr(&postat, fp);
}
return (0);
nfsmout:
if (nd.ni_cnd.cn_nameiop) {
nameidone(&nd);
vnode_put(dvp);
if (vp)
vnode_put(vp);
}
if (dirp)
vnode_put(dirp);
return (error);
}
int
nfsrv_rmdir(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, len;
int dirfor_ret = 1, diraft_ret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mreq;
vnode_t vp, dvp, dirp = NULL;
struct vnode_attr dirfor, diraft;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct nameidata nd;
struct vfs_context context;
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
vp = dvp = NULL;
nfsm_srvmtofh(&nfh);
nfsm_srvnamesiz(len, v3);
nd.ni_cnd.cn_nameiop = DELETE;
nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_path_mbuftond(&md, &dpos, v3, FALSE, &len, &nd);
if (!error)
error = nfs_namei(nfsd, &context, &nd, &nfh, nam, FALSE, &dirp, &nx, &nxo);
if (dirp) {
if (v3) {
nfsm_srv_pre_vattr_init(&dirfor, v3);
dirfor_ret = vnode_getattr(dirp, &dirfor, &context);
} else {
vnode_put(dirp);
dirp = NULL;
}
}
if (error) {
nfsm_reply(NFSX_WCCDATA(v3));
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
if (dirp)
vnode_put(dirp);
return (0);
}
dvp = nd.ni_dvp;
vp = nd.ni_vp;
if (vnode_vtype(vp) != VDIR) {
error = ENOTDIR;
goto out;
}
if (dvp == vp) {
error = EINVAL;
goto out;
}
if (vnode_isvroot(vp))
error = EBUSY;
if (!error)
error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, &context, nxo, 0);
if (!error)
error = VNOP_RMDIR(dvp, vp, &nd.ni_cnd, &context);
out:
nameidone(&nd);
vnode_put(dvp);
vnode_put(vp);
if (dirp) {
nfsm_srv_vattr_init(&diraft, v3);
diraft_ret = vnode_getattr(dirp, &diraft, &context);
vnode_put(dirp);
}
nfsm_reply(NFSX_WCCDATA(v3));
if (v3) {
nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft);
return (0);
}
nfsmout:
return (error);
}
int
nfsrv_readdir(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
char *bp, *be;
mbuf_t mp;
struct direntry *dp;
caddr_t cp;
u_long *tl;
long t1;
caddr_t bpos;
mbuf_t mb, mb2, mreq, mp2;
char *cpos, *cend, *cp2, *rbuf;
vnode_t vp;
struct vnode_attr at;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t auio;
char uio_buf[ UIO_SIZEOF(1) ];
int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
int siz, count, fullsiz, eofflag, nentries = 0;
int v3 = (nfsd->nd_flag & ND_NFSV3);
u_quad_t off, toff, verf;
nfsuint64 tquad;
int vnopflag;
struct vfs_context context;
vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
nfsm_srvmtofh(&nfh);
if (v3) {
nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED);
fxdr_hyper(tl, &toff);
tl += 2;
fxdr_hyper(tl, &verf);
tl += 2;
} else {
nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED);
toff = fxdr_unsigned(u_quad_t, *tl++);
}
off = toff;
count = fxdr_unsigned(int, *tl);
siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
xfer = NFS_SRVMAXDATA(nfsd);
if (siz > xfer)
siz = xfer;
fullsiz = siz;
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (!v3 || (nxo->nxo_flags & NX_32BITCLIENTS))
vnopflag |= VNODE_READDIR_SEEKOFF32;
if (v3) {
nfsm_srv_vattr_init(&at, v3);
error = getret = vnode_getattr(vp, &at, &context);
if (!error && toff && verf && verf != at.va_filerev)
error = NFSERR_BAD_COOKIE;
}
if (!error)
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0);
if (error) {
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, &at);
return (0);
}
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
if (!rbuf) {
error = ENOMEM;
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, &at);
return (0);
}
auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
if (!auio) {
error = ENOMEM;
FREE(rbuf, M_TEMP);
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, &at);
return (0);
}
again:
uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
eofflag = 0;
error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, &context);
off = uio_offset(auio);
if (v3) {
nfsm_srv_vattr_init(&at, v3);
getret = vnode_getattr(vp, &at, &context);
if (!error)
error = getret;
}
if (error) {
vnode_put(vp);
FREE(rbuf, M_TEMP);
nfsm_reply(NFSX_POSTOPATTR(v3));
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if (uio_resid(auio) != 0) {
siz -= uio_resid(auio);
if (siz == 0) {
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) +
2 * NFSX_UNSIGNED);
if (v3) {
nfsm_srvpostop_attr(getret, &at);
nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
txdr_hyper(&at.va_filerev, tl);
tl += 2;
} else
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
*tl++ = nfs_false;
*tl = nfs_true;
FREE(rbuf, M_TEMP);
return (0);
}
}
cpos = rbuf;
cend = rbuf + siz;
dp = (struct direntry *)cpos;
while (dp->d_fileno == 0 && cpos < cend && nentries > 0) {
cpos += dp->d_reclen;
dp = (struct direntry *)cpos;
nentries--;
}
if (cpos >= cend || nentries == 0) {
toff = off;
siz = fullsiz;
goto again;
}
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + siz);
if (v3) {
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);
} else
len = 2 * NFSX_UNSIGNED;
mp = mp2 = mb;
bp = bpos;
be = bp + mbuf_trailingspace(mp);
while (cpos < cend && nentries > 0) {
if (dp->d_fileno != 0) {
nlen = dp->d_namlen;
if (!v3 && (nlen > NFS_MAXNAMLEN))
nlen = NFS_MAXNAMLEN;
rem = nfsm_rndup(nlen)-nlen;
len += (4 * NFSX_UNSIGNED + nlen + rem);
if (v3)
len += 2 * NFSX_UNSIGNED;
if (len > count) {
eofflag = 0;
break;
}
nfsm_clget;
*tl = nfs_true;
bp += NFSX_UNSIGNED;
nfsm_clget;
if (v3) {
txdr_hyper(&dp->d_fileno, &tquad);
*tl = tquad.nfsuquad[0];
bp += NFSX_UNSIGNED;
nfsm_clget;
*tl = tquad.nfsuquad[1];
bp += NFSX_UNSIGNED;
} else {
*tl = txdr_unsigned(dp->d_fileno);
bp += NFSX_UNSIGNED;
}
nfsm_clget;
*tl = txdr_unsigned(nlen);
bp += NFSX_UNSIGNED;
xfer = nlen;
cp = dp->d_name;
while (xfer > 0) {
nfsm_clget;
if ((bp+xfer) > be)
tsiz = be-bp;
else
tsiz = xfer;
bcopy(cp, bp, tsiz);
bp += tsiz;
xfer -= tsiz;
if (xfer > 0)
cp += tsiz;
}
for (i = 0; i < rem; i++)
*bp++ = '\0';
nfsm_clget;
if (v3) {
if (vnopflag & VNODE_READDIR_SEEKOFF32)
dp->d_seekoff &= 0x00000000ffffffffULL;
txdr_hyper(&dp->d_seekoff, &tquad);
*tl = tquad.nfsuquad[0];
bp += NFSX_UNSIGNED;
nfsm_clget;
*tl = tquad.nfsuquad[1];
bp += NFSX_UNSIGNED;
} else {
*tl = txdr_unsigned(dp->d_seekoff);
bp += NFSX_UNSIGNED;
}
}
cpos += dp->d_reclen;
dp = (struct direntry *)cpos;
nentries--;
}
nfsm_clget;
*tl = nfs_false;
bp += NFSX_UNSIGNED;
nfsm_clget;
if (eofflag)
*tl = nfs_true;
else
*tl = nfs_false;
bp += NFSX_UNSIGNED;
if (mp != mb) {
if (bp < be)
mbuf_setlen(mp, bp - (char*)mbuf_data(mp));
} else
mbuf_setlen(mp, mbuf_len(mp) + (bp - bpos));
FREE(rbuf, M_TEMP);
nfsmout:
return (error);
}
struct flrep {
nfsuint64 fl_off;
u_long fl_postopok;
u_long fl_fattr[NFSX_V3FATTR / sizeof (u_long)];
u_long fl_fhok;
u_long fl_fhsize;
u_long fl_nfh[NFSX_V3FHMAX / sizeof (u_long)];
};
int
nfsrv_readdirplus(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
char *bp, *be;
mbuf_t mp;
struct direntry *dp;
caddr_t cp;
u_long *tl;
long t1;
caddr_t bpos;
mbuf_t mb, mb2, mreq, mp2;
char *cpos, *cend, *cp2, *rbuf;
vnode_t vp, nvp;
struct flrep fl;
struct nfs_filehandle dnfh, *nfhp = (struct nfs_filehandle *)&fl.fl_fhsize;
struct nfs_export *nx;
struct nfs_export_options *nxo;
uio_t auio;
char uio_buf[ UIO_SIZEOF(1) ];
struct vnode_attr va, at, *vap = &va;
struct nfs_fattr *fp;
int len, nlen, rem, xfer, tsiz, i, error = 0, getret = 1;
int siz, count, fullsiz, eofflag, dirlen, nentries = 0, isdotdot;
u_quad_t off, toff, verf;
nfsuint64 tquad;
int vnopflag;
struct vfs_context context;
vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF;
vp = NULL;
nfsm_srvmtofh(&dnfh);
nfsm_dissect(tl, u_long *, 6 * NFSX_UNSIGNED);
fxdr_hyper(tl, &toff);
tl += 2;
fxdr_hyper(tl, &verf);
tl += 2;
siz = fxdr_unsigned(int, *tl++);
count = fxdr_unsigned(int, *tl);
off = toff;
siz = ((siz + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
xfer = NFS_SRVMAXDATA(nfsd);
if (siz > xfer)
siz = xfer;
fullsiz = siz;
if ((error = nfsrv_fhtovp(&dnfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
if (nxo->nxo_flags & NX_32BITCLIENTS)
vnopflag |= VNODE_READDIR_SEEKOFF32;
nfsm_srv_vattr_init(&at, 1);
error = getret = vnode_getattr(vp, &at, &context);
if (!error && toff && verf && verf != at.va_filerev)
error = NFSERR_BAD_COOKIE;
if (!error)
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0);
if (error) {
vnode_put(vp);
vp = NULL;
nfsm_reply(NFSX_V3POSTOPATTR);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
if (!rbuf) {
error = ENOMEM;
vnode_put(vp);
vp = NULL;
nfsm_reply(NFSX_V3POSTOPATTR);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
&uio_buf[0], sizeof(uio_buf));
if (!auio) {
error = ENOMEM;
FREE(rbuf, M_TEMP);
vnode_put(vp);
vp = NULL;
nfsm_reply(NFSX_V3POSTOPATTR);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
again:
uio_reset(auio, off, UIO_SYSSPACE, UIO_READ);
uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz);
eofflag = 0;
error = VNOP_READDIR(vp, auio, vnopflag, &eofflag, &nentries, &context);
off = uio_offset(auio);
nfsm_srv_vattr_init(&at, 1);
getret = vnode_getattr(vp, &at, &context);
if (!error)
error = getret;
if (error) {
vnode_put(vp);
vp = NULL;
FREE(rbuf, M_TEMP);
nfsm_reply(NFSX_V3POSTOPATTR);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if (uio_resid(auio) != 0) {
siz -= uio_resid(auio);
if (siz == 0) {
vnode_put(vp);
vp = NULL;
nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF +
2 * NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED);
txdr_hyper(&at.va_filerev, tl);
tl += 2;
*tl++ = nfs_false;
*tl = nfs_true;
FREE(rbuf, M_TEMP);
return (0);
}
}
cpos = rbuf;
cend = rbuf + siz;
dp = (struct direntry *)cpos;
while (dp->d_fileno == 0 && cpos < cend && nentries > 0) {
cpos += dp->d_reclen;
dp = (struct direntry *)cpos;
nentries--;
}
if (cpos >= cend || nentries == 0) {
toff = off;
siz = fullsiz;
goto again;
}
if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context))) {
if (error == ENOTSUP)
error = NFSERR_NOTSUPP;
vnode_put(vp);
vp = NULL;
FREE(rbuf, M_TEMP);
nfsm_reply(NFSX_V3POSTOPATTR);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
vnode_put(nvp);
dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED;
nfsm_reply(count);
nfsm_srvpostop_attr(getret, &at);
nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED);
txdr_hyper(&at.va_filerev, tl);
mp = mp2 = mb;
bp = bpos;
be = bp + mbuf_trailingspace(mp);
while (cpos < cend && nentries > 0) {
if (dp->d_fileno != 0) {
nlen = dp->d_namlen;
rem = nfsm_rndup(nlen)-nlen;
if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context))
goto invalid;
isdotdot = ((dp->d_namlen == 2) &&
(dp->d_name[0] == '.') && (dp->d_name[1] == '.'));
if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, &context, nfhp)) {
vnode_put(nvp);
goto invalid;
}
nfsm_srv_vattr_init(vap, 1);
if (vnode_getattr(nvp, vap, &context)) {
vnode_put(nvp);
goto invalid;
}
vnode_put(nvp);
len += (8 * NFSX_UNSIGNED + nlen + rem + nfhp->nfh_len +
NFSX_V3POSTOPATTR);
dirlen += (6 * NFSX_UNSIGNED + nlen + rem);
if (len > count || dirlen > fullsiz) {
eofflag = 0;
break;
}
fp = (struct nfs_fattr *)&fl.fl_fattr;
nfsm_srvfillattr(vap, fp);
fl.fl_fhsize = txdr_unsigned(nfhp->nfh_len);
fl.fl_fhok = nfs_true;
fl.fl_postopok = nfs_true;
if (vnopflag & VNODE_READDIR_SEEKOFF32)
dp->d_seekoff &= 0x00000000ffffffffULL;
txdr_hyper(&dp->d_seekoff, &fl.fl_off);
nfsm_clget;
*tl = nfs_true;
bp += NFSX_UNSIGNED;
nfsm_clget;
txdr_hyper(&dp->d_fileno, &tquad);
*tl = tquad.nfsuquad[0];
bp += NFSX_UNSIGNED;
nfsm_clget;
*tl = tquad.nfsuquad[1];
bp += NFSX_UNSIGNED;
nfsm_clget;
*tl = txdr_unsigned(nlen);
bp += NFSX_UNSIGNED;
xfer = nlen;
cp = dp->d_name;
while (xfer > 0) {
nfsm_clget;
if ((bp + xfer) > be)
tsiz = be - bp;
else
tsiz = xfer;
bcopy(cp, bp, tsiz);
bp += tsiz;
xfer -= tsiz;
if (xfer > 0)
cp += tsiz;
}
for (i = 0; i < rem; i++)
*bp++ = '\0';
xfer = sizeof(struct flrep) - sizeof(fl.fl_nfh) + fl.fl_fhsize;
cp = (caddr_t)&fl;
while (xfer > 0) {
nfsm_clget;
if ((bp + xfer) > be)
tsiz = be - bp;
else
tsiz = xfer;
bcopy(cp, bp, tsiz);
bp += tsiz;
xfer -= tsiz;
if (xfer > 0)
cp += tsiz;
}
}
invalid:
cpos += dp->d_reclen;
dp = (struct direntry *)cpos;
nentries--;
}
vnode_put(vp);
vp = NULL;
nfsm_clget;
*tl = nfs_false;
bp += NFSX_UNSIGNED;
nfsm_clget;
if (eofflag)
*tl = nfs_true;
else
*tl = nfs_false;
bp += NFSX_UNSIGNED;
if (mp != mb) {
if (bp < be)
mbuf_setlen(mp, bp - (char*)mbuf_data(mp));
} else
mbuf_setlen(mp, mbuf_len(mp) + (bp - bpos));
FREE(rbuf, M_TEMP);
nfsmout:
if (vp)
vnode_put(vp);
return (error);
}
int
nfsrv_commit(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vnode_attr bfor, aft;
vnode_t vp;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, for_ret = 1, aft_ret = 1, count;
char *cp2;
mbuf_t mb, mb2, mreq;
u_quad_t off;
struct vfs_context context;
nfsm_srvmtofh(&nfh);
nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED);
fxdr_hyper(tl, &off);
tl += 2;
count = fxdr_unsigned(int, *tl);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(2 * NFSX_UNSIGNED);
nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
nfsm_srv_pre_vattr_init(&bfor, 1);
for_ret = vnode_getattr(vp, &bfor, &context);
error = VNOP_FSYNC(vp, MNT_WAIT, &context);
nfsm_srv_vattr_init(&aft, 1);
aft_ret = vnode_getattr(vp, &aft, &context);
vnode_put(vp);
nfsm_reply(NFSX_V3WCCDATA + NFSX_V3WRITEVERF);
nfsm_srvwcc_data(for_ret, &bfor, aft_ret, &aft);
if (!error) {
nfsm_build(tl, u_long *, NFSX_V3WRITEVERF);
*tl++ = txdr_unsigned(boottime_sec());
*tl = txdr_unsigned(0);
} else
return (0);
nfsmout:
return (error);
}
int
nfsrv_statfs(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
struct vfs_attr va;
struct nfs_statfs *sfp;
u_long *tl;
long t1;
caddr_t bpos;
int error = 0, getret = 1;
int v3 = (nfsd->nd_flag & ND_NFSV3);
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp;
struct vnode_attr at;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
u_quad_t tval;
off_t blksize;
struct vfs_context context;
nfsm_srvmtofh(&nfh);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
VFSATTR_INIT(&va);
VFSATTR_WANTED(&va, f_blocks);
VFSATTR_WANTED(&va, f_bavail);
VFSATTR_WANTED(&va, f_files);
VFSATTR_WANTED(&va, f_ffree);
error = vfs_getattr(vnode_mount(vp), &va, &context);
blksize = vnode_mount(vp)->mnt_vfsstat.f_bsize;
nfsm_srv_vattr_init(&at, v3);
getret = vnode_getattr(vp, &at, &context);
vnode_put(vp);
nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_STATFS(v3));
if (v3)
nfsm_srvpostop_attr(getret, &at);
if (error)
return (0);
nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
if (v3) {
tval = (u_quad_t)(va.f_blocks * blksize);
txdr_hyper(&tval, &sfp->sf_tbytes);
tval = (u_quad_t)(va.f_bfree * blksize);
txdr_hyper(&tval, &sfp->sf_fbytes);
tval = (u_quad_t)(va.f_bavail * blksize);
txdr_hyper(&tval, &sfp->sf_abytes);
txdr_hyper(&va.f_files, &sfp->sf_tfiles);
txdr_hyper(&va.f_ffree, &sfp->sf_ffiles);
txdr_hyper(&va.f_ffree, &sfp->sf_afiles);
sfp->sf_invarsec = 0;
} else {
sfp->sf_tsize = txdr_unsigned(NFS_V2MAXDATA);
sfp->sf_bsize = txdr_unsigned((unsigned)blksize);
sfp->sf_blocks = txdr_unsigned((unsigned)va.f_blocks);
sfp->sf_bfree = txdr_unsigned((unsigned)va.f_bfree);
sfp->sf_bavail = txdr_unsigned((unsigned)va.f_bavail);
}
nfsmout:
return (error);
}
int
nfsrv_fsinfo(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
u_long *tl;
struct nfsv3_fsinfo *sip;
long t1;
caddr_t bpos;
int error = 0, getret = 1, prefsize, maxsize;
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp;
struct vnode_attr at;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context context;
nfsm_srvmtofh(&nfh);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
nfsm_srv_vattr_init(&at, 1);
getret = vnode_getattr(vp, &at, &context);
vnode_put(vp);
nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3FSINFO);
nfsm_srvpostop_attr(getret, &at);
nfsm_build(sip, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
if (slp->ns_sotype == SOCK_DGRAM) {
maxsize = NFS_MAXDGRAMDATA;
prefsize = NFS_PREFDGRAMDATA;
} else
maxsize = prefsize = NFS_MAXDATA;
sip->fs_rtmax = txdr_unsigned(maxsize);
sip->fs_rtpref = txdr_unsigned(prefsize);
sip->fs_rtmult = txdr_unsigned(NFS_FABLKSIZE);
sip->fs_wtmax = txdr_unsigned(maxsize);
sip->fs_wtpref = txdr_unsigned(prefsize);
sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE);
sip->fs_dtpref = txdr_unsigned(prefsize);
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 |
NFSV3FSINFO_CANSETTIME);
nfsmout:
return (error);
}
int
nfsrv_pathconf(nfsd, slp, procp, mrq)
struct nfsrv_descript *nfsd;
struct nfssvc_sock *slp;
proc_t procp;
mbuf_t *mrq;
{
mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md;
mbuf_t nam = nfsd->nd_nam;
caddr_t dpos = nfsd->nd_dpos;
u_long *tl;
struct nfsv3_pathconf *pc;
long t1;
caddr_t bpos;
int error = 0, getret = 1, linkmax, namemax;
int chownres, notrunc, case_sensitive, case_preserving;
char *cp2;
mbuf_t mb, mb2, mreq;
vnode_t vp;
struct vnode_attr at;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
struct vfs_context context;
nfsm_srvmtofh(&nfh);
if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) {
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
if ((error = nfsrv_credcheck(nfsd, nx, nxo))) {
vnode_put(vp);
nfsm_reply(NFSX_UNSIGNED);
nfsm_srvpostop_attr(getret, &at);
return (0);
}
context.vc_proc = procp;
context.vc_ucred = nfsd->nd_cr;
error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, &context);
if (!error)
error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, &context);
if (!error)
error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, &context);
if (!error)
error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc, &context);
if (!error)
error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, &context);
if (!error)
error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, &context);
nfsm_srv_vattr_init(&at, 1);
getret = vnode_getattr(vp, &at, &context);
vnode_put(vp);
nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3PATHCONF);
nfsm_srvpostop_attr(getret, &at);
if (error)
return (0);
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);
pc->pc_caseinsensitive = txdr_unsigned(!case_sensitive);
pc->pc_casepreserving = txdr_unsigned(case_preserving);
nfsmout:
return (error);
}
int
nfsrv_null(
struct nfsrv_descript *nfsd,
struct nfssvc_sock *slp,
__unused proc_t procp,
mbuf_t *mrq)
{
mbuf_t mrep = nfsd->nd_mrep;
caddr_t bpos;
int error = NFSERR_RETVOID;
mbuf_t mb, mreq;
nfsm_reply(0);
nfsmout:
return (0);
}
int
nfsrv_noop(
struct nfsrv_descript *nfsd,
struct nfssvc_sock *slp,
__unused proc_t procp,
mbuf_t *mrq)
{
mbuf_t mrep = nfsd->nd_mrep;
caddr_t bpos;
int error;
mbuf_t mb, mreq;
if (nfsd->nd_repstat)
error = nfsd->nd_repstat;
else
error = EPROCUNAVAIL;
nfsm_reply(0);
nfsmout:
return (0);
}
static int
nfsrv_authorize(
vnode_t vp,
vnode_t dvp,
kauth_action_t action,
vfs_context_t context,
struct nfs_export_options *nxo,
int override)
{
struct vnode_attr vattr;
int error;
if (action & KAUTH_VNODE_WRITE_RIGHTS) {
if (nxo->nxo_flags & NX_READONLY) {
switch (vnode_vtype(vp)) {
case VREG: case VDIR: case VLNK: case VCPLX:
return (EROFS);
default:
break;
}
}
}
error = vnode_authorize(vp, dvp, action, context);
if (override && (error == EACCES)) {
VATTR_INIT(&vattr);
VATTR_WANTED(&vattr, va_uid);
if ((vnode_getattr(vp, &vattr, context) == 0) &&
(kauth_cred_getuid(vfs_context_ucred(context)) == vattr.va_uid))
error = 0;
}
return error;
}
#endif