X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3e170ce000f1506b7b5d2c5c7faec85ceabb573d..HEAD:/bsd/nfs/nfs_serv.c?ds=inline diff --git a/bsd/nfs/nfs_serv.c b/bsd/nfs/nfs_serv.c index 7b9df74b4..0346a7502 100644 --- a/bsd/nfs/nfs_serv.c +++ b/bsd/nfs/nfs_serv.c @@ -1,8 +1,8 @@ /* - * Copyright (c) 2000-2014 Apple Inc. All rights reserved. + * Copyright (c) 2000-2020 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ - * + * * 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 @@ -11,10 +11,10 @@ * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. - * + * * 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 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, @@ -22,7 +22,7 @@ * 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. - * + * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ @@ -65,6 +65,9 @@ * FreeBSD-Id: nfs_serv.c,v 1.52 1997/10/28 15:59:05 bde Exp $ */ +#include +#if CONFIG_NFS_SERVER + #include #include #include @@ -85,10 +88,13 @@ #include #include #include +#include #include #include +#include + #include #include @@ -99,7 +105,10 @@ #include #include -#if NFSSERVER +#if CONFIG_MACF +#include +#include +#endif /* * NFS server globals @@ -107,14 +116,14 @@ int nfsd_thread_count = 0; int nfsd_thread_max = 0; -lck_grp_t *nfsd_lck_grp; -lck_mtx_t *nfsd_mutex; +static LCK_GRP_DECLARE(nfsd_lck_grp, "nfsd"); +LCK_MTX_DECLARE(nfsd_mutex, &nfsd_lck_grp); struct nfsd_head nfsd_head, nfsd_queue; -lck_grp_t *nfsrv_slp_rwlock_group; -lck_grp_t *nfsrv_slp_mutex_group; +LCK_GRP_DECLARE(nfsrv_slp_rwlock_group, "nfsrv-slp-rwlock"); +LCK_GRP_DECLARE(nfsrv_slp_mutex_group, "nfsrv-slp-mutex"); struct nfsrv_sockhead nfsrv_socklist, nfsrv_sockwg, - nfsrv_sockwait, nfsrv_sockwork; + nfsrv_sockwait, nfsrv_sockwork; struct nfsrv_sock *nfsrv_udpsock = NULL; struct nfsrv_sock *nfsrv_udp6sock = NULL; @@ -123,25 +132,25 @@ struct nfsrv_expfs_list nfsrv_exports; struct nfsrv_export_hashhead *nfsrv_export_hashtbl = NULL; int nfsrv_export_hash_size = NFSRVEXPHASHSZ; u_long nfsrv_export_hash; -lck_grp_t *nfsrv_export_rwlock_group; -lck_rw_t nfsrv_export_rwlock; +static LCK_GRP_DECLARE(nfsrv_export_rwlock_group, "nfsrv-export-rwlock"); +LCK_RW_DECLARE(nfsrv_export_rwlock, &nfsrv_export_rwlock_group); #if CONFIG_FSE /* NFS server file modification event generator */ struct nfsrv_fmod_hashhead *nfsrv_fmod_hashtbl; u_long nfsrv_fmod_hash; -lck_grp_t *nfsrv_fmod_grp; -lck_mtx_t *nfsrv_fmod_mutex; +static LCK_GRP_DECLARE(nfsrv_fmod_grp, "nfsrv_fmod"); +LCK_MTX_DECLARE(nfsrv_fmod_mutex, &nfsrv_fmod_grp); static int nfsrv_fmod_timer_on = 0; int nfsrv_fsevents_enabled = 1; #endif /* NFS server timers */ #if CONFIG_FSE -thread_call_t nfsrv_fmod_timer_call; +thread_call_t nfsrv_fmod_timer_call; #endif -thread_call_t nfsrv_idlesock_timer_call; -thread_call_t nfsrv_wg_timer_call; +thread_call_t nfsrv_idlesock_timer_call; +thread_call_t nfsrv_wg_timer_call; int nfsrv_wg_timer_on; /* globals for the active user list */ @@ -149,14 +158,14 @@ uint32_t nfsrv_user_stat_enabled = 1; uint32_t nfsrv_user_stat_node_count = 0; uint32_t nfsrv_user_stat_max_idle_sec = NFSRV_USER_STAT_DEF_IDLE_SEC; uint32_t nfsrv_user_stat_max_nodes = NFSRV_USER_STAT_DEF_MAX_NODES; -lck_grp_t *nfsrv_active_user_mutex_group; +LCK_GRP_DECLARE(nfsrv_active_user_mutex_group, "nfs-active-user-mutex"); int nfsrv_wg_delay = NFSRV_WGATHERDELAY * 1000; int nfsrv_wg_delay_v3 = 0; int nfsrv_async = 0; -int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int); +int nfsrv_authorize(vnode_t, vnode_t, kauth_action_t, vfs_context_t, struct nfs_export_options*, int); int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *); void nfsrv_modified(vnode_t, vfs_context_t); @@ -167,15 +176,15 @@ extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, * Initialize the data structures for the server. */ -#define NFSRV_NOT_INITIALIZED 0 -#define NFSRV_INITIALIZING 1 -#define NFSRV_INITIALIZED 2 +#define NFSRV_NOT_INITIALIZED 0 +#define NFSRV_INITIALIZING 1 +#define NFSRV_INITIALIZED 2 static volatile UInt32 nfsrv_initted = NFSRV_NOT_INITIALIZED; int nfsrv_is_initialized(void) { - return (nfsrv_initted == NFSRV_INITIALIZED); + return nfsrv_initted == NFSRV_INITIALIZED; } void @@ -184,39 +193,22 @@ nfsrv_init(void) /* make sure we init only once */ if (!OSCompareAndSwap(NFSRV_NOT_INITIALIZED, NFSRV_INITIALIZING, &nfsrv_initted)) { /* wait until initialization is complete */ - while (!nfsrv_is_initialized()) + while (!nfsrv_is_initialized()) { IOSleep(500); + } return; } - if (sizeof (struct nfsrv_sock) > NFS_SVCALLOC) - printf("struct nfsrv_sock bloated (> %dbytes)\n",NFS_SVCALLOC); - - /* init nfsd mutex */ - nfsd_lck_grp = lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL); - nfsd_mutex = lck_mtx_alloc_init(nfsd_lck_grp, LCK_ATTR_NULL); - - /* init slp rwlock */ - nfsrv_slp_rwlock_group = lck_grp_alloc_init("nfsrv-slp-rwlock", LCK_GRP_ATTR_NULL); - nfsrv_slp_mutex_group = lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL); + if (sizeof(struct nfsrv_sock) > NFS_SVCALLOC) { + printf("struct nfsrv_sock bloated (> %dbytes)\n", NFS_SVCALLOC); + } /* init export data structures */ LIST_INIT(&nfsrv_exports); - nfsrv_export_rwlock_group = lck_grp_alloc_init("nfsrv-export-rwlock", LCK_GRP_ATTR_NULL); - lck_rw_init(&nfsrv_export_rwlock, nfsrv_export_rwlock_group, LCK_ATTR_NULL); - - /* init active user list mutex structures */ - nfsrv_active_user_mutex_group = lck_grp_alloc_init("nfs-active-user-mutex", LCK_GRP_ATTR_NULL); - - /* init nfs server request cache mutex */ - nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", LCK_GRP_ATTR_NULL); - nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, LCK_ATTR_NULL); #if CONFIG_FSE /* init NFS server file modified event generation */ nfsrv_fmod_hashtbl = hashinit(NFSRVFMODHASHSZ, M_TEMP, &nfsrv_fmod_hash); - nfsrv_fmod_grp = lck_grp_alloc_init("nfsrv_fmod", LCK_GRP_ATTR_NULL); - nfsrv_fmod_mutex = lck_mtx_alloc_init(nfsrv_fmod_grp, LCK_ATTR_NULL); #endif /* initialize NFS server timer callouts */ @@ -238,7 +230,7 @@ nfsrv_init(void) /* Setup the up-call handling */ nfsrv_uc_init(); - + /* initialization complete */ nfsrv_initted = NFSRV_INITIALIZED; } @@ -319,13 +311,15 @@ nfsrv_access( */ if (nfsmode & NFS_ACCESS_READ) { testaction = vnode_isdir(vp) ? KAUTH_VNODE_LIST_DIRECTORY : KAUTH_VNODE_READ_DATA; - if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) + if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) { nfsmode &= ~NFS_ACCESS_READ; + } } if ((nfsmode & NFS_ACCESS_LOOKUP) && (!vnode_isdir(vp) || - nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx, nxo, 0))) + nfsrv_authorize(vp, NULL, KAUTH_VNODE_SEARCH, ctx, nxo, 0))) { nfsmode &= ~NFS_ACCESS_LOOKUP; + } if (nfsmode & NFS_ACCESS_MODIFY) { if (vnode_isdir(vp)) { testaction = @@ -336,8 +330,9 @@ nfsrv_access( testaction = KAUTH_VNODE_WRITE_DATA; } - if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) + if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) { nfsmode &= ~NFS_ACCESS_MODIFY; + } } if (nfsmode & NFS_ACCESS_EXTEND) { if (vnode_isdir(vp)) { @@ -349,8 +344,9 @@ nfsrv_access( KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA; } - if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) + if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0)) { nfsmode &= ~NFS_ACCESS_EXTEND; + } } /* @@ -366,8 +362,9 @@ nfsrv_access( if ((nfsmode & NFS_ACCESS_EXECUTE) && (vnode_isdir(vp) || - nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 0))) + nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 0))) { nfsmode &= ~NFS_ACCESS_EXECUTE; + } /* get postop attributes */ nfsm_srv_vattr_init(&vattr, NFS_VER3); @@ -381,17 +378,19 @@ nfsmerr: *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr); - if (!nd->nd_repstat) + if (!nd->nd_repstat) { nfsm_chain_add_32(error, &nmrep, nfsmode); + } nfsmout: nfsm_chain_build_done(error, &nmrep); - if (vp) + if (vp) { vnode_put(vp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -432,8 +431,27 @@ nfsrv_getattr( error = nfsrv_credcheck(nd, ctx, nx, nxo); nfsmerr_if(error); +#if CONFIG_MACF + if (mac_vnode_check_open(ctx, vp, FREAD)) { + error = ESTALE; + } + nfsmerr_if(error); +#endif + nfsm_srv_vattr_init(&vattr, nd->nd_vers); error = vnode_getattr(vp, &vattr, ctx); + +#if CONFIG_MACF + /* XXXab: Comment in the VFS code makes it sound like + * some arguments can be filtered out, but not + * what it actually means. Hopefully not like + * they gonna set mtime to 0 or something. For + * now trust there are no shenanigans here. + */ + error = mac_vnode_check_getattr(ctx, NOCRED, vp, &vattr); + nfsmerr_if(error); +#endif + vnode_put(vp); vp = NULL; @@ -447,13 +465,14 @@ nfsmerr: error = nfsm_chain_add_fattr(nd, &nmrep, &vattr); nfsmout: nfsm_chain_build_done(error, &nmrep); - if (vp) + if (vp) { vnode_put(vp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -474,7 +493,7 @@ nfsrv_setattr( struct nfs_export_options *nxo; int error, preattrerr, postattrerr, gcheck; struct nfs_filehandle nfh; - struct timespec guard = { 0, 0 }; + struct timespec guard = { .tv_sec = 0, .tv_nsec = 0 }; kauth_action_t action; uid_t saved_uid; @@ -493,8 +512,9 @@ nfsrv_setattr( error = nfsm_chain_get_sattr(nd, nmreq, vap); if (nd->nd_vers == NFS_VER3) { nfsm_chain_get_32(error, nmreq, gcheck); - if (gcheck) + if (gcheck) { nfsm_chain_get_time(error, nmreq, nd->nd_vers, guard.tv_sec, guard.tv_nsec); + } } nfsmerr_if(error); @@ -523,11 +543,13 @@ nfsrv_setattr( nfsm_srv_pre_vattr_init(&preattr); error = preattrerr = vnode_getattr(vp, &preattr, ctx); if (!error && gcheck && VATTR_IS_SUPPORTED(&preattr, va_change_time) && - (preattr.va_change_time.tv_sec != guard.tv_sec || - preattr.va_change_time.tv_nsec != guard.tv_nsec)) + (preattr.va_change_time.tv_sec != guard.tv_sec || + preattr.va_change_time.tv_nsec != guard.tv_nsec)) { error = NFSERR_NOT_SYNC; - if (!preattrerr && !VATTR_ALL_SUPPORTED(&preattr)) + } + if (!preattrerr && !VATTR_ALL_SUPPORTED(&preattr)) { preattrerr = ENOENT; + } nfsmerr_if(error); } @@ -538,29 +560,66 @@ nfsrv_setattr( if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) { int ismember; VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr)); - if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember) + if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember) { VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr)); + } } /* Authorize the attribute changes. */ error = vnode_authattr(vp, vap, &action, ctx); - if (!error) + if (!error) { error = nfsrv_authorize(vp, NULL, action, ctx, nxo, 0); + } + +#if CONFIG_MACF + if (!error && mac_vnode_check_open(ctx, vp, FREAD | FWRITE)) { + error = ESTALE; + } + if (!error) { + /* chown case */ + if (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid)) { + error = mac_vnode_check_setowner(ctx, vp, + VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : -1, + VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : -1); + } + /* chmod case */ + if (!error && VATTR_IS_ACTIVE(vap, va_mode)) { + error = mac_vnode_check_setmode(ctx, vp, (mode_t)vap->va_mode); + } + /* truncate case */ + if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) { + /* NOTE: File has not been open for NFS case, so NOCRED for filecred */ + error = mac_vnode_check_truncate(ctx, NOCRED, vp); + } + /* set utimes case */ + if (!error && (VATTR_IS_ACTIVE(vap, va_access_time) || VATTR_IS_ACTIVE(vap, va_modify_time))) { + struct timespec current_time; + nanotime(¤t_time); + + error = mac_vnode_check_setutimes(ctx, vp, + VATTR_IS_ACTIVE(vap, va_access_time) ? vap->va_access_time : current_time, + VATTR_IS_ACTIVE(vap, va_modify_time) ? vap->va_modify_time : current_time); + } + } +#endif /* set the new attributes */ - if (!error) + if (!error) { error = vnode_setattr(vp, vap, ctx); + } if (!error || (nd->nd_vers == NFS_VER3)) { nfsm_srv_vattr_init(&postattr, nd->nd_vers); postattrerr = vnode_getattr(vp, &postattr, ctx); - if (!error) + if (!error) { error = postattrerr; + } } nfsmerr: - if (vp) + if (vp) { vnode_put(vp); + } /* assemble reply */ nd->nd_repstat = error; @@ -568,18 +627,19 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); - else + preattrerr, &preattr, postattrerr, &postattr); + } else { error = nfsm_chain_add_fattr(nd, &nmrep, &postattr); + } nfsmout: nfsm_chain_build_done(error, &nmrep); if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -630,6 +690,18 @@ nfsrv_lookup( /* update active user stats */ nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, 0); } + if (!error && mac_vnode_check_open(ctx, ni.ni_vp, FREAD)) { + error = EACCES; + if (dirp) { + vnode_put(dirp); + dirp = NULL; + } + + if (ni.ni_vp) { + vnode_put(ni.ni_vp); + ni.ni_vp = NULL; + } + } } if (dirp) { @@ -655,12 +727,13 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) + - NFSX_POSTOPORFATTR(nd->nd_vers) + NFSX_POSTOPATTR(nd->nd_vers)); + NFSX_POSTOPORFATTR(nd->nd_vers) + NFSX_POSTOPATTR(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; if (nd->nd_repstat) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, dirattrerr, &dirattr); + } goto nfsmout; } nfsm_chain_add_fh(error, &nmrep, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len); @@ -676,7 +749,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -698,7 +771,7 @@ nfsrv_readlink( struct nfsm_chain *nmreq, nmrep; mbuf_t mpath, mp; uio_t auio = NULL; - char uio_buf[ UIO_SIZEOF(4) ]; + char uio_buf[UIO_SIZEOF(4)]; char *uio_bufp = &uio_buf[0]; int uio_buflen = UIO_SIZEOF(4); @@ -719,17 +792,20 @@ nfsrv_readlink( if (mpcnt > 4) { uio_buflen = UIO_SIZEOF(mpcnt); MALLOC(uio_bufp, char*, uio_buflen, M_TEMP, M_WAITOK); - if (!uio_bufp) + if (!uio_bufp) { error = ENOMEM; + } nfsmerr_if(error); } auio = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen); - if (!auio) + if (!auio) { error = ENOMEM; + } nfsmerr_if(error); - for (mp = mpath; mp; mp = mbuf_next(mp)) + for (mp = mpath; mp; mp = mbuf_next(mp)) { uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp)); + } error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo); nfsmerr_if(error); @@ -744,16 +820,28 @@ nfsrv_readlink( nfsmerr_if(error); if (vnode_vtype(vp) != VLNK) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EINVAL; - else + } else { error = ENXIO; + } } - if (!error) + if (!error) { error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 0); - if (!error) + } +#if CONFIG_MACF + if (mac_vnode_check_open(ctx, vp, FREAD)) { + error = ESTALE; + } + nfsmerr_if(error); + if (!error) { + error = mac_vnode_check_readlink(ctx, vp); + } +#endif + if (!error) { error = VNOP_READLINK(vp, auio, ctx); + } if (vp) { if (nd->nd_vers == NFS_VER3) { nfsm_srv_vattr_init(&vattr, NFS_VER3); @@ -774,8 +862,9 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &vattr); + } if (error || nd->nd_repstat) { nfsm_chain_build_done(error, &nmrep); goto nfsmout; @@ -783,26 +872,30 @@ nfsmerr: if (auio && (uio_resid(auio) > 0)) { len -= uio_resid(auio); tlen = nfsm_rndup(len); - nfsm_adj(mpath, NFS_MAXPATHLEN-tlen, tlen-len); + nfsm_adj(mpath, NFS_MAXPATHLEN - tlen, tlen - len); } nfsm_chain_add_32(error, &nmrep, len); nfsm_chain_build_done(error, &nmrep); nfsmout_if(error); error = mbuf_setnext(nmrep.nmc_mcur, mpath); - if (!error) + if (!error) { mpath = NULL; + } nfsmout: - if (vp) + if (vp) { vnode_put(vp); - if (mpath) + } + if (mpath) { mbuf_freem(mpath); - if (uio_bufp != &uio_buf[0]) + } + if (uio_bufp != &uio_buf[0]) { FREE(uio_bufp, M_TEMP); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -816,18 +909,18 @@ nfsrv_read( mbuf_t *mrepp) { int error, attrerr, mreadcnt; - uint32_t reqlen, maxlen, count, len, tlen, left; + uint32_t reqlen, maxlen, count, len, tlen; mbuf_t mread, m; vnode_t vp; struct nfs_filehandle nfh; - struct nfs_export *nx; + struct nfs_export *nx = NULL; struct nfs_export_options *nxo; uio_t auio = NULL; char *uio_bufp = NULL; struct vnode_attr vattr, *vap = &vattr; off_t off; uid_t saved_uid; - char uio_buf[ UIO_SIZEOF(0) ]; + char uio_buf[UIO_SIZEOF(0)]; struct nfsm_chain *nmreq, nmrep; error = 0; @@ -841,14 +934,16 @@ nfsrv_read( nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len); nfsmerr_if(error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_get_64(error, nmreq, off); - else + } else { nfsm_chain_get_32(error, nmreq, off); + } nfsm_chain_get_32(error, nmreq, reqlen); maxlen = NFSRV_NDMAXDATA(nd); - if (reqlen > maxlen) + if (reqlen > maxlen) { reqlen = maxlen; + } nfsmerr_if(error); error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo); nfsmerr_if(error); @@ -860,44 +955,66 @@ nfsrv_read( nfsmerr_if(error); if (vnode_vtype(vp) != VREG) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EINVAL; - else + } else { error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; + } } if (!error) { - if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 1))) - error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 1); + if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 1))) { + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_EXECUTE, ctx, nxo, 1); + } } +#if CONFIG_MACF + if (!error) { + error = mac_vnode_check_open(ctx, vp, FREAD); + if (error) { + error = EACCES; + } else { + /* XXXab: Do we need to do this?! */ + error = mac_vnode_check_read(ctx, vfs_context_ucred(ctx), vp); + if (error) { + error = EACCES; + } + /* mac_vnode_check_exec() can't be done here. */ + } + } + nfsmerr_if(error); +#endif nfsm_srv_vattr_init(vap, nd->nd_vers); attrerr = vnode_getattr(vp, vap, ctx); - if (!error) + if (!error) { error = attrerr; + } nfsmerr_if(error); - if ((u_quad_t)off >= vap->va_data_size) + 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 + } else if (((u_quad_t)off + reqlen) > vap->va_data_size) { + count = (int)nfsm_rndup(vap->va_data_size - off); + } else { count = reqlen; + } - len = left = count; + len = count; if (count > 0) { /* get mbuf list to hold read data */ error = nfsm_mbuf_get_list(count, &mread, &mreadcnt); nfsmerr_if(error); MALLOC(uio_bufp, char *, UIO_SIZEOF(mreadcnt), M_TEMP, M_WAITOK); - if (uio_bufp) + if (uio_bufp) { auio = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE, - UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt)); + UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt)); + } if (!uio_bufp || !auio) { error = ENOMEM; goto errorexit; } - for (m = mread; m; m = mbuf_next(m)) + for (m = mread; m; m = mbuf_next(m)) { uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m)); + } error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx); } else { auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf)); @@ -911,8 +1028,9 @@ errorexit: if (!error || (nd->nd_vers == NFS_VER3)) { nfsm_srv_vattr_init(vap, nd->nd_vers); attrerr = vnode_getattr(vp, vap, ctx); - if (!error && (nd->nd_vers == NFS_VER2)) + if (!error && (nd->nd_vers == NFS_VER2)) { error = attrerr; /* NFSv2 must have attributes to return */ + } } nfsmerr_if(error); @@ -922,8 +1040,9 @@ errorexit: /* trim off any data not actually read */ len -= uio_resid(auio); tlen = nfsm_rndup(len); - if (count != tlen || tlen != len) + if (count != tlen || tlen != len) { nfsm_adj(mread, count - tlen, tlen - len); + } nfsmerr: /* assemble reply */ @@ -932,8 +1051,9 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap); + } if (error || nd->nd_repstat) { nfsm_chain_build_done(error, &nmrep); goto nfsmout; @@ -948,8 +1068,9 @@ nfsmerr: nfsm_chain_build_done(error, &nmrep); nfsmout_if(error); error = mbuf_setnext(nmrep.nmc_mcur, mread); - if (!error) + if (!error) { mread = NULL; + } /* update export stats */ NFSStatAdd64(&nx->nx_stats.bytes_read, len); @@ -957,17 +1078,20 @@ nfsmerr: /* update active user stats */ nfsrv_update_user_stat(nx, nd, saved_uid, 1, len, 0); nfsmout: - if (vp) + if (vp) { vnode_put(vp); - if (mread) + } + if (mread) { mbuf_freem(mread); - if (uio_bufp != NULL) + } + if (uio_bufp != NULL) { FREE(uio_bufp, M_TEMP); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } #if CONFIG_FSE @@ -983,9 +1107,9 @@ nfsmout: * "content modified" fsevent only if there are no writes to * a vnode for nfsrv_fmod_pendtime milliseconds. */ -int nfsrv_fmod_pending; /* count of vnodes being written to */ -int nfsrv_fmod_pendtime = 1000; /* msec to wait */ -int nfsrv_fmod_min_interval = 100; /* msec min interval between callbacks */ +int nfsrv_fmod_pending; /* count of vnodes being written to */ +int nfsrv_fmod_pendtime = 1000; /* msec to wait */ +int nfsrv_fmod_min_interval = 100; /* msec min interval between callbacks */ /* * This function is called via the kernel's callout @@ -999,14 +1123,15 @@ nfsrv_fmod_timer(__unused void *param0, __unused void *param1) struct nfsrv_fmod_hashhead *headp, firehead; struct nfsrv_fmod *fp, *nfp, *pfp; uint64_t timenow, next_deadline; - int interval = 0, i, fmod_fire; + time_t interval = 0; + int i, fmod_fire; LIST_INIT(&firehead); - lck_mtx_lock(nfsrv_fmod_mutex); + lck_mtx_lock(&nfsrv_fmod_mutex); again: clock_get_uptime(&timenow); clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000, - &next_deadline); + &next_deadline); /* * Scan all the hash chains @@ -1019,10 +1144,12 @@ again: */ headp = &nfsrv_fmod_hashtbl[i]; LIST_FOREACH(fp, headp, fm_link) { - if (timenow >= fp->fm_deadline) + if (timenow >= fp->fm_deadline) { break; - if (fp->fm_deadline < next_deadline) + } + if (fp->fm_deadline < next_deadline) { next_deadline = fp->fm_deadline; + } } /* @@ -1037,17 +1164,18 @@ again: nfp = LIST_NEXT(fp, fm_link); LIST_REMOVE(fp, fm_link); fmod_fire++; - if (pfp) + if (pfp) { LIST_INSERT_AFTER(pfp, fp, fm_link); - else + } else { LIST_INSERT_HEAD(&firehead, fp, fm_link); + } pfp = fp; fp = nfp; } } if (fmod_fire) { - lck_mtx_unlock(nfsrv_fmod_mutex); + lck_mtx_unlock(&nfsrv_fmod_mutex); /* * Fire off the content modified fsevent for each * entry and free it. @@ -1056,15 +1184,15 @@ again: if (nfsrv_fsevents_enabled) { fp->fm_context.vc_thread = current_thread(); add_fsevent(FSE_CONTENT_MODIFIED, &fp->fm_context, - FSE_ARG_VNODE, fp->fm_vp, - FSE_ARG_DONE); + FSE_ARG_VNODE, fp->fm_vp, + FSE_ARG_DONE); } vnode_put(fp->fm_vp); kauth_cred_unref(&fp->fm_context.vc_ucred); LIST_REMOVE(fp, fm_link); FREE(fp, M_TEMP); } - lck_mtx_lock(nfsrv_fmod_mutex); + lck_mtx_lock(&nfsrv_fmod_mutex); nfsrv_fmod_pending -= fmod_fire; goto again; } @@ -1076,16 +1204,18 @@ again: * entry is ready to send its fsevent. */ if (nfsrv_fmod_pending > 0) { - interval = (next_deadline - timenow) / (1000 * 1000); - if (interval < nfsrv_fmod_min_interval) + interval = ((time_t)(next_deadline - timenow)) / (1000 * 1000); + if (interval < nfsrv_fmod_min_interval) { interval = nfsrv_fmod_min_interval; + } } nfsrv_fmod_timer_on = interval > 0; - if (nfsrv_fmod_timer_on) + if (nfsrv_fmod_timer_on) { nfs_interval_timer_start(nfsrv_fmod_timer_call, interval); + } - lck_mtx_unlock(nfsrv_fmod_mutex); + lck_mtx_unlock(&nfsrv_fmod_mutex); } /* @@ -1101,7 +1231,7 @@ nfsrv_modified(vnode_t vp, vfs_context_t ctx) struct nfsrv_fmod *fp; struct nfsrv_fmod_hashhead *head; - lck_mtx_lock(nfsrv_fmod_mutex); + lck_mtx_lock(&nfsrv_fmod_mutex); /* * Compute the time in the future when the @@ -1122,7 +1252,7 @@ nfsrv_modified(vnode_t vp, vfs_context_t ctx) LIST_REMOVE(fp, fm_link); LIST_INSERT_HEAD(head, fp, fm_link); } - lck_mtx_unlock(nfsrv_fmod_mutex); + lck_mtx_unlock(&nfsrv_fmod_mutex); return; } } @@ -1132,8 +1262,9 @@ nfsrv_modified(vnode_t vp, vfs_context_t ctx) * Allocate a new file mod entry and add it * on the front of the hash chain. */ - if (vnode_get(vp) != 0) + if (vnode_get(vp) != 0) { goto done; + } MALLOC(fp, struct nfsrv_fmod *, sizeof(*fp), M_TEMP, M_WAITOK); if (fp == NULL) { vnode_put(vp); @@ -1153,10 +1284,10 @@ nfsrv_modified(vnode_t vp, vfs_context_t ctx) if (!nfsrv_fmod_timer_on) { nfsrv_fmod_timer_on = 1; nfs_interval_timer_start(nfsrv_fmod_timer_call, - nfsrv_fmod_pendtime); + nfsrv_fmod_pendtime); } done: - lck_mtx_unlock(nfsrv_fmod_mutex); + lck_mtx_unlock(&nfsrv_fmod_mutex); return; } #endif /* CONFIG_FSE */ @@ -1179,7 +1310,7 @@ nfsrv_write( mbuf_t m; vnode_t vp; struct nfs_filehandle nfh; - struct nfs_export *nx; + struct nfs_export *nx = NULL; struct nfs_export_options *nxo; uio_t auio = NULL; char *uio_bufp = NULL; @@ -1189,7 +1320,7 @@ nfsrv_write( if (nd->nd_nmreq.nmc_mhead == NULL) { *mrepp = NULL; - return (0); + return 0; } error = 0; @@ -1210,8 +1341,9 @@ nfsrv_write( nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); nfsm_chain_get_32(error, nmreq, off); nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); - if (nfsrv_async) - stable = NFS_WRITE_UNSTABLE; + if (nfsrv_async) { + stable = NFS_WRITE_UNSTABLE; + } } nfsm_chain_get_32(error, nmreq, len); nfsmerr_if(error); @@ -1247,39 +1379,64 @@ nfsrv_write( preattrerr = vnode_getattr(vp, &preattr, ctx); } if (vnode_vtype(vp) != VREG) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EINVAL; - else + } else { error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; + } } - if (!error) + if (!error) { error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1); + } nfsmerr_if(error); +#if CONFIG_MACF + if (!error) { + error = mac_vnode_check_open(ctx, vp, FWRITE); + if (error) { + error = EACCES; + } else { + /* XXXab: Do we need to do this?! */ + error = mac_vnode_check_write(ctx, vfs_context_ucred(ctx), vp); + if (error) { + error = EACCES; + } + } + } + nfsmerr_if(error); +#endif + if (len > 0) { - for (mcount=0, m=nmreq->nmc_mcur; m; m = mbuf_next(m)) - if (mbuf_len(m) > 0) + for (mcount = 0, m = nmreq->nmc_mcur; m; m = mbuf_next(m)) { + if (mbuf_len(m) > 0) { mcount++; + } + } MALLOC(uio_bufp, char *, UIO_SIZEOF(mcount), M_TEMP, M_WAITOK); - if (uio_bufp) + if (uio_bufp) { auio = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount)); - if (!uio_bufp || !auio) + } + if (!uio_bufp || !auio) { error = ENOMEM; + } nfsmerr_if(error); - for (m = nmreq->nmc_mcur; m; m = mbuf_next(m)) - if ((mlen = mbuf_len(m)) > 0) + for (m = nmreq->nmc_mcur; m; m = mbuf_next(m)) { + if ((mlen = (int)mbuf_len(m)) > 0) { uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen); + } + } /* * XXX The IO_METASYNC flag indicates that all metadata (and not just * enough to ensure data integrity) mus be written to stable storage * synchronously. (IO_METASYNC is not yet implemented in 4.4BSD-Lite.) */ - if (stable == NFS_WRITE_UNSTABLE) + if (stable == NFS_WRITE_UNSTABLE) { ioflags = IO_NODELOCKED; - else if (stable == NFS_WRITE_DATASYNC) + } else if (stable == NFS_WRITE_DATASYNC) { ioflags = (IO_SYNC | IO_NODELOCKED); - else + } else { ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); + } error = VNOP_WRITE(vp, auio, ioflags, ctx); OSAddAtomic64(1, &nfsstats.srvvop_writes); @@ -1291,14 +1448,16 @@ nfsrv_write( nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, len); #if CONFIG_FSE - if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) + if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) { nfsrv_modified(vp, ctx); + } #endif } nfsm_srv_vattr_init(&postattr, nd->nd_vers); postattrerr = vnode_getattr(vp, &postattr, ctx); - if (!error && (nd->nd_vers == NFS_VER2)) + if (!error && (nd->nd_vers == NFS_VER2)) { error = postattrerr; /* NFSv2 must have attributes to return */ + } vnode_put(vp); vp = NULL; @@ -1306,21 +1465,22 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) + - NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED + - NFSX_WRITEVERF(nd->nd_vers)); + NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED + + NFSX_WRITEVERF(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); + preattrerr, &preattr, postattrerr, &postattr); nfsmout_if(error || nd->nd_repstat); nfsm_chain_add_32(error, &nmrep, retlen); /* If nfsrv_async is set, then pretend the write was FILESYNC. */ - if ((stable == NFS_WRITE_UNSTABLE) && !nfsrv_async) + if ((stable == NFS_WRITE_UNSTABLE) && !nfsrv_async) { nfsm_chain_add_32(error, &nmrep, stable); - else + } else { nfsm_chain_add_32(error, &nmrep, NFS_WRITE_FILESYNC); + } /* write verifier */ nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec); nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec); @@ -1329,15 +1489,17 @@ nfsmerr: } nfsmout: nfsm_chain_build_done(error, &nmrep); - if (vp) + if (vp) { vnode_put(vp); - if (uio_bufp != NULL) + } + if (uio_bufp != NULL) { FREE(uio_bufp, M_TEMP); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -1348,11 +1510,11 @@ nfsmout: * Jan. 1994. */ -#define NWDELAYHASH(sock, f) \ +#define NWDELAYHASH(sock, f) \ (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ]) /* These macros compare nfsrv_descript structures. */ #define NFSW_CONTIG(o, n) \ - (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh)) + (((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh)) /* * XXX The following is an incorrect comparison; it fails to take into account * XXX scoping of MAC labels, but we currently lack KPI for credential @@ -1360,7 +1522,7 @@ nfsmout: */ #define NFSW_SAMECRED(o, n) \ (!bcmp((caddr_t)(o)->nd_cr, (caddr_t)(n)->nd_cr, \ - sizeof (struct ucred))) + sizeof (struct ucred))) int nfsrv_writegather( @@ -1375,13 +1537,14 @@ nfsrv_writegather( struct nfsrv_wg_delayhash *wpp; uid_t saved_uid; struct vnode_attr preattr, postattr; - int error, mlen, i, ioflags, tlen; + int error, mlen, i, ioflags; + size_t tlen; int preattrerr, postattrerr; vnode_t vp; mbuf_t m; uio_t auio = NULL; char *uio_bufp = NULL; - u_quad_t cur_usec; + time_t cur_usec; struct timeval now; struct nfsm_chain *nmreq, nmrep; @@ -1392,103 +1555,106 @@ nfsrv_writegather( *mrepp = NULL; if (*ndp) { - nd = *ndp; - *ndp = NULL; - nmreq = &nd->nd_nmreq; - LIST_INIT(&nd->nd_coalesce); - nd->nd_mrep = NULL; - nd->nd_stable = NFS_WRITE_FILESYNC; - microuptime(&now); - cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec; - nd->nd_time = cur_usec + - ((nd->nd_vers == NFS_VER3) ? nfsrv_wg_delay_v3 : nfsrv_wg_delay); - - /* Now, get the write header... */ - nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nd->nd_fh.nfh_fhp, nd->nd_fh.nfh_len); - /* XXX shouldn't we be checking for invalid FHs before doing any more work? */ - nfsmerr_if(error); - if (nd->nd_vers == NFS_VER3) { - nfsm_chain_get_64(error, nmreq, nd->nd_off); - nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); - nfsm_chain_get_32(error, nmreq, nd->nd_stable); - } else { - nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); - nfsm_chain_get_32(error, nmreq, nd->nd_off); - nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); - if (nfsrv_async) - nd->nd_stable = NFS_WRITE_UNSTABLE; - } - nfsm_chain_get_32(error, nmreq, nd->nd_len); - nfsmerr_if(error); - nd->nd_eoff = nd->nd_off + nd->nd_len; - - if (nd->nd_len > 0) { - error = nfsm_chain_trim_data(nmreq, nd->nd_len, &mlen); - nfsmerr_if(error); - } else { - mlen = 0; - } + nd = *ndp; + *ndp = NULL; + nmreq = &nd->nd_nmreq; + LIST_INIT(&nd->nd_coalesce); + nd->nd_mrep = NULL; + nd->nd_stable = NFS_WRITE_FILESYNC; + microuptime(&now); + cur_usec = now.tv_sec * 1000000 + now.tv_usec; + nd->nd_time = cur_usec + + ((nd->nd_vers == NFS_VER3) ? nfsrv_wg_delay_v3 : nfsrv_wg_delay); + + /* Now, get the write header... */ + nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nd->nd_fh.nfh_fhp, nd->nd_fh.nfh_len); + /* XXX shouldn't we be checking for invalid FHs before doing any more work? */ + nfsmerr_if(error); + if (nd->nd_vers == NFS_VER3) { + nfsm_chain_get_64(error, nmreq, nd->nd_off); + nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); + nfsm_chain_get_32(error, nmreq, nd->nd_stable); + } else { + nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); + nfsm_chain_get_32(error, nmreq, nd->nd_off); + nfsm_chain_adv(error, nmreq, NFSX_UNSIGNED); + if (nfsrv_async) { + nd->nd_stable = NFS_WRITE_UNSTABLE; + } + } + nfsm_chain_get_32(error, nmreq, nd->nd_len); + nfsmerr_if(error); + nd->nd_eoff = nd->nd_off + nd->nd_len; - if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) { - error = EIO; + if (nd->nd_len > 0) { + error = nfsm_chain_trim_data(nmreq, nd->nd_len, &mlen); + nfsmerr_if(error); + } else { + mlen = 0; + } + + if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) { + error = EIO; nfsmerr: - nd->nd_repstat = error; - error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers)); - if (!error) { - nd->nd_mrep = nmrep.nmc_mhead; - if (nd->nd_vers == NFS_VER3) - nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); + nd->nd_repstat = error; + error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers)); + if (!error) { + nd->nd_mrep = nmrep.nmc_mhead; + if (nd->nd_vers == NFS_VER3) { + nfsm_chain_add_wcc_data(error, nd, &nmrep, + preattrerr, &preattr, postattrerr, &postattr); + } + } + nfsm_chain_build_done(error, &nmrep); + nd->nd_time = 1; } - nfsm_chain_build_done(error, &nmrep); - nd->nd_time = 1; - } - - /* - * Add this entry to the hash and time queues. - */ - lck_mtx_lock(&slp->ns_wgmutex); - owp = NULL; - wp = slp->ns_tq.lh_first; - while (wp && wp->nd_time < nd->nd_time) { - owp = wp; - wp = wp->nd_tq.le_next; - } - if (owp) { - LIST_INSERT_AFTER(owp, nd, nd_tq); - } else { - LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq); - } - if (!error) { - wpp = NWDELAYHASH(slp, nd->nd_fh.nfh_fid); + + /* + * Add this entry to the hash and time queues. + */ + lck_mtx_lock(&slp->ns_wgmutex); owp = NULL; - wp = wpp->lh_first; - while (wp && !nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) { - owp = wp; - wp = wp->nd_hash.le_next; - } - while (wp && (wp->nd_off < nd->nd_off) && - nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) { - owp = wp; - wp = wp->nd_hash.le_next; + wp = slp->ns_tq.lh_first; + while (wp && wp->nd_time < nd->nd_time) { + owp = wp; + wp = wp->nd_tq.le_next; } if (owp) { - LIST_INSERT_AFTER(owp, nd, nd_hash); - /* - * Search the hash list for overlapping entries and - * coalesce. - */ - for(; nd && NFSW_CONTIG(owp, nd); nd = wp) { - wp = nd->nd_hash.le_next; - if (NFSW_SAMECRED(owp, nd)) - nfsrv_wg_coalesce(owp, nd); - } + LIST_INSERT_AFTER(owp, nd, nd_tq); } else { - LIST_INSERT_HEAD(wpp, nd, nd_hash); + LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq); + } + if (!error) { + wpp = NWDELAYHASH(slp, nd->nd_fh.nfh_fid); + owp = NULL; + wp = wpp->lh_first; + while (wp && !nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) { + owp = wp; + wp = wp->nd_hash.le_next; + } + while (wp && (wp->nd_off < nd->nd_off) && + nfsrv_fhmatch(&nd->nd_fh, &wp->nd_fh)) { + owp = wp; + wp = wp->nd_hash.le_next; + } + if (owp) { + LIST_INSERT_AFTER(owp, nd, nd_hash); + /* + * Search the hash list for overlapping entries and + * coalesce. + */ + for (; nd && NFSW_CONTIG(owp, nd); nd = wp) { + wp = nd->nd_hash.le_next; + if (NFSW_SAMECRED(owp, nd)) { + nfsrv_wg_coalesce(owp, nd); + } + } + } else { + LIST_INSERT_HEAD(wpp, nd, nd_hash); + } } - } } else { - lck_mtx_lock(&slp->ns_wgmutex); + lck_mtx_lock(&slp->ns_wgmutex); } /* @@ -1497,91 +1663,105 @@ nfsmerr: */ loop1: microuptime(&now); - cur_usec = (u_quad_t)now.tv_sec * 1000000 + (u_quad_t)now.tv_usec; + cur_usec = now.tv_sec * 1000000 + now.tv_usec; for (nd = slp->ns_tq.lh_first; nd; nd = owp) { owp = nd->nd_tq.le_next; - if (nd->nd_time > cur_usec) - break; - if (nd->nd_mrep) - continue; + if (nd->nd_time > cur_usec) { + break; + } + if (nd->nd_mrep) { + continue; + } LIST_REMOVE(nd, nd_tq); LIST_REMOVE(nd, nd_hash); nmreq = &nd->nd_nmreq; preattrerr = postattrerr = ENOENT; - /* save the incoming uid before mapping, */ + /* save the incoming uid before mapping, */ /* for updating active user stats later */ saved_uid = kauth_cred_getuid(nd->nd_cr); error = nfsrv_fhtovp(&nd->nd_fh, nd, &vp, &nx, &nxo); if (!error) { - /* update per-export stats */ - NFSStatAdd64(&nx->nx_stats.ops, 1); + /* update per-export stats */ + NFSStatAdd64(&nx->nx_stats.ops, 1); - error = nfsrv_credcheck(nd, ctx, nx, nxo); - if (error) - vnode_put(vp); + error = nfsrv_credcheck(nd, ctx, nx, nxo); + if (error) { + vnode_put(vp); + } } if (!error) { - if (nd->nd_vers == NFS_VER3) { - nfsm_srv_pre_vattr_init(&preattr); - preattrerr = vnode_getattr(vp, &preattr, ctx); - } - if (vnode_vtype(vp) != VREG) { - if (nd->nd_vers == NFS_VER3) - error = EINVAL; - else - error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; - } - } else - vp = NULL; - if (!error) - error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1); - - if (nd->nd_stable == NFS_WRITE_UNSTABLE) - ioflags = IO_NODELOCKED; - else if (nd->nd_stable == NFS_WRITE_DATASYNC) - ioflags = (IO_SYNC | IO_NODELOCKED); - else - ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); + if (nd->nd_vers == NFS_VER3) { + nfsm_srv_pre_vattr_init(&preattr); + preattrerr = vnode_getattr(vp, &preattr, ctx); + } + if (vnode_vtype(vp) != VREG) { + if (nd->nd_vers == NFS_VER3) { + error = EINVAL; + } else { + error = (vnode_vtype(vp) == VDIR) ? EISDIR : EACCES; + } + } + } else { + vp = NULL; + } + if (!error) { + error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 1); + } + + if (nd->nd_stable == NFS_WRITE_UNSTABLE) { + ioflags = IO_NODELOCKED; + } else if (nd->nd_stable == NFS_WRITE_DATASYNC) { + ioflags = (IO_SYNC | IO_NODELOCKED); + } else { + ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED); + } if (!error && ((nd->nd_eoff - nd->nd_off) > 0)) { - for (i=0, m=nmreq->nmc_mhead; m; m = mbuf_next(m)) - if (mbuf_len(m) > 0) - i++; - - MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK); - if (uio_bufp) - auio = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE, - UIO_WRITE, uio_bufp, UIO_SIZEOF(i)); - if (!uio_bufp || !auio) - error = ENOMEM; - if (!error) { - for (m = nmreq->nmc_mhead; m; m = mbuf_next(m)) - if ((tlen = mbuf_len(m)) > 0) - uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen); - error = VNOP_WRITE(vp, auio, ioflags, ctx); - OSAddAtomic64(1, &nfsstats.srvvop_writes); + for (i = 0, m = nmreq->nmc_mhead; m; m = mbuf_next(m)) { + if (mbuf_len(m) > 0) { + i++; + } + } - /* update export stats */ - NFSStatAdd64(&nx->nx_stats.bytes_written, nd->nd_len); - /* update active user stats */ - nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, nd->nd_len); + MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK); + if (uio_bufp) { + auio = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE, + UIO_WRITE, uio_bufp, UIO_SIZEOF(i)); + } + if (!uio_bufp || !auio) { + error = ENOMEM; + } + if (!error) { + for (m = nmreq->nmc_mhead; m; m = mbuf_next(m)) { + if ((tlen = mbuf_len(m)) > 0) { + uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen); + } + } + error = VNOP_WRITE(vp, auio, ioflags, ctx); + OSAddAtomic64(1, &nfsstats.srvvop_writes); + + /* update export stats */ + NFSStatAdd64(&nx->nx_stats.bytes_written, nd->nd_len); + /* update active user stats */ + nfsrv_update_user_stat(nx, nd, saved_uid, 1, 0, nd->nd_len); #if CONFIG_FSE - if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) - nfsrv_modified(vp, ctx); + if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CONTENT_MODIFIED, vp)) { + nfsrv_modified(vp, ctx); + } #endif - } - if (uio_bufp) { - FREE(uio_bufp, M_TEMP); - uio_bufp = NULL; - } + } + if (uio_bufp) { + FREE(uio_bufp, M_TEMP); + uio_bufp = NULL; + } } if (vp) { - nfsm_srv_vattr_init(&postattr, nd->nd_vers); - postattrerr = vnode_getattr(vp, &postattr, ctx); - vnode_put(vp); + nfsm_srv_vattr_init(&postattr, nd->nd_vers); + postattrerr = vnode_getattr(vp, &postattr, ctx); + vnode_put(vp); } /* @@ -1590,46 +1770,46 @@ loop1: */ swp = nd; do { - if (error) { - nd->nd_repstat = error; - error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers)); - if (!error && (nd->nd_vers == NFS_VER3)) { - nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); + if (error) { + nd->nd_repstat = error; + error = nfsrv_rephead(nd, slp, &nmrep, NFSX_WCCDATA(nd->nd_vers)); + if (!error && (nd->nd_vers == NFS_VER3)) { + nfsm_chain_add_wcc_data(error, nd, &nmrep, + preattrerr, &preattr, postattrerr, &postattr); + } + } else { + nd->nd_repstat = error; + error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) + + NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED + + NFSX_WRITEVERF(nd->nd_vers)); + if (!error && (nd->nd_vers == NFS_VER3)) { + nfsm_chain_add_wcc_data(error, nd, &nmrep, + preattrerr, &preattr, postattrerr, &postattr); + nfsm_chain_add_32(error, &nmrep, nd->nd_len); + nfsm_chain_add_32(error, &nmrep, nd->nd_stable); + /* write verifier */ + nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec); + nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec); + } else if (!error) { + error = nfsm_chain_add_fattr(nd, &nmrep, &postattr); + } } - } else { - nd->nd_repstat = error; - error = nfsrv_rephead(nd, slp, &nmrep, NFSX_PREOPATTR(nd->nd_vers) + - NFSX_POSTOPORFATTR(nd->nd_vers) + 2 * NFSX_UNSIGNED + - NFSX_WRITEVERF(nd->nd_vers)); - if (!error && (nd->nd_vers == NFS_VER3)) { - nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); - nfsm_chain_add_32(error, &nmrep, nd->nd_len); - nfsm_chain_add_32(error, &nmrep, nd->nd_stable); - /* write verifier */ - nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec); - nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec); - } else if (!error) { - error = nfsm_chain_add_fattr(nd, &nmrep, &postattr); + nfsm_chain_build_done(error, &nmrep); + nfsmerr_if(error); + nd->nd_mrep = nmrep.nmc_mhead; + + /* + * Done. Put it at the head of the timer queue so that + * the final phase can return the reply. + */ + if (nd != swp) { + nd->nd_time = 1; + LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq); + } + nd = swp->nd_coalesce.lh_first; + if (nd) { + LIST_REMOVE(nd, nd_tq); } - } - nfsm_chain_build_done(error, &nmrep); - nfsmerr_if(error); - nd->nd_mrep = nmrep.nmc_mhead; - - /* - * Done. Put it at the head of the timer queue so that - * the final phase can return the reply. - */ - if (nd != swp) { - nd->nd_time = 1; - LIST_INSERT_HEAD(&slp->ns_tq, nd, nd_tq); - } - nd = swp->nd_coalesce.lh_first; - if (nd) { - LIST_REMOVE(nd, nd_tq); - } } while (nd); swp->nd_time = 1; LIST_INSERT_HEAD(&slp->ns_tq, swp, nd_tq); @@ -1639,13 +1819,14 @@ loop1: /* * Search for a reply to return. */ - for (nd = slp->ns_tq.lh_first; nd; nd = nd->nd_tq.le_next) + for (nd = slp->ns_tq.lh_first; nd; nd = nd->nd_tq.le_next) { if (nd->nd_mrep) { - LIST_REMOVE(nd, nd_tq); - *mrepp = nd->nd_mrep; - *ndp = nd; - break; + LIST_REMOVE(nd, nd_tq); + *mrepp = nd->nd_mrep; + *ndp = nd; + break; } + } slp->ns_wgtime = slp->ns_tq.lh_first ? slp->ns_tq.lh_first->nd_time : 0; lck_mtx_unlock(&slp->ns_wgmutex); @@ -1656,7 +1837,7 @@ loop1: * * Add/Remove the socket in the nfsrv_sockwg queue as needed. */ - lck_mtx_lock(nfsd_mutex); + lck_mtx_lock(&nfsd_mutex); if (slp->ns_wgtime) { if (slp->ns_wgq.tqe_next == SLPNOLIST) { TAILQ_INSERT_HEAD(&nfsrv_sockwg, slp, ns_wgq); @@ -1664,15 +1845,15 @@ loop1: if (!nfsrv_wg_timer_on) { nfsrv_wg_timer_on = 1; nfs_interval_timer_start(nfsrv_wg_timer_call, - NFSRV_WGATHERDELAY); + NFSRV_WGATHERDELAY); } } else if (slp->ns_wgq.tqe_next != SLPNOLIST) { TAILQ_REMOVE(&nfsrv_sockwg, slp, ns_wgq); slp->ns_wgq.tqe_next = SLPNOLIST; } - lck_mtx_unlock(nfsd_mutex); + lck_mtx_unlock(&nfsd_mutex); - return (0); + return 0; } /* @@ -1685,7 +1866,8 @@ loop1: int nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd) { - int overlap, error; + int error; + off_t overlap; mbuf_t mp, mpnext; struct nfsrv_descript *p; @@ -1693,27 +1875,32 @@ nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd) LIST_REMOVE(nd, nd_tq); if (owp->nd_eoff < nd->nd_eoff) { overlap = owp->nd_eoff - nd->nd_off; - if (overlap < 0) - return (EIO); - if (overlap > 0) - mbuf_adj(nd->nd_nmreq.nmc_mhead, overlap); + if (overlap < 0) { + return EIO; + } + if (overlap > 0) { + mbuf_adj(nd->nd_nmreq.nmc_mhead, (int)overlap); + } mp = owp->nd_nmreq.nmc_mhead; - while ((mpnext = mbuf_next(mp))) + while ((mpnext = mbuf_next(mp))) { mp = mpnext; + } error = mbuf_setnext(mp, nd->nd_nmreq.nmc_mhead); - if (error) - return (error); + if (error) { + return error; + } owp->nd_eoff = nd->nd_eoff; } else { mbuf_freem(nd->nd_nmreq.nmc_mhead); } nd->nd_nmreq.nmc_mhead = NULL; nd->nd_nmreq.nmc_mcur = NULL; - if (nd->nd_stable == NFS_WRITE_FILESYNC) + if (nd->nd_stable == NFS_WRITE_FILESYNC) { owp->nd_stable = NFS_WRITE_FILESYNC; - else if ((nd->nd_stable == NFS_WRITE_DATASYNC) && - (owp->nd_stable == NFS_WRITE_UNSTABLE)) + } else if ((nd->nd_stable == NFS_WRITE_DATASYNC) && + (owp->nd_stable == NFS_WRITE_UNSTABLE)) { owp->nd_stable = NFS_WRITE_DATASYNC; + } LIST_INSERT_HEAD(&owp->nd_coalesce, nd, nd_tq); /* @@ -1724,7 +1911,7 @@ nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd) LIST_REMOVE(p, nd_tq); LIST_INSERT_HEAD(&owp->nd_coalesce, p, nd_tq); } - return (0); + return 0; } /* @@ -1735,16 +1922,16 @@ void nfsrv_wg_timer(__unused void *param0, __unused void *param1) { struct timeval now; - uint64_t cur_usec, next_usec; - int interval; + time_t cur_usec, next_usec; + time_t interval; struct nfsrv_sock *slp; int writes_pending = 0; microuptime(&now); - cur_usec = (uint64_t)now.tv_sec * 1000000 + (uint64_t)now.tv_usec; + cur_usec = now.tv_sec * 1000000 + now.tv_usec; next_usec = cur_usec + (NFSRV_WGATHERDELAY * 1000); - lck_mtx_lock(nfsd_mutex); + lck_mtx_lock(&nfsd_mutex); TAILQ_FOREACH(slp, &nfsrv_sockwg, ns_wgq) { if (slp->ns_wgtime) { writes_pending++; @@ -1755,24 +1942,26 @@ nfsrv_wg_timer(__unused void *param0, __unused void *param1) nfsrv_wakenfsd(slp); continue; } - if (slp->ns_wgtime < next_usec) + if (slp->ns_wgtime < next_usec) { next_usec = slp->ns_wgtime; + } } } if (writes_pending == 0) { nfsrv_wg_timer_on = 0; - lck_mtx_unlock(nfsd_mutex); + lck_mtx_unlock(&nfsd_mutex); return; } - lck_mtx_unlock(nfsd_mutex); + lck_mtx_unlock(&nfsd_mutex); /* * Return the number of msec to wait again */ interval = (next_usec - cur_usec) / 1000; - if (interval < 1) + if (interval < 1) { interval = 1; + } nfs_interval_timer_start(nfsrv_wg_timer_call, interval); } @@ -1791,8 +1980,9 @@ nfsrv_group_sort(gid_t *list, int num) for (i = 1; i < num; i++) { v = list[i]; /* find correct slot for value v, moving others up */ - for (j = i; --j >= 0 && v < list[j];) + for (j = i; --j >= 0 && v < list[j];) { list[j + 1] = list[j]; + } list[j + 1] = v; } } @@ -1811,13 +2001,14 @@ nfsrv_create( struct vnode_attr dpreattr, dpostattr, postattr; struct vnode_attr va, *vap = &va; struct nameidata ni; - int error, rdev, dpreattrerr, dpostattrerr, postattrerr; + int error, dpreattrerr, dpostattrerr, postattrerr; int how, exclusive_flag; uint32_t len = 0, cnflags; + uint64_t rdev; vnode_t vp, dvp, dirp; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; u_quad_t tempsize; u_char cverf[NFSX_V3CREATEVERF]; uid_t saved_uid; @@ -1844,6 +2035,8 @@ nfsrv_create( ni.ni_op = OP_LINK; #endif ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; + ni.ni_cnd.cn_ndp = ∋ + error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -1883,25 +2076,29 @@ nfsrv_create( error = EEXIST; break; } + OS_FALLTHROUGH; case NFS_CREATE_UNCHECKED: error = nfsm_chain_get_sattr(nd, nmreq, vap); break; case NFS_CREATE_EXCLUSIVE: nfsm_chain_get_opaque(error, nmreq, NFSX_V3CREATEVERF, cverf); exclusive_flag = 1; - if (vp == NULL) + if (vp == NULL) { VATTR_SET(vap, va_mode, 0); + } break; - }; + } + ; VATTR_SET(vap, va_type, VREG); } else { - enum vtype v_type; + enum vtype v_type; error = nfsm_chain_get_sattr(nd, nmreq, vap); nfsmerr_if(error); v_type = vap->va_type; - if (v_type == VNON) + if (v_type == VNON) { v_type = VREG; + } VATTR_SET(vap, va_type, v_type); switch (v_type) { @@ -1913,7 +2110,8 @@ nfsrv_create( break; default: break; - }; + } + ; } nfsmerr_if(error); @@ -1923,7 +2121,7 @@ nfsrv_create( * should I set the mode too ?? */ if (vp == NULL) { - kauth_acl_t xacl = NULL; + kauth_acl_t xacl = NULL; /* authorize before creating */ error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0); @@ -1936,13 +2134,14 @@ nfsrv_create( 0 /* !isdir */, ctx); - if (!error && xacl != NULL) - VATTR_SET(vap, va_acl, xacl); + if (!error && xacl != NULL) { + VATTR_SET(vap, va_acl, xacl); + } } VATTR_CLEAR_ACTIVE(vap, va_data_size); VATTR_CLEAR_ACTIVE(vap, va_access_time); /* - * Server policy is to alway use the mapped rpc credential for + * Server policy is to alway use the mapped rpc credential for * file system object creation. This has the nice side effect of * enforcing BSD creation semantics */ @@ -1950,30 +2149,40 @@ nfsrv_create( VATTR_CLEAR_ACTIVE(vap, va_gid); /* validate new-file security information */ - if (!error) + if (!error) { error = vnode_authattr_new(dvp, vap, 0, ctx); + } - if (vap->va_type == VREG || vap->va_type == VSOCK) { + if (!error) { + error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL); + if (error) { + error = EACCES; + } + } - if (!error) + if (vap->va_type == VREG || vap->va_type == VSOCK) { + if (!error) { error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx); + } - if (!error && !VATTR_ALL_SUPPORTED(vap)) - /* + 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, ctx); + } - if (xacl != NULL) + if (xacl != NULL) { kauth_acl_free(xacl); + } if (!error) { if (exclusive_flag) { exclusive_flag = 0; VATTR_INIT(vap); bcopy(cverf, (caddr_t)&vap->va_access_time, - NFSX_V3CREATEVERF); + NFSX_V3CREATEVERF); VATTR_SET_ACTIVE(vap, va_access_time); // skip authorization, as this is an // NFS internal implementation detail. @@ -1982,17 +2191,17 @@ nfsrv_create( #if CONFIG_FSE if (nfsrv_fsevents_enabled && need_fsevent(FSE_CREATE_FILE, vp)) { - add_fsevent(FSE_CREATE_FILE, ctx, - FSE_ARG_VNODE, vp, - FSE_ARG_DONE); + add_fsevent(FSE_CREATE_FILE, ctx, + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); } #endif } - } else if (vap->va_type == VCHR || vap->va_type == VBLK || - vap->va_type == VFIFO) { - if (vap->va_type == VCHR && rdev == (int)0xffffffff) + vap->va_type == VFIFO) { + if (vap->va_type == VCHR && rdev == 0xffffffff) { VATTR_SET(vap, va_type, VFIFO); + } if (vap->va_type != VFIFO) { error = suser(nd->nd_cr, NULL); nfsmerr_if(error); @@ -2001,8 +2210,9 @@ nfsrv_create( error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx); - if (xacl != NULL) + if (xacl != NULL) { kauth_acl_free(xacl); + } nfsmerr_if(error); @@ -2019,6 +2229,7 @@ nfsrv_create( ni.ni_cnd.cn_context = ctx; ni.ni_startdir = dvp; ni.ni_usedvp = dvp; + ni.ni_rootdir = rootvnode; cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */ while ((error = lookup(&ni)) == ERECYCLE) { ni.ni_cnd.cn_flags = cnflags; @@ -2026,8 +2237,9 @@ nfsrv_create( ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp; } if (!error) { - if (ni.ni_cnd.cn_flags & ISSYMLINK) + if (ni.ni_cnd.cn_flags & ISSYMLINK) { error = EINVAL; + } vp = ni.ni_vp; } nfsmerr_if(error); @@ -2043,15 +2255,24 @@ nfsrv_create( vnode_put(dvp); } else { - /* + /* * nameidone has to happen before we vnode_put(dvp) * since it may need to release the fs_nodelock on the dvp */ - nameidone(&ni); + nameidone(&ni); ni.ni_cnd.cn_nameiop = 0; vnode_put(dvp); +#if CONFIG_MACF + if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) { + /* NOTE: File has not been open for NFS case, so NOCRED for filecred */ + error = mac_vnode_check_truncate(ctx, NOCRED, vp); + if (error) { + error = EACCES; + } + } +#endif if (!error && VATTR_IS_ACTIVE(vap, va_data_size)) { error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx, nxo, 0); @@ -2068,17 +2289,20 @@ nfsrv_create( if (!error) { nfsm_srv_vattr_init(&postattr, nd->nd_vers); postattrerr = vnode_getattr(vp, &postattr, ctx); - if (nd->nd_vers == NFS_VER2) + if (nd->nd_vers == NFS_VER2) { error = postattrerr; + } } } - if (vp) - vnode_put(vp); + if (vp) { + vnode_put(vp); + } if (nd->nd_vers == NFS_VER3) { if (exclusive_flag && !error && - bcmp(cverf, &postattr.va_access_time, NFSX_V3CREATEVERF)) + bcmp(cverf, &postattr.va_access_time, NFSX_V3CREATEVERF)) { error = EEXIST; + } nfsm_srv_vattr_init(&dpostattr, NFS_VER3); dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx); vnode_put(dirp); @@ -2089,7 +2313,7 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) + - NFSX_FATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); + NFSX_FATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -2099,32 +2323,35 @@ nfsmerr: nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr); } nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); } else { nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len); - if (!error) + if (!error) { error = nfsm_chain_add_fattr(nd, &nmrep, &postattr); + } } nfsmout: nfsm_chain_build_done(error, &nmrep); if (ni.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(&ni); - if (vp) + if (vp) { vnode_put(vp); + } vnode_put(dvp); } - if (dirp) + if (dirp) { vnode_put(dirp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -2148,7 +2375,7 @@ nfsrv_mknod( vnode_t vp, dvp, dirp; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; uid_t saved_uid; kauth_acl_t xacl = NULL; struct nfsm_chain *nmreq, nmrep; @@ -2172,6 +2399,7 @@ nfsrv_mknod( ni.ni_op = OP_LINK; #endif ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; + ni.ni_cnd.cn_ndp = ∋ error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -2233,13 +2461,14 @@ nfsrv_mknod( 0 /* !isdir */, ctx); - if (!error && xacl != NULL) - VATTR_SET(vap, va_acl, xacl); + if (!error && xacl != NULL) { + VATTR_SET(vap, va_acl, xacl); + } } VATTR_CLEAR_ACTIVE(vap, va_data_size); VATTR_CLEAR_ACTIVE(vap, va_access_time); /* - * Server policy is to alway use the mapped rpc credential for + * Server policy is to alway use the mapped rpc credential for * file system object creation. This has the nice side effect of * enforcing BSD creation semantics */ @@ -2247,26 +2476,36 @@ nfsrv_mknod( VATTR_CLEAR_ACTIVE(vap, va_gid); /* validate new-file security information */ - if (!error) + if (!error) { error = vnode_authattr_new(dvp, vap, 0, ctx); - - if (error) + } + if (!error) { + error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL); + if (error) { + error = EACCES; + } + } + if (error) { goto out1; + } if (vtyp == VSOCK) { error = VNOP_CREATE(dvp, &vp, &ni.ni_cnd, vap, ctx); - if (!error && !VATTR_ALL_SUPPORTED(vap)) - /* + 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, ctx); + } } else { - if (vtyp != VFIFO && (error = suser(nd->nd_cr, (u_short *)0))) + if (vtyp != VFIFO && (error = suser(nd->nd_cr, (u_short *)0))) { goto out1; - if ((error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx))) + } + if ((error = VNOP_MKNOD(dvp, &vp, &ni.ni_cnd, vap, ctx))) { goto out1; + } if (vp) { vnode_recycle(vp); vnode_put(vp); @@ -2280,6 +2519,7 @@ nfsrv_mknod( ni.ni_cnd.cn_context = vfs_context_current(); ni.ni_startdir = dvp; ni.ni_usedvp = dvp; + ni.ni_rootdir = rootvnode; cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */ while ((error = lookup(&ni)) == ERECYCLE) { ni.ni_cnd.cn_flags = cnflags; @@ -2287,14 +2527,16 @@ nfsrv_mknod( ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp; } if (!error) { - vp = ni.ni_vp; - if (ni.ni_cnd.cn_flags & ISSYMLINK) - error = EINVAL; + vp = ni.ni_vp; + if (ni.ni_cnd.cn_flags & ISSYMLINK) { + error = EINVAL; + } } } out1: - if (xacl != NULL) + if (xacl != NULL) { kauth_acl_free(xacl); + } out: /* * nameidone has to happen before we vnode_put(dvp) @@ -2327,7 +2569,7 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(NFS_VER3, &nfh) + - NFSX_POSTOPATTR(NFS_VER3) + NFSX_WCCDATA(NFS_VER3)); + NFSX_POSTOPATTR(NFS_VER3) + NFSX_WCCDATA(NFS_VER3)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -2336,31 +2578,35 @@ nfsmerr: nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr); } nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); nfsmout: nfsm_chain_build_done(error, &nmrep); if (ni.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(&ni); - if (vp) + if (vp) { vnode_put(vp); + } vnode_put(dvp); } - if (dvp) + if (dvp) { vnode_put(dvp); - if (vp) + } + if (vp) { vnode_put(vp); - if (dirp) + } + if (dirp) { vnode_put(dirp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -2381,7 +2627,7 @@ nfsrv_remove( struct vnode_attr dpreattr, dpostattr; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; struct nfsm_chain *nmreq, nmrep; error = 0; @@ -2401,6 +2647,7 @@ nfsrv_remove( ni.ni_op = OP_UNLINK; #endif ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; + ni.ni_cnd.cn_ndp = ∋ error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -2413,7 +2660,7 @@ nfsrv_remove( } } if (dirp) { - if (nd->nd_vers == NFS_VER3) { + if (nd->nd_vers == NFS_VER3) { nfsm_srv_pre_vattr_init(&dpreattr); dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx); } else { @@ -2426,22 +2673,30 @@ nfsrv_remove( dvp = ni.ni_dvp; vp = ni.ni_vp; - if (vnode_vtype(vp) == VDIR) - error = EPERM; /* POSIX */ - else if (vnode_isvroot(vp)) - /* + if (vnode_vtype(vp) == VDIR) { + error = EPERM; /* POSIX */ + } else if (vnode_isvroot(vp)) { + /* * The root of a mounted filesystem cannot be deleted. */ error = EBUSY; - else + } else { error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0); + } + + if (!error) { + error = vn_authorize_unlink(dvp, vp, &ni.ni_cnd, ctx, NULL); + if (error) { + error = EACCES; + } + } if (!error) { #if CONFIG_FSE char *path = NULL; - int plen; + int plen = 0; fse_info finfo; - + if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) { plen = MAXPATHLEN; if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) { @@ -2452,16 +2707,17 @@ nfsrv_remove( } } #endif - error = VNOP_REMOVE(dvp, vp, &ni.ni_cnd, 0, ctx); - + error = VNOP_REMOVE(dvp, vp, &ni.ni_cnd, 0, ctx); + #if CONFIG_FSE if (path) { - if (!error) + if (!error) { add_fsevent(FSE_DELETE, ctx, - FSE_ARG_STRING, plen, path, - FSE_ARG_FINFO, &finfo, - FSE_ARG_DONE); - release_pathbuff(path); + FSE_ARG_STRING, plen, path, + FSE_ARG_FINFO, &finfo, + FSE_ARG_DONE); + } + release_pathbuff(path); } #endif } @@ -2473,13 +2729,13 @@ nfsrv_remove( nameidone(&ni); vnode_put(vp); - vnode_put(dvp); + vnode_put(dvp); } nfsmerr: if (dirp) { nfsm_srv_vattr_init(&dpostattr, nd->nd_vers); - dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx); + dpostattrerr = vnode_getattr(dirp, &dpostattr, ctx); vnode_put(dirp); } @@ -2489,16 +2745,17 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + } nfsmout: nfsm_chain_build_done(error, &nmrep); if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -2531,7 +2788,7 @@ nfsrv_rename( struct nfsm_chain *nmreq, nmrep; char *from_name, *to_name; #if CONFIG_FSE - int from_len=0, to_len=0; + int from_len = 0, to_len = 0; fse_info from_finfo, to_finfo; #endif u_char didstats = 0; @@ -2589,10 +2846,12 @@ retry: frompath = NULL; fromni.ni_cnd.cn_pnlen = MAXPATHLEN; fromni.ni_cnd.cn_flags |= HASBUF; + fromni.ni_cnd.cn_ndp = &fromni; error = nfsrv_namei(nd, ctx, &fromni, &fnfh, &fdirp, &fnx, &fnxo); - if (error) + if (error) { goto out; + } fdvp = fromni.ni_dvp; fvp = fromni.ni_vp; @@ -2624,9 +2883,11 @@ retry: topath = NULL; toni.ni_cnd.cn_pnlen = MAXPATHLEN; toni.ni_cnd.cn_flags |= HASBUF; + toni.ni_cnd.cn_ndp = &toni; - if (fvtype == VDIR) + if (fvtype == VDIR) { toni.ni_cnd.cn_flags |= WILLBEDIR; + } tnx = NULL; error = nfsrv_namei(nd, ctx, &toni, &tnfh, &tdirp, &tnx, &tnxo); @@ -2634,11 +2895,12 @@ retry: /* * Translate error code for rename("dir1", "dir2/."). */ - if (error == EISDIR && fvtype == VDIR) { - if (nd->nd_vers == NFS_VER3) - error = EINVAL; - else - error = ENOTEMPTY; + if (error == EISDIR && fvtype == VDIR) { + if (nd->nd_vers == NFS_VER3) { + error = EINVAL; + } else { + error = ENOTEMPTY; + } } goto out; } @@ -2671,31 +2933,35 @@ retry: tvtype = vnode_vtype(tvp); if (fvtype == VDIR && tvtype != VDIR) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EEXIST; - else + } else { error = EISDIR; + } goto out; } else if (fvtype != VDIR && tvtype == VDIR) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EEXIST; - else + } else { error = ENOTDIR; + } goto out; } if (tvtype == VDIR && vnode_mountedhere(tvp)) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EXDEV; - else + } else { error = ENOTEMPTY; + } goto out; } } if (fvp == tdvp) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EINVAL; - else + } else { error = ENOTEMPTY; + } goto out; } @@ -2716,47 +2982,61 @@ retry: error = 0; if ((tvp != NULL) && vnode_isdir(tvp)) { - if (tvp != fdvp) + 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, ctx, fnxo, 0)) != 0) + if ((error = nfsrv_authorize(fvp, fdvp, KAUTH_VNODE_DELETE, ctx, 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, - ctx, tnxo, 0)) != 0) + NULL, + vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, + ctx, 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, - ctx, fnxo, 0)) != 0) + vnode_isdir(fvp) ? KAUTH_VNODE_ADD_SUBDIRECTORY : KAUTH_VNODE_ADD_FILE, + ctx, fnxo, 0)) != 0) { goto auth_exit; + } } /* overwriting tvp */ if ((tvp != NULL) && !vnode_isdir(tvp) && - ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) + ((error = nfsrv_authorize(tvp, tdvp, KAUTH_VNODE_DELETE, ctx, tnxo, 0)) != 0)) { goto auth_exit; + } + if (!error && + ((error = vn_authorize_rename(fdvp, fvp, &fromni.ni_cnd, tdvp, tvp, &toni.ni_cnd, ctx, NULL)) != 0)) { + if (error) { + error = EACCES; + } + goto auth_exit; + } /* XXX more checks? */ auth_exit: /* authorization denied */ - if (error != 0) + if (error != 0) { goto out; + } } if ((vnode_mount(fvp) != vnode_mount(tdvp)) || (tvp && (vnode_mount(fvp) != vnode_mount(tvp)))) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EXDEV; - else + } else { error = ENOTEMPTY; + } goto out; } /* @@ -2775,17 +3055,19 @@ auth_exit: * o tvp */ if (tdvp->v_parent == fvp) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EXDEV; - else + } else { error = ENOTEMPTY; + } goto out; } if (fvtype == VDIR && vnode_mountedhere(fvp)) { - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = EXDEV; - else + } else { error = ENOTEMPTY; + } goto out; } /* @@ -2807,24 +3089,24 @@ auth_exit: */ if ((fvp == tvp) && (fdvp == tdvp)) { if (fromni.ni_cnd.cn_namelen == toni.ni_cnd.cn_namelen && - !bcmp(fromni.ni_cnd.cn_nameptr, toni.ni_cnd.cn_nameptr, - fromni.ni_cnd.cn_namelen)) { + !bcmp(fromni.ni_cnd.cn_nameptr, toni.ni_cnd.cn_nameptr, + fromni.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; + 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... @@ -2835,8 +3117,8 @@ auth_exit: * then finally start the lookup * process over with the lock held */ - if (!holding_mntlock) { - /* + if (!holding_mntlock) { + /* * need to grab a reference on * the mount point before we * drop all the iocounts... once @@ -2847,9 +3129,8 @@ auth_exit: mount_ref(locked_mp, 0); /* make a copy of to path to pass to nfsrv_namei() again */ - MALLOC_ZONE(topath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); - if (topath) - bcopy(toni.ni_cnd.cn_pnbuf, topath, tolen + 1); + topath = zalloc(ZV_NAMEI); + bcopy(toni.ni_cnd.cn_pnbuf, topath, tolen + 1); /* * nameidone has to happen before we vnode_put(tdvp) @@ -2857,14 +3138,14 @@ auth_exit: */ nameidone(&toni); - if (tvp) - vnode_put(tvp); + if (tvp) { + vnode_put(tvp); + } vnode_put(tdvp); /* make a copy of from path to pass to nfsrv_namei() again */ - MALLOC_ZONE(frompath, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); - if (frompath) - bcopy(fromni.ni_cnd.cn_pnbuf, frompath, fromlen + 1); + frompath = zalloc(ZV_NAMEI); + bcopy(fromni.ni_cnd.cn_pnbuf, frompath, fromlen + 1); /* * nameidone has to happen before we vnode_put(fdvp) @@ -2876,11 +3157,11 @@ auth_exit: vnode_put(fdvp); if (fdirp) { - vnode_put(fdirp); + vnode_put(fdirp); fdirp = NULL; } if (tdirp) { - vnode_put(tdirp); + vnode_put(tdirp); tdirp = NULL; } mount_lock_renames(locked_mp); @@ -2907,7 +3188,7 @@ auth_exit: goto retry; } } else { - /* + /* * when we dropped the iocounts to take * the lock, we allowed the identity of * the various vnodes to change... if they did, @@ -2917,10 +3198,10 @@ auth_exit: * so we're free to drop the lock at this point * and continue on */ - if (holding_mntlock) { + if (holding_mntlock) { mount_unlock_renames(locked_mp); mount_drop(locked_mp, 0); - holding_mntlock = 0; + holding_mntlock = 0; } } @@ -2936,16 +3217,17 @@ auth_exit: #if CONFIG_FSE if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, fvp)) { int from_truncated = 0, to_truncated = 0; - - get_fse_info(fvp, &from_finfo, ctx); - if (tvp) - get_fse_info(tvp, &to_finfo, ctx); - from_name = get_pathbuff(); + get_fse_info(fvp, &from_finfo, ctx); + if (tvp) { + get_fse_info(tvp, &to_finfo, ctx); + } + + from_name = get_pathbuff(); if (from_name) { from_len = safe_getpath(fdvp, fromni.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated); } - + to_name = from_name ? get_pathbuff() : NULL; if (to_name) { to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated); @@ -2954,10 +3236,9 @@ auth_exit: if (from_truncated || to_truncated) { from_finfo.mode |= FSE_TRUNCATED_PATH; } - } else { - from_name = NULL; - to_name = NULL; + from_name = NULL; + to_name = NULL; } #else /* CONFIG_FSE */ from_name = NULL; @@ -2965,9 +3246,9 @@ auth_exit: #endif /* CONFIG_FSE */ error = VNOP_RENAME(fromni.ni_dvp, fromni.ni_vp, &fromni.ni_cnd, - toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx); + toni.ni_dvp, toni.ni_vp, &toni.ni_cnd, ctx); /* - * fix up name & parent pointers. note that we first + * 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... @@ -2975,10 +3256,11 @@ auth_exit: if (oname == fvp->v_name && oparent == fvp->v_parent) { int update_flags; update_flags = VNODE_UPDATE_NAME; - if (fdvp != tdvp) + if (fdvp != tdvp) { update_flags |= VNODE_UPDATE_PARENT; + } vnode_update_identity(fvp, tdvp, toni.ni_cnd.cn_nameptr, - toni.ni_cnd.cn_namelen, toni.ni_cnd.cn_hash, update_flags); + toni.ni_cnd.cn_namelen, toni.ni_cnd.cn_hash, update_flags); } /* @@ -2987,43 +3269,46 @@ auth_exit: */ #if CONFIG_FSE if (nfsrv_fsevents_enabled && !error && from_name && to_name) { - if (tvp) { - add_fsevent(FSE_RENAME, ctx, - FSE_ARG_STRING, from_len, from_name, - FSE_ARG_FINFO, &from_finfo, - FSE_ARG_STRING, to_len, to_name, - FSE_ARG_FINFO, &to_finfo, - FSE_ARG_DONE); + if (tvp) { + add_fsevent(FSE_RENAME, ctx, + FSE_ARG_STRING, from_len, from_name, + FSE_ARG_FINFO, &from_finfo, + FSE_ARG_STRING, to_len, to_name, + FSE_ARG_FINFO, &to_finfo, + FSE_ARG_DONE); } else { - add_fsevent(FSE_RENAME, ctx, - FSE_ARG_STRING, from_len, from_name, - FSE_ARG_FINFO, &from_finfo, - FSE_ARG_STRING, to_len, to_name, - FSE_ARG_DONE); + add_fsevent(FSE_RENAME, ctx, + FSE_ARG_STRING, from_len, from_name, + FSE_ARG_FINFO, &from_finfo, + FSE_ARG_STRING, to_len, to_name, + FSE_ARG_DONE); } } - if (from_name) - release_pathbuff(from_name); - if (to_name) - release_pathbuff(to_name); + if (from_name) { + release_pathbuff(from_name); + } + if (to_name) { + release_pathbuff(to_name); + } #endif /* CONFIG_FSE */ from_name = to_name = NULL; - + out: if (holding_mntlock) { - mount_unlock_renames(locked_mp); + 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(&toni); - if (tvp) - vnode_put(tvp); - vnode_put(tdvp); + if (tvp) { + vnode_put(tvp); + } + vnode_put(tdvp); tdvp = NULL; } @@ -3034,9 +3319,10 @@ out: */ nameidone(&fromni); - if (fvp) - vnode_put(fvp); - vnode_put(fdvp); + if (fvp) { + vnode_put(fvp); + } + vnode_put(fdvp); fdvp = NULL; } @@ -3062,14 +3348,14 @@ nfsmerr: nfsmout_on_status(nd, error); if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_wcc_data(error, nd, &nmrep, - fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr); + fdpreattrerr, &fdpreattr, fdpostattrerr, &fdpostattr); nfsm_chain_add_wcc_data(error, nd, &nmrep, - tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr); + tdpreattrerr, &tdpreattr, tdpostattrerr, &tdpostattr); } nfsmout: nfsm_chain_build_done(error, &nmrep); if (holding_mntlock) { - mount_unlock_renames(locked_mp); + mount_unlock_renames(locked_mp); mount_drop(locked_mp, 0); } if (tdvp) { @@ -3079,9 +3365,10 @@ nfsmout: */ nameidone(&toni); - if (tvp) - vnode_put(tvp); - vnode_put(tdvp); + if (tvp) { + vnode_put(tvp); + } + vnode_put(tdvp); } if (fdvp) { /* @@ -3090,25 +3377,31 @@ nfsmout: */ nameidone(&fromni); - if (fvp) - vnode_put(fvp); - vnode_put(fdvp); + if (fvp) { + vnode_put(fvp); + } + vnode_put(fdvp); } - if (fdirp) + if (fdirp) { vnode_put(fdirp); - if (tdirp) + } + if (tdirp) { vnode_put(tdirp); - if (frompath) - FREE_ZONE(frompath, MAXPATHLEN, M_NAMEI); - if (topath) - FREE_ZONE(topath, MAXPATHLEN, M_NAMEI); - if (saved_cred) + } + if (frompath) { + NFS_ZFREE(ZV_NAMEI, frompath); + } + if (topath) { + NFS_ZFREE(ZV_NAMEI, topath); + } + if (saved_cred) { kauth_cred_unref(&saved_cred); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -3156,13 +3449,14 @@ nfsrv_link( /* we're not allowed to link to directories... */ if (vnode_vtype(vp) == VDIR) { - error = EPERM; /* POSIX */ + error = EPERM; /* POSIX */ goto out; } /* ...or to anything that kauth doesn't want us to (eg. immutable items) */ - if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) + if ((error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LINKTARGET, ctx, nxo, 0)) != 0) { goto out; + } ni.ni_cnd.cn_nameiop = CREATE; #if CONFIG_TRIGGERS @@ -3170,8 +3464,9 @@ nfsrv_link( #endif ni.ni_cnd.cn_flags = LOCKPARENT; error = nfsm_chain_get_path_namei(nmreq, len, &ni); - if (!error) + if (!error) { error = nfsrv_namei(nd, ctx, &ni, &dnfh, &dirp, &nx, &nxo); + } if (dirp) { if (nd->nd_vers == NFS_VER3) { nfsm_srv_pre_vattr_init(&dpreattr); @@ -3181,25 +3476,36 @@ nfsrv_link( dirp = NULL; } } - if (error) + if (error) { goto out; + } dvp = ni.ni_dvp; xp = ni.ni_vp; - if (xp != NULL) + if (xp != NULL) { error = EEXIST; - else if (vnode_mount(vp) != vnode_mount(dvp)) + } else if (vnode_mount(vp) != vnode_mount(dvp)) { error = EXDEV; - else + } else { error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0); + } - if (!error) +#if CONFIG_MACF + if (!error) { + error = mac_vnode_check_link(ctx, dvp, vp, &ni.ni_cnd); + if (error) { + error = EACCES; + } + } +#endif + if (!error) { error = VNOP_LINK(vp, dvp, &ni.ni_cnd, ctx); + } #if CONFIG_FSE if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) { char *target_path = NULL; - int plen, truncated=0; + int plen, truncated = 0; fse_info finfo; /* build the path to the new link file */ @@ -3212,9 +3518,9 @@ nfsrv_link( finfo.mode |= FSE_TRUNCATED_PATH; } add_fsevent(FSE_CREATE_FILE, ctx, - FSE_ARG_STRING, plen, target_path, - FSE_ARG_FINFO, &finfo, - FSE_ARG_DONE); + FSE_ARG_STRING, plen, target_path, + FSE_ARG_FINFO, &finfo, + FSE_ARG_DONE); } release_pathbuff(target_path); @@ -3228,8 +3534,9 @@ nfsrv_link( */ nameidone(&ni); - if (xp) + if (xp) { vnode_put(xp); + } vnode_put(dvp); out: if (nd->nd_vers == NFS_VER3) { @@ -3255,17 +3562,18 @@ nfsmerr: if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr); nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); } nfsmout: nfsm_chain_build_done(error, &nmrep); - if (vp) + if (vp) { vnode_put(vp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -3288,9 +3596,9 @@ nfsrv_symlink( vnode_t vp, dvp, dirp; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; uio_t auio = NULL; - char uio_buf[ UIO_SIZEOF(1) ]; + char uio_buf[UIO_SIZEOF(1)]; struct nfsm_chain *nmreq, nmrep; error = 0; @@ -3315,6 +3623,8 @@ nfsrv_symlink( ni.ni_op = OP_LINK; #endif ni.ni_cnd.cn_flags = LOCKPARENT; + ni.ni_flag = 0; + ni.ni_cnd.cn_ndp = ∋ error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -3343,25 +3653,29 @@ nfsrv_symlink( vp = ni.ni_vp; VATTR_INIT(vap); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { error = nfsm_chain_get_sattr(nd, nmreq, vap); + } nfsm_chain_get_32(error, nmreq, linkdatalen); if (!error && (((nd->nd_vers == NFS_VER2) && (linkdatalen > NFS_MAXPATHLEN)) || - ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) + ((nd->nd_vers == NFS_VER3) && (linkdatalen > MAXPATHLEN)))) { error = NFSERR_NAMETOL; + } nfsmerr_if(error); MALLOC(linkdata, caddr_t, linkdatalen + 1, M_TEMP, M_WAITOK); - if (linkdata) + if (linkdata) { auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, - &uio_buf[0], sizeof(uio_buf)); + &uio_buf[0], sizeof(uio_buf)); + } if (!linkdata || !auio) { error = ENOMEM; goto out; } uio_addiov(auio, CAST_USER_ADDR_T(linkdata), linkdatalen); error = nfsm_chain_get_uio(nmreq, linkdatalen, auio); - if (!error && (nd->nd_vers == NFS_VER2)) + if (!error && (nd->nd_vers == NFS_VER2)) { error = nfsm_chain_get_sattr(nd, nmreq, vap); + } nfsmerr_if(error); *(linkdata + linkdatalen) = '\0'; if (vp) { @@ -3373,7 +3687,7 @@ nfsrv_symlink( VATTR_CLEAR_ACTIVE(vap, va_data_size); VATTR_CLEAR_ACTIVE(vap, va_access_time); /* - * Server policy is to alway use the mapped rpc credential for + * Server policy is to alway use the mapped rpc credential for * file system object creation. This has the nice side effect of * enforcing BSD creation semantics */ @@ -3384,11 +3698,19 @@ nfsrv_symlink( error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0); /* validate given attributes */ - if (!error) + if (!error) { error = vnode_authattr_new(dvp, vap, 0, ctx); + } + if (!error) { + error = vn_authorize_create(dvp, &ni.ni_cnd, vap, ctx, NULL); + if (error) { + error = EACCES; + } + } - if (!error) + if (!error) { error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx); + } if (!error && (nd->nd_vers == NFS_VER3)) { if (vp == NULL) { @@ -3401,14 +3723,16 @@ nfsrv_symlink( ni.ni_cnd.cn_context = ctx; ni.ni_startdir = dvp; ni.ni_usedvp = dvp; + ni.ni_rootdir = rootvnode; cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */ while ((error = lookup(&ni)) == ERECYCLE) { ni.ni_cnd.cn_flags = cnflags; ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf; ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp; } - if (!error) - vp = ni.ni_vp; + if (!error) { + vp = ni.ni_vp; + } } if (!error) { error = nfsrv_vptofh(nx, NFS_VER3, NULL, vp, ctx, &nfh); @@ -3422,8 +3746,8 @@ nfsrv_symlink( #if CONFIG_FSE if (nfsrv_fsevents_enabled && !error && vp) { add_fsevent(FSE_CREATE_FILE, ctx, - FSE_ARG_VNODE, vp, - FSE_ARG_DONE); + FSE_ARG_VNODE, vp, + FSE_ARG_DONE); } #endif out: @@ -3433,8 +3757,9 @@ out: */ nameidone(&ni); ni.ni_cnd.cn_nameiop = 0; - if (vp) - vnode_put(vp); + if (vp) { + vnode_put(vp); + } vnode_put(dvp); out1: if (linkdata) { @@ -3452,7 +3777,7 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) + - NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); + NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -3462,36 +3787,39 @@ nfsmerr: nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr); } nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); } nfsmout: nfsm_chain_build_done(error, &nmrep); if (ni.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(&ni); - if (vp) + if (vp) { vnode_put(vp); + } vnode_put(dvp); } - if (dirp) + if (dirp) { vnode_put(dirp); - if (linkdata) + } + if (linkdata) { FREE(linkdata, M_TEMP); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* * nfs mkdir service */ - + int nfsrv_mkdir( struct nfsrv_descript *nd, @@ -3507,7 +3835,7 @@ nfsrv_mkdir( vnode_t vp, dvp, dirp; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; uid_t saved_uid; kauth_acl_t xacl = NULL; struct nfsm_chain *nmreq, nmrep; @@ -3531,7 +3859,8 @@ nfsrv_mkdir( #if CONFIG_TRIGGERS ni.ni_op = OP_LINK; #endif - ni.ni_cnd.cn_flags = LOCKPARENT; + ni.ni_cnd.cn_flags = LOCKPARENT | WILLBEDIR; + ni.ni_cnd.cn_ndp = ∋ error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -3565,11 +3894,11 @@ nfsrv_mkdir( VATTR_SET(vap, va_type, VDIR); if (vp != NULL) { - /* + /* * nameidone has to happen before we vnode_put(dvp) * since it may need to release the fs_nodelock on the dvp */ - nameidone(&ni); + nameidone(&ni); vnode_put(dvp); vnode_put(vp); error = EEXIST; @@ -3582,12 +3911,13 @@ nfsrv_mkdir( if (!error) { error = kauth_acl_inherit(dvp, NULL, - &xacl, /* isdir */ + &xacl, /* isdir */ 1, ctx); - - if (!error && xacl != NULL) - VATTR_SET(vap, va_acl, xacl); + + if (!error && xacl != NULL) { + VATTR_SET(vap, va_acl, xacl); + } } VATTR_CLEAR_ACTIVE(vap, va_data_size); @@ -3595,12 +3925,13 @@ nfsrv_mkdir( /* * We don't support the S_ISGID bit for directories. Solaris and other * SRV4 derived systems might set this to get BSD semantics, which we enforce - * any ways. + * any ways. */ - if (VATTR_IS_ACTIVE(vap, va_mode)) + if (VATTR_IS_ACTIVE(vap, va_mode)) { vap->va_mode &= ~S_ISGID; + } /* - * Server policy is to alway use the mapped rpc credential for + * Server policy is to alway use the mapped rpc credential for * file system object creation. This has the nice side effect of * enforcing BSD creation semantics */ @@ -3608,40 +3939,54 @@ nfsrv_mkdir( VATTR_CLEAR_ACTIVE(vap, va_gid); /* validate new-file security information */ - if (!error) + if (!error) { error = vnode_authattr_new(dvp, vap, 0, ctx); + } /* - * vnode_authattr_new can return errors other than EPERM, but that's not going to + * vnode_authattr_new can return errors other than EPERM, but that's not going to * sit well with our clients so we map all errors to EPERM. - */ - if (error) + */ + if (error) { error = EPERM; + } + + if (!error) { + error = vn_authorize_mkdir(dvp, &ni.ni_cnd, vap, ctx, NULL); + if (error) { + error = EACCES; + } + } - if (!error) + if (!error) { error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx); + } #if CONFIG_FSE - if (nfsrv_fsevents_enabled && !error) + if (nfsrv_fsevents_enabled && !error) { add_fsevent(FSE_CREATE_DIR, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE); + } #endif - if (!error && !VATTR_ALL_SUPPORTED(vap)) - /* + 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, ctx); + } - if (xacl != NULL) + if (xacl != NULL) { kauth_acl_free(xacl); - + } + if (!error) { error = nfsrv_vptofh(nx, nd->nd_vers, NULL, vp, ctx, &nfh); if (!error) { nfsm_srv_vattr_init(&postattr, nd->nd_vers); postattrerr = vnode_getattr(vp, &postattr, ctx); - if (nd->nd_vers == NFS_VER2) + if (nd->nd_vers == NFS_VER2) { error = postattrerr; + } } vnode_put(vp); vp = NULL; @@ -3666,7 +4011,7 @@ nfsmerr: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_SRVFH(nd->nd_vers, &nfh) + - NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); + NFSX_POSTOPATTR(nd->nd_vers) + NFSX_WCCDATA(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -3676,31 +4021,34 @@ nfsmerr: nfsm_chain_add_postop_attr(error, nd, &nmrep, postattrerr, &postattr); } nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); } else { nfsm_chain_add_fh(error, &nmrep, NFS_VER2, nfh.nfh_fhp, nfh.nfh_len); - if (!error) + if (!error) { error = nfsm_chain_add_fattr(nd, &nmrep, &postattr); + } } nfsmout: nfsm_chain_build_done(error, &nmrep); if (ni.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(&ni); vnode_put(dvp); - if (vp) + if (vp) { vnode_put(vp); + } } - if (dirp) + if (dirp) { vnode_put(dirp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -3720,7 +4068,7 @@ nfsrv_rmdir( struct vnode_attr dpreattr, dpostattr; struct nfs_filehandle nfh; struct nfs_export *nx = NULL; - struct nfs_export_options *nxo; + struct nfs_export_options *nxo = NULL; struct nameidata ni; struct nfsm_chain *nmreq, nmrep; @@ -3742,6 +4090,7 @@ nfsrv_rmdir( ni.ni_op = OP_UNLINK; #endif ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF; + ni.ni_cnd.cn_ndp = ∋ error = nfsm_chain_get_path_namei(nmreq, len, &ni); if (!error) { error = nfsrv_namei(nd, ctx, &ni, &nfh, &dirp, &nx, &nxo); @@ -3754,7 +4103,7 @@ nfsrv_rmdir( } } if (dirp) { - if (nd->nd_vers == NFS_VER3) { + if (nd->nd_vers == NFS_VER3) { nfsm_srv_pre_vattr_init(&dpreattr); dpreattrerr = vnode_getattr(dirp, &dpreattr, ctx); } else { @@ -3781,19 +4130,28 @@ nfsrv_rmdir( /* * The root of a mounted filesystem cannot be deleted. */ - if (vnode_isvroot(vp)) + if (vnode_isvroot(vp)) { error = EBUSY; - if (!error) + } + if (!error) { error = nfsrv_authorize(vp, dvp, KAUTH_VNODE_DELETE, ctx, nxo, 0); + } + if (!error) { + error = vn_authorize_rmdir(dvp, vp, &ni.ni_cnd, ctx, NULL); + if (error) { + error = EACCES; + } + } + if (!error) { #if CONFIG_FSE char *path = NULL; - int plen; + int plen = 0; fse_info finfo; - + if (nfsrv_fsevents_enabled && need_fsevent(FSE_DELETE, dvp)) { plen = MAXPATHLEN; - if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) { + if ((path = get_pathbuff()) && !vn_getpath(vp, path, &plen)) { get_fse_info(vp, &finfo, ctx); } else if (path) { release_pathbuff(path); @@ -3806,12 +4164,13 @@ nfsrv_rmdir( #if CONFIG_FSE if (path) { - if (!error) + if (!error) { add_fsevent(FSE_DELETE, ctx, - FSE_ARG_STRING, plen, path, - FSE_ARG_FINFO, &finfo, - FSE_ARG_DONE); - release_pathbuff(path); + FSE_ARG_STRING, plen, path, + FSE_ARG_FINFO, &finfo, + FSE_ARG_DONE); + } + release_pathbuff(path); } #endif /* CONFIG_FSE */ } @@ -3839,18 +4198,20 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_wcc_data(error, nd, &nmrep, - dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + dpreattrerr, &dpreattr, dpostattrerr, &dpostattr); + } nfsmout: nfsm_chain_build_done(error, &nmrep); - if (dirp) + if (dirp) { vnode_put(dirp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -3895,10 +4256,10 @@ nfsrv_readdir( struct nfs_export *nx; struct nfs_export_options *nxo; uio_t auio = NULL; - char uio_buf[ UIO_SIZEOF(1) ]; + char uio_buf[UIO_SIZEOF(1)]; int len, nlen, rem, xfer, error, attrerr; int siz, count, fullsiz, eofflag, nentries; - u_quad_t off, toff, verf; + u_quad_t off, toff, verf = 0; int vnopflag; struct nfsm_chain *nmreq, nmrep; @@ -3925,8 +4286,9 @@ nfsrv_readdir( off = toff; siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); xfer = NFSRV_NDMAXDATA(nd); - if (siz > xfer) + if (siz > xfer) { siz = xfer; + } fullsiz = siz; error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo); @@ -3941,26 +4303,42 @@ nfsrv_readdir( error = nfsrv_credcheck(nd, ctx, nx, nxo); nfsmerr_if(error); - if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) + if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2) { vnopflag |= VNODE_READDIR_NAMEMAX; + } - if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) + if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS)) { vnopflag |= VNODE_READDIR_SEEKOFF32; + } if (nd->nd_vers == NFS_VER3) { nfsm_srv_vattr_init(&attr, NFS_VER3); error = attrerr = vnode_getattr(vp, &attr, ctx); - if (!error && toff && verf && (verf != attr.va_filerev)) + if (!error && toff && verf && (verf != attr.va_filerev)) { error = NFSERR_BAD_COOKIE; + } } - if (!error) + if (!error) { error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0); + } +#if CONFIG_MACF + if (!error) { + if (!error && mac_vnode_check_open(ctx, vp, FREAD)) { + error = EACCES; + } + + if (!error) { + error = mac_vnode_check_readdir(ctx, vp); + } + } +#endif nfsmerr_if(error); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); - if (rbuf) + if (rbuf) { auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, - &uio_buf[0], sizeof(uio_buf)); + &uio_buf[0], sizeof(uio_buf)); + } if (!rbuf || !auio) { error = ENOMEM; goto nfsmerr; @@ -3989,7 +4367,7 @@ again: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + - NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED); + NFSX_COOKIEVERF(nd->nd_vers) + 2 * NFSX_UNSIGNED); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -4000,7 +4378,7 @@ again: nfsm_chain_add_32(error, &nmrep, FALSE); nfsm_chain_add_32(error, &nmrep, TRUE); nfsm_chain_build_done(error, &nmrep); - return (error); + return error; } } @@ -4028,7 +4406,7 @@ again: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers) + - NFSX_COOKIEVERF(nd->nd_vers) + siz); + NFSX_COOKIEVERF(nd->nd_vers) + siz); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -4046,12 +4424,14 @@ again: while ((cpos < cend) && (nentries > 0)) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; - if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) + if ((nd->nd_vers == NFS_VER2) && (nlen > NFS_MAXNAMLEN)) { nlen = NFS_MAXNAMLEN; - rem = nfsm_rndup(nlen)-nlen; + } + rem = nfsm_rndup(nlen) - nlen; len += (4 * NFSX_UNSIGNED + nlen + rem); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { len += 2 * NFSX_UNSIGNED; + } if (len > count) { eofflag = 0; break; @@ -4065,8 +4445,9 @@ again: } nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen); if (nd->nd_vers == NFS_VER3) { - if (vnopflag & VNODE_READDIR_SEEKOFF32) + if (vnopflag & VNODE_READDIR_SEEKOFF32) { dp->d_seekoff &= 0x00000000ffffffffULL; + } nfsm_chain_add_64(error, &nmrep, dp->d_seekoff); } else { nfsm_chain_add_32(error, &nmrep, dp->d_seekoff); @@ -4082,24 +4463,27 @@ again: FREE(rbuf, M_TEMP); goto nfsmout; nfsmerr: - if (rbuf) + if (rbuf) { FREE(rbuf, M_TEMP); - if (vp) + } + if (vp) { vnode_put(vp); + } nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPATTR(nd->nd_vers)); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr); + } nfsmout: nfsm_chain_build_done(error, &nmrep); if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } int @@ -4116,7 +4500,7 @@ nfsrv_readdirplus( struct nfs_export *nx; struct nfs_export_options *nxo; uio_t auio = NULL; - char uio_buf[ UIO_SIZEOF(1) ]; + char uio_buf[UIO_SIZEOF(1)]; struct vnode_attr attr, va, *vap = &va; int len, nlen, rem, xfer, error, attrerr, gotfh, gotattr; int siz, dircount, maxcount, fullsiz, eofflag, dirlen, nentries, isdotdot; @@ -4145,12 +4529,14 @@ nfsrv_readdirplus( off = toff; xfer = NFSRV_NDMAXDATA(nd); dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); - if (dircount > xfer) + if (dircount > xfer) { dircount = xfer; + } fullsiz = siz = dircount; maxcount = ((maxcount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1)); - if (maxcount > xfer) + if (maxcount > xfer) { maxcount = xfer; + } error = nfsrv_fhtovp(&dnfh, nd, &vp, &nx, &nxo); nfsmerr_if(error); @@ -4164,24 +4550,40 @@ nfsrv_readdirplus( error = nfsrv_credcheck(nd, ctx, nx, nxo); nfsmerr_if(error); - if (nxo->nxo_flags & NX_32BITCLIENTS) + if (nxo->nxo_flags & NX_32BITCLIENTS) { vnopflag |= VNODE_READDIR_SEEKOFF32; + } - if (nxo->nxo_flags & NX_MANGLEDNAMES) + if (nxo->nxo_flags & NX_MANGLEDNAMES) { vnopflag |= VNODE_READDIR_NAMEMAX; + } nfsm_srv_vattr_init(&attr, NFS_VER3); error = attrerr = vnode_getattr(vp, &attr, ctx); - if (!error && toff && verf && (verf != attr.va_filerev)) + if (!error && toff && verf && (verf != attr.va_filerev)) { error = NFSERR_BAD_COOKIE; - if (!error) + } + if (!error) { error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_LIST_DIRECTORY, ctx, nxo, 0); + } +#if CONFIG_MACF + if (!error) { + if (!error && mac_vnode_check_open(ctx, vp, FREAD)) { + error = EACCES; + } + + if (!error) { + error = mac_vnode_check_readdir(ctx, vp); + } + } +#endif nfsmerr_if(error); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); - if (rbuf) + if (rbuf) { auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, - &uio_buf[0], sizeof(uio_buf)); + &uio_buf[0], sizeof(uio_buf)); + } if (!rbuf || !auio) { error = ENOMEM; goto nfsmerr; @@ -4208,7 +4610,7 @@ again: /* assemble reply */ nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR + - NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED); + NFSX_V3COOKIEVERF + 2 * NFSX_UNSIGNED); nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); @@ -4217,7 +4619,7 @@ again: nfsm_chain_add_32(error, &nmrep, FALSE); nfsm_chain_add_32(error, &nmrep, TRUE); nfsm_chain_build_done(error, &nmrep); - return (error); + return error; } } @@ -4244,8 +4646,9 @@ again: * supports VGET. */ if ((error = VFS_VGET(vnode_mount(vp), (ino64_t)dp->d_fileno, &nvp, ctx))) { - if (error == ENOTSUP) /* let others get passed back */ + if (error == ENOTSUP) { /* let others get passed back */ error = NFSERR_NOTSUPP; + } goto nfsmerr; } vnode_put(nvp); @@ -4267,7 +4670,7 @@ again: while ((cpos < cend) && (nentries > 0)) { if (dp->d_fileno != 0) { nlen = dp->d_namlen; - rem = nfsm_rndup(nlen)-nlen; + rem = nfsm_rndup(nlen) - nlen; gotfh = gotattr = 1; /* Got to get the vnode for lookup per entry. */ @@ -4276,12 +4679,14 @@ again: gotfh = gotattr = 0; } else { isdotdot = ((dp->d_namlen == 2) && - (dp->d_name[0] == '.') && (dp->d_name[1] == '.')); - if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) + (dp->d_name[0] == '.') && (dp->d_name[1] == '.')); + if (nfsrv_vptofh(nx, 0, (isdotdot ? &dnfh : NULL), nvp, ctx, &nfh)) { gotfh = 0; + } nfsm_srv_vattr_init(vap, NFS_VER3); - if (vnode_getattr(nvp, vap, ctx)) + if (vnode_getattr(nvp, vap, ctx)) { gotattr = 0; + } vnode_put(nvp); } @@ -4292,10 +4697,12 @@ again: * XDR overheads. */ len += 8 * NFSX_UNSIGNED + nlen + rem; - if (gotattr) + if (gotattr) { len += NFSX_V3FATTR; - if (gotfh) + } + if (gotfh) { len += NFSX_UNSIGNED + nfsm_rndup(nfh.nfh_len); + } dirlen += 6 * NFSX_UNSIGNED + nlen + rem; if ((len > maxcount) || (dirlen > dircount)) { eofflag = 0; @@ -4306,14 +4713,16 @@ again: nfsm_chain_add_32(error, &nmrep, TRUE); nfsm_chain_add_64(error, &nmrep, dp->d_fileno); nfsm_chain_add_string(error, &nmrep, dp->d_name, nlen); - if (vnopflag & VNODE_READDIR_SEEKOFF32) + if (vnopflag & VNODE_READDIR_SEEKOFF32) { dp->d_seekoff &= 0x00000000ffffffffULL; + } nfsm_chain_add_64(error, &nmrep, dp->d_seekoff); nfsm_chain_add_postop_attr(error, nd, &nmrep, (gotattr ? 0 : ENOENT), vap); - if (gotfh) + if (gotfh) { nfsm_chain_add_postop_fh(error, &nmrep, nfh.nfh_fhp, nfh.nfh_len); - else + } else { nfsm_chain_add_32(error, &nmrep, FALSE); + } nfsmerr_if(error); } cpos += dp->d_reclen; @@ -4327,8 +4736,9 @@ again: FREE(rbuf, M_TEMP); goto nfsmout; nfsmerr: - if (rbuf) + if (rbuf) { FREE(rbuf, M_TEMP); + } nd->nd_repstat = error; error = nfsrv_rephead(nd, slp, &nmrep, NFSX_V3POSTOPATTR); nfsmout_if(error); @@ -4337,13 +4747,14 @@ nfsmerr: nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr); nfsmout: nfsm_chain_build_done(error, &nmrep); - if (vp) + if (vp) { vnode_put(vp); + } if (error) { nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4358,7 +4769,7 @@ nfsrv_commit( { vnode_t vp; struct nfs_filehandle nfh; - struct nfs_export *nx; + struct nfs_export *nx = NULL; struct nfs_export_options *nxo; int error, preattrerr, postattrerr, count; struct vnode_attr preattr, postattr; @@ -4402,8 +4813,9 @@ nfsrv_commit( postattrerr = vnode_getattr(vp, &postattr, ctx); nfsmerr: - if (vp) + if (vp) { vnode_put(vp); + } /* assemble reply */ nd->nd_repstat = error; @@ -4412,7 +4824,7 @@ nfsmerr: *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); nfsm_chain_add_wcc_data(error, nd, &nmrep, - preattrerr, &preattr, postattrerr, &postattr); + preattrerr, &preattr, postattrerr, &postattr); if (!nd->nd_repstat) { nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_sec); nfsm_chain_add_32(error, &nmrep, nx->nx_exptime.tv_usec); @@ -4423,7 +4835,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4469,6 +4881,7 @@ nfsrv_statfs( VFSATTR_INIT(&va); VFSATTR_WANTED(&va, f_blocks); + VFSATTR_WANTED(&va, f_bfree); VFSATTR_WANTED(&va, f_bavail); VFSATTR_WANTED(&va, f_files); VFSATTR_WANTED(&va, f_ffree); @@ -4481,8 +4894,9 @@ nfsrv_statfs( } nfsmerr: - if (vp) + if (vp) { vnode_put(vp); + } /* assemble reply */ nd->nd_repstat = error; @@ -4490,8 +4904,9 @@ nfsmerr: nfsmout_if(error); *mrepp = nmrep.nmc_mhead; nfsmout_on_status(nd, error); - if (nd->nd_vers == NFS_VER3) + if (nd->nd_vers == NFS_VER3) { nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, &attr); + } nfsmout_if(nd->nd_repstat); if (nd->nd_vers == NFS_VER3) { @@ -4515,7 +4930,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4560,8 +4975,9 @@ nfsrv_fsinfo( attrerr = vnode_getattr(vp, &attr, ctx); nfsmerr: - if (vp) + if (vp) { vnode_put(vp); + } /* assemble reply */ nd->nd_repstat = error; @@ -4579,8 +4995,9 @@ nfsmerr: if (slp->ns_sotype == SOCK_DGRAM) { maxsize = NFS_MAXDGRAMDATA; prefsize = NFS_PREFDGRAMDATA; - } else - maxsize = prefsize = NFSRV_MAXDATA; + } else { + maxsize = prefsize = slp->ns_sobufsize ? slp->ns_sobufsize / 2 : NFSRV_MAXDATA; + } nfsm_chain_add_32(error, &nmrep, maxsize); nfsm_chain_add_32(error, &nmrep, prefsize); @@ -4594,8 +5011,8 @@ nfsmerr: nfsm_chain_add_32(error, &nmrep, 1); /* XXX link/symlink support should be taken from volume capabilities */ nfsm_chain_add_32(error, &nmrep, - NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK | - NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME); + NFSV3FSINFO_LINK | NFSV3FSINFO_SYMLINK | + NFSV3FSINFO_HOMOGENEOUS | NFSV3FSINFO_CANSETTIME); nfsmout: nfsm_chain_build_done(error, &nmrep); @@ -4603,7 +5020,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4616,8 +5033,8 @@ nfsrv_pathconf( vfs_context_t ctx, mbuf_t *mrepp) { - int error, attrerr, linkmax, namemax; - int chownres, notrunc, case_sensitive, case_preserving; + int error, attrerr, linkmax = 0, namemax = 0; + int chownres = 0, notrunc = 0, case_sensitive = 0, case_preserving = 0; vnode_t vp; struct vnode_attr attr; struct nfs_filehandle nfh; @@ -4646,23 +5063,29 @@ nfsrv_pathconf( nfsmerr_if(error); error = VNOP_PATHCONF(vp, _PC_LINK_MAX, &linkmax, ctx); - if (!error) + if (!error) { error = VNOP_PATHCONF(vp, _PC_NAME_MAX, &namemax, ctx); - if (!error) + } + if (!error) { error = VNOP_PATHCONF(vp, _PC_CHOWN_RESTRICTED, &chownres, ctx); - if (!error) + } + if (!error) { error = VNOP_PATHCONF(vp, _PC_NO_TRUNC, ¬runc, ctx); - if (!error) + } + if (!error) { error = VNOP_PATHCONF(vp, _PC_CASE_SENSITIVE, &case_sensitive, ctx); - if (!error) + } + if (!error) { error = VNOP_PATHCONF(vp, _PC_CASE_PRESERVING, &case_preserving, ctx); + } nfsm_srv_vattr_init(&attr, NFS_VER3); attrerr = vnode_getattr(vp, &attr, ctx); nfsmerr: - if (vp) + if (vp) { vnode_put(vp); + } /* assemble reply */ nd->nd_repstat = error; @@ -4686,7 +5109,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4706,8 +5129,9 @@ nfsrv_null( /* * RPCSEC_GSS context setup ? */ - if (nd->nd_gss_context) - return(nfs_gss_svc_ctx_init(nd, slp, mrepp)); + if (nd->nd_gss_context) { + return nfs_gss_svc_ctx_init(nd, slp, mrepp); + } nfsm_chain_null(&nmrep); @@ -4722,7 +5146,7 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } /* @@ -4741,10 +5165,11 @@ nfsrv_noop( nfsm_chain_null(&nmrep); - if (nd->nd_repstat) + if (nd->nd_repstat) { error = nd->nd_repstat; - else + } else { error = EPROCUNAVAIL; + } /* assemble reply */ nd->nd_repstat = error; @@ -4757,13 +5182,10 @@ nfsmout: nfsm_chain_cleanup(&nmrep); *mrepp = NULL; } - return (error); + return error; } -int (*nfsrv_procs[NFS_NPROCS])(struct nfsrv_descript *nd, - struct nfsrv_sock *slp, - vfs_context_t ctx, - mbuf_t *mrepp) = { +const nfsrv_proc_t nfsrv_procs[NFS_NPROCS] = { nfsrv_null, nfsrv_getattr, nfsrv_setattr, @@ -4798,7 +5220,7 @@ int (*nfsrv_procs[NFS_NPROCS])(struct nfsrv_descript *nd, * 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, vnode_authorize() * will return EPERM instead of EACCESS. EPERM is always an error. */ @@ -4824,7 +5246,7 @@ nfsrv_authorize( if (nxo->nxo_flags & NX_READONLY) { switch (vnode_vtype(vp)) { case VREG: case VDIR: case VLNK: case VCPLX: - return (EROFS); + return EROFS; default: break; } @@ -4839,11 +5261,11 @@ nfsrv_authorize( VATTR_INIT(&vattr); VATTR_WANTED(&vattr, va_uid); if ((vnode_getattr(vp, &vattr, ctx) == 0) && - (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) + (kauth_cred_getuid(vfs_context_ucred(ctx)) == vattr.va_uid)) { error = 0; + } } return error; } -#endif /* NFSSERVER */ - +#endif /* CONFIG_NFS_SERVER */