]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_subs.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_subs.c
index 6b8cf9140f60e0ee148609d1a54b71234a4d5482..a58fc7869ea8f271cc002ad6a8a6a42b3a2fbfa6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -65,6 +65,9 @@
  * FreeBSD-Id: nfs_subs.c,v 1.47 1997/11/07 08:53:24 phk Exp $
  */
 
+#include <nfs/nfs_conf.h>
+#if CONFIG_NFS
+
 /*
  * These functions support the macros and help fiddle mbuf chains for
  * the nfs op functions. They do things like create the rpc header and
@@ -79,6 +82,7 @@
 #include <sys/vnode_internal.h>
 #include <sys/kpi_mbuf.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 #include <sys/stat.h>
 #include <sys/malloc.h>
 #include <sys/syscall.h>
@@ -88,6 +92,7 @@
 #include <sys/domain.h>
 #include <libkern/OSAtomic.h>
 #include <kern/thread_call.h>
+#include <kern/task.h>
 
 #include <sys/vm.h>
 #include <sys/vmparam.h>
 #include <nfs/nfsproto.h>
 #include <nfs/nfs.h>
 #include <nfs/nfsnode.h>
-#if NFSCLIENT
+#if CONFIG_NFS_CLIENT
 #define _NFS_XDR_SUBS_FUNCS_ /* define this to get xdrbuf function definitions */
 #endif
 #include <nfs/xdr_subs.h>
@@ -144,10 +149,12 @@ vtonfs_type(enum vtype vtype, int nfsvers)
                if (nfsvers > NFS_VER2) {
                        return NFSOCK;
                }
+               return NFNON;
        case VFIFO:
                if (nfsvers > NFS_VER2) {
                        return NFFIFO;
                }
+               return NFNON;
        case VBAD:
        case VSTR:
        case VCPLX:
@@ -176,18 +183,22 @@ nfstov_type(nfstype nvtype, int nfsvers)
                if (nfsvers > NFS_VER2) {
                        return VSOCK;
                }
+               OS_FALLTHROUGH;
        case NFFIFO:
                if (nfsvers > NFS_VER2) {
                        return VFIFO;
                }
+               OS_FALLTHROUGH;
        case NFATTRDIR:
                if (nfsvers > NFS_VER3) {
                        return VDIR;
                }
+               OS_FALLTHROUGH;
        case NFNAMEDATTR:
                if (nfsvers > NFS_VER3) {
                        return VREG;
                }
+               OS_FALLTHROUGH;
        default:
                return VNON;
        }
@@ -215,7 +226,7 @@ vtonfsv2_mode(enum vtype vtype, mode_t m)
        }
 }
 
-#if NFSSERVER
+#if CONFIG_NFS_SERVER
 
 /*
  * Mapping of old NFS Version 2 RPC numbers to generic numbers.
@@ -246,7 +257,7 @@ int nfsv3_procid[NFS_NPROCS] = {
        NFSPROC_NOOP
 };
 
-#endif /* NFSSERVER */
+#endif /* CONFIG_NFS_SERVER */
 
 /*
  * and the reverse mapping from generic to Version 2 procedure numbers
@@ -291,7 +302,7 @@ nfs_mbuf_init(void)
        nfs_mbuf_minclsize = ms.minclsize;
 }
 
-#if NFSSERVER
+#if CONFIG_NFS_SERVER
 
 /*
  * allocate a list of mbufs to hold the given amount of data
@@ -308,7 +319,7 @@ nfsm_mbuf_get_list(size_t size, mbuf_t *mp, int *mbcnt)
        len = 0;
 
        while (len < size) {
-               nfsm_mbuf_get(error, &m, (size - len));
+               nfsm_mbuf_getcluster(error, &m, (size - len));
                if (error) {
                        break;
                }
@@ -336,7 +347,7 @@ nfsm_mbuf_get_list(size_t size, mbuf_t *mp, int *mbcnt)
        return error;
 }
 
-#endif /* NFSSERVER */
+#endif /* CONFIG_NFS_SERVER */
 
 /*
  * nfsm_chain_new_mbuf()
@@ -354,7 +365,7 @@ nfsm_chain_new_mbuf(struct nfsm_chain *nmc, size_t sizehint)
        }
 
        /* allocate a new mbuf */
-       nfsm_mbuf_get(error, &mb, sizehint);
+       nfsm_mbuf_getcluster(error, &mb, sizehint);
        if (error) {
                return error;
        }
@@ -388,9 +399,9 @@ nfsm_chain_new_mbuf(struct nfsm_chain *nmc, size_t sizehint)
  * Add "len" bytes of opaque data pointed to by "buf" to the given chain.
  */
 int
-nfsm_chain_add_opaque_f(struct nfsm_chain *nmc, const u_char *buf, uint32_t len)
+nfsm_chain_add_opaque_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
 {
-       uint32_t paddedlen, tlen;
+       size_t paddedlen, tlen;
        int error;
 
        paddedlen = nfsm_rndup(len);
@@ -431,9 +442,9 @@ nfsm_chain_add_opaque_f(struct nfsm_chain *nmc, const u_char *buf, uint32_t len)
  * Do not XDR pad.
  */
 int
-nfsm_chain_add_opaque_nopad_f(struct nfsm_chain *nmc, const u_char *buf, uint32_t len)
+nfsm_chain_add_opaque_nopad_f(struct nfsm_chain *nmc, const u_char *buf, size_t len)
 {
-       uint32_t tlen;
+       size_t tlen;
        int error;
 
        while (len > 0) {
@@ -459,9 +470,9 @@ nfsm_chain_add_opaque_nopad_f(struct nfsm_chain *nmc, const u_char *buf, uint32_
  * Add "len" bytes of data from "uio" to the given chain.
  */
 int
-nfsm_chain_add_uio(struct nfsm_chain *nmc, uio_t uio, uint32_t len)
+nfsm_chain_add_uio(struct nfsm_chain *nmc, uio_t uio, size_t len)
 {
-       uint32_t paddedlen, tlen;
+       size_t paddedlen, tlen;
        int error;
 
        paddedlen = nfsm_rndup(len);
@@ -476,10 +487,8 @@ nfsm_chain_add_uio(struct nfsm_chain *nmc, uio_t uio, uint32_t len)
                tlen = MIN(nmc->nmc_left, paddedlen);
                if (tlen) {
                        if (len) {
-                               if (tlen > len) {
-                                       tlen = len;
-                               }
-                               uiomove(nmc->nmc_ptr, tlen, uio);
+                               tlen = MIN(INT32_MAX, MIN(tlen, len));
+                               uiomove(nmc->nmc_ptr, (int)tlen, uio);
                        } else {
                                bzero(nmc->nmc_ptr, tlen);
                        }
@@ -498,11 +507,11 @@ nfsm_chain_add_uio(struct nfsm_chain *nmc, uio_t uio, uint32_t len)
  * Find the length of the NFS mbuf chain
  * up to the current encoding/decoding offset.
  */
-int
+size_t
 nfsm_chain_offset(struct nfsm_chain *nmc)
 {
        mbuf_t mb;
-       int len = 0;
+       size_t len = 0;
 
        for (mb = nmc->nmc_mhead; mb; mb = mbuf_next(mb)) {
                if (mb == nmc->nmc_mcur) {
@@ -520,7 +529,7 @@ nfsm_chain_offset(struct nfsm_chain *nmc)
  * Advance an nfsm_chain by "len" bytes.
  */
 int
-nfsm_chain_advance(struct nfsm_chain *nmc, uint32_t len)
+nfsm_chain_advance(struct nfsm_chain *nmc, size_t len)
 {
        mbuf_t mb;
 
@@ -548,9 +557,9 @@ nfsm_chain_advance(struct nfsm_chain *nmc, uint32_t len)
  * Reverse decode offset in an nfsm_chain by "len" bytes.
  */
 int
-nfsm_chain_reverse(struct nfsm_chain *nmc, uint32_t len)
+nfsm_chain_reverse(struct nfsm_chain *nmc, size_t len)
 {
-       uint32_t mlen, new_offset;
+       size_t mlen, new_offset;
        int error = 0;
 
        mlen = nmc->nmc_ptr - (caddr_t) mbuf_data(nmc->nmc_mcur);
@@ -582,7 +591,8 @@ int
 nfsm_chain_get_opaque_pointer_f(struct nfsm_chain *nmc, uint32_t len, u_char **pptr)
 {
        mbuf_t mbcur, mb;
-       uint32_t left, need, mblen, cplen, padlen;
+       uint32_t padlen;
+       size_t mblen, cplen, need, left;
        u_char *ptr;
        int error = 0;
 
@@ -621,7 +631,7 @@ nfsm_chain_get_opaque_pointer_f(struct nfsm_chain *nmc, uint32_t len, u_char **p
                 * The needed bytes won't fit in the current mbuf so we'll
                 * allocate a new mbuf to hold the contiguous range of data.
                 */
-               nfsm_mbuf_get(error, &mb, len);
+               nfsm_mbuf_getcluster(error, &mb, len);
                if (error) {
                        return error;
                }
@@ -741,9 +751,9 @@ nfsm_chain_get_opaque_pointer_f(struct nfsm_chain *nmc, uint32_t len, u_char **p
  * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
  */
 int
-nfsm_chain_get_opaque_f(struct nfsm_chain *nmc, uint32_t len, u_char *buf)
+nfsm_chain_get_opaque_f(struct nfsm_chain *nmc, size_t len, u_char *buf)
 {
-       uint32_t cplen, padlen;
+       size_t cplen, padlen;
        int error = 0;
 
        padlen = nfsm_rndup(len) - len;
@@ -787,9 +797,9 @@ nfsm_chain_get_opaque_f(struct nfsm_chain *nmc, uint32_t len, u_char *buf)
  * The nfsm_chain is advanced by nfsm_rndup("len") bytes.
  */
 int
-nfsm_chain_get_uio(struct nfsm_chain *nmc, uint32_t len, uio_t uio)
+nfsm_chain_get_uio(struct nfsm_chain *nmc, size_t len, uio_t uio)
 {
-       uint32_t cplen, padlen;
+       size_t cplen, padlen;
        int error = 0;
 
        padlen = nfsm_rndup(len) - len;
@@ -799,7 +809,8 @@ nfsm_chain_get_uio(struct nfsm_chain *nmc, uint32_t len, uio_t uio)
                /* copy as much as we need/can */
                cplen = MIN(nmc->nmc_left, len);
                if (cplen) {
-                       error = uiomove(nmc->nmc_ptr, cplen, uio);
+                       cplen = MIN(cplen, INT32_MAX);
+                       error = uiomove(nmc->nmc_ptr, (int)cplen, uio);
                        if (error) {
                                return error;
                        }
@@ -828,10 +839,10 @@ nfsm_chain_get_uio(struct nfsm_chain *nmc, uint32_t len, uio_t uio)
        return error;
 }
 
-#if NFSCLIENT
+#if CONFIG_NFS_CLIENT
 
 int
-nfsm_chain_add_string_nfc(struct nfsm_chain *nmc, const uint8_t *s, uint32_t slen)
+nfsm_chain_add_string_nfc(struct nfsm_chain *nmc, const uint8_t *s, size_t slen)
 {
        uint8_t smallbuf[64];
        uint8_t *nfcname = smallbuf;
@@ -841,10 +852,8 @@ nfsm_chain_add_string_nfc(struct nfsm_chain *nmc, const uint8_t *s, uint32_t sle
        error = utf8_normalizestr(s, slen, nfcname, &nfclen, buflen, UTF_PRECOMPOSED | UTF_NO_NULL_TERM);
        if (error == ENAMETOOLONG) {
                buflen = MAXPATHLEN;
-               MALLOC_ZONE(nfcname, uint8_t *, MAXPATHLEN, M_NAMEI, M_WAITOK);
-               if (nfcname) {
-                       error = utf8_normalizestr(s, slen, nfcname, &nfclen, buflen, UTF_PRECOMPOSED | UTF_NO_NULL_TERM);
-               }
+               nfcname = zalloc(ZV_NAMEI);
+               error = utf8_normalizestr(s, slen, nfcname, &nfclen, buflen, UTF_PRECOMPOSED | UTF_NO_NULL_TERM);
        }
 
        /* if we got an error, just use the original string */
@@ -855,7 +864,7 @@ nfsm_chain_add_string_nfc(struct nfsm_chain *nmc, const uint8_t *s, uint32_t sle
        }
 
        if (nfcname && (nfcname != smallbuf)) {
-               FREE_ZONE(nfcname, MAXPATHLEN, M_NAMEI);
+               NFS_ZFREE(ZV_NAMEI, nfcname);
        }
        return error;
 }
@@ -889,7 +898,10 @@ nfsm_chain_add_v2sattr_f(struct nfsm_chain *nmc, struct vnode_attr *vap, uint32_
  * Add an NFSv3 "sattr" structure to an mbuf chain
  */
 int
-nfsm_chain_add_v3sattr_f(struct nfsm_chain *nmc, struct vnode_attr *vap)
+nfsm_chain_add_v3sattr_f(
+       __unused struct nfsmount *nmp,
+       struct nfsm_chain *nmc,
+       struct vnode_attr *vap)
 {
        int error = 0;
 
@@ -937,6 +949,7 @@ nfsm_chain_add_v3sattr_f(struct nfsm_chain *nmc, struct vnode_attr *vap)
                }
        }
 
+
        return error;
 }
 
@@ -948,6 +961,7 @@ nfsm_chain_add_v3sattr_f(struct nfsm_chain *nmc, struct vnode_attr *vap)
  */
 int
 nfsm_chain_get_fh_attr(
+       struct nfsmount *nmp,
        struct nfsm_chain *nmc,
        nfsnode_t dnp,
        vfs_context_t ctx,
@@ -976,7 +990,7 @@ nfsm_chain_get_fh_attr(
                if (!gotfh) { /* skip attributes */
                        nfsm_chain_adv(error, nmc, NFSX_V3FATTR);
                } else { /* get attributes */
-                       error = nfs_parsefattr(nmc, nfsvers, nvap);
+                       error = nfs_parsefattr(nmp, nmc, nfsvers, nvap);
                }
        } else if (gotfh) {
                /* we need valid attributes in order to call nfs_nget() */
@@ -1026,7 +1040,7 @@ nfs_get_xid(uint64_t *xidp)
 {
        struct timeval tv;
 
-       lck_mtx_lock(nfs_request_mutex);
+       lck_mtx_lock(&nfs_request_mutex);
        if (!nfs_xid) {
                /*
                 * Derive initial xid from system time.
@@ -1044,8 +1058,8 @@ nfs_get_xid(uint64_t *xidp)
                nfs_xidwrap++;
                nfs_xid++;
        }
-       *xidp = nfs_xid + ((uint64_t)nfs_xidwrap << 32);
-       lck_mtx_unlock(nfs_request_mutex);
+       *xidp = nfs_xid + (nfs_xidwrap << 32);
+       lck_mtx_unlock(&nfs_request_mutex);
 }
 
 /*
@@ -1080,12 +1094,12 @@ nfsm_rpchead(
  * Just a wrapper around kauth_cred_getgroups to handle the case of a server supporting less
  * than NGROUPS.
  */
-static int
-get_auxiliary_groups(kauth_cred_t cred, gid_t groups[NGROUPS], int count)
+static size_t
+get_auxiliary_groups(kauth_cred_t cred, gid_t groups[NGROUPS], size_t count)
 {
        gid_t pgid;
-       int maxcount = count < NGROUPS ? count + 1 : NGROUPS;
-       int i;
+       size_t maxcount = count < NGROUPS ? count + 1 : NGROUPS;
+       size_t i;
 
        for (i = 0; i < NGROUPS; i++) {
                groups[i] = -2; /* Initialize to the nobody group */
@@ -1117,15 +1131,16 @@ get_auxiliary_groups(kauth_cred_t cred, gid_t groups[NGROUPS], int count)
 }
 
 int
-nfsm_rpchead2(struct nfsmount *nmp, int sotype, int prog, int vers, int proc, int auth_type,
+nfsm_rpchead2(__unused struct nfsmount *nmp, int sotype, int prog, int vers, int proc, int auth_type,
     kauth_cred_t cred, struct nfsreq *req, mbuf_t mrest, u_int64_t *xidp, mbuf_t *mreqp)
 {
        mbuf_t mreq, mb;
-       int error, i, auth_len = 0, authsiz, reqlen;
+       size_t i;
+       int error, auth_len = 0, authsiz, reqlen;
        size_t headlen;
        struct nfsm_chain nmreq;
        gid_t grouplist[NGROUPS];
-       int groupcount;
+       size_t groupcount = 0;
 
        /* calculate expected auth length */
        switch (auth_type) {
@@ -1134,18 +1149,16 @@ nfsm_rpchead2(struct nfsmount *nmp, int sotype, int prog, int vers, int proc, in
                break;
        case RPCAUTH_SYS:
        {
-               int count = nmp->nm_numgrps < NGROUPS ? nmp->nm_numgrps : NGROUPS;
+               size_t count = nmp->nm_numgrps < NGROUPS ? nmp->nm_numgrps : NGROUPS;
 
                if (!cred) {
                        return EINVAL;
                }
                groupcount = get_auxiliary_groups(cred, grouplist, count);
-               if (groupcount < 0) {
-                       return EINVAL;
-               }
                auth_len = ((uint32_t)groupcount + 5) * NFSX_UNSIGNED;
                break;
        }
+#if CONFIG_NFS_GSS
        case RPCAUTH_KRB5:
        case RPCAUTH_KRB5I:
        case RPCAUTH_KRB5P:
@@ -1154,6 +1167,7 @@ nfsm_rpchead2(struct nfsmount *nmp, int sotype, int prog, int vers, int proc, in
                }
                auth_len = 5 * NFSX_UNSIGNED + 0;         // zero context handle for now
                break;
+#endif /* CONFIG_NFS_GSS */
        default:
                return EINVAL;
        }
@@ -1207,7 +1221,9 @@ nfsm_rpchead2(struct nfsmount *nmp, int sotype, int prog, int vers, int proc, in
        nfsm_chain_add_32(error, &nmreq, vers);
        nfsm_chain_add_32(error, &nmreq, proc);
 
+#if CONFIG_NFS_GSS
 add_cred:
+#endif
        switch (auth_type) {
        case RPCAUTH_NONE:
                nfsm_chain_add_32(error, &nmreq, RPCAUTH_NONE); /* auth */
@@ -1223,7 +1239,9 @@ add_cred:
        case RPCAUTH_SYS: {
                nfsm_chain_add_32(error, &nmreq, RPCAUTH_SYS);
                nfsm_chain_add_32(error, &nmreq, authsiz);
-               nfsm_chain_add_32(error, &nmreq, 0);    /* stamp */
+               {
+                       nfsm_chain_add_32(error, &nmreq, 0);    /* stamp */
+               }
                nfsm_chain_add_32(error, &nmreq, 0);    /* zero-length hostname */
                nfsm_chain_add_32(error, &nmreq, kauth_cred_getuid(cred));      /* UID */
                nfsm_chain_add_32(error, &nmreq, kauth_cred_getgid(cred));      /* GID */
@@ -1243,12 +1261,13 @@ add_cred:
                }
                break;
        }
+#if CONFIG_NFS_GSS
        case RPCAUTH_KRB5:
        case RPCAUTH_KRB5I:
        case RPCAUTH_KRB5P:
                error = nfs_gss_clnt_cred_put(req, &nmreq, mrest);
                if (error == ENEEDAUTH) {
-                       int count = nmp->nm_numgrps < NGROUPS ? nmp->nm_numgrps : NGROUPS;
+                       size_t count = nmp->nm_numgrps < NGROUPS ? nmp->nm_numgrps : NGROUPS;
 
                        /*
                         * Use sec=sys for this user
@@ -1256,14 +1275,12 @@ add_cred:
                        error = 0;
                        req->r_auth = auth_type = RPCAUTH_SYS;
                        groupcount = get_auxiliary_groups(cred, grouplist, count);
-                       if (groupcount < 0) {
-                               return EINVAL;
-                       }
                        auth_len = ((uint32_t)groupcount + 5) * NFSX_UNSIGNED;
                        authsiz = nfsm_rndup(auth_len);
                        goto add_cred;
                }
                break;
+#endif /* CONFIG_NFS_GSS */
        }
        ;
 
@@ -1304,13 +1321,16 @@ add_cred:
  * Parse an NFS file attribute structure out of an mbuf chain.
  */
 int
-nfs_parsefattr(struct nfsm_chain *nmc, int nfsvers, struct nfs_vattr *nvap)
+nfs_parsefattr(
+       __unused struct nfsmount *nmp,
+       struct nfsm_chain *nmc,
+       int nfsvers,
+       struct nfs_vattr *nvap)
 {
        int error = 0;
        enum vtype vtype;
        nfstype nvtype;
-       u_short vmode;
-       uint32_t val, val2;
+       uint32_t vmode, val, val2;
        dev_t rdev;
 
        val = val2 = 0;
@@ -1407,10 +1427,12 @@ nfs_parsefattr(struct nfsm_chain *nmc, int nfsvers, struct nfs_vattr *nvap)
        nfsm_chain_get_time(error, nmc, nfsvers,
            nvap->nva_timesec[NFSTIME_CHANGE],
            nvap->nva_timensec[NFSTIME_CHANGE]);
+
 nfsmout:
        return error;
 }
 
+
 /*
  * Load the attribute cache (that lives in the nfsnode entry) with
  * the value pointed to by nvap, unless the file type in the attribute
@@ -1531,6 +1553,7 @@ nfs_loadattrcache(
                } else if (NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_OWNER_GROUP) &&
                    (nvap->nva_gid != npnvap->nva_gid)) {
                        events |= VNODE_EVENT_ATTRIB | VNODE_EVENT_PERMS;
+#if CONFIG_NFS4
                } else if (nmp->nm_vers >= NFS_VER4) {
                        if (NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_OWNER) &&
                            !kauth_guid_equal(&nvap->nva_uuuid, &npnvap->nva_uuuid)) {
@@ -1544,47 +1567,66 @@ nfs_loadattrcache(
                            bcmp(nvap->nva_acl, npnvap->nva_acl, KAUTH_ACL_COPYSIZE(nvap->nva_acl))))) {
                                events |= VNODE_EVENT_ATTRIB | VNODE_EVENT_PERMS;
                        }
+#endif
                }
-               if (((nmp->nm_vers >= NFS_VER4) && (nvap->nva_change != npnvap->nva_change)) ||
-                   (NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_TIME_MODIFY) &&
-                   ((nvap->nva_timesec[NFSTIME_MODIFY] != npnvap->nva_timesec[NFSTIME_MODIFY]) ||
-                   (nvap->nva_timensec[NFSTIME_MODIFY] != npnvap->nva_timensec[NFSTIME_MODIFY])))) {
+               if (/* Oh, C... */
+#if CONFIG_NFS4
+                       ((nmp->nm_vers >= NFS_VER4) && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_CHANGE) && (nvap->nva_change != npnvap->nva_change)) ||
+#endif
+                       (NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_MODIFY) &&
+                       ((nvap->nva_timesec[NFSTIME_MODIFY] != npnvap->nva_timesec[NFSTIME_MODIFY]) ||
+                       (nvap->nva_timensec[NFSTIME_MODIFY] != npnvap->nva_timensec[NFSTIME_MODIFY])))) {
                        events |= VNODE_EVENT_ATTRIB | VNODE_EVENT_WRITE;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_RAWDEV) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_RAWDEV) &&
                    ((nvap->nva_rawdev.specdata1 != npnvap->nva_rawdev.specdata1) ||
                    (nvap->nva_rawdev.specdata2 != npnvap->nva_rawdev.specdata2))) {
                        events |= VNODE_EVENT_ATTRIB;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_FILEID) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_FILEID) &&
                    (nvap->nva_fileid != npnvap->nva_fileid)) {
                        events |= VNODE_EVENT_ATTRIB;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_ARCHIVE) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_ARCHIVE) &&
                    ((nvap->nva_flags & NFS_FFLAG_ARCHIVED) != (npnvap->nva_flags & NFS_FFLAG_ARCHIVED))) {
                        events |= VNODE_EVENT_ATTRIB;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_HIDDEN) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_HIDDEN) &&
                    ((nvap->nva_flags & NFS_FFLAG_HIDDEN) != (npnvap->nva_flags & NFS_FFLAG_HIDDEN))) {
                        events |= VNODE_EVENT_ATTRIB;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_TIME_CREATE) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_CREATE) &&
                    ((nvap->nva_timesec[NFSTIME_CREATE] != npnvap->nva_timesec[NFSTIME_CREATE]) ||
                    (nvap->nva_timensec[NFSTIME_CREATE] != npnvap->nva_timensec[NFSTIME_CREATE]))) {
                        events |= VNODE_EVENT_ATTRIB;
                }
-               if (!events && NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_TIME_BACKUP) &&
+               if (!events && NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_BACKUP) &&
                    ((nvap->nva_timesec[NFSTIME_BACKUP] != npnvap->nva_timesec[NFSTIME_BACKUP]) ||
                    (nvap->nva_timensec[NFSTIME_BACKUP] != npnvap->nva_timensec[NFSTIME_BACKUP]))) {
                        events |= VNODE_EVENT_ATTRIB;
                }
        }
 
+#if CONFIG_NFS4
        /* Copy the attributes to the attribute cache */
-       bcopy((caddr_t)nvap, (caddr_t)npnvap, sizeof(*nvap));
+       if (nmp->nm_vers >= NFS_VER4 && npnvap->nva_flags & NFS_FFLAG_PARTIAL_WRITE) {
+               /*
+                * NFSv4 WRITE RPCs contain partial GETATTR requests - only type, change, size, metadatatime and modifytime are requested.
+                * In such cases,  we do not update the time stamp - but the requested attributes.
+                */
+               NFS_BITMAP_COPY_ATTR(nvap, npnvap, TYPE, type);
+               NFS_BITMAP_COPY_ATTR(nvap, npnvap, CHANGE, change);
+               NFS_BITMAP_COPY_ATTR(nvap, npnvap, SIZE, size);
+               NFS_BITMAP_COPY_TIME(nvap, npnvap, METADATA, CHANGE);
+               NFS_BITMAP_COPY_TIME(nvap, npnvap, MODIFY, MODIFY);
+       } else
+#endif /* CONFIG_NFS4 */
+       {
+               bcopy((caddr_t)nvap, (caddr_t)npnvap, sizeof(*nvap));
+               microuptime(&now);
+               np->n_attrstamp = now.tv_sec;
+       }
 
-       microuptime(&now);
-       np->n_attrstamp = now.tv_sec;
        np->n_xid = *xidp;
        /* NFS_FFLAG_IS_ATTR and NFS_FFLAG_TRIGGER_REFERRAL need to be sticky... */
        if (vp && xattr) {
@@ -1594,7 +1636,7 @@ nfs_loadattrcache(
                nvap->nva_flags |= referral;
        }
 
-       if (NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_ACL)) {
+       if (NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_ACL)) {
                /* we're updating the ACL */
                if (nvap->nva_acl) {
                        /* make a copy of the acl for the cache */
@@ -1613,7 +1655,7 @@ nfs_loadattrcache(
                        acl = NULL;
                }
        }
-       if (NFS_BITMAP_ISSET(npnvap->nva_bitmap, NFS_FATTR_ACL)) {
+       if (NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_ACL)) {
                /* update the ACL timestamp */
                np->n_aclstamp = now.tv_sec;
        } else {
@@ -1625,6 +1667,7 @@ nfs_loadattrcache(
        }
 
 #if CONFIG_TRIGGERS
+#if CONFIG_NFS4
        /*
         * For NFSv4, if the fsid doesn't match the fsid for the mount, then
         * this node is for a different file system on the server.  So we mark
@@ -1635,7 +1678,8 @@ nfs_loadattrcache(
            (np->n_vattr.nva_fsid.minor != nmp->nm_fsid.minor))) {
                np->n_vattr.nva_flags |= NFS_FFLAG_TRIGGER;
        }
-#endif
+#endif /* CONFIG_NFS4 */
+#endif /* CONFIG_TRIGGERS */
 
        if (!vp || (nvap->nva_type != VREG)) {
                np->n_size = nvap->nva_size;
@@ -1689,13 +1733,13 @@ out:
  * Calculate the attribute timeout based on
  * how recently the file has been modified.
  */
-int
+long
 nfs_attrcachetimeout(nfsnode_t np)
 {
        struct nfsmount *nmp;
        struct timeval now;
        int isdir;
-       uint32_t timeo;
+       long timeo;
 
        nmp = NFSTONMP(np);
        if (nfs_mount_gone(nmp)) {
@@ -1703,11 +1747,13 @@ nfs_attrcachetimeout(nfsnode_t np)
        }
 
        isdir = vnode_isdir(NFSTOV(np));
-
+#if CONFIG_NFS4
        if ((nmp->nm_vers >= NFS_VER4) && (np->n_openflags & N_DELEG_MASK)) {
                /* If we have a delegation, we always use the max timeout. */
                timeo = isdir ? nmp->nm_acdirmax : nmp->nm_acregmax;
-       } else if ((np)->n_flag & NMODIFIED) {
+       } else
+#endif
+       if ((np)->n_flag & NMODIFIED) {
                /* If we have modifications, we always use the min timeout. */
                timeo = isdir ? nmp->nm_acdirmin : nmp->nm_acregmin;
        } else {
@@ -1745,7 +1791,7 @@ nfs_getattrcache(nfsnode_t np, struct nfs_vattr *nvaper, int flags)
 {
        struct nfs_vattr *nvap;
        struct timeval nowup;
-       int32_t timeo;
+       long timeo;
        struct nfsmount *nmp;
 
        /* Check if the attributes are valid. */
@@ -1914,8 +1960,19 @@ nfs_uaddr2sockaddr(const char *uaddr, struct sockaddr *addr)
        unsigned long val;      /* decoded value */
        int s;                  /* index used for sliding array to insert elided zeroes */
 
+       /* AF_LOCAL address are paths that start with '/' or are empty */
+       if (*uaddr == '/' || *uaddr == '\0') { /* AF_LOCAL address */
+               struct sockaddr_un *sun = (struct sockaddr_un *)addr;
+               sun->sun_family = AF_LOCAL;
+               sun->sun_len = sizeof(struct sockaddr_un);
+               strlcpy(sun->sun_path, uaddr, sizeof(sun->sun_path));
+
+               return 1;
+       }
+
 #define HEXVALUE        0
 #define DECIMALVALUE    1
+
 #define GET(TYPE) \
        do { \
                if ((dcount <= 0) || (dcount > (((TYPE) == DECIMALVALUE) ? 3 : 4))) \
@@ -2055,7 +2112,7 @@ nfs_uaddr2sockaddr(const char *uaddr, struct sockaddr *addr)
                sin6->sin6_family = AF_INET6;
                bcopy(a, &sin6->sin6_addr.s6_addr, sizeof(struct in6_addr));
                if ((dots == 5) || (dots == 2)) {
-                       sin6->sin6_port = htons((a[16] << 8) | a[17]);
+                       sin6->sin6_port = htons((in_port_t)((a[16] << 8) | a[17]));
                }
                if (pscope) {
                        for (p = pscope; IS_DIGIT(*p); p++) {
@@ -2070,7 +2127,7 @@ nfs_uaddr2sockaddr(const char *uaddr, struct sockaddr *addr)
                                        ifnet_release(interface);
                                }
                        } else { /* decimal number */
-                               sin6->sin6_scope_id = strtoul(pscope, NULL, 10);
+                               sin6->sin6_scope_id = (uint32_t)strtoul(pscope, NULL, 10);
                        }
                        /* XXX should we also embed scope id for linklocal? */
                }
@@ -2090,7 +2147,7 @@ nfs_uaddr2sockaddr(const char *uaddr, struct sockaddr *addr)
                sin->sin_family = AF_INET;
                bcopy(a, &sin->sin_addr.s_addr, sizeof(struct in_addr));
                if (dots == 5) {
-                       sin->sin_port = htons((a[4] << 8) | a[5]);
+                       sin->sin_port = htons((in_port_t)((a[4] << 8) | a[5]));
                }
        }
        return 1;
@@ -2104,20 +2161,57 @@ uint32_t nfs_debug_ctl;
 #include <stdarg.h>
 
 void
-nfs_printf(int facility, int level, const char *fmt, ...)
+nfs_printf(unsigned int facility, unsigned int level, const char *fmt, ...)
 {
        va_list ap;
 
-       if ((uint32_t)level > NFS_DEBUG_LEVEL) {
-               return;
+       if (NFS_IS_DBG(facility, level)) {
+               va_start(ap, fmt);
+               vprintf(fmt, ap);
+               va_end(ap);
        }
-       if (NFS_DEBUG_FACILITY && !((uint32_t)facility & NFS_DEBUG_FACILITY)) {
-               return;
+}
+
+
+#define DISPLAYLEN 16
+
+static bool
+isprint(int ch)
+{
+       return ch >= 0x20 && ch <= 0x7e;
+}
+
+static void
+hexdump(void *data, size_t len)
+{
+       size_t i, j;
+       unsigned char *d = data;
+       char *p, disbuf[3 * DISPLAYLEN + 1];
+
+       for (i = 0; i < len; i += DISPLAYLEN) {
+               for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p += 3) {
+                       snprintf(p, 4, "%2.2x ", d[i + j]);
+               }
+               for (; j < DISPLAYLEN; j++, p += 3) {
+                       snprintf(p, 4, "   ");
+               }
+               printf("%s    ", disbuf);
+               for (p = disbuf, j = 0; (j + i) < len && j < DISPLAYLEN; j++, p++) {
+                       snprintf(p, 2, "%c", isprint(d[i + j]) ? d[i + j] : '.');
+               }
+               printf("%s\n", disbuf);
        }
+}
+
+void
+nfs_dump_mbuf(const char *func, int lineno, const char *msg, mbuf_t mb)
+{
+       mbuf_t m;
 
-       va_start(ap, fmt);
-       vprintf(fmt, ap);
-       va_end(ap);
+       printf("%s:%d %s\n", func, lineno, msg);
+       for (m = mb; m; m = mbuf_next(m)) {
+               hexdump(mbuf_data(m), mbuf_len(m));
+       }
 }
 
 /* Is a mount gone away? */
@@ -2154,23 +2248,23 @@ nfs_mountopts(struct nfsmount *nmp, char *buf, int buflen)
        return c > buflen ? ENOMEM : 0;
 }
 
-#endif /* NFSCLIENT */
+#endif /* CONFIG_NFS_CLIENT */
 
 /*
  * Schedule a callout thread to run an NFS timer function
  * interval milliseconds in the future.
  */
 void
-nfs_interval_timer_start(thread_call_t call, int interval)
+nfs_interval_timer_start(thread_call_t call, time_t interval)
 {
        uint64_t deadline;
 
-       clock_interval_to_deadline(interval, 1000 * 1000, &deadline);
+       clock_interval_to_deadline((int)interval, 1000 * 1000, &deadline);
        thread_call_enter_delayed(call, deadline);
 }
 
 
-#if NFSSERVER
+#if CONFIG_NFS_SERVER
 
 int nfsrv_cmp_secflavs(struct nfs_sec *, struct nfs_sec *);
 int nfsrv_hang_addrlist(struct nfs_export *, struct user_nfs_export_args *);
@@ -2231,10 +2325,7 @@ nfsm_chain_get_path_namei(
         * Get a buffer for the name to be translated, and copy the
         * name into the buffer.
         */
-       MALLOC_ZONE(cnp->cn_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
-       if (!cnp->cn_pnbuf) {
-               return ENOMEM;
-       }
+       cnp->cn_pnbuf = zalloc(ZV_NAMEI);
        cnp->cn_pnlen = MAXPATHLEN;
        cnp->cn_flags |= HASBUF;
 
@@ -2253,7 +2344,7 @@ nfsm_chain_get_path_namei(
 out:
        if (error) {
                if (cnp->cn_pnbuf) {
-                       FREE_ZONE(cnp->cn_pnbuf, MAXPATHLEN, M_NAMEI);
+                       NFS_ZFREE(ZV_NAMEI, cnp->cn_pnbuf);
                }
                cnp->cn_flags &= ~HASBUF;
        } else {
@@ -2338,7 +2429,7 @@ out:
                tmppn = cnp->cn_pnbuf;
                cnp->cn_pnbuf = NULL;
                cnp->cn_flags &= ~HASBUF;
-               FREE_ZONE(tmppn, cnp->cn_pnlen, M_NAMEI);
+               NFS_ZFREE(ZV_NAMEI, tmppn);
        }
        return error;
 }
@@ -2351,7 +2442,8 @@ void
 nfsm_adj(mbuf_t mp, int len, int nul)
 {
        mbuf_t m, mnext;
-       int count, i, mlen;
+       int count, i;
+       long mlen;
        char *cp;
 
        /*
@@ -2419,7 +2511,8 @@ nfsm_adj(mbuf_t mp, int len, int nul)
 int
 nfsm_chain_trim_data(struct nfsm_chain *nmc, int len, int *mlen)
 {
-       int cnt = 0, dlen, adjust;
+       int cnt = 0;
+       long dlen, adjust;
        caddr_t data;
        mbuf_t m;
 
@@ -2662,12 +2755,14 @@ nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
        struct radix_node *rn;
        struct sockaddr *saddr, *smask;
        struct domain *dom;
-       int i, error;
+       size_t i, ss_minsize;
+       int error;
        unsigned int net;
        user_addr_t uaddr;
        kauth_cred_t cred;
 
        uaddr = unxa->nxa_nets;
+       ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
        for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
                error = copyin(uaddr, &nxna, sizeof(nxna));
                if (error) {
@@ -2675,7 +2770,9 @@ nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
                }
 
                if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
+                   (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
                    nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
+                   (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
                    nxna.nxna_addr.ss_family > AF_MAX ||
                    nxna.nxna_mask.ss_family > AF_MAX) {
                        return EINVAL;
@@ -2686,7 +2783,7 @@ nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
                        bzero(&temp_pcred, sizeof(temp_pcred));
                        temp_pcred.cr_uid = nxna.nxna_cred.cr_uid;
                        temp_pcred.cr_ngroups = nxna.nxna_cred.cr_ngroups;
-                       for (i = 0; i < nxna.nxna_cred.cr_ngroups && i < NGROUPS; i++) {
+                       for (i = 0; i < (size_t)nxna.nxna_cred.cr_ngroups && i < NGROUPS; i++) {
                                temp_pcred.cr_groups[i] = nxna.nxna_cred.cr_groups[i];
                        }
                        cred = posix_cred_create(&temp_pcred);
@@ -2735,20 +2832,20 @@ nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
                } else {
                        smask = NULL;
                }
-               i = saddr->sa_family;
-               if ((rnh = nx->nx_rtable[i]) == 0) {
+               sa_family_t family = saddr->sa_family;
+               if ((rnh = nx->nx_rtable[family]) == 0) {
                        /*
                         * Seems silly to initialize every AF when most are not
                         * used, do so on demand here
                         */
                        TAILQ_FOREACH(dom, &domains, dom_entry) {
-                               if (dom->dom_family == i && dom->dom_rtattach) {
-                                       dom->dom_rtattach((void **)&nx->nx_rtable[i],
+                               if (dom->dom_family == family && dom->dom_rtattach) {
+                                       dom->dom_rtattach((void **)&nx->nx_rtable[family],
                                            dom->dom_rtoffset);
                                        break;
                                }
                        }
-                       if ((rnh = nx->nx_rtable[i]) == 0) {
+                       if ((rnh = nx->nx_rtable[family]) == 0) {
                                if (IS_VALID_CRED(cred)) {
                                        kauth_cred_unref(&cred);
                                }
@@ -2792,8 +2889,8 @@ nfsrv_hang_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
                                         */
                                        gid_t groups[NGROUPS];
                                        gid_t groups2[NGROUPS];
-                                       int groupcount = NGROUPS;
-                                       int group2count = NGROUPS;
+                                       size_t groupcount = NGROUPS;
+                                       size_t group2count = NGROUPS;
 
                                        if (!kauth_cred_getgroups(cred, groups, &groupcount) &&
                                            !kauth_cred_getgroups(cred2, groups2, &group2count) &&
@@ -2862,6 +2959,7 @@ nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
        struct radix_node *rn;
        struct nfsrv_free_netopt_arg fna;
        struct nfs_netopt *nno;
+       size_t ss_minsize;
        user_addr_t uaddr;
        unsigned int net;
        int i, error;
@@ -2882,6 +2980,7 @@ nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
 
        /* delete only the exports specified */
        uaddr = unxa->nxa_nets;
+       ss_minsize = sizeof(((struct sockaddr_storage *)0)->ss_len) + sizeof(((struct sockaddr_storage *)0)->ss_family);
        for (net = 0; net < unxa->nxa_netcount; net++, uaddr += sizeof(nxna)) {
                error = copyin(uaddr, &nxna, sizeof(nxna));
                if (error) {
@@ -2900,6 +2999,20 @@ nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
                        continue;
                }
 
+               if (nxna.nxna_addr.ss_len > sizeof(struct sockaddr_storage) ||
+                   (nxna.nxna_addr.ss_len != 0 && nxna.nxna_addr.ss_len < ss_minsize) ||
+                   nxna.nxna_addr.ss_family > AF_MAX) {
+                       printf("nfsrv_free_addrlist: invalid socket address (%u)\n", net);
+                       continue;
+               }
+
+               if (nxna.nxna_mask.ss_len > sizeof(struct sockaddr_storage) ||
+                   (nxna.nxna_mask.ss_len != 0 && nxna.nxna_mask.ss_len < ss_minsize) ||
+                   nxna.nxna_mask.ss_family > AF_MAX) {
+                       printf("nfsrv_free_addrlist: invalid socket mask (%u)\n", net);
+                       continue;
+               }
+
                if ((rnh = nx->nx_rtable[nxna.nxna_addr.ss_family]) == 0) {
                        /* AF not initialized? */
                        if (!(unxa->nxa_flags & NXA_ADD)) {
@@ -2937,19 +3050,24 @@ nfsrv_free_addrlist(struct nfs_export *nx, struct user_nfs_export_args *unxa)
 
 void enablequotas(struct mount *mp, vfs_context_t ctx); // XXX
 
+#define DATA_VOLUME_MP "/System/Volumes/Data" // PLATFORM_DATA_VOLUME_MOUNT_POINT
+
 int
 nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
 {
        int error = 0;
-       size_t pathlen;
+       size_t pathlen, nxfs_pathlen;
        struct nfs_exportfs *nxfs, *nxfs2, *nxfs3;
        struct nfs_export *nx, *nx2, *nx3;
        struct nfs_filehandle nfh;
        struct nameidata mnd, xnd;
        vnode_t mvp = NULL, xvp = NULL;
        mount_t mp = NULL;
-       char path[MAXPATHLEN];
+       char path[MAXPATHLEN], *nxfs_path;
+       char fl_pathbuff[MAXPATHLEN];
+       int fl_pathbuff_len = MAXPATHLEN;
        int expisroot;
+       size_t datavol_len = strlen(DATA_VOLUME_MP);
 
        if (unxa->nxa_flags == NXA_CHECK) {
                /* just check if the path is an NFS-exportable file system */
@@ -3051,17 +3169,12 @@ nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
        }
        if (nxfs) {
                /* verify exported FS path matches given path */
-               if (strncmp(path, nxfs->nxfs_path, MAXPATHLEN)) {
+               if (strncmp(path, nxfs->nxfs_path, MAXPATHLEN) &&
+                   (strncmp(path, DATA_VOLUME_MP, datavol_len) || strncmp(path + datavol_len, nxfs->nxfs_path, MAXPATHLEN - datavol_len))) {
                        error = EEXIST;
                        goto unlock_out;
                }
                if ((unxa->nxa_flags & (NXA_ADD | NXA_OFFLINE)) == NXA_ADD) {
-                       /* if adding, verify that the mount is still what we expect */
-                       mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
-                       if (mp) {
-                               mount_ref(mp, 0);
-                               mount_iterdrop(mp);
-                       }
                        /* find exported FS root vnode */
                        NDINIT(&mnd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF | AUDITVNPATH1,
                            UIO_SYSSPACE, CAST_USER_ADDR_T(nxfs->nxfs_path), ctx);
@@ -3075,6 +3188,20 @@ nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
                                error = EINVAL;
                                goto out;
                        }
+                       /* if adding, verify that the mount is still what we expect */
+                       mp = vfs_getvfs_by_mntonname(nxfs->nxfs_path);
+                       if (!mp) {
+                               /* check for firmlink-free path */
+                               if (vn_getpath_no_firmlink(mvp, fl_pathbuff, &fl_pathbuff_len) == 0 &&
+                                   fl_pathbuff_len > 0 &&
+                                   !strncmp(nxfs->nxfs_path, fl_pathbuff, MAXPATHLEN)) {
+                                       mp = vfs_getvfs_by_mntonname(vnode_mount(mvp)->mnt_vfsstat.f_mntonname);
+                               }
+                       }
+                       if (mp) {
+                               mount_ref(mp, 0);
+                               mount_iterdrop(mp);
+                       }
                        /* sanity check: this should be same mount */
                        if (mp != vnode_mount(mvp)) {
                                error = EINVAL;
@@ -3135,13 +3262,20 @@ nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
                }
                bzero(nxfs, sizeof(struct nfs_exportfs));
                nxfs->nxfs_id = unxa->nxa_fsid;
-               MALLOC(nxfs->nxfs_path, char*, pathlen, M_TEMP, M_WAITOK);
+               if (mp) {
+                       nxfs_path = mp->mnt_vfsstat.f_mntonname;
+                       nxfs_pathlen = sizeof(mp->mnt_vfsstat.f_mntonname);
+               } else {
+                       nxfs_path = path;
+                       nxfs_pathlen = pathlen;
+               }
+               MALLOC(nxfs->nxfs_path, char*, nxfs_pathlen, M_TEMP, M_WAITOK);
                if (!nxfs->nxfs_path) {
                        FREE(nxfs, M_TEMP);
                        error = ENOMEM;
                        goto out;
                }
-               bcopy(path, nxfs->nxfs_path, pathlen);
+               bcopy(nxfs_path, nxfs->nxfs_path, nxfs_pathlen);
                /* insert into list in reverse-sorted order */
                nxfs3 = NULL;
                LIST_FOREACH(nxfs2, &nfsrv_exports, nxfs_next) {
@@ -3294,7 +3428,7 @@ nfsrv_export(struct user_nfs_export_args *unxa, vfs_context_t ctx)
                                        xnd.ni_op = OP_LOOKUP;
 #endif
                                        xnd.ni_cnd.cn_flags = LOCKLEAF;
-                                       xnd.ni_pathlen = pathlen - 1;
+                                       xnd.ni_pathlen = (uint32_t)pathlen - 1; // pathlen max value is equal to MAXPATHLEN
                                        xnd.ni_cnd.cn_nameptr = xnd.ni_cnd.cn_pnbuf = path;
                                        xnd.ni_startdir = mvp;
                                        xnd.ni_usedvp   = mvp;
@@ -3519,6 +3653,65 @@ nfsrv_fhtoexport(struct nfs_filehandle *nfhp)
        return nx;
 }
 
+struct nfsrv_getvfs_by_mntonname_callback_args {
+       const char      *path;          /* IN */
+       mount_t         mp;             /* OUT */
+};
+
+static int
+nfsrv_getvfs_by_mntonname_callback(mount_t mp, void *v)
+{
+       struct nfsrv_getvfs_by_mntonname_callback_args * const args = v;
+       char real_mntonname[MAXPATHLEN];
+       int pathbuflen = MAXPATHLEN;
+       vnode_t rvp;
+       int error;
+
+       error = VFS_ROOT(mp, &rvp, vfs_context_current());
+       if (error) {
+               goto out;
+       }
+       error = vn_getpath_ext(rvp, NULLVP, real_mntonname, &pathbuflen,
+           VN_GETPATH_FSENTER | VN_GETPATH_NO_FIRMLINK);
+       vnode_put(rvp);
+       if (error) {
+               goto out;
+       }
+       if (strcmp(args->path, real_mntonname) == 0) {
+               error = vfs_busy(mp, LK_NOWAIT);
+               if (error == 0) {
+                       args->mp = mp;
+               }
+               return VFS_RETURNED_DONE;
+       }
+out:
+       return VFS_RETURNED;
+}
+
+static mount_t
+nfsrv_getvfs_by_mntonname(char *path)
+{
+       struct nfsrv_getvfs_by_mntonname_callback_args args = {
+               .path = path,
+               .mp = NULL,
+       };
+       mount_t mp;
+       int error;
+
+       mp = vfs_getvfs_by_mntonname(path);
+       if (mp) {
+               error = vfs_busy(mp, LK_NOWAIT);
+               mount_iterdrop(mp);
+               if (error) {
+                       mp = NULL;
+               }
+       } else if (vfs_iterate(0, nfsrv_getvfs_by_mntonname_callback,
+           &args) == 0) {
+               mp = args.mp;
+       }
+       return mp;
+}
+
 /*
  * nfsrv_fhtovp() - convert FH to vnode and export info
  */
@@ -3612,14 +3805,7 @@ nfsrv_fhtovp(
        }
 
        /* find mount structure */
-       mp = vfs_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
-       if (mp) {
-               error = vfs_busy(mp, LK_NOWAIT);
-               mount_iterdrop(mp);
-               if (error) {
-                       mp = NULL;
-               }
-       }
+       mp = nfsrv_getvfs_by_mntonname((*nxp)->nx_fs->nxfs_path);
        if (!mp) {
                /*
                 * We have an export, but no mount?
@@ -3763,7 +3949,7 @@ nfsrv_fhmatch(struct nfs_filehandle *fh1, struct nfs_filehandle *fh2)
  * If found, the node's tm_last timestamp is updated and the node is returned.
  *
  * If not found, a new node is allocated (or reclaimed via LRU), initialized, and returned.
- * Returns NULL if a new node could not be allcoated.
+ * Returns NULL if a new node could not be allocated OR saddr length exceeds sizeof(unode->sock).
  *
  * The list's user_mutex lock MUST be held.
  */
@@ -3794,6 +3980,11 @@ nfsrv_get_user_stat_node(struct nfs_active_user_list *list, struct sockaddr *sad
                return unode;
        }
 
+       if (saddr->sa_len > sizeof(((struct nfs_user_stat_node *)0)->sock)) {
+               /* saddr length exceeds maximum value */
+               return NULL;
+       }
+
        if (list->node_count < nfsrv_user_stat_max_nodes) {
                /* Allocate a new node */
                MALLOC(unode, struct nfs_user_stat_node *, sizeof(struct nfs_user_stat_node),
@@ -3821,7 +4012,7 @@ nfsrv_get_user_stat_node(struct nfs_active_user_list *list, struct sockaddr *sad
 
        /* Initialize the node */
        unode->uid = uid;
-       bcopy(saddr, &unode->sock, saddr->sa_len);
+       bcopy(saddr, &unode->sock, MIN(saddr->sa_len, sizeof(unode->sock)));
        microtime(&now);
        unode->ops = 0;
        unode->bytes_read = 0;
@@ -3891,7 +4082,7 @@ nfsrv_init_user_list(struct nfs_active_user_list *ulist)
        }
        ulist->node_count = 0;
 
-       lck_mtx_init(&ulist->user_mutex, nfsrv_active_user_mutex_group, LCK_ATTR_NULL);
+       lck_mtx_init(&ulist->user_mutex, &nfsrv_active_user_mutex_group, LCK_ATTR_NULL);
 }
 
 /* Free all nodes in an active user list */
@@ -3915,7 +4106,7 @@ nfsrv_free_user_list(struct nfs_active_user_list *ulist)
        }
        ulist->node_count = 0;
 
-       lck_mtx_destroy(&ulist->user_mutex, nfsrv_active_user_mutex_group);
+       lck_mtx_destroy(&ulist->user_mutex, &nfsrv_active_user_mutex_group);
 }
 
 /* Reclaim old expired user nodes from active user lists. */
@@ -3928,7 +4119,7 @@ nfsrv_active_user_list_reclaim(void)
        struct nfs_user_stat_hashtbl_head       oldlist;
        struct nfs_user_stat_node               *unode, *unode_next;
        struct timeval                          now;
-       uint32_t                                tstale;
+       long                                    tstale;
 
        LIST_INIT(&oldlist);
 
@@ -4377,4 +4568,6 @@ nfsrv_errmap(struct nfsrv_descript *nd, int err)
        return (int)*defaulterrp;
 }
 
-#endif /* NFSSERVER */
+#endif /* CONFIG_NFS_SERVER */
+
+#endif /* CONFIG_NFS */