X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/55e303ae13a4cf49d70f2294092726f2fffb9ef2..378393581903b274cb7a4d18e0d978071a6b592d:/bsd/nfs/nfs_serv.c diff --git a/bsd/nfs/nfs_serv.c b/bsd/nfs/nfs_serv.c index 505a9a75a..f6fc25fd4 100644 --- a/bsd/nfs/nfs_serv.c +++ b/bsd/nfs/nfs_serv.c @@ -1,24 +1,21 @@ /* - * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This Original Code and all software distributed under the License are + * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -71,7 +68,7 @@ * 3 - build the rpc reply in an mbuf list * nb: * - 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 + * on a bad rpc or similar and do not do any vnode_rele()s or vnode_put()s * * - the nfsm_reply() macro generates an nfs rpc reply with the nfs * error number iff error != 0 whereas @@ -85,30 +82,31 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include #include -#include +#include #include #include #include #include #include +#include +#include +#include #include #include -#include #include #include #include #include #include -#include nfstype nfsv3_type[9] = { NFNON, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, NFSOCK, NFFIFO, NFNON }; @@ -130,10 +128,11 @@ int nfs_async = 0; SYSCTL_INT(_vfs_nfs, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, ""); #endif -static int nfsrv_access __P((struct vnode *,int,struct ucred *,int, - struct proc *, int)); -static void nfsrvw_coalesce __P((struct nfsrv_descript *, - struct nfsrv_descript *)); +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) /* * nfs v3 access service @@ -142,64 +141,125 @@ int nfsrv3_access(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - register u_long *tl; - register long t1; + vnode_t vp; + struct nfs_filehandle nfh; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, getret; + int error = 0, getret; char *cp2; - struct mbuf *mb, *mreq, *mb2; - struct vattr vattr, *vap = &vattr; - u_long testmode, nfsmode; - u_quad_t frev; + 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; -#ifndef nolint - cache = 0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + if ((error = nfsrv_fhtovp(&nfh, nam, TRUE, &vp, &nx, &nxo))) { nfsm_reply(NFSX_UNSIGNED); - nfsm_srvpostop_attr(1, (struct vattr *)0); + 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); - if ((nfsmode & NFSV3ACCESS_READ) && - nfsrv_access(vp, VREAD, cred, rdonly, procp, 0)) - nfsmode &= ~NFSV3ACCESS_READ; - if (vp->v_type == VDIR) - testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND | - NFSV3ACCESS_DELETE); - else - testmode = (NFSV3ACCESS_MODIFY | NFSV3ACCESS_EXTEND); - if ((nfsmode & testmode) && - nfsrv_access(vp, VWRITE, cred, rdonly, procp, 0)) - nfsmode &= ~testmode; - if (vp->v_type == VDIR) - testmode = NFSV3ACCESS_LOOKUP; - else - testmode = NFSV3ACCESS_EXECUTE; - if ((nfsmode & testmode) && - nfsrv_access(vp, VEXEC, cred, rdonly, procp, 0)) - nfsmode &= ~testmode; - getret = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); + + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + + /* + * Each NFS mode bit is tested separately. + * + * XXX this code is nominally correct, but returns a pessimistic + * rather than optimistic result. It will be necessary to add + * an NFS-specific interface to the vnode_authorize code to + * obtain good performance in the optimistic mode. + */ + 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 | + KAUTH_VNODE_WRITE_ATTRIBUTES | + KAUTH_VNODE_WRITE_EXTATTRIBUTES | + KAUTH_VNODE_WRITE_SECURITY; + } + 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; + } + + /* + * Note concerning NFSV3ACCESS_DELETE: + * For hard links, the answer may be wrong if the vnode + * has multiple parents with different permissions. + * Also, some clients (e.g. MacOSX 10.3) may incorrectly + * interpret the missing/cleared DELETE bit. + * So we'll just leave the DELETE bit alone. At worst, + * we're telling the client it might be able to do + * something it really can't. + */ + + 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); - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -209,43 +269,51 @@ int nfsrv_getattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct nfs_fattr *fp; - struct vattr va; - register struct vattr *vap = &va; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - register u_long *tl; - register long t1; + 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, rdonly, cache; + int error = 0; char *cp2; - struct mbuf *mb, *mb2, *mreq; - u_quad_t frev; + 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); - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - nqsrv_getl(vp, ND_READ); - error = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); - nfsm_reply(NFSX_FATTR(nfsd->nd_flag & ND_NFSV3)); + 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(nfsd->nd_flag & ND_NFSV3)); + nfsm_build(fp, struct nfs_fattr *, NFSX_FATTR(v3)); nfsm_srvfillattr(vap, fp); - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -255,33 +323,36 @@ int nfsrv_setattr(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vattr va, preat; - register struct vattr *vap = &va; - register struct nfsv2_sattr *sp; - register struct nfs_fattr *fp; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - register u_long *tl; - register long t1; + 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, rdonly, cache, preat_ret = 1, postat_ret = 1; + int error = 0, preat_ret = 1, postat_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3), gcheck = 0; char *cp2; - struct mbuf *mb, *mb2, *mreq; - u_quad_t frev; + mbuf_t mb, mb2, mreq; struct timespec guard; + struct vfs_context context; + kauth_action_t action; + uid_t saved_uid; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - VATTR_NULL(vap); + nfsm_srvmtofh(&nfh); + VATTR_INIT(vap); if (v3) { nfsm_srvsattr(vap); nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); @@ -300,83 +371,99 @@ nfsrv_setattr(nfsd, slp, procp, mrq) * --> 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); + VATTR_SET(vap, va_mode, nfstov_mode(sp->sa_mode)); if (sp->sa_uid != nfs_xdrneg1) - vap->va_uid = fxdr_unsigned(uid_t, sp->sa_uid); + VATTR_SET(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); + VATTR_SET(vap, va_gid, fxdr_unsigned(gid_t, sp->sa_gid)); if (sp->sa_size != nfs_xdrneg1) - vap->va_size = fxdr_unsigned(u_quad_t, sp->sa_size); + VATTR_SET(vap, va_data_size, fxdr_unsigned(u_quad_t, sp->sa_size)); if (sp->sa_atime.nfsv2_sec != nfs_xdrneg1) { -#ifdef notyet - fxdr_nfsv2time(&sp->sa_atime, &vap->va_atime); -#else - vap->va_atime.tv_sec = - fxdr_unsigned(long, sp->sa_atime.nfsv2_sec); - vap->va_atime.tv_nsec = 0; -#endif + 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); } - if (sp->sa_mtime.nfsv2_sec != nfs_xdrneg1) - fxdr_nfsv2time(&sp->sa_mtime, &vap->va_mtime); - } + /* + * Save the original credential UID in case they are + * mapped and we need to map the IDs in the attributes. + */ + saved_uid = kauth_cred_getuid(nfsd->nd_cr); + /* * Now that we have all the fields, lets do it. */ - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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, vap); + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat); return (0); } - nqsrv_getl(vp, ND_WRITE); + + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + if (v3) { - error = preat_ret = VOP_GETATTR(vp, &preat, cred, procp); - if (!error && gcheck && - (preat.va_ctime.tv_sec != guard.tv_sec || - preat.va_ctime.tv_nsec != guard.tv_nsec)) + 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) { - vput(vp); + vnode_put(vp); nfsm_reply(NFSX_WCCDATA(v3)); - nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat); return (0); } } /* - * If the size is being changed write acces is required, otherwise - * just check for a read only file system. + * If the credentials were mapped, we should + * map the same values in the attributes. */ - if (vap->va_size == ((u_quad_t)((quad_t) -1))) { - if (rdonly || (vp->v_mount->mnt_flag & MNT_RDONLY)) { - error = EROFS; - goto out; - } - } else { - if (vp->v_type == VDIR) { - error = EISDIR; - goto out; - } else if ((error = nfsrv_access(vp, VWRITE, cred, rdonly, - procp, 0))) - 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 = VOP_SETATTR(vp, vap, cred, procp); - postat_ret = VOP_GETATTR(vp, vap, cred, procp); + + /* + * Authorize the attribute changes. + */ + 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: - vput(vp); + vnode_put(vp); nfsm_reply(NFSX_WCCORFATTR(v3)); if (v3) { - nfsm_srvwcc_data(preat_ret, &preat, postat_ret, vap); + nfsm_srvwcc_data(preat_ret, &preat, postat_ret, &postat); return (0); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); - nfsm_srvfillattr(vap, fp); + nfsm_srvfillattr(&postat, fp); } - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -386,70 +473,73 @@ int nfsrv_lookup(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct nfs_fattr *fp; + struct nfs_fattr *fp; struct nameidata nd, *ndp = &nd; -#ifdef notdef +/* XXX Revisit when enabling WebNFS */ +#ifdef WEBNFS_ENABLED struct nameidata ind; #endif - struct vnode *vp, *dirp; - nfsfh_t nfh; - fhandle_t *fhp; - register caddr_t cp; - register u_long *tl; - register long t1; + 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, cache, len, dirattr_ret = 1; + int error = 0, len, dirattr_ret = 1, isdotdot; int v3 = (nfsd->nd_flag & ND_NFSV3), pubflag; char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vattr va, dirattr, *vap = &va; - u_quad_t frev; + mbuf_t mb, mb2, mreq; + struct vnode_attr va, dirattr, *vap = &va; + struct vfs_context context; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; - pubflag = nfs_ispublicfh(fhp); + nfsm_srvmtofh(&dnfh); + nfsm_srvnamesiz(len, v3); + + pubflag = nfs_ispublicfh(&dnfh); - nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = LOOKUP; - nd.ni_cnd.cn_flags = LOCKLEAF | SAVESTART; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), pubflag); + 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); -/* XXX CSM 12/4/97 Revisit when enabling WebNFS */ -#ifdef notyet +/* XXX Revisit when enabling WebNFS */ +#ifdef WEBNFS_ENABLED if (!error && pubflag) { - if (nd.ni_vp->v_type == VDIR && nfs_pub.np_index != NULL) { + if (vnode_vtype(nd.ni_vp) == VDIR && nfs_pub.np_index != NULL) { /* * Setup call to lookup() to see if we can find * the index file. Arguably, this doesn't belong * in a kernel.. Ugh. */ ind = nd; - VOP_UNLOCK(nd.ni_vp, 0, procp); 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; - VREF(ind.ni_startdir); - error = lookup(&ind); - if (!error) { + ind.ni_usedvp = nd.ni_vp; + + if (!(error = lookup(&ind))) { /* * Found an index file. Get rid of * the old references. */ if (dirp) - vrele(dirp); + vnode_put(dirp); dirp = nd.ni_vp; - vrele(nd.ni_startdir); + vnode_put(nd.ni_startdir); ndp = &ind; } else error = 0; @@ -460,18 +550,20 @@ nfsrv_lookup(nfsd, slp, procp, mrq) * filesystem. */ - if (!error && ndp->ni_vp->v_mount != nfs_pub.np_mount) { - vput(nd.ni_vp); + 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) - dirattr_ret = VOP_GETATTR(dirp, &dirattr, cred, - procp); - vrele(dirp); + if (v3) { + nfsm_srv_vattr_init(&dirattr, v3); + dirattr_ret = vnode_getattr(dirp, &dirattr, &context); + } + vnode_put(dirp); } if (error) { @@ -479,24 +571,21 @@ nfsrv_lookup(nfsd, slp, procp, mrq) nfsm_srvpostop_attr(dirattr_ret, &dirattr); return (0); } + nameidone(&nd); - nqsrv_getl(ndp->ni_startdir, ND_READ); - vrele(ndp->ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; vp = ndp->ni_vp; - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (!error) - error = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); - nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPORFATTR(v3) + NFSX_POSTOPATTR(v3)); + 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(fhp, v3); + nfsm_srvfhtom(&nfh, v3); if (v3) { nfsm_srvpostop_attr(0, vap); nfsm_srvpostop_attr(dirattr_ret, &dirattr); @@ -504,7 +593,8 @@ nfsrv_lookup(nfsd, slp, procp, mrq) nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -514,100 +604,160 @@ int nfsrv_readlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct iovec iv[(NFS_MAXPATHLEN+MLEN-1)/MLEN]; - register struct iovec *ivp = iv; - register struct mbuf *mp; - register u_long *tl; - register long t1; + mbuf_t mp; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, i, tlen, len, getret; + int error = 0, i, tlen, len, getret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mb2, *mp2, *mp3, *mreq; - struct vnode *vp; - struct vattr attr; - nfsfh_t nfh; - fhandle_t *fhp; - struct uio io, *uiop = &io; - u_quad_t frev; + 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; -#ifndef nolint - mp2 = mp3 = (struct mbuf *)0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); len = 0; i = 0; + + mp2 = mp3 = NULL; + vp = NULL; while (len < NFS_MAXPATHLEN) { - MGET(mp, M_WAIT, MT_DATA); - MCLGET(mp, M_WAIT); - mp->m_len = NFSMSIZ(mp); + 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 { - mp2->m_next = mp; + if ((error = mbuf_setnext(mp2, mp))) { + mbuf_free(mp); + goto out; + } mp2 = mp; } - if ((len+mp->m_len) > NFS_MAXPATHLEN) { - mp->m_len = NFS_MAXPATHLEN-len; + if ((len + mblen) > NFS_MAXPATHLEN) { + mbuf_setlen(mp, NFS_MAXPATHLEN - len); len = NFS_MAXPATHLEN; } else - len += mp->m_len; - ivp->iov_base = mtod(mp, caddr_t); - ivp->iov_len = mp->m_len; - i++; - ivp++; - } - uiop->uio_iov = iv; - uiop->uio_iovcnt = i; - uiop->uio_offset = 0; - uiop->uio_resid = len; - uiop->uio_rw = UIO_READ; - uiop->uio_segflg = UIO_SYSSPACE; - uiop->uio_procp = (struct proc *)0; - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { - m_freem(mp3); + 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, (struct vattr *)0); + nfsm_srvpostop_attr(1, NULL); return (0); } - if (vp->v_type != VLNK) { + if (vnode_vtype(vp) != VLNK) { if (v3) error = EINVAL; else error = ENXIO; goto out; } - nqsrv_getl(vp, ND_READ); - error = VOP_READLINK(vp, uiop, cred); + + 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: - getret = VOP_GETATTR(vp, &attr, cred, procp); - vput(vp); - if (error) - m_freem(mp3); + 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 (error) { + if (uio_bufp != &uio_buf[0]) + FREE(uio_bufp, M_TEMP); return (0); + } } - if (uiop->uio_resid > 0) { - len -= uiop->uio_resid; - tlen = nfsm_rndup(len); - nfsm_adj(mp3, NFS_MAXPATHLEN-tlen, tlen-len); + if (!error) { + if (uiop && (uio_resid(uiop) > 0)) { + // LP64todo - fix this + 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); } - nfsm_build(tl, u_long *, NFSX_UNSIGNED); - *tl = txdr_unsigned(len); - mb->m_next = mp3; - nfsm_srvdone; +nfsmout: + if (uio_bufp != &uio_buf[0]) + FREE(uio_bufp, M_TEMP); + return (error); } /* @@ -617,37 +767,35 @@ int nfsrv_read(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct iovec *iv; - struct iovec *iv2; - register struct mbuf *m; - register struct nfs_fattr *fp; - register u_long *tl; - register long t1; - register int i; + mbuf_t m; + struct nfs_fattr *fp; + u_long *tl; + long t1; + int i; caddr_t bpos; - int error = 0, rdonly, cache, cnt, len, left, siz, tlen, getret; + int error = 0, count, len, left, siz, tlen, getret; int v3 = (nfsd->nd_flag & ND_NFSV3), reqlen; char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct mbuf *m2; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - struct uio io, *uiop = &io; - struct vattr va, *vap = &va; + 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; - u_quad_t frev; - int didhold = 0; + char uio_buf[ UIO_SIZEOF(0) ]; + struct vfs_context context; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); if (v3) { nfsm_dissect(tl, u_long *, 2 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); @@ -656,39 +804,48 @@ nfsrv_read(nfsd, slp, procp, mrq) off = (off_t)fxdr_unsigned(u_long, *tl); } nfsm_srvstrsiz(reqlen, NFS_SRVMAXDATA(nfsd)); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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, (struct vattr *)0); + nfsm_srvpostop_attr(1, NULL); return (0); } - if (vp->v_type != VREG) { + if (vnode_vtype(vp) != VREG) { if (v3) error = EINVAL; else - error = (vp->v_type == VDIR) ? EISDIR : EACCES; + error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; } + + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + if (!error) { - nqsrv_getl(vp, ND_READ); - if ((error = nfsrv_access(vp, VREAD, cred, rdonly, procp, 1))) - error = nfsrv_access(vp, VEXEC, cred, rdonly, procp, 1); + if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, &context, nxo, 1))) + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, &context, nxo, 1); } - getret = VOP_GETATTR(vp, vap, cred, procp); + nfsm_srv_vattr_init(vap, v3); + getret = vnode_getattr(vp, vap, &context); if (!error) error = getret; if (error) { - vput(vp); + vnode_put(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, vap); return (0); } - if (off >= vap->va_size) - cnt = 0; - else if ((off + reqlen) > vap->va_size) - cnt = nfsm_rndup(vap->va_size - off); + 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 - cnt = reqlen; - nfsm_reply(NFSX_POSTOPORFATTR(v3) + 3 * NFSX_UNSIGNED+nfsm_rndup(cnt)); + 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; @@ -699,86 +856,95 @@ nfsrv_read(nfsd, slp, procp, mrq) fp = (struct nfs_fattr *)tl; tl += (NFSX_V2FATTR / sizeof (u_long)); } - len = left = cnt; - if (cnt > 0) { + len = left = count; + if (count > 0) { /* * Generate the mbuf list with the uio_iov ref. to it. */ i = 0; m = m2 = mb; while (left > 0) { - siz = min(M_TRAILINGSPACE(m), left); + siz = min(mbuf_trailingspace(m), left); if (siz > 0) { left -= siz; i++; } if (left > 0) { - MGET(m, M_WAIT, MT_DATA); - MCLGET(m, M_WAIT); - m->m_len = 0; - m2->m_next = m; + m = NULL; + if ((error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, &m))) + goto errorexit; + mbuf_setnext(m2, m); m2 = m; } } - MALLOC(iv, struct iovec *, i * sizeof (struct iovec), - M_TEMP, M_WAITOK); - uiop->uio_iov = iv2 = iv; + 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 = cnt; + left = count; i = 0; while (left > 0) { if (m == NULL) panic("nfsrv_read iov"); - siz = min(M_TRAILINGSPACE(m), left); + siz = min(mbuf_trailingspace(m), left); if (siz > 0) { - iv->iov_base = mtod(m, caddr_t) + m->m_len; - iv->iov_len = siz; - m->m_len += siz; + tlen = mbuf_len(m); + uio_addiov(uiop, CAST_USER_ADDR_T((char *)mbuf_data(m) + tlen), siz); + mbuf_setlen(m, tlen + siz); left -= siz; - iv++; i++; } - m = m->m_next; - } - uiop->uio_iovcnt = i; - uiop->uio_offset = off; - uiop->uio_resid = cnt; - uiop->uio_rw = UIO_READ; - uiop->uio_segflg = UIO_SYSSPACE; - didhold = ubc_hold(vp); - error = VOP_READ(vp, uiop, IO_NODELOCKED, cred); - off = uiop->uio_offset; - FREE((caddr_t)iv2, M_TEMP); - /* Though our code replaces error with getret, the way I read - * the v3 spec, it appears you should leave the error alone, but - * still return vap and not assign error = getret. But leaving - * that alone. m_freem(mreq) looks bogus. Taking it out. Should be - * mrep or not there at all. Causes panic. ekn */ - if (error || (getret = VOP_GETATTR(vp, vap, cred, procp))) { - VOP_UNLOCK(vp, 0, procp); - if (didhold) - ubc_rele(vp); + m = mbuf_next(m); + } + error = VNOP_READ(vp, uiop, IO_NODELOCKED, &context); + off = uio_offset(uiop); +errorexit: + /* + * This may seem a little weird that we drop the whole + * successful read if we get an error on the getattr. + * The reason is because we've already set up the reply + * to have postop attrs and omitting these optional bits + * would require shifting all the data in the reply. + * + * It would be more correct if we would simply drop the + * postop attrs if the getattr fails. We might be able to + * do that easier if we allocated separate mbufs for the data. + */ + nfsm_srv_vattr_init(vap, v3); + if (error || (getret = vnode_getattr(vp, vap, &context))) { if (!error) error = getret; - /* m_freem(mreq);*/ - vrele(vp); + 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); } - VOP_UNLOCK(vp, 0, procp); - if (didhold) - ubc_rele(vp); - vrele(vp); } else { - uiop->uio_resid = 0; - vput(vp); + 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 -= uiop->uio_resid; + // LP64todo - fix this + len -= uio_resid(uiop); tlen = nfsm_rndup(len); - if (cnt != tlen || tlen != len) - nfsm_adj(mb, cnt - tlen, tlen - len); + if (count != tlen || tlen != len) + nfsm_adj(mb, count - tlen, tlen - len); if (v3) { *tl++ = txdr_unsigned(len); if (len < reqlen) @@ -787,7 +953,11 @@ nfsrv_read(nfsd, slp, procp, mrq) *tl++ = nfs_false; } *tl = txdr_unsigned(len); - nfsm_srvdone; +nfsmout: + if (uio_bufp != NULL) { + FREE(uio_bufp, M_TEMP); + } + return (error); } /* @@ -797,43 +967,40 @@ int nfsrv_write(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct iovec *ivp; - register int i, cnt; - register struct mbuf *mp; - register struct nfs_fattr *fp; - struct iovec *iv; - struct vattr va, forat; - register struct vattr *vap = &va; - register u_long *tl; - register long t1; - caddr_t bpos; - int error = 0, rdonly, cache, len, forat_ret = 1; - int ioflags, aftat_ret = 1, retlen, zeroing, adjust; + 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; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - struct uio io, *uiop = &io; + 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; - u_quad_t frev; - int didhold = 0; + char *uio_bufp = NULL; + struct vfs_context context; if (mrep == NULL) { *mrq = NULL; return (0); } - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &off); @@ -847,7 +1014,7 @@ nfsrv_write(nfsd, slp, procp, mrq) stable = NFSV3WRITE_UNSTABLE; } retlen = len = fxdr_unsigned(long, *tl); - cnt = i = 0; + count = i = 0; /* * For NFS Version 2, it is not obvious what a write of zero length @@ -860,23 +1027,32 @@ nfsrv_write(nfsd, slp, procp, mrq) while (mp) { if (mp == md) { zeroing = 0; - adjust = dpos - mtod(mp, caddr_t); - mp->m_len -= adjust; - if (mp->m_len > 0 && adjust > 0) - NFSMADV(mp, adjust); + 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) - mp->m_len = 0; - else if (mp->m_len > 0) { - i += mp->m_len; + mbuf_setlen(mp, 0); + else if ((tlen = mbuf_len(mp)) > 0) { + i += tlen; if (i > len) { - mp->m_len -= (i - len); + mbuf_setlen(mp, tlen - (i - len)); zeroing = 1; } - if (mp->m_len > 0) - cnt++; + if (mbuf_len(mp) > 0) + count++; } - mp = mp->m_next; + mp = mbuf_next(mp); } } if (len > NFS_MAXDATA || len < 0 || i < len) { @@ -885,44 +1061,65 @@ nfsrv_write(nfsd, slp, procp, mrq) nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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 (v3) - forat_ret = VOP_GETATTR(vp, &forat, cred, procp); - if (vp->v_type != VREG) { + 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 = (vp->v_type == VDIR) ? EISDIR : EACCES; + error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; } if (!error) { - nqsrv_getl(vp, ND_WRITE); - error = nfsrv_access(vp, VWRITE, cred, rdonly, procp, 1); + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1); } if (error) { - vput(vp); + vnode_put(vp); nfsm_reply(NFSX_WCCDATA(v3)); nfsm_srvwcc_data(forat_ret, &forat, aftat_ret, vap); return (0); } if (len > 0) { - MALLOC(ivp, struct iovec *, cnt * sizeof (struct iovec), M_TEMP, - M_WAITOK); - uiop->uio_iov = iv = ivp; - uiop->uio_iovcnt = cnt; + 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 (mp->m_len > 0) { - ivp->iov_base = mtod(mp, caddr_t); - ivp->iov_len = mp->m_len; - ivp++; - } - mp = mp->m_next; + if ((tlen = mbuf_len(mp)) > 0) + uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), tlen); + mp = mbuf_next(mp); } /* @@ -938,29 +1135,25 @@ nfsrv_write(nfsd, slp, procp, mrq) ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); - uiop->uio_resid = len; - uiop->uio_rw = UIO_WRITE; - uiop->uio_segflg = UIO_SYSSPACE; - uiop->uio_procp = (struct proc *)0; - uiop->uio_offset = off; - didhold = ubc_hold(vp); - error = VOP_WRITE(vp, uiop, ioflags, cred); - nfsstats.srvvop_writes++; - FREE((caddr_t)iv, M_TEMP); - } - aftat_ret = VOP_GETATTR(vp, vap, cred, procp); - VOP_UNLOCK(vp, 0, procp); - if (didhold) - ubc_rele(vp); - vrele(vp); + + 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 (error) { + if (uio_bufp != NULL) { + FREE(uio_bufp, M_TEMP); + } return (0); + } nfsm_build(tl, u_long *, 4 * NFSX_UNSIGNED); *tl++ = txdr_unsigned(retlen); /* @@ -975,13 +1168,17 @@ nfsrv_write(nfsd, slp, procp, mrq) * but it may make the values more human readable, * for debugging purposes. */ - *tl++ = txdr_unsigned(boottime.tv_sec); - *tl = txdr_unsigned(boottime.tv_usec); + *tl++ = txdr_unsigned(boottime_sec()); + *tl = txdr_unsigned(0); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(vap, fp); } - nfsm_srvdone; +nfsmout: + if (uio_bufp != NULL) { + FREE(uio_bufp, M_TEMP); + } + return (error); } /* @@ -995,35 +1192,39 @@ int nfsrv_writegather(ndp, slp, procp, mrq) struct nfsrv_descript **ndp; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - register struct iovec *ivp; - register struct mbuf *mp; - register struct nfsrv_descript *wp, *nfsd, *owp, *swp; - register struct nfs_fattr *fp; - register int i; - struct iovec *iov; + 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; - struct ucred *cred; - struct vattr va, forat; - register u_long *tl; - register long t1; - caddr_t bpos, dpos; - int error = 0, rdonly, cache, len, forat_ret = 1; - int ioflags, aftat_ret = 1, s, adjust, v3, zeroing; + 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; - struct mbuf *mb, *mb2, *mreq, *mrep, *md; - struct vnode *vp; - struct uio io, *uiop = &io; - u_quad_t frev, cur_usec; - int didhold; + 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; @@ -1031,7 +1232,8 @@ nfsrv_writegather(ndp, slp, procp, mrq) mrep = nfsd->nd_mrep; md = nfsd->nd_md; dpos = nfsd->nd_dpos; - cred = &nfsd->nd_cr; + cred = nfsd->nd_cr; + context.vc_ucred = cred; v3 = (nfsd->nd_flag & ND_NFSV3); LIST_INIT(&nfsd->nd_coalesce); nfsd->nd_mreq = NULL; @@ -1045,6 +1247,7 @@ nfsrv_writegather(ndp, slp, procp, mrq) * Now, get the write header.. */ nfsm_srvmtofh(&nfsd->nd_fh); + /* XXX shouldn't we be checking for invalid FHs before doing any more work? */ if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &nfsd->nd_off); @@ -1071,61 +1274,67 @@ nfsrv_writegather(ndp, slp, procp, mrq) while (mp) { if (mp == md) { zeroing = 0; - adjust = dpos - mtod(mp, caddr_t); - mp->m_len -= adjust; - if (mp->m_len > 0 && adjust > 0) - NFSMADV(mp, adjust); + 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) - mp->m_len = 0; + mbuf_setlen(mp, 0); else { - i += mp->m_len; + tlen = mbuf_len(mp); + i += tlen; if (i > len) { - mp->m_len -= (i - len); + mbuf_setlen(mp, tlen - (i - len)); zeroing = 1; } } - mp = mp->m_next; + mp = mbuf_next(mp); } if (len > NFS_MAXDATA || len < 0 || i < len) { nfsmout: - m_freem(mrep); + 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 = 0; + nfsd->nd_time = 1; } /* * Add this entry to the hash and time queues. */ - s = splsoftclock(); + 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; } - NFS_DPF(WG, ("Q%03x", nfsd->nd_retxid & 0xfff)); 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.fh_fid.fid_data); + wpp = NWDELAYHASH(slp, nfsd->nd_fh.nfh_fid); owp = NULL; wp = wpp->lh_first; - while (wp && - bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { + 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 && - !bcmp((caddr_t)&nfsd->nd_fh,(caddr_t)&wp->nd_fh,NFSX_V3FH)) { + while (wp && (wp->nd_off < nfsd->nd_off) && + nfsrv_fhmatch(&nfsd->nd_fh, &wp->nd_fh)) { owp = wp; wp = wp->nd_hash.le_next; } @@ -1145,48 +1354,52 @@ nfsmout: LIST_INSERT_HEAD(wpp, nfsd, nd_hash); } } - splx(s); + } else { + lck_mtx_lock(&slp->ns_wgmutex); } /* - * Now, do VOP_WRITE()s for any one(s) that need to be done now + * Now, do VNOP_WRITE()s for any one(s) that need to be done now * and generate the associated reply mbuf list(s). */ loop1: microuptime(&now); cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec; - s = splsoftclock(); 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; - NFS_DPF(WG, ("P%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); LIST_REMOVE(nfsd, nd_hash); - splx(s); mrep = nfsd->nd_mrep; nfsd->nd_mrep = NULL; - cred = &nfsd->nd_cr; v3 = (nfsd->nd_flag & ND_NFSV3); forat_ret = aftat_ret = 1; - error = nfsrv_fhtovp(&nfsd->nd_fh, 1, &vp, cred, slp, - nfsd->nd_nam, &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE); + 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) - forat_ret = VOP_GETATTR(vp, &forat, cred, procp); - if (vp->v_type != VREG) { + 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 = (vp->v_type == VDIR) ? EISDIR : EACCES; + error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; } } else vp = NULL; if (!error) { - nqsrv_getl(vp, ND_WRITE); - error = nfsrv_access(vp, VWRITE, cred, rdonly, procp, 1); + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context, nxo, 1); } if (nfsd->nd_stable == NFSV3WRITE_UNSTABLE) @@ -1195,47 +1408,43 @@ loop1: ioflags = (IO_SYNC | IO_NODELOCKED); else ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); - uiop->uio_rw = UIO_WRITE; - uiop->uio_segflg = UIO_SYSSPACE; - uiop->uio_procp = (struct proc *)0; - uiop->uio_offset = nfsd->nd_off; - uiop->uio_resid = nfsd->nd_eoff - nfsd->nd_off; - didhold = 0; - if (uiop->uio_resid > 0) { + + if (!error && ((nfsd->nd_eoff - nfsd->nd_off) > 0)) { mp = mrep; i = 0; while (mp) { - if (mp->m_len > 0) + if (mbuf_len(mp) > 0) i++; - mp = mp->m_next; + mp = mbuf_next(mp); } - uiop->uio_iovcnt = i; - MALLOC(iov, struct iovec *, i * sizeof (struct iovec), - M_TEMP, M_WAITOK); - uiop->uio_iov = ivp = iov; - mp = mrep; - while (mp) { - if (mp->m_len > 0) { - ivp->iov_base = mtod(mp, caddr_t); - ivp->iov_len = mp->m_len; - ivp++; + + 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); } - mp = mp->m_next; + error = VNOP_WRITE(vp, uiop, ioflags, &context); + OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes); } - if (!error) { - didhold = ubc_hold(vp); - error = VOP_WRITE(vp, uiop, ioflags, cred); - nfsstats.srvvop_writes++; + if (uio_bufp) { + FREE(uio_bufp, M_TEMP); + uio_bufp = NULL; } - FREE((caddr_t)iov, M_TEMP); } - m_freem(mrep); + mbuf_freem(mrep); + mrep = NULL; if (vp) { - aftat_ret = VOP_GETATTR(vp, &va, cred, procp); - VOP_UNLOCK(vp, 0, procp); - if (didhold) - ubc_rele(vp); - vrele(vp); + nfsm_srv_pre_vattr_init(&va, v3); + aftat_ret = vnode_getattr(vp, &va, &context); + vnode_put(vp); } /* @@ -1244,7 +1453,6 @@ loop1: */ swp = nfsd; do { - NFS_DPF(WG, ("R%03x", nfsd->nd_retxid & 0xfff)); if (error) { nfsm_writereply(NFSX_WCCDATA(v3), v3); if (v3) { @@ -1264,8 +1472,8 @@ loop1: * but it may make the values more human readable, * for debugging purposes. */ - *tl++ = txdr_unsigned(boottime.tv_sec); - *tl = txdr_unsigned(boottime.tv_usec); + *tl++ = txdr_unsigned(boottime_sec()); + *tl = txdr_unsigned(0); } else { nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); nfsm_srvfillattr(&va, fp); @@ -1279,38 +1487,32 @@ loop1: * Done. Put it at the head of the timer queue so that * the final phase can return the reply. */ - s = splsoftclock(); if (nfsd != swp) { - nfsd->nd_time = 0; + 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); } - splx(s); } while (nfsd); - s = splsoftclock(); - swp->nd_time = 0; + swp->nd_time = 1; LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq); - splx(s); goto loop1; } - splx(s); /* * Search for a reply to return. */ - s = splsoftclock(); for (nfsd = slp->ns_tq.lh_first; nfsd; nfsd = nfsd->nd_tq.le_next) if (nfsd->nd_mreq) { - NFS_DPF(WG, ("X%03x", nfsd->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_tq); *mrq = nfsd->nd_mreq; *ndp = nfsd; break; } - splx(s); + slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0; + lck_mtx_unlock(&slp->ns_wgmutex); return (0); } @@ -1320,19 +1522,16 @@ loop1: * - merge nfsd->nd_mrep into owp->nd_mrep * - update the nd_eoff and nd_stable for owp * - put nfsd on owp's nd_coalesce list - * NB: Must be called at splsoftclock(). */ static void -nfsrvw_coalesce(owp, nfsd) - register struct nfsrv_descript *owp; - register struct nfsrv_descript *nfsd; +nfsrvw_coalesce( + struct nfsrv_descript *owp, + struct nfsrv_descript *nfsd) { - register int overlap; - register struct mbuf *mp; + int overlap, error; + mbuf_t mp, mpnext; struct nfsrv_descript *p; - NFS_DPF(WG, ("C%03x-%03x", - nfsd->nd_retxid & 0xfff, owp->nd_retxid & 0xfff)); LIST_REMOVE(nfsd, nd_hash); LIST_REMOVE(nfsd, nd_tq); if (owp->nd_eoff < nfsd->nd_eoff) { @@ -1340,14 +1539,17 @@ nfsrvw_coalesce(owp, nfsd) if (overlap < 0) panic("nfsrv_coalesce: bad off"); if (overlap > 0) - m_adj(nfsd->nd_mrep, overlap); + mbuf_adj(nfsd->nd_mrep, overlap); mp = owp->nd_mrep; - while (mp->m_next) - mp = mp->m_next; - mp->m_next = nfsd->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 - m_freem(nfsd->nd_mrep); + } else { + mbuf_freem(nfsd->nd_mrep); + } nfsd->nd_mrep = NULL; if (nfsd->nd_stable == NFSV3WRITE_FILESYNC) owp->nd_stable = NFSV3WRITE_FILESYNC; @@ -1371,13 +1573,15 @@ nfsrvw_coalesce(owp, nfsd) * Sort the group list in increasing numerical order. * (Insertion sort by Chris Torek, who was grossed out by the bubble sort * that used to be here.) + * + * XXX ILLEGAL */ void nfsrvw_sort(list, num) - register gid_t *list; - register int num; + gid_t *list; + int num; { - register int i, j; + int i, j; gid_t v; /* Insertion sort. */ @@ -1392,16 +1596,17 @@ nfsrvw_sort(list, num) /* * copy credentials making sure that the result can be compared with bcmp(). + * + * XXX ILLEGAL */ void -nfsrv_setcred(incred, outcred) - register struct ucred *incred, *outcred; +nfsrv_setcred(kauth_cred_t incred, kauth_cred_t outcred) { - register int i; + int i; - bzero((caddr_t)outcred, sizeof (struct ucred)); + bzero((caddr_t)outcred, sizeof (*outcred)); outcred->cr_ref = 1; - outcred->cr_uid = incred->cr_uid; + 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]; @@ -1416,67 +1621,85 @@ int nfsrv_create(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct nfs_fattr *fp; - struct vattr va, dirfor, diraft; - register struct vattr *vap = &va; - register struct nfsv2_sattr *sp; - register u_long *tl; + 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; - register caddr_t cp; - register long t1; + caddr_t cp; + long t1; caddr_t bpos; - int error = 0, rdev, cache, len, tsize, dirfor_ret = 1, diraft_ret = 1; + 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; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp, *dirp = (struct vnode *)0; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev, tempsize; + 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; + + /* + * Save the original credential UID in case they are + * mapped and we need to map the IDs in the attributes. + */ + saved_uid = kauth_cred_getuid(nfsd->nd_cr); #ifndef nolint rdev = 0; #endif nd.ni_cnd.cn_nameiop = 0; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; + vp = dvp = NULL; + nfsm_srvmtofh(&nfh); + nfsm_srvnamesiz(len, v3); + nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else { - vrele(dirp); - dirp = (struct vnode *)0; + 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) - vrele(dirp); + vnode_put(dirp); return (0); } - VATTR_NULL(vap); + 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 (nd.ni_vp) { + if (vp) { error = EEXIST; break; } @@ -1487,179 +1710,229 @@ nfsrv_create(nfsd, slp, procp, mrq) nfsm_dissect(cp, caddr_t, NFSX_V3CREATEVERF); bcopy(cp, cverf, NFSX_V3CREATEVERF); exclusive_flag = 1; - if (nd.ni_vp == NULL) - vap->va_mode = 0; + if (vp == NULL) + VATTR_SET(vap, va_mode, 0); break; }; - vap->va_type = VREG; + VATTR_SET(vap, va_type, VREG); } else { + enum vtype v_type; + nfsm_dissect(sp, struct nfsv2_sattr *, NFSX_V2SATTR); - vap->va_type = IFTOVT(fxdr_unsigned(u_long, sp->sa_mode)); - if (vap->va_type == VNON) - vap->va_type = VREG; - vap->va_mode = nfstov_mode(sp->sa_mode); - switch (vap->va_type) { + 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) - vap->va_size = (u_quad_t)tsize; + 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; }; } /* - * Iff doesn't exist, create it + * If it doesn't exist, create it * otherwise just truncate to 0 length * should I set the mode too ?? */ - if (nd.ni_vp == NULL) { + if (vp == NULL) { + kauth_acl_t xacl = NULL; + + /* + * If the credentials were mapped, we should + * map the same values in the attributes. + */ + 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)); + } + + /* authorize before creating */ + error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0); + + /* construct ACL and handle inheritance */ + if (!error) { + error = kauth_acl_inherit(dvp, + NULL, + &xacl, + 0 /* !isdir */, + &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); + + /* validate new-file security information */ + if (!error) { + error = vnode_authattr_new(dvp, vap, 0, &context); + if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) { + /* + * Most NFS servers just ignore the UID/GID attributes, so we + * try ignoring them if that'll help the request succeed. + */ + 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) { - vrele(nd.ni_startdir); - nqsrv_getl(nd.ni_dvp, ND_WRITE); - error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); + + if (!error) + error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context); + + if (!error && !VATTR_ALL_SUPPORTED(vap)) + /* + * If some of the requested attributes weren't handled by the VNOP, + * use our fallback code. + */ + error = vnode_setattr_fallback(vp, vap, &context); + + if (xacl != NULL) + kauth_acl_free(xacl); + if (!error) { - nfsrv_object_create(nd.ni_vp); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; if (exclusive_flag) { exclusive_flag = 0; - VATTR_NULL(vap); - bcopy(cverf, (caddr_t)&vap->va_atime, + VATTR_INIT(vap); + bcopy(cverf, (caddr_t)&vap->va_access_time, NFSX_V3CREATEVERF); - error = VOP_SETATTR(nd.ni_vp, vap, cred, - procp); + VATTR_SET_ACTIVE(vap, va_access_time); + // skip authorization, as this is an + // NFS internal implementation detail. + 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 == 0xffffffff) - 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(cred, (u_short *)0))) { - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); + (error = suser(nfsd->nd_cr, (u_short *)0))) { nfsm_reply(0); - return (error); } else - vap->va_rdev = (dev_t)rdev; - nqsrv_getl(nd.ni_dvp, ND_WRITE); - if ((error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))) { - vrele(nd.ni_startdir); + 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 | SAVESTART); - nd.ni_cnd.cn_proc = procp; - nd.ni_cnd.cn_cred = cred; - if ((error = lookup(&nd))) { - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - nfsm_reply(0); + 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; } - nfsrv_object_create(nd.ni_vp); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - if (nd.ni_cnd.cn_flags & ISSYMLINK) { - vrele(nd.ni_dvp); - vput(nd.ni_vp); - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - error = EINVAL; + if (error) nfsm_reply(0); - } } else { - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); error = ENXIO; } - vp = nd.ni_vp; + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + nd.ni_cnd.cn_nameiop = 0; + + vnode_put(dvp); } else { - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - vp = nd.ni_vp; - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (vap->va_size != -1) { - error = nfsrv_access(vp, VWRITE, cred, - (nd.ni_cnd.cn_flags & RDONLY), procp, 0); + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + 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) { - nqsrv_getl(vp, ND_WRITE); - tempsize = vap->va_size; - VATTR_NULL(vap); - vap->va_size = tempsize; - error = VOP_SETATTR(vp, vap, cred, - procp); + tempsize = vap->va_data_size; + VATTR_INIT(vap); + VATTR_SET(vap, va_data_size, tempsize); + error = vnode_setattr(vp, vap, &context); } - if (error) - vput(vp); - } else { - if (error) - vput(vp); /* make sure we catch the EEXIST for nfsv3 */ } } if (!error) { - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (!error) - error = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); + 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)&vap->va_atime, NFSX_V3CREATEVERF)) + bcmp(cverf, (caddr_t)&postat.va_access_time, NFSX_V3CREATEVERF)) error = EEXIST; - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); + nfsm_srv_vattr_init(&diraft, v3); + diraft_ret = vnode_getattr(dirp, &diraft, &context); + vnode_put(dirp); + dirp = NULL; } - nfsm_reply(NFSX_SRVFH(v3) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3)); + nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_FATTR(v3) + NFSX_WCCDATA(v3)); + if (v3) { if (!error) { - nfsm_srvpostop_fh(fhp); - nfsm_srvpostop_attr(0, vap); + nfsm_srvpostop_fh(&nfh); + nfsm_srvpostop_attr(0, &postat); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { - nfsm_srvfhtom(fhp, v3); + nfsm_srvfhtom(&nfh, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); - nfsm_srvfillattr(vap, fp); + nfsm_srvfillattr(&postat, fp); } return (0); nfsmout: - if (dirp) - vrele(dirp); if (nd.ni_cnd.cn_nameiop) { - vrele(nd.ni_startdir); - FREE_ZONE((caddr_t)nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - } - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vput(nd.ni_vp); + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + if (vp) + vnode_put(vp); + vnode_put(dvp); + } + if (dirp) + vnode_put(dirp); return (error); } @@ -1670,156 +1943,218 @@ int nfsrv_mknod(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vattr va, dirfor, diraft; - register struct vattr *vap = &va; - register u_long *tl; + struct vnode_attr dirfor, diraft, postat; + struct vnode_attr va; + struct vnode_attr *vap = &va; + u_long *tl; struct nameidata nd; - register long t1; + long t1; caddr_t bpos; - int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int error = 0, len, dirfor_ret = 1, diraft_ret = 1; u_long major, minor; enum vtype vtyp; char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp, *dirp = (struct vnode *)0; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; + 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; /* XXX should we have this? */ + 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); + /* + * Save the original credential UID in case they are + * mapped and we need to map the IDs in the attributes. + */ + saved_uid = kauth_cred_getuid(nfsd->nd_cr); + + vp = dvp = NULL; nd.ni_cnd.cn_nameiop = 0; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; + nfsm_srvmtofh(&nfh); + nfsm_srvnamesiz(len, 1); + nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | SAVESTART; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); - if (dirp) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, procp); + 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) - vrele(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) { - vrele(nd.ni_startdir); - FREE_ZONE((caddr_t)nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; error = NFSERR_BADTYPE; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); goto out; } - VATTR_NULL(vap); + 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); - vap->va_rdev = makedev(major, minor); + VATTR_SET(vap, va_rdev, makedev(major, minor)); } /* - * Iff doesn't exist, create it. + * If it doesn't exist, create it. */ - if (nd.ni_vp) { - vrele(nd.ni_startdir); - FREE_ZONE((caddr_t)nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; + if (vp) { error = EEXIST; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); goto out; } - vap->va_type = vtyp; + VATTR_SET(vap, va_type, vtyp); + + /* + * If the credentials were mapped, we should + * map the same values in the attributes. + */ + 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)); + } + + /* authorize before creating */ + error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0); + + /* construct ACL and handle inheritance */ + if (!error) { + error = kauth_acl_inherit(dvp, + NULL, + &xacl, + 0 /* !isdir */, + &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); + + /* validate new-file security information */ + if (!error) { + error = vnode_authattr_new(dvp, vap, 0, &context); + if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) { + /* + * Most NFS servers just ignore the UID/GID attributes, so we + * try ignoring them if that'll help the request succeed. + */ + VATTR_CLEAR_ACTIVE(vap, va_uid); + VATTR_CLEAR_ACTIVE(vap, va_gid); + error = vnode_authattr_new(dvp, vap, 0, &context); + } + } + if (vtyp == VSOCK) { - vrele(nd.ni_startdir); - nqsrv_getl(nd.ni_dvp, ND_WRITE); - error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); - if (!error) - FREE_ZONE(nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; + error = VNOP_CREATE(dvp, &vp, &nd.ni_cnd, vap, &context); + + if (!error && !VATTR_ALL_SUPPORTED(vap)) + /* + * If some of the requested attributes weren't handled by the VNOP, + * use our fallback code. + */ + error = vnode_setattr_fallback(vp, vap, &context); } else { - if (vtyp != VFIFO && (error = suser(cred, (u_short *)0))) { - vrele(nd.ni_startdir); - FREE_ZONE((caddr_t)nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - vput(nd.ni_dvp); - goto out; + if (vtyp != VFIFO && (error = suser(nfsd->nd_cr, (u_short *)0))) { + goto out1; } - nqsrv_getl(nd.ni_dvp, ND_WRITE); - if ((error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap))) { - vrele(nd.ni_startdir); - goto out; + 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 | SAVESTART); - nd.ni_cnd.cn_proc = procp; - nd.ni_cnd.cn_cred = procp->p_ucred; + nd.ni_cnd.cn_flags &= ~LOCKPARENT; + nd.ni_cnd.cn_context = &hacked_context; + nd.ni_startdir = dvp; + nd.ni_usedvp = dvp; error = lookup(&nd); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - if (error) - goto out; - if (nd.ni_cnd.cn_flags & ISSYMLINK) { - vrele(nd.ni_dvp); - vput(nd.ni_vp); - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - error = EINVAL; + if (!error) { + vp = nd.ni_vp; + if (nd.ni_cnd.cn_flags & ISSYMLINK) + error = EINVAL; } } +out1: + if (xacl != NULL) + kauth_acl_free(xacl); out: - vp = nd.ni_vp; + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + nd.ni_cnd.cn_nameiop = 0; + + vnode_put(dvp); + if (!error) { - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (!error) - error = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); + error = nfsrv_vptofh(nx, 0, NULL, vp, &context, &nfh); + if (!error) { + nfsm_srv_vattr_init(&postat, 1); + error = vnode_getattr(vp, &postat, &context); + } } - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); - nfsm_reply(NFSX_SRVFH(1) + NFSX_POSTOPATTR(1) + NFSX_WCCDATA(1)); + 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(fhp); - nfsm_srvpostop_attr(0, vap); + nfsm_srvpostop_fh(&nfh); + nfsm_srvpostop_attr(0, &postat); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); return (0); nfsmout: - if (dirp) - vrele(dirp); if (nd.ni_cnd.cn_nameiop) { - vrele(nd.ni_startdir); - FREE_ZONE((caddr_t)nd.ni_cnd.cn_pnbuf, - nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - } - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vput(nd.ni_vp); + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + if (vp) + vnode_put(vp); + vnode_put(dvp); + } + if (dirp) + vnode_put(dirp); return (error); } @@ -1830,84 +2165,86 @@ int nfsrv_remove(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; - register u_long *tl; - register long t1; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int error = 0, len, dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp, *dirp; - struct vattr dirfor, diraft; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; + 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); -#ifndef nolint - vp = (struct vnode *)0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else - vrele(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 (vp->v_type == VDIR) { + + if (vnode_vtype(vp) == VDIR) error = EPERM; /* POSIX */ - goto out; - } - /* - * The root of a mounted filesystem cannot be deleted. - */ - if (vp->v_flag & VROOT) { + else if (vnode_isvroot(vp)) + /* + * The root of a mounted filesystem cannot be deleted. + */ error = EBUSY; - goto out; - } -out: - if (!error) { - nqsrv_getl(nd.ni_dvp, ND_WRITE); - nqsrv_getl(vp, ND_WRITE); + else + error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, &context, nxo, 0); - error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + if (!error) + error = VNOP_REMOVE(dvp, vp, &nd.ni_cnd, 0, &context); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vput(vp); - } + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + vnode_put(vp); + vnode_put(dvp); } - if (dirp && v3) { - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); + 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); } - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -1917,107 +2254,163 @@ int nfsrv_rename(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register u_long *tl; - register long t1; + kauth_cred_t saved_cred = NULL; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, cache, len, len2, fdirfor_ret = 1, fdiraft_ret = 1; + 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; - struct mbuf *mb, *mreq; + char *cp2, *frompath = NULL, *topath = NULL; + mbuf_t mb, mreq; struct nameidata fromnd, tond; - struct vnode *fvp, *tvp, *tdvp, *fdirp = (struct vnode *)0; - struct vnode *tdirp = (struct vnode *)0; - struct vattr fdirfor, fdiraft, tdirfor, tdiraft; - nfsfh_t fnfh, tnfh; - fhandle_t *ffhp, *tfhp; - u_quad_t frev; - uid_t saved_uid; + 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 = (struct vnode *)0; + fvp = (vnode_t)0; #endif - ffhp = &fnfh.fh_generic; - tfhp = &tnfh.fh_generic; - fromnd.ni_cnd.cn_nameiop = 0; - tond.ni_cnd.cn_nameiop = 0; - nfsm_srvmtofh(ffhp); - nfsm_srvnamesiz(len); + + /* + * these need to be set before + * calling any nfsm_xxxx macros + * since they may take us out + * through the error path + */ + 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; + /* * 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; + saved_cred = nfsd->nd_cr; + kauth_cred_ref(saved_cred); +retry: fromnd.ni_cnd.cn_nameiop = DELETE; - fromnd.ni_cnd.cn_flags = WANTPARENT | SAVESTART; - error = nfs_namei(&fromnd, ffhp, len, slp, nam, &md, - &dpos, &fdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - fdirfor_ret = VOP_GETATTR(fdirp, &fdirfor, cred, - procp); - else { - vrele(fdirp); - fdirp = (struct vnode *)0; + if (v3) { + nfsm_srv_pre_vattr_init(&fdirfor, v3); + fdirfor_ret = vnode_getattr(fdirp, &fdirfor, &context); + } else { + vnode_put(fdirp); + fdirp = NULL; } } - if (error) { - nfsm_reply(2 * NFSX_WCCDATA(v3)); - nfsm_srvwcc_data(fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); - nfsm_srvwcc_data(tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); - if (fdirp) - vrele(fdirp); - return (0); + fvtype = vnode_vtype(fvp); + + /* reset credential if it was remapped */ + if (nfsd->nd_cr != saved_cred) { + kauth_cred_rele(nfsd->nd_cr); + nfsd->nd_cr = saved_cred; + kauth_cred_ref(nfsd->nd_cr); } - fvp = fromnd.ni_vp; - nfsm_srvmtofh(tfhp); - nfsm_strsiz(len2, NFS_MAXNAMLEN); - cred->cr_uid = saved_uid; - tond.ni_cnd.cn_cred = cred; + tond.ni_cnd.cn_nameiop = RENAME; - tond.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; - error = nfs_namei(&tond, tfhp, len2, slp, nam, &md, - &dpos, &tdirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); - if (tdirp) { - if (v3) - tdirfor_ret = VOP_GETATTR(tdirp, &tdirfor, cred, - procp); - else { - vrele(tdirp); - tdirp = (struct vnode *)0; - } - } + 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) { - VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); - vrele(fromnd.ni_dvp); - vrele(fvp); - goto out1; + /* + * Translate error code for rename("dir1", "dir2/."). + */ + if (error == EISDIR && fvtype == VDIR) { + if (v3) + error = EINVAL; + else + error = ENOTEMPTY; + } + goto out; } tdvp = tond.ni_dvp; - tvp = tond.ni_vp; + 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) { - if (fvp->v_type == VDIR && tvp->v_type != VDIR) { + tvtype = vnode_vtype(tvp); + + if (fvtype == VDIR && tvtype != VDIR) { if (v3) error = EEXIST; else error = EISDIR; goto out; - } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { + } else if (fvtype != VDIR && tvtype == VDIR) { if (v3) error = EEXIST; else error = ENOTDIR; goto out; } - if (tvp->v_type == VDIR && tvp->v_mountedhere) { + if (tvtype == VDIR && vnode_mountedhere(tvp)) { if (v3) error = EXDEV; else @@ -2025,95 +2418,346 @@ nfsrv_rename(nfsd, slp, procp, mrq) goto out; } } - if (fvp->v_type == VDIR && fvp->v_mountedhere) { + if (fvp == tdvp) { + if (v3) + error = EINVAL; + else + error = ENOTEMPTY; + goto out; + } + + /* + * Authorization. + * + * If tvp is a directory and not the same as fdvp, or tdvp is not the same as fdvp, + * the node is moving between directories and we need rights to remove from the + * old and add to the new. + * + * If tvp already exists and is not a directory, we need to be allowed to delete it. + * + * Note that we do not inherit when renaming. XXX this needs to be revisited to + * implement the deferred-inherit bit. + */ + { + int moving = 0; + + error = 0; + if ((tvp != NULL) && vnode_isdir(tvp)) { + if (tvp != fdvp) + moving = 1; + } else if (tdvp != fdvp) { + moving = 1; + } + if (moving) { + /* moving out of fdvp, must have delete rights */ + if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, &context, fnxo, 0)) != 0) + goto auth_exit; + /* moving into tdvp or tvp, must have rights to add */ + 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 { + /* node staying in same directory, must be allowed to add new name */ + if ((error = nfsrv_authorize(fdvp, NULL, + vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, + &context, fnxo, 0)) != 0) + goto auth_exit; + } + /* overwriting tvp */ + if ((tvp != NULL) && !vnode_isdir(tvp) && + ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, &context, tnxo, 0)) != 0)) + goto auth_exit; + + /* XXX more checks? */ + +auth_exit: + /* authorization denied */ + 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 (fvp->v_mount != tdvp->v_mount) { + /* + * The following edge case is caught here: + * (to cannot be a descendent of from) + * + * o fdvp + * / + * / + * o fvp + * \ + * \ + * o tdvp + * / + * / + * o tvp + */ + if (tdvp->v_parent == fvp) { if (v3) error = EXDEV; else error = ENOTEMPTY; goto out; } - if (fvp == tdvp) + if (fvtype == VDIR && vnode_mountedhere(fvp)) { if (v3) - error = EINVAL; + error = EXDEV; else error = ENOTEMPTY; + goto out; + } /* * If source is the same as the destination (that is the - * same vnode) then there is nothing to do. - * (fixed to have POSIX semantics - CSM 3/2/98) + * same vnode) then there is nothing to do... + * EXCEPT if the underlying file system supports case + * insensitivity and is case preserving. In this case + * the file system needs to handle the special case of + * getting the same vnode as target (fvp) and source (tvp). + * + * Only file systems that support pathconf selectors _PC_CASE_SENSITIVE + * and _PC_CASE_PRESERVING can have this exception, and they need to + * handle the special case of getting the same vnode as target and + * source. NOTE: Then the target is unlocked going into vnop_rename, + * so not to cause locking problems. There is a single reference on tvp. + * + * NOTE - that fvp == tvp also occurs if they are hard linked - NOTE + * that correct behaviour then is just to remove the source (link) */ - if (fvp == tvp) - error = -1; -out: - if (!error) { - nqsrv_getl(fromnd.ni_dvp, ND_WRITE); - nqsrv_getl(tdvp, ND_WRITE); - if (tvp) - nqsrv_getl(tvp, ND_WRITE); - error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, - tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); + 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) { + /* + * we're holding a reference and lock + * on locked_mp, but it no longer matches + * what we want to do... so drop our hold + */ + mount_unlock_renames(locked_mp); + mount_drop(locked_mp, 0); + holding_mntlock = 0; + } + if (tdvp != fdvp && fvtype == VDIR) { + /* + * serialize renames that re-shape + * the tree... if holding_mntlock is + * set, then we're ready to go... + * otherwise we + * first need to drop the iocounts + * we picked up, second take the + * lock to serialize the access, + * then finally start the lookup + * process over with the lock held + */ + if (!holding_mntlock) { + /* + * need to grab a reference on + * the mount point before we + * drop all the iocounts... once + * the iocounts are gone, the mount + * could follow + */ + locked_mp = vnode_mount(fvp); + mount_ref(locked_mp, 0); + + /* make a copy of to path to pass to nfs_namei() again */ + MALLOC_ZONE(topath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); + if (topath) + bcopy(tond.ni_cnd.cn_pnbuf, topath, tolen + 1); + + /* + * nameidone has to happen before we vnode_put(tdvp) + * since it may need to release the fs_nodelock on the tdvp + */ + nameidone(&tond); + + if (tvp) + vnode_put(tvp); + vnode_put(tdvp); + + /* make a copy of from path to pass to nfs_namei() again */ + MALLOC_ZONE(frompath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); + if (frompath) + bcopy(fromnd.ni_cnd.cn_pnbuf, frompath, fromlen + 1); + + /* + * nameidone has to happen before we vnode_put(fdvp) + * since it may need to release the fs_nodelock on the fdvp + */ + 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) { + /* we couldn't allocate a path, so bail */ + error = ENOMEM; + goto out; + } + + goto retry; + } } else { - VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); - if (tdvp == tvp) - vrele(tdvp); - else - vput(tdvp); + /* + * when we dropped the iocounts to take + * the lock, we allowed the identity of + * the various vnodes to change... if they did, + * we may no longer be dealing with a rename + * that reshapes the tree... once we're holding + * the iocounts, the vnodes can't change type + * so we're free to drop the lock at this point + * and continue on + */ + if (holding_mntlock) { + mount_unlock_renames(locked_mp); + mount_drop(locked_mp, 0); + holding_mntlock = 0; + } + } + + // save these off so we can later verify that fvp is the same + 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); + /* + * fix up name & parent pointers. note that we first + * check that fvp has the same name/parent pointers it + * had before the rename call... this is a 'weak' check + * at best... + */ + 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 has to happen before we vnode_put(tdvp) + * since it may need to release the fs_nodelock on the tdvp + */ + nameidone(&tond); if (tvp) - vput(tvp); - VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); - vrele(fromnd.ni_dvp); - vrele(fvp); - if (error == -1) - error = 0; + vnode_put(tvp); + vnode_put(tdvp); + + tdvp = NULL; + } + if (fdvp) { + /* + * nameidone has to happen before we vnode_put(fdvp) + * since it may need to release the fs_nodelock on the fdvp + */ + nameidone(&fromnd); + + if (fvp) + vnode_put(fvp); + vnode_put(fdvp); + + fdvp = NULL; } - vrele(tond.ni_startdir); - FREE_ZONE(tond.ni_cnd.cn_pnbuf, tond.ni_cnd.cn_pnlen, M_NAMEI); - tond.ni_cnd.cn_flags &= ~HASBUF; -out1: if (fdirp) { - fdiraft_ret = VOP_GETATTR(fdirp, &fdiraft, cred, procp); - vrele(fdirp); + nfsm_srv_vattr_init(&fdiraft, v3); + fdiraft_ret = vnode_getattr(fdirp, &fdiraft, &context); + vnode_put(fdirp); + fdirp = NULL; } if (tdirp) { - tdiraft_ret = VOP_GETATTR(tdirp, &tdiraft, cred, procp); - vrele(tdirp); + nfsm_srv_vattr_init(&tdiraft, v3); + tdiraft_ret = vnode_getattr(tdirp, &tdiraft, &context); + vnode_put(tdirp); + tdirp = NULL; } - vrele(fromnd.ni_startdir); - FREE_ZONE(fromnd.ni_cnd.cn_pnbuf, fromnd.ni_cnd.cn_pnlen, M_NAMEI); - fromnd.ni_cnd.cn_flags &= ~HASBUF; 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 has to happen before we vnode_put(tdvp) + * since it may need to release the fs_nodelock on the tdvp + */ + nameidone(&tond); + + if (tvp) + vnode_put(tvp); + vnode_put(tdvp); + } + if (fdvp) { + /* + * nameidone has to happen before we vnode_put(fdvp) + * since it may need to release the fs_nodelock on the fdvp + */ + nameidone(&fromnd); + + if (fvp) + vnode_put(fvp); + vnode_put(fdvp); + } if (fdirp) - vrele(fdirp); + vnode_put(fdirp); if (tdirp) - vrele(tdirp); - if (tond.ni_cnd.cn_nameiop) { - vrele(tond.ni_startdir); - FREE_ZONE(tond.ni_cnd.cn_pnbuf, tond.ni_cnd.cn_pnlen, M_NAMEI); - tond.ni_cnd.cn_flags &= ~HASBUF; - } - if (fromnd.ni_cnd.cn_nameiop) { - vrele(fromnd.ni_startdir); - FREE_ZONE(fromnd.ni_cnd.cn_pnbuf, - fromnd.ni_cnd.cn_pnlen, M_NAMEI); - fromnd.ni_cnd.cn_flags &= ~HASBUF; - VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); - vrele(fromnd.ni_dvp); - vrele(fvp); - } + 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); } @@ -2124,96 +2768,116 @@ int nfsrv_link(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; struct nameidata nd; - register u_long *tl; - register long t1; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, len, dirfor_ret = 1, diraft_ret = 1; + int error = 0, len, dirfor_ret = 1, diraft_ret = 1; int getret = 1, v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp, *xp, *dirp = (struct vnode *)0; - struct vattr dirfor, diraft, at; - nfsfh_t nfh, dnfh; - fhandle_t *fhp, *dfhp; - u_quad_t frev; - - fhp = &nfh.fh_generic; - dfhp = &dnfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvmtofh(dfhp); - nfsm_srvnamesiz(len); - if ((error = nfsrv_fhtovp(fhp, FALSE, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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 (vp->v_type == VDIR) { + + /* we're not allowed to link to directories... */ + if (vnode_vtype(vp) == VDIR) { error = EPERM; /* POSIX */ goto out1; } - nd.ni_cnd.cn_cred = cred; + + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + + /* ...or to anything that kauth doesn't want us to (eg. immutable items) */ + 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 = nfs_namei(&nd, dfhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else { - vrele(dirp); - dirp = (struct vnode *)0; + 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) { + + if (xp != NULL) error = EEXIST; - goto out; - } - xp = nd.ni_dvp; - if (vp->v_mount != xp->v_mount) + else if (vnode_mount(vp) != vnode_mount(dvp)) error = EXDEV; -out: - if (!error) { - nqsrv_getl(vp, ND_WRITE); - nqsrv_getl(xp, ND_WRITE); - error = VOP_LINK(vp, nd.ni_dvp, &nd.ni_cnd); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vrele(nd.ni_vp); - } + 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 has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + if (xp) + vnode_put(xp); + vnode_put(dvp); out1: - if (v3) - getret = VOP_GETATTR(vp, &at, cred, procp); + if (v3) { + nfsm_srv_vattr_init(&at, v3); + getret = vnode_getattr(vp, &at, &context); + } if (dirp) { - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); + nfsm_srv_vattr_init(&diraft, v3); + diraft_ret = vnode_getattr(dirp, &diraft, &context); + vnode_put(dirp); } - vrele(vp); + 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); } - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -2223,142 +2887,200 @@ int nfsrv_symlink(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vattr va, dirfor, diraft; + struct vnode_attr dirfor, diraft, postat; struct nameidata nd; - register struct vattr *vap = &va; - register u_long *tl; - register long t1; + struct vnode_attr va; + struct vnode_attr *vap = &va; + u_long *tl; + long t1; struct nfsv2_sattr *sp; - char *bpos, *pathcp = (char *)0, *cp2; - struct uio io; - struct iovec iv; - int error = 0, cache, len, len2, dirfor_ret = 1, diraft_ret = 1; + char *bpos, *linkdata = NULL, *cp2; + int error = 0, len, linkdatalen; + int dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); - struct mbuf *mb, *mreq, *mb2; - struct vnode *dirp = (struct vnode *)0; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; + 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; + + /* + * Save the original credential UID in case they are + * mapped and we need to map the IDs in the attributes. + */ + saved_uid = kauth_cred_getuid(nfsd->nd_cr); nd.ni_cnd.cn_nameiop = 0; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; + vp = dvp = NULL; + nfsm_srvmtofh(&nfh); + nfsm_srvnamesiz(len, v3); + nd.ni_cnd.cn_nameiop = CREATE; - nd.ni_cnd.cn_flags = LOCKPARENT | SAVESTART; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else { - vrele(dirp); - dirp = (struct vnode *)0; + 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 out; - VATTR_NULL(vap); + 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(len2, NFS_MAXPATHLEN); - MALLOC(pathcp, caddr_t, len2 + 1, M_TEMP, M_WAITOK); - iv.iov_base = pathcp; - iv.iov_len = len2; - io.uio_resid = len2; - io.uio_offset = 0; - io.uio_iov = &iv; - io.uio_iovcnt = 1; - io.uio_segflg = UIO_SYSSPACE; - io.uio_rw = UIO_READ; - io.uio_procp = (struct proc *)0; - nfsm_mtouio(&io, len2); + 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); - vap->va_mode = fxdr_unsigned(u_short, sp->sa_mode); - } - *(pathcp + len2) = '\0'; - if (nd.ni_vp) { - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(nd.ni_vp); + VATTR_SET(vap, va_mode, fxdr_unsigned(u_short, sp->sa_mode)); + } + *(linkdata + linkdatalen) = '\0'; + if (vp) { error = EEXIST; goto out; } - nqsrv_getl(nd.ni_dvp, ND_WRITE); - error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap, pathcp); - if (error) - vrele(nd.ni_startdir); - else { - if (v3) { - nd.ni_cnd.cn_nameiop = LOOKUP; - nd.ni_cnd.cn_flags &= ~(LOCKPARENT | SAVESTART | FOLLOW); - nd.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF); - nd.ni_cnd.cn_proc = procp; - nd.ni_cnd.cn_cred = cred; - error = lookup(&nd); - if (!error) { - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = nd.ni_vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(nd.ni_vp, &fhp->fh_fid); + + /* + * If the credentials were mapped, we should + * map the same values in the attributes. + */ + 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); + + /* authorize before creating */ + error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context, nxo, 0); + + /* validate given attributes */ + if (!error) { + error = vnode_authattr_new(dvp, vap, 0, &context); + if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) { + /* + * Most NFS servers just ignore the UID/GID attributes, so we + * try ignoring them if that'll help the request succeed. + */ + 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) - error = VOP_GETATTR(nd.ni_vp, vap, cred, - procp); - vput(nd.ni_vp); + 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); + } } - } else - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; } out: - if (pathcp) - FREE(pathcp, M_TEMP); + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + 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) { - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); + nfsm_srv_vattr_init(&diraft, v3); + diraft_ret = vnode_getattr(dirp, &diraft, &context); + vnode_put(dirp); } - nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { - nfsm_srvpostop_fh(fhp); - nfsm_srvpostop_attr(0, vap); + 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) { - vrele(nd.ni_startdir); - FREE_ZONE(nd.ni_cnd.cn_pnbuf, nd.ni_cnd.cn_pnlen, M_NAMEI); - nd.ni_cnd.cn_flags &= ~HASBUF; + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + if (vp) + vnode_put(vp); + vnode_put(dvp); } if (dirp) - vrele(dirp); - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vrele(nd.ni_vp); - if (pathcp) - FREE(pathcp, M_TEMP); + vnode_put(dirp); + if (linkdata) + FREE(linkdata, M_TEMP); return (error); } @@ -2369,112 +3091,199 @@ int nfsrv_mkdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vattr va, dirfor, diraft; - register struct vattr *vap = &va; - register struct nfs_fattr *fp; + struct vnode_attr dirfor, diraft, postat; + struct vnode_attr va; + struct vnode_attr *vap = &va; + struct nfs_fattr *fp; struct nameidata nd; - register caddr_t cp; - register u_long *tl; - register long t1; + caddr_t cp; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int error = 0, len; + int dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp, *dirp = (struct vnode *)0; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; - - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; + 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; + + /* + * Save the original credential UID in case they are + * mapped and we need to map the IDs in the attributes. + */ + 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 = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else { - vrele(dirp); - dirp = (struct vnode *)0; + 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) - vrele(dirp); + vnode_put(dirp); return (0); } - VATTR_NULL(vap); + dvp = nd.ni_dvp; + vp = nd.ni_vp; + + VATTR_INIT(vap); if (v3) { nfsm_srvsattr(vap); } else { nfsm_dissect(tl, u_long *, NFSX_UNSIGNED); - vap->va_mode = nfstov_mode(*tl++); + VATTR_SET(vap, va_mode, nfstov_mode(*tl++)); } - vap->va_type = VDIR; - vp = nd.ni_vp; + VATTR_SET(vap, va_type, VDIR); + if (vp != NULL) { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vrele(vp); + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + vnode_put(dvp); + vnode_put(vp); error = EEXIST; goto out; } - nqsrv_getl(nd.ni_dvp, ND_WRITE); - error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, vap); + + /* + * If the credentials were mapped, we should + * map the same values in the attributes. + */ + 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); + + /* construct ACL and handle inheritance */ if (!error) { - vp = nd.ni_vp; - bzero((caddr_t)fhp, sizeof(nfh)); - fhp->fh_fsid = vp->v_mount->mnt_stat.f_fsid; - error = VFS_VPTOFH(vp, &fhp->fh_fid); - if (!error) - error = VOP_GETATTR(vp, vap, cred, procp); - vput(vp); + error = kauth_acl_inherit(dvp, + NULL, + &xacl, /* isdir */ + 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); + + /* validate new-file security information */ + if (!error) { + error = vnode_authattr_new(dvp, vap, 0, &context); + if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) { + /* + * Most NFS servers just ignore the UID/GID attributes, so we + * try ignoring them if that'll help the request succeed. + */ + 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)) + /* + * If some of the requested attributes weren't handled by the VNOP, + * use our fallback code. + */ + 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 has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + vnode_put(dvp); out: + nd.ni_cnd.cn_nameiop = 0; + if (dirp) { - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(dirp); + nfsm_srv_vattr_init(&diraft, v3); + diraft_ret = vnode_getattr(dirp, &diraft, &context); + vnode_put(dirp); } - nfsm_reply(NFSX_SRVFH(v3) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); + nfsm_reply(NFSX_SRVFH(v3, &nfh) + NFSX_POSTOPATTR(v3) + NFSX_WCCDATA(v3)); if (v3) { if (!error) { - nfsm_srvpostop_fh(fhp); - nfsm_srvpostop_attr(0, vap); + nfsm_srvpostop_fh(&nfh); + nfsm_srvpostop_attr(0, &postat); } nfsm_srvwcc_data(dirfor_ret, &dirfor, diraft_ret, &diraft); } else { - nfsm_srvfhtom(fhp, v3); + nfsm_srvfhtom(&nfh, v3); nfsm_build(fp, struct nfs_fattr *, NFSX_V2FATTR); - nfsm_srvfillattr(vap, fp); + nfsm_srvfillattr(&postat, fp); } return (0); nfsmout: + if (nd.ni_cnd.cn_nameiop) { + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + vnode_put(dvp); + if (vp) + vnode_put(vp); + } if (dirp) - vrele(dirp); - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - if (nd.ni_vp) - vrele(nd.ni_vp); + vnode_put(dirp); return (error); } @@ -2485,110 +3294,120 @@ int nfsrv_rmdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register u_long *tl; - register long t1; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, cache, len, dirfor_ret = 1, diraft_ret = 1; + int error = 0, len; + int dirfor_ret = 1, diraft_ret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mreq; - struct vnode *vp, *dirp = (struct vnode *)0; - struct vattr dirfor, diraft; - nfsfh_t nfh; - fhandle_t *fhp; + 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; - u_quad_t frev; + struct vfs_context context; + + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + + vp = dvp = NULL; + nfsm_srvmtofh(&nfh); + nfsm_srvnamesiz(len, v3); - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - nfsm_srvnamesiz(len); - nd.ni_cnd.cn_cred = cred; nd.ni_cnd.cn_nameiop = DELETE; nd.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; - error = nfs_namei(&nd, fhp, len, slp, nam, &md, &dpos, - &dirp, procp, (nfsd->nd_flag & ND_KERBAUTH), FALSE); + 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) - dirfor_ret = VOP_GETATTR(dirp, &dirfor, cred, - procp); - else { - vrele(dirp); - dirp = (struct vnode *)0; + 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) - vrele(dirp); + vnode_put(dirp); return (0); } + dvp = nd.ni_dvp; vp = nd.ni_vp; - if (vp->v_type != VDIR) { + + if (vnode_vtype(vp) != VDIR) { error = ENOTDIR; goto out; } /* * No rmdir "." please. */ - if (nd.ni_dvp == vp) { + if (dvp == vp) { error = EINVAL; goto out; } /* * The root of a mounted filesystem cannot be deleted. */ - if (vp->v_flag & VROOT) + 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: - if (!error) { - nqsrv_getl(nd.ni_dvp, ND_WRITE); - nqsrv_getl(vp, ND_WRITE); - error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); - } else { - VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); - if (nd.ni_dvp == nd.ni_vp) - vrele(nd.ni_dvp); - else - vput(nd.ni_dvp); - vput(vp); - } + /* + * nameidone has to happen before we vnode_put(dvp) + * since it may need to release the fs_nodelock on the dvp + */ + nameidone(&nd); + + vnode_put(dvp); + vnode_put(vp); + if (dirp) { - diraft_ret = VOP_GETATTR(dirp, &diraft, cred, procp); - vrele(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); } - nfsm_srvdone; +nfsmout: + return (error); } /* * nfs readdir service * - mallocs what it thinks is enough to read * count rounded up to a multiple of NFS_DIRBLKSIZ <= NFS_MAXREADDIR - * - calls VOP_READDIR() + * - calls VNOP_READDIR() * - 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() + * - it only knows that it has encountered eof when the VNOP_READDIR() * reads nothing * - 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 * for other os'. - * NB: It is tempting to set eof to true if the VOP_READDIR() reads less + * NB: It is tempting to set eof to true if the VNOP_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 * anyhow } @@ -2599,49 +3418,44 @@ out: * the EOF flag. For readdirplus, the maxcount is the same, and the * dircount includes all that except for the entry attributes and handles. */ -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_V3FH / sizeof (u_long)]; -}; int nfsrv_readdir(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register char *bp, *be; - register struct mbuf *mp; - register struct dirent *dp; - register caddr_t cp; - register u_long *tl; - register long t1; + char *bp, *be; + mbuf_t mp; + struct direntry *dp; + caddr_t cp; + u_long *tl; + long t1; caddr_t bpos; - struct mbuf *mb, *mb2, *mreq, *mp2; + mbuf_t mb, mb2, mreq, mp2; char *cpos, *cend, *cp2, *rbuf; - struct vnode *vp; - struct vattr at; - nfsfh_t nfh; - fhandle_t *fhp; - struct uio io; - struct iovec iv; + 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, cnt, fullsiz, eofflag, rdonly, cache, ncookies = 0; + int siz, count, fullsiz, eofflag, nentries = 0; int v3 = (nfsd->nd_flag & ND_NFSV3); - u_quad_t frev, off, toff, verf; - u_long *cookies = NULL, *cookiep; + u_quad_t off, toff, verf; + nfsuint64 tquad; + int vnopflag; + struct vfs_context context; + + vnopflag = VNODE_READDIR_EXTENDED | VNODE_READDIR_REQSEEKOFF; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); if (v3) { nfsm_dissect(tl, u_long *, 5 * NFSX_UNSIGNED); fxdr_hyper(tl, &toff); @@ -2653,99 +3467,90 @@ nfsrv_readdir(nfsd, slp, procp, mrq) toff = fxdr_unsigned(u_quad_t, *tl++); } off = toff; - cnt = fxdr_unsigned(int, *tl); - siz = ((cnt + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); + 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(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - nqsrv_getl(vp, ND_READ); + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + if (!v3 || (nxo->nxo_flags & NX_32BITCLIENTS)) + vnopflag |= VNODE_READDIR_SEEKOFF32; if (v3) { - error = getret = VOP_GETATTR(vp, &at, cred, procp); + 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_access(vp, VEXEC, cred, rdonly, procp, 0); + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0); if (error) { - vput(vp); + vnode_put(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } - VOP_UNLOCK(vp, 0, procp); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); -again: - iv.iov_base = rbuf; - iv.iov_len = fullsiz; - io.uio_iov = &iv; - io.uio_iovcnt = 1; - io.uio_offset = (off_t)off; - io.uio_resid = fullsiz; - io.uio_segflg = UIO_SYSSPACE; - io.uio_rw = UIO_READ; - io.uio_procp = (struct proc *)0; - eofflag = 0; - - if (cookies) { - _FREE((caddr_t)cookies, M_TEMP); - cookies = NULL; - } - if (error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp)) { - FREE((caddr_t)rbuf, M_TEMP); + if (!rbuf) { + error = ENOMEM; + vnode_put(vp); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } - error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); - off = (off_t)io.uio_offset; - /* - * We cannot set the error in the case where there are no cookies - * and no error, only, as FreeBSD. In the scenario the client is - * calling us back being told there were "more" entries on last readdir - * return, and we have no more entries, our VOP_READDIR can give - * cookies = NULL and no error. This is due to a zero size to MALLOC - * returning NULL unlike FreeBSD which returns a pointer. - * With FreeBSD it makes sense if the MALLOC failed and you get in that - * bind. For us, we need something more. Thus, we should make sure we - * had some cookies to return, but no pointer and no error for EPERM case. - * Otherwise, go thru normal processing of sending back the eofflag. This check - * is also legit on first call to the routine by client since . and .. - * should be returned. Make same change to nfsrv_readdirplus. - */ - if ((ncookies != 0) && !cookies && !error) - error = NFSERR_PERM; - + 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) { - getret = VOP_GETATTR(vp, &at, cred, procp); + nfsm_srv_vattr_init(&at, v3); + getret = vnode_getattr(vp, &at, &context); if (!error) error = getret; } - VOP_UNLOCK(vp, 0, procp); if (error) { - vrele(vp); - _FREE((caddr_t)rbuf, M_TEMP); - if (cookies) - _FREE((caddr_t)cookies, M_TEMP); + vnode_put(vp); + FREE(rbuf, M_TEMP); nfsm_reply(NFSX_POSTOPATTR(v3)); nfsm_srvpostop_attr(getret, &at); return (0); } - if (io.uio_resid) { - siz -= io.uio_resid; + if (uio_resid(auio) != 0) { + // LP64todo - fix this + siz -= uio_resid(auio); /* * If nothing read, return eof * rpc reply */ if (siz == 0) { - vrele(vp); + vnode_put(vp); nfsm_reply(NFSX_POSTOPATTR(v3) + NFSX_COOKIEVERF(v3) + 2 * NFSX_UNSIGNED); if (v3) { @@ -2757,8 +3562,7 @@ again: nfsm_build(tl, u_long *, 2 * NFSX_UNSIGNED); *tl++ = nfs_false; *tl = nfs_true; - FREE((caddr_t)rbuf, M_TEMP); - FREE((caddr_t)cookies, M_TEMP); + FREE(rbuf, M_TEMP); return (0); } } @@ -2769,32 +3573,19 @@ again: */ cpos = rbuf; cend = rbuf + siz; - dp = (struct dirent *)cpos; - cookiep = cookies; -#ifdef __FreeBSD__ - /* - * For some reason FreeBSD's ufs_readdir() chooses to back the - * directory offset up to a block boundary, so it is necessary to - * skip over the records that preceed the requested offset. This - * requires the assumption that file offset cookies monotonically - * increase. - */ - while (cpos < cend && ncookies > 0 && - (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { -#else - while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) { -#endif + dp = (struct direntry *)cpos; + while (dp->d_fileno == 0 && cpos < cend && nentries > 0) { cpos += dp->d_reclen; - dp = (struct dirent *)cpos; - cookiep++; - ncookies--; + dp = (struct direntry *)cpos; + nentries--; } - if (cpos >= cend || ncookies == 0) { + 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; @@ -2805,36 +3596,42 @@ again: len = 2 * NFSX_UNSIGNED; mp = mp2 = mb; bp = bpos; - be = bp + M_TRAILINGSPACE(mp); + be = bp + mbuf_trailingspace(mp); /* Loop through the records and build reply */ - while (cpos < cend && ncookies > 0) { + 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 > cnt) { + if (len > count) { eofflag = 0; break; } /* * Build the directory record xdr from - * the dirent entry. + * the direntry entry. */ 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 = 0; + *tl = tquad.nfsuquad[1]; + bp += NFSX_UNSIGNED; + } else { + *tl = txdr_unsigned(dp->d_fileno); bp += NFSX_UNSIGNED; } nfsm_clget; - *tl = txdr_unsigned(dp->d_fileno); - bp += NFSX_UNSIGNED; - nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; @@ -2856,23 +3653,27 @@ again: /* And null pad to a long boundary */ for (i = 0; i < rem; i++) *bp++ = '\0'; - nfsm_clget; - /* Finish off the record */ + /* Finish off the record with the cookie */ + nfsm_clget; if (v3) { - *tl = 0; + 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; } - *tl = txdr_unsigned(*cookiep); - bp += NFSX_UNSIGNED; } cpos += dp->d_reclen; - dp = (struct dirent *)cpos; - cookiep++; - ncookies--; + dp = (struct direntry *)cpos; + nentries--; } - vrele(vp); nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; @@ -2884,135 +3685,152 @@ again: bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) - mp->m_len = bp - mtod(mp, caddr_t); + mbuf_setlen(mp, bp - (char*)mbuf_data(mp)); } else - mp->m_len += bp - bpos; - FREE((caddr_t)rbuf, M_TEMP); - FREE((caddr_t)cookies, M_TEMP); - nfsm_srvdone; + 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; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register char *bp, *be; - register struct mbuf *mp; - register struct dirent *dp; - register caddr_t cp; - register u_long *tl; - register long t1; + char *bp, *be; + mbuf_t mp; + struct direntry *dp; + caddr_t cp; + u_long *tl; + long t1; caddr_t bpos; - struct mbuf *mb, *mb2, *mreq, *mp2; + mbuf_t mb, mb2, mreq, mp2; char *cpos, *cend, *cp2, *rbuf; - struct vnode *vp, *nvp; + vnode_t vp, nvp; struct flrep fl; - nfsfh_t nfh; - fhandle_t *fhp, *nfhp = (fhandle_t *)fl.fl_nfh; - struct uio io; - struct iovec iv; - struct vattr va, at, *vap = &va; + 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, cnt, fullsiz, eofflag, rdonly, cache, dirlen, ncookies = 0; - u_quad_t frev, off, toff, verf; - u_long *cookies = NULL, *cookiep; - void *file; + int siz, count, fullsiz, eofflag, dirlen, nentries = 0, isdotdot; + u_quad_t off, toff, verf; + nfsuint64 tquad; + int vnopflag; + struct vfs_context context; - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + 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++); - cnt = 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(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - error = getret = VOP_GETATTR(vp, &at, cred, procp); + 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) { - nqsrv_getl(vp, ND_READ); - error = nfsrv_access(vp, VEXEC, cred, rdonly, procp, 0); - } + if (!error) + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, &context, nxo, 0); if (error) { - vput(vp); + vnode_put(vp); + vp = NULL; nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } - VOP_UNLOCK(vp, 0, procp); 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: - iv.iov_base = rbuf; - iv.iov_len = fullsiz; - io.uio_iov = &iv; - io.uio_iovcnt = 1; - io.uio_offset = (off_t)off; - io.uio_resid = fullsiz; - io.uio_segflg = UIO_SYSSPACE; - io.uio_rw = UIO_READ; - io.uio_procp = (struct proc *)0; + uio_reset(auio, off, UIO_SYSSPACE, UIO_READ); + uio_addiov(auio, CAST_USER_ADDR_T(rbuf), fullsiz); eofflag = 0; - if (cookies) { - _FREE((caddr_t)cookies, M_TEMP); - cookies = NULL; - } - if (error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, procp)) { - FREE((caddr_t)rbuf, M_TEMP); - nfsm_reply(NFSX_V3POSTOPATTR); - nfsm_srvpostop_attr(getret, &at); - return (0); - } - error = VOP_READDIR(vp, &io, cred, &eofflag, &ncookies, &cookies); - off = (u_quad_t)io.uio_offset; - getret = VOP_GETATTR(vp, &at, cred, procp); - VOP_UNLOCK(vp, 0, procp); - /* - * See nfsrv_readdir comment above on this - */ - if ((ncookies != 0) && !cookies && !error) - error = NFSERR_PERM; + 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) { - vrele(vp); - if (cookies) - _FREE((caddr_t)cookies, M_TEMP); - _FREE((caddr_t)rbuf, M_TEMP); + vnode_put(vp); + vp = NULL; + FREE(rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } - if (io.uio_resid) { - siz -= io.uio_resid; + if (uio_resid(auio) != 0) { + // LP64todo - fix this + siz -= uio_resid(auio); /* * If nothing read, return eof * rpc reply */ if (siz == 0) { - vrele(vp); + vnode_put(vp); + vp = NULL; nfsm_reply(NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED); nfsm_srvpostop_attr(getret, &at); @@ -3021,8 +3839,7 @@ again: tl += 2; *tl++ = nfs_false; *tl = nfs_true; - FREE((caddr_t)cookies, M_TEMP); - FREE((caddr_t)rbuf, M_TEMP); + FREE(rbuf, M_TEMP); return (0); } } @@ -3033,27 +3850,13 @@ again: */ cpos = rbuf; cend = rbuf + siz; - dp = (struct dirent *)cpos; - cookiep = cookies; -#ifdef __FreeBSD__ - /* - * For some reason FreeBSD's ufs_readdir() chooses to back the - * directory offset up to a block boundary, so it is necessary to - * skip over the records that preceed the requested offset. This - * requires the assumption that file offset cookies monotonically - * increase. - */ - while (cpos < cend && ncookies > 0 && - (dp->d_fileno == 0 || ((u_quad_t)(*cookiep)) <= toff)) { -#else - while (dp->d_fileno == 0 && cpos < cend && ncookies > 0) { -#endif + dp = (struct direntry *)cpos; + while (dp->d_fileno == 0 && cpos < cend && nentries > 0) { cpos += dp->d_reclen; - dp = (struct dirent *)cpos; - cookiep++; - ncookies--; + dp = (struct direntry *)cpos; + nentries--; } - if (cpos >= cend || ncookies == 0) { + if (cpos >= cend || nentries == 0) { toff = off; siz = fullsiz; goto again; @@ -3061,70 +3864,56 @@ again: /* * Probe one of the directory entries to see if the filesystem - * supports VGET. See later comment for VFS_VGET changes. + * supports VGET. */ - if (vp->v_tag == VT_UFS) - file = (void *) dp->d_fileno; - else { - file = &dp->d_fileno; - } - - if (error = VFS_VGET(vp->v_mount, file, &nvp)) { - if (error == EOPNOTSUPP) /* let others get passed back */ + if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context))) { + if (error == ENOTSUP) /* let others get passed back */ error = NFSERR_NOTSUPP; - vrele(vp); - _FREE((caddr_t)cookies, M_TEMP); - _FREE((caddr_t)rbuf, M_TEMP); + vnode_put(vp); + vp = NULL; + FREE(rbuf, M_TEMP); nfsm_reply(NFSX_V3POSTOPATTR); nfsm_srvpostop_attr(getret, &at); return (0); } - vput(nvp); + vnode_put(nvp); dirlen = len = NFSX_V3POSTOPATTR + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED; - nfsm_reply(cnt); + 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 + M_TRAILINGSPACE(mp); + be = bp + mbuf_trailingspace(mp); /* Loop through the records and build reply */ - while (cpos < cend && ncookies > 0) { + while (cpos < cend && nentries > 0) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; rem = nfsm_rndup(nlen)-nlen; /* * Got to get the vnode for lookup per entry. - * HFS+/volfs and others use address of file identifier to VGET - * UFS, nullfs, umapfs use inode (u_int32_t) - * until they are consistent, we must differentiate now. - * UFS is the only one of the latter class that is exported. - * Note this will be pulled out as we resolve the VGET issue - * of which it should use u_in32_t or addresses. */ - - if (vp->v_tag == VT_UFS) - file = (void *) dp->d_fileno; - else - file = &dp->d_fileno; - - if (VFS_VGET(vp->v_mount, file, &nvp)) + if (VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, &context)) goto invalid; - bzero((caddr_t)nfhp, NFSX_V3FH); - nfhp->fh_fsid = - nvp->v_mount->mnt_stat.f_fsid; - if (VFS_VPTOFH(nvp, &nfhp->fh_fid)) { - vput(nvp); + isdotdot = ((dp->d_namlen == 2) && + (dp->d_name[0] == '.') && (dp->d_name[1] == '.')); + if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, &context, nfhp)) { + // XXX file handle is optional, so we should be able to + // XXX return this entry without the file handle + vnode_put(nvp); goto invalid; } - if (VOP_GETATTR(nvp, vap, cred, procp)) { - vput(nvp); + nfsm_srv_vattr_init(vap, 1); + if (vnode_getattr(nvp, vap, &context)) { + // XXX attributes are optional, so we should be able to + // XXX return this entry without the attributes + vnode_put(nvp); goto invalid; } - vput(nvp); + vnode_put(nvp); /* * If either the dircount or maxcount will be @@ -3132,35 +3921,39 @@ again: * are calculated conservatively, including all * XDR overheads. */ - len += (8 * NFSX_UNSIGNED + nlen + rem + NFSX_V3FH + + len += (8 * NFSX_UNSIGNED + nlen + rem + nfhp->nfh_len + NFSX_V3POSTOPATTR); dirlen += (6 * NFSX_UNSIGNED + nlen + rem); - if (len > cnt || dirlen > fullsiz) { + if (len > count || dirlen > fullsiz) { eofflag = 0; break; } /* * Build the directory record xdr from - * the dirent entry. + * the direntry entry. */ fp = (struct nfs_fattr *)&fl.fl_fattr; nfsm_srvfillattr(vap, fp); - fl.fl_fhsize = txdr_unsigned(NFSX_V3FH); + fl.fl_fhsize = txdr_unsigned(nfhp->nfh_len); fl.fl_fhok = nfs_true; fl.fl_postopok = nfs_true; - fl.fl_off.nfsuquad[0] = 0; - fl.fl_off.nfsuquad[1] = txdr_unsigned(*cookiep); + 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; - *tl = 0; + txdr_hyper(&dp->d_fileno, &tquad); + *tl = tquad.nfsuquad[0]; bp += NFSX_UNSIGNED; nfsm_clget; - *tl = txdr_unsigned(dp->d_fileno); + *tl = tquad.nfsuquad[1]; bp += NFSX_UNSIGNED; + nfsm_clget; *tl = txdr_unsigned(nlen); bp += NFSX_UNSIGNED; @@ -3187,7 +3980,7 @@ again: /* * Now copy the flrep structure out. */ - xfer = sizeof (struct flrep); + xfer = sizeof(struct flrep) - sizeof(fl.fl_nfh) + fl.fl_fhsize; cp = (caddr_t)&fl; while (xfer > 0) { nfsm_clget; @@ -3204,11 +3997,11 @@ again: } invalid: cpos += dp->d_reclen; - dp = (struct dirent *)cpos; - cookiep++; - ncookies--; + dp = (struct direntry *)cpos; + nentries--; } - vrele(vp); + vnode_put(vp); + vp = NULL; nfsm_clget; *tl = nfs_false; bp += NFSX_UNSIGNED; @@ -3220,12 +4013,14 @@ invalid: bp += NFSX_UNSIGNED; if (mp != mb) { if (bp < be) - mp->m_len = bp - mtod(mp, caddr_t); + mbuf_setlen(mp, bp - (char*)mbuf_data(mp)); } else - mp->m_len += bp - bpos; - FREE((caddr_t)cookies, M_TEMP); - FREE((caddr_t)rbuf, M_TEMP); - nfsm_srvdone; + mbuf_setlen(mp, mbuf_len(mp) + (bp - bpos)); + FREE(rbuf, M_TEMP); +nfsmout: + if (vp) + vnode_put(vp); + return (error); } /* @@ -3235,63 +4030,66 @@ int nfsrv_commit(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - struct vattr bfor, aft; - struct vnode *vp; - nfsfh_t nfh; - fhandle_t *fhp; - register u_long *tl; - register long t1; + 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, rdonly, for_ret = 1, aft_ret = 1, cnt, cache; + int error = 0, for_ret = 1, aft_ret = 1, count; char *cp2; - struct mbuf *mb, *mb2, *mreq; - u_quad_t frev, off; - int didhold; + mbuf_t mb, mb2, mreq; + u_quad_t off; + struct vfs_context context; -#ifndef nolint - cache = 0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); + nfsm_srvmtofh(&nfh); nfsm_dissect(tl, u_long *, 3 * NFSX_UNSIGNED); /* - * XXX At this time VOP_FSYNC() does not accept offset and byte + * XXX At this time VNOP_FSYNC() does not accept offset and byte * count parameters, so these arguments are useless (someday maybe). */ fxdr_hyper(tl, &off); tl += 2; - cnt = fxdr_unsigned(int, *tl); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - for_ret = VOP_GETATTR(vp, &bfor, cred, procp); - didhold = ubc_hold(vp); - error = VOP_FSYNC(vp, cred, MNT_WAIT, procp); - aft_ret = VOP_GETATTR(vp, &aft, cred, procp); - VOP_UNLOCK(vp, 0, procp); - if (didhold) - ubc_rele(vp); - vrele(vp); + 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.tv_sec); - *tl = txdr_unsigned(boottime.tv_usec); + *tl++ = txdr_unsigned(boottime_sec()); + *tl = txdr_unsigned(0); } else return (0); - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -3301,44 +4099,55 @@ int nfsrv_statfs(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register struct statfs *sf; - register struct nfs_statfs *sfp; - register u_long *tl; - register long t1; + struct vfs_attr va; + struct nfs_statfs *sfp; + u_long *tl; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, getret = 1; + int error = 0, getret = 1; int v3 = (nfsd->nd_flag & ND_NFSV3); char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - struct vattr at; - nfsfh_t nfh; - fhandle_t *fhp; - struct statfs statfs; - u_quad_t frev, tval; + 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; -#ifndef nolint - cache = 0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - sf = &statfs; - error = VFS_STATFS(vp->v_mount, sf, procp); - getret = VOP_GETATTR(vp, &at, cred, procp); - vput(vp); + 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); @@ -3346,30 +4155,25 @@ nfsrv_statfs(nfsd, slp, procp, mrq) return (0); nfsm_build(sfp, struct nfs_statfs *, NFSX_STATFS(v3)); if (v3) { - tval = (u_quad_t)sf->f_blocks; - tval *= (u_quad_t)sf->f_bsize; + tval = (u_quad_t)(va.f_blocks * blksize); txdr_hyper(&tval, &sfp->sf_tbytes); - tval = (u_quad_t)sf->f_bfree; - tval *= (u_quad_t)sf->f_bsize; + tval = (u_quad_t)(va.f_bfree * blksize); txdr_hyper(&tval, &sfp->sf_fbytes); - tval = (u_quad_t)sf->f_bavail; - tval *= (u_quad_t)sf->f_bsize; + tval = (u_quad_t)(va.f_bavail * blksize); txdr_hyper(&tval, &sfp->sf_abytes); - sfp->sf_tfiles.nfsuquad[0] = 0; - sfp->sf_tfiles.nfsuquad[1] = txdr_unsigned(sf->f_files); - sfp->sf_ffiles.nfsuquad[0] = 0; - sfp->sf_ffiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); - sfp->sf_afiles.nfsuquad[0] = 0; - sfp->sf_afiles.nfsuquad[1] = txdr_unsigned(sf->f_ffree); + 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(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); + 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); } - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -3379,39 +4183,44 @@ int nfsrv_fsinfo(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register u_long *tl; - register struct nfsv3_fsinfo *sip; - register long t1; + u_long *tl; + struct nfsv3_fsinfo *sip; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, getret = 1, pref, max; + int error = 0, getret = 1, prefsize, maxsize; char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - struct vattr at; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; + 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; -#ifndef nolint - cache = 0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - getret = VOP_GETATTR(vp, &at, cred, procp); - vput(vp); + 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); @@ -3421,17 +4230,18 @@ nfsrv_fsinfo(nfsd, slp, procp, mrq) * There should be file system VFS OP(s) to get this information. * For now, assume our usual NFS defaults. */ - if (slp->ns_so->so_type == SOCK_DGRAM) - max = pref = NFS_MAXDGRAMDATA; - else - max = pref = NFS_MAXDATA; - sip->fs_rtmax = txdr_unsigned(max); - sip->fs_rtpref = txdr_unsigned(pref); + 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(max); - sip->fs_wtpref = txdr_unsigned(pref); + sip->fs_wtmax = txdr_unsigned(maxsize); + sip->fs_wtpref = txdr_unsigned(prefsize); sip->fs_wtmult = txdr_unsigned(NFS_FABLKSIZE); - sip->fs_dtpref = txdr_unsigned(pref); + sip->fs_dtpref = txdr_unsigned(prefsize); sip->fs_maxfilesize.nfsuquad[0] = 0xffffffff; sip->fs_maxfilesize.nfsuquad[1] = 0xffffffff; sip->fs_timedelta.nfsv3_sec = 0; @@ -3439,7 +4249,8 @@ nfsrv_fsinfo(nfsd, slp, procp, mrq) sip->fs_properties = txdr_unsigned(NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK | NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME); - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -3449,51 +4260,56 @@ int nfsrv_pathconf(nfsd, slp, procp, mrq) struct nfsrv_descript *nfsd; struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; + proc_t procp; + mbuf_t *mrq; { - struct mbuf *mrep = nfsd->nd_mrep, *md = nfsd->nd_md; - struct mbuf *nam = nfsd->nd_nam; + mbuf_t mrep = nfsd->nd_mrep, md = nfsd->nd_md; + mbuf_t nam = nfsd->nd_nam; caddr_t dpos = nfsd->nd_dpos; - struct ucred *cred = &nfsd->nd_cr; - register u_long *tl; - register struct nfsv3_pathconf *pc; - register long t1; + u_long *tl; + struct nfsv3_pathconf *pc; + long t1; caddr_t bpos; - int error = 0, rdonly, cache, getret = 1, linkmax, namemax; + int error = 0, getret = 1, linkmax, namemax; int chownres, notrunc, case_sensitive, case_preserving; char *cp2; - struct mbuf *mb, *mb2, *mreq; - struct vnode *vp; - struct vattr at; - nfsfh_t nfh; - fhandle_t *fhp; - u_quad_t frev; + 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; -#ifndef nolint - cache = 0; -#endif - fhp = &nfh.fh_generic; - nfsm_srvmtofh(fhp); - if ((error = nfsrv_fhtovp(fhp, 1, &vp, cred, slp, nam, - &rdonly, (nfsd->nd_flag & ND_KERBAUTH), TRUE))) { + 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); } - error = VOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax); + context.vc_proc = procp; + context.vc_ucred = nfsd->nd_cr; + + error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, &context); if (!error) - error = VOP_PATHCONF(vp, _PC_NAME_MAX, &namemax); + error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, &context); if (!error) - error = VOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres); + error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, &context); if (!error) - error = VOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc); + error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc, &context); if (!error) - error = VOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive); + error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, &context); if (!error) - error = VOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving); - getret = VOP_GETATTR(vp, &at, cred, procp); - vput(vp); + 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) @@ -3507,7 +4323,8 @@ nfsrv_pathconf(nfsd, slp, procp, mrq) pc->pc_caseinsensitive = txdr_unsigned(!case_sensitive); pc->pc_casepreserving = txdr_unsigned(case_preserving); - nfsm_srvdone; +nfsmout: + return (error); } /* @@ -3515,22 +4332,19 @@ nfsrv_pathconf(nfsd, slp, procp, mrq) */ /* ARGSUSED */ int -nfsrv_null(nfsd, slp, procp, mrq) - struct nfsrv_descript *nfsd; - struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; +nfsrv_null( + struct nfsrv_descript *nfsd, + struct nfssvc_sock *slp, + __unused proc_t procp, + mbuf_t *mrq) { - struct mbuf *mrep = nfsd->nd_mrep; + mbuf_t mrep = nfsd->nd_mrep; caddr_t bpos; - int error = NFSERR_RETVOID, cache; - struct mbuf *mb, *mreq; - u_quad_t frev; + int error = NFSERR_RETVOID; + mbuf_t mb, mreq; -#ifndef nolint - cache = 0; -#endif nfsm_reply(0); +nfsmout: return (0); } @@ -3539,83 +4353,79 @@ nfsrv_null(nfsd, slp, procp, mrq) */ /* ARGSUSED */ int -nfsrv_noop(nfsd, slp, procp, mrq) - struct nfsrv_descript *nfsd; - struct nfssvc_sock *slp; - struct proc *procp; - struct mbuf **mrq; +nfsrv_noop( + struct nfsrv_descript *nfsd, + struct nfssvc_sock *slp, + __unused proc_t procp, + mbuf_t *mrq) { - struct mbuf *mrep = nfsd->nd_mrep; + mbuf_t mrep = nfsd->nd_mrep; caddr_t bpos; - int error, cache; - struct mbuf *mb, *mreq; - u_quad_t frev; + int error; + mbuf_t mb, mreq; -#ifndef nolint - cache = 0; -#endif if (nfsd->nd_repstat) error = nfsd->nd_repstat; else error = EPROCUNAVAIL; nfsm_reply(0); +nfsmout: return (0); } /* * 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. + * vnode_authorize() 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. - - * The exception to rule 2 is EPERM. If a file is IMMUTABLE, VOP_ACCESS() + * + * The exception to rule 2 is EPERM. If a file is IMMUTABLE, vnode_authorize() * will return EPERM instead of EACCESS. EPERM is always an error. */ static int -nfsrv_access(vp, flags, cred, rdonly, p, override) - register struct vnode *vp; - int flags; - register struct ucred *cred; - int rdonly; - struct proc *p; - int override; +nfsrv_authorize( + vnode_t vp, + vnode_t dvp, + kauth_action_t action, + vfs_context_t context, + struct nfs_export_options *nxo, + int override) { - struct vattr vattr; + struct vnode_attr vattr; int error; - if (flags & VWRITE) { - /* Just vn_writechk() changed to check rdonly */ + + if (action & KAUTH_VNODE_WRITE_RIGHTS) { /* - * Disallow write attempts on read-only file systems; + * Disallow write attempts on read-only exports; * 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)) { - switch (vp->v_type) { + if (nxo->nxo_flags & NX_READONLY) { + switch (vnode_vtype(vp)) { case VREG: case VDIR: case VLNK: case VCPLX: return (EROFS); + default: + break; } } - /* - * If there's shared text associated with - * the inode, we can't allow writing. - */ - if (vp->v_flag & VTEXT) - return (ETXTBSY); } - if ((error = VOP_GETATTR(vp, &vattr, cred, p))) - return (error); - error = VOP_ACCESS(vp, flags, cred, p); + error = vnode_authorize(vp, dvp, action, context); /* * Allow certain operations for the owner (reads and writes * on files that are already open). Picking up from FreeBSD. */ - if (override && error == EACCES && cred->cr_uid == vattr.va_uid) - error = 0; + 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 /* NFS_NOSERVER */