]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/nfs/nfs_serv.c
xnu-2422.90.20.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_serv.c
index b7be9946831511c78cff8e321e24d627d0c8596d..8cc717b8eb779834c179490a43921d957447c5f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2007 Apple Inc.  All rights reserved.
+ * Copyright (c) 2000-2011 Apple Inc.  All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -89,6 +89,8 @@
 #include <sys/vm.h>
 #include <sys/vmparam.h>
 
+#include <netinet/in.h>
+
 #include <nfs/nfsproto.h>
 #include <nfs/rpcv2.h>
 #include <nfs/nfs.h>
@@ -114,25 +116,30 @@ lck_grp_t *nfsrv_slp_mutex_group;
 struct nfsrv_sockhead nfsrv_socklist, nfsrv_deadsocklist, nfsrv_sockwg,
                        nfsrv_sockwait, nfsrv_sockwork;
 struct nfsrv_sock *nfsrv_udpsock = NULL;
+struct nfsrv_sock *nfsrv_udp6sock = NULL;
 
 /* NFS exports */
 struct nfsrv_expfs_list nfsrv_exports;
-struct nfsrv_export_hashhead *nfsrv_export_hashtbl;
+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;
 
+#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 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;
+#endif
 thread_call_t  nfsrv_deadsock_timer_call;
 thread_call_t  nfsrv_wg_timer_call;
 int nfsrv_wg_timer_on;
@@ -149,10 +156,12 @@ int nfsrv_wg_delay_v3 = 0;
 
 int nfsrv_async = 0;
 
-static int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int);
-static int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
+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);
 
 extern void IOSleep(int);
+extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path);
 
 /*
  * Initialize the data structures for the server.
@@ -180,10 +189,8 @@ nfsrv_init(void)
                return;
        }
 
-       if (sizeof (struct nfsrv_sock) > NFS_SVCALLOC) {
+       if (sizeof (struct nfsrv_sock) > NFS_SVCALLOC)
                printf("struct nfsrv_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
-               printf("Try reducing NFS_UIDHASHSIZ\n");
-       }
 
        /* init nfsd mutex */
        nfsd_lck_grp = lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL);
@@ -194,7 +201,6 @@ nfsrv_init(void)
        nfsrv_slp_mutex_group  = lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL);
 
        /* init export data structures */
-       nfsrv_export_hashtbl = hashinit(8, M_TEMP, &nfsrv_export_hash);
        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);
@@ -206,13 +212,17 @@ nfsrv_init(void)
        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 */
+#if CONFIG_FSE
        nfsrv_fmod_timer_call = thread_call_allocate(nfsrv_fmod_timer, NULL);
+#endif
        nfsrv_deadsock_timer_call = thread_call_allocate(nfsrv_deadsock_timer, NULL);
        nfsrv_wg_timer_call = thread_call_allocate(nfsrv_wg_timer, NULL);
 
@@ -225,7 +235,11 @@ nfsrv_init(void)
        TAILQ_INIT(&nfsd_head);
        TAILQ_INIT(&nfsd_queue);
        nfsrv_udpsock = NULL;
+       nfsrv_udp6sock = NULL;
 
+       /* Setup the up-call handling */
+       nfsrv_uc_init();
+       
        /* initialization complete */
        nfsrv_initted = NFSRV_INITIALIZED;
 }
@@ -268,7 +282,7 @@ nfsrv_access(
        int error, attrerr;
        struct vnode_attr vattr;
        struct nfs_filehandle nfh;
-       u_long nfsmode;
+       u_int32_t nfsmode;
        kauth_action_t testaction;
        struct nfs_export *nx;
        struct nfs_export_options *nxo;
@@ -305,15 +319,7 @@ nfsrv_access(
         *     obtain good performance in the optimistic mode.
         */
        if (nfsmode & NFS_ACCESS_READ) {
-               if (vnode_isdir(vp)) {
-                       testaction =
-                           KAUTH_VNODE_LIST_DIRECTORY |
-                           KAUTH_VNODE_READ_EXTATTRIBUTES;
-               } else {
-                       testaction =
-                           KAUTH_VNODE_READ_DATA |
-                           KAUTH_VNODE_READ_EXTATTRIBUTES;
-               }
+               testaction = vnode_isdir(vp) ? KAUTH_VNODE_LIST_DIRECTORY : KAUTH_VNODE_READ_DATA;
                if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0))
                        nfsmode &= ~NFS_ACCESS_READ;
        }
@@ -587,13 +593,13 @@ nfsrv_lookup(
        vfs_context_t ctx,
        mbuf_t *mrepp)
 {
-       struct nameidata ni, *nip = &ni;
+       struct nameidata ni;
        vnode_t vp, dirp = NULL;
        struct nfs_filehandle dnfh, nfh;
        struct nfs_export *nx = NULL;
        struct nfs_export_options *nxo;
        int error, attrerr, dirattrerr, isdotdot;
-       uint32_t len;
+       uint32_t len = 0;
        uid_t saved_uid;
        struct vnode_attr va, dirattr, *vap = &va;
        struct nfsm_chain *nmreq, nmrep;
@@ -610,6 +616,9 @@ nfsrv_lookup(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LOOKUP;
+#endif
        ni.ni_cnd.cn_flags = LOCKLEAF;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        isdotdot = ((len == 2) && (ni.ni_cnd.cn_pnbuf[0] == '.') && (ni.ni_cnd.cn_pnbuf[1] == '.'));
@@ -635,7 +644,7 @@ nfsrv_lookup(
 
        nameidone(&ni);
 
-       vp = nip->ni_vp;
+       vp = ni.ni_vp;
        error = nfsrv_vptofh(nx, nd->nd_vers, (isdotdot ? &dnfh : NULL), vp, ctx, &nfh);
        if (!error) {
                nfsm_srv_vattr_init(vap, nd->nd_vers);
@@ -689,7 +698,7 @@ nfsrv_readlink(
        struct nfs_export_options *nxo;
        struct nfsm_chain *nmreq, nmrep;
        mbuf_t mpath, mp;
-       uio_t uiop = NULL;
+       uio_t auio = NULL;
        char uio_buf[ UIO_SIZEOF(4) ];
        char *uio_bufp = &uio_buf[0];
        int uio_buflen = UIO_SIZEOF(4);
@@ -715,13 +724,13 @@ nfsrv_readlink(
                        error = ENOMEM;
                nfsmerr_if(error);
        }
-       uiop = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
-       if (!uiop)
+       auio = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
+       if (!auio)
                error = ENOMEM;
        nfsmerr_if(error);
 
        for (mp = mpath; mp; mp = mbuf_next(mp))
-               uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(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);
@@ -745,7 +754,7 @@ nfsrv_readlink(
        if (!error)
                error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 0);
        if (!error)
-               error = VNOP_READLINK(vp, uiop, ctx);
+               error = VNOP_READLINK(vp, auio, ctx);
        if (vp) {
                if (nd->nd_vers == NFS_VER3) {
                        nfsm_srv_vattr_init(&vattr, NFS_VER3);
@@ -772,9 +781,8 @@ nfsmerr:
                nfsm_chain_build_done(error, &nmrep);
                goto nfsmout;
        }
-       if (uiop && (uio_resid(uiop) > 0)) {
-               // LP64todo - fix this
-               len -= uio_resid(uiop);
+       if (auio && (uio_resid(auio) > 0)) {
+               len -= uio_resid(auio);
                tlen = nfsm_rndup(len);
                nfsm_adj(mpath, NFS_MAXPATHLEN-tlen, tlen-len);
        }
@@ -815,7 +823,7 @@ nfsrv_read(
        struct nfs_filehandle nfh;
        struct nfs_export *nx;
        struct nfs_export_options *nxo;
-       uio_t uiop = NULL;
+       uio_t auio = NULL;
        char *uio_bufp = NULL;
        struct vnode_attr vattr, *vap = &vattr;
        off_t off;
@@ -839,7 +847,7 @@ nfsrv_read(
        else
                nfsm_chain_get_32(error, nmreq, off);
        nfsm_chain_get_32(error, nmreq, reqlen);
-       maxlen = NFS_SRVMAXDATA(nd);
+       maxlen = NFSRV_NDMAXDATA(nd);
        if (reqlen > maxlen)
                reqlen = maxlen;
        nfsmerr_if(error);
@@ -883,18 +891,18 @@ nfsrv_read(
                nfsmerr_if(error);
                MALLOC(uio_bufp, char *, UIO_SIZEOF(mreadcnt), M_TEMP, M_WAITOK);
                if (uio_bufp)
-                       uiop = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
+                       auio = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
                                        UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt));
-               if (!uio_bufp || !uiop) {
+               if (!uio_bufp || !auio) {
                        error = ENOMEM;
                        goto errorexit;
                }
                for (m = mread; m; m = mbuf_next(m))
-                       uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
-               error = VNOP_READ(vp, uiop, IO_NODELOCKED, ctx);
+                       uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
+               error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx);
        } else {
-               uiop = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
-               if (!uiop) {
+               auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+               if (!auio) {
                        error = ENOMEM;
                        goto errorexit;
                }
@@ -913,8 +921,7 @@ errorexit:
        vp = NULL;
 
        /* trim off any data not actually read */
-       // LP64todo - fix this
-       len -= uio_resid(uiop);
+       len -= uio_resid(auio);
        tlen = nfsm_rndup(len);
        if (count != tlen || tlen != len)
                nfsm_adj(mread, count - tlen, tlen - len);
@@ -964,6 +971,7 @@ nfsmout:
        return (error);
 }
 
+#if CONFIG_FSE
 /*
  * NFS File modification reporting
  *
@@ -989,13 +997,14 @@ int nfsrv_fmod_min_interval = 100;        /* msec min interval between callbacks */
 void
 nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
 {
-       struct nfsrv_fmod_hashhead *head;
-       struct nfsrv_fmod *fp, *nfp;
+       struct nfsrv_fmod_hashhead *headp, firehead;
+       struct nfsrv_fmod *fp, *nfp, *pfp;
        uint64_t timenow, next_deadline;
-       int interval = 0;
-       int i;
+       int interval = 0, i, fmod_fire;
 
+       LIST_INIT(&firehead);
        lck_mtx_lock(nfsrv_fmod_mutex);
+again:
        clock_get_uptime(&timenow);
        clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000,
                &next_deadline);
@@ -1003,13 +1012,14 @@ nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
        /*
         * Scan all the hash chains
         */
+       fmod_fire = 0;
        for (i = 0; i < NFSRVFMODHASHSZ; i++) {
                /*
                 * For each hash chain, look for an entry
                 * that has exceeded the deadline.
                 */
-               head = &nfsrv_fmod_hashtbl[i];
-               LIST_FOREACH(fp, head, fm_link) {
+               headp = &nfsrv_fmod_hashtbl[i];
+               LIST_FOREACH(fp, headp, fm_link) {
                        if (timenow >= fp->fm_deadline)
                                break;
                        if (fp->fm_deadline < next_deadline)
@@ -1022,25 +1032,42 @@ nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
                 * following entries in the chain, since they're
                 * sorted in time order.
                 */
+               pfp = NULL;
                while (fp) {
-                       /*
-                        * Fire off the content modified fsevent for each
-                        * entry, remove it from the list, and free it.
-                        */
-#if CONFIG_FSE
-                       if (nfsrv_fsevents_enabled)
+                       /* move each entry to the fire list */
+                       nfp = LIST_NEXT(fp, fm_link);
+                       LIST_REMOVE(fp, fm_link);
+                       fmod_fire++;
+                       if (pfp)
+                               LIST_INSERT_AFTER(pfp, fp, fm_link);
+                       else
+                               LIST_INSERT_HEAD(&firehead, fp, fm_link);
+                       pfp = fp;
+                       fp = nfp;
+               }
+       }
+
+       if (fmod_fire) {
+               lck_mtx_unlock(nfsrv_fmod_mutex);
+               /*
+                * Fire off the content modified fsevent for each
+                * entry and free it.
+                */
+               LIST_FOREACH_SAFE(fp, &firehead, fm_link, nfp) {
+                       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);
-#endif
+                       }
                        vnode_put(fp->fm_vp);
                        kauth_cred_unref(&fp->fm_context.vc_ucred);
-                       nfp = LIST_NEXT(fp, fm_link);
                        LIST_REMOVE(fp, fm_link);
                        FREE(fp, M_TEMP);
-                       nfsrv_fmod_pending--;
-                       fp = nfp;
                }
+               lck_mtx_lock(nfsrv_fmod_mutex);
+               nfsrv_fmod_pending -= fmod_fire;
+               goto again;
        }
 
        /*
@@ -1062,14 +1089,13 @@ nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
        lck_mtx_unlock(nfsrv_fmod_mutex);
 }
 
-#if CONFIG_FSE
 /*
  * When a vnode has been written to, enter it in the hash
  * table of vnodes pending creation of an fsevent. If the
  * callout timer isn't already running, schedule a callback
  * for nfsrv_fmod_pendtime msec from now.
  */
-static void
+void
 nfsrv_modified(vnode_t vp, vfs_context_t ctx)
 {
        uint64_t deadline;
@@ -1156,7 +1182,7 @@ nfsrv_write(
        struct nfs_filehandle nfh;
        struct nfs_export *nx;
        struct nfs_export_options *nxo;
-       uio_t uiop = NULL;
+       uio_t auio = NULL;
        char *uio_bufp = NULL;
        off_t off;
        uid_t saved_uid;
@@ -1204,7 +1230,7 @@ nfsrv_write(
        } else {
                mlen = 0;
        }
-       if ((len > NFS_MAXDATA) || (len < 0) || (mlen < len)) {
+       if ((len > NFSRV_MAXDATA) || (len < 0) || (mlen < len)) {
                error = EIO;
                goto nfsmerr;
        }
@@ -1237,13 +1263,13 @@ nfsrv_write(
                                mcount++;
                MALLOC(uio_bufp, char *, UIO_SIZEOF(mcount), M_TEMP, M_WAITOK);
                if (uio_bufp)
-                       uiop = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount));
-               if (!uio_bufp || !uiop)
+                       auio = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount));
+               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)
-                               uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen);
+                               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
@@ -1256,8 +1282,8 @@ nfsrv_write(
                else
                        ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
 
-               error = VNOP_WRITE(vp, uiop, ioflags, ctx);
-               OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
+               error = VNOP_WRITE(vp, auio, ioflags, ctx);
+               OSAddAtomic64(1, &nfsstats.srvvop_writes);
 
                /* update export stats */
                NFSStatAdd64(&nx->nx_stats.bytes_written, len);
@@ -1324,7 +1350,7 @@ nfsmout:
  */
 
 #define        NWDELAYHASH(sock, f) \
-       (&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ])
+       (&(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))
@@ -1354,7 +1380,7 @@ nfsrv_writegather(
        int preattrerr, postattrerr;
        vnode_t vp;
        mbuf_t m;
-       uio_t uiop = NULL;
+       uio_t auio = NULL;
        char *uio_bufp = NULL;
        u_quad_t cur_usec;
        struct timeval now;
@@ -1404,7 +1430,7 @@ nfsrv_writegather(
                mlen = 0;
            }
 
-           if ((nd->nd_len > NFS_MAXDATA) || (nd->nd_len < 0)  || (mlen < nd->nd_len)) {
+           if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0)  || (mlen < nd->nd_len)) {
                error = EIO;
 nfsmerr:
                nd->nd_repstat = error;
@@ -1527,16 +1553,16 @@ loop1:
 
                    MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
                    if (uio_bufp)
-                       uiop = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE,
+                       auio = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE,
                                                UIO_WRITE, uio_bufp, UIO_SIZEOF(i));
-                   if (!uio_bufp || !uiop)
+                   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(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen);
-                       error = VNOP_WRITE(vp, uiop, ioflags, ctx);
-                       OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
+                               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);
@@ -1657,7 +1683,7 @@ loop1:
  * - update the nd_eoff and nd_stable for owp
  * - put nd on owp's nd_coalesce list
  */
-static int
+int
 nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd)
 {
        int overlap, error;
@@ -1788,7 +1814,7 @@ nfsrv_create(
        struct nameidata ni;
        int error, rdev, dpreattrerr, dpostattrerr, postattrerr;
        int how, exclusive_flag;
-       uint32_t len;
+       uint32_t len = 0, cnflags;
        vnode_t vp, dvp, dirp;
        struct nfs_filehandle nfh;
        struct nfs_export *nx = NULL;
@@ -1807,10 +1833,6 @@ nfsrv_create(
        ni.ni_cnd.cn_nameiop = 0;
        rdev = 0;
 
-       /*
-        * Save the original credential UID in case they are
-        * mapped and we need to map the IDs in the attributes.
-        */
        saved_uid = kauth_cred_getuid(nd->nd_cr);
 
        nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
@@ -1819,6 +1841,9 @@ nfsrv_create(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -1901,17 +1926,6 @@ nfsrv_create(
        if (vp == NULL) {
                kauth_acl_t xacl = NULL;
 
-               /*
-                * If the credentials were mapped, we should
-                * map the same values in the attributes.
-                */
-               if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(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)
-                               VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
-               }
-
                /* authorize before creating */
                error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
 
@@ -1928,20 +1942,17 @@ nfsrv_create(
                }
                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 
+                * file system object creation. This has the nice side effect of
+                * enforcing BSD creation semantics
+                */
+               VATTR_CLEAR_ACTIVE(vap, va_uid);
+               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 && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
-                               /*
-                                * Most NFS servers just ignore the UID/GID attributes, so we
-                                * try ignoring them if that'll help the request succeed.
-                                */
-                               VATTR_CLEAR_ACTIVE(vap, va_uid);
-                               VATTR_CLEAR_ACTIVE(vap, va_gid);
-                               error = vnode_authattr_new(dvp, vap, 0, ctx);
-                       }
-               }
 
                if (vap->va_type == VREG || vap->va_type == VSOCK) {
 
@@ -2002,11 +2013,19 @@ nfsrv_create(
                                vp = NULL;
                        }
                        ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+                       ni.ni_op = OP_LOOKUP;
+#endif
                        ni.ni_cnd.cn_flags &= ~LOCKPARENT;
                        ni.ni_cnd.cn_context = ctx;
                        ni.ni_startdir = dvp;
                        ni.ni_usedvp   = dvp;
-                       error = lookup(&ni);
+                       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) {
                                if (ni.ni_cnd.cn_flags & ISSYMLINK)
                                        error = EINVAL;
@@ -2123,9 +2142,10 @@ nfsrv_mknod(
        struct vnode_attr va, *vap = &va;
        struct nameidata ni;
        int error, dpreattrerr, dpostattrerr, postattrerr;
-       uint32_t len;
-       u_long major, minor;
+       uint32_t len = 0, cnflags;
+       u_int32_t major = 0, minor = 0;
        enum vtype vtyp;
+       nfstype nvtype;
        vnode_t vp, dvp, dirp;
        struct nfs_filehandle nfh;
        struct nfs_export *nx = NULL;
@@ -2141,10 +2161,6 @@ nfsrv_mknod(
        vp = dvp = dirp = NULL;
        ni.ni_cnd.cn_nameiop = 0;
 
-       /*
-        * Save the original credential UID in case they are
-        * mapped and we need to map the IDs in the attributes.
-        */
        saved_uid = kauth_cred_getuid(nd->nd_cr);
 
        nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
@@ -2153,6 +2169,9 @@ nfsrv_mknod(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -2177,9 +2196,9 @@ nfsrv_mknod(
        dvp = ni.ni_dvp;
        vp = ni.ni_vp;
 
-       nfsm_chain_get_32(error, nmreq, vtyp);
+       nfsm_chain_get_32(error, nmreq, nvtype);
        nfsmerr_if(error);
-       vtyp = nfstov_type(vtyp, NFS_VER3);
+       vtyp = nfstov_type(nvtype, NFS_VER3);
        if (!error && (vtyp != VCHR) && (vtyp != VBLK) && (vtyp != VSOCK) && (vtyp != VFIFO)) {
                error = NFSERR_BADTYPE;
                goto out;
@@ -2204,17 +2223,6 @@ nfsrv_mknod(
        }
        VATTR_SET(vap, va_type, vtyp);
 
-       /*
-        * If the credentials were mapped, we should
-        * map the same values in the attributes.
-        */
-       if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(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)
-                       VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
-       }
-
        /* authorize before creating */
        error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
 
@@ -2231,20 +2239,18 @@ nfsrv_mknod(
        }
        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 
+        * file system object creation. This has the nice side effect of
+        * enforcing BSD creation semantics
+        */
+       VATTR_CLEAR_ACTIVE(vap, va_uid);
+       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 && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
-                       /*
-                        * Most NFS servers just ignore the UID/GID attributes, so we
-                        * try ignoring them if that'll help the request succeed.
-                        */
-                       VATTR_CLEAR_ACTIVE(vap, va_uid);
-                       VATTR_CLEAR_ACTIVE(vap, va_gid);
-                       error = vnode_authattr_new(dvp, vap, 0, ctx);
-               }
-       }
+
        if (error)
                goto out1;
 
@@ -2268,11 +2274,19 @@ nfsrv_mknod(
                        vp = NULL;
                }
                ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+               ni.ni_op = OP_LOOKUP;
+#endif
                ni.ni_cnd.cn_flags &= ~LOCKPARENT;
                ni.ni_cnd.cn_context = vfs_context_current();
                ni.ni_startdir = dvp;
                ni.ni_usedvp   = dvp;
-               error = lookup(&ni);
+               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 (ni.ni_cnd.cn_flags & ISSYMLINK)
@@ -2362,7 +2376,7 @@ nfsrv_remove(
 {
        struct nameidata ni;
        int error, dpreattrerr, dpostattrerr;
-       uint32_t len;
+       uint32_t len = 0;
        uid_t saved_uid;
        vnode_t vp, dvp, dirp = NULL;
        struct vnode_attr dpreattr, dpostattr;
@@ -2384,6 +2398,9 @@ nfsrv_remove(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_UNLINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -2515,7 +2532,7 @@ nfsrv_rename(
        struct nfsm_chain *nmreq, nmrep;
        char *from_name, *to_name;
 #if CONFIG_FSE
-       int from_len, to_len;
+       int from_len=0, to_len=0;
        fse_info from_finfo, to_finfo;
 #endif
        u_char didstats = 0;
@@ -2525,6 +2542,7 @@ nfsrv_rename(
        fdpreattrerr = fdpostattrerr = ENOENT;
        tdpreattrerr = tdpostattrerr = ENOENT;
        saved_uid = kauth_cred_getuid(nd->nd_cr);
+       fromlen = tolen = 0;
        frompath = topath = NULL;
        fdirp = tdirp = NULL;
        nmreq = &nd->nd_nmreq;
@@ -2563,6 +2581,9 @@ nfsrv_rename(
        kauth_cred_ref(saved_cred);
 retry:
        fromni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+       fromni.ni_op = OP_UNLINK;
+#endif
        fromni.ni_cnd.cn_flags = WANTPARENT;
 
        fromni.ni_cnd.cn_pnbuf = frompath;
@@ -2595,6 +2616,9 @@ retry:
        }
 
        toni.ni_cnd.cn_nameiop = RENAME;
+#if CONFIG_TRIGGERS
+       toni.ni_op = OP_RENAME;
+#endif
        toni.ni_cnd.cn_flags = WANTPARENT;
 
        toni.ni_cnd.cn_pnbuf = topath;
@@ -2912,50 +2936,26 @@ 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();
-               from_len = MAXPATHLEN;
-               if (from_name && vn_getpath(fdvp, from_name, &from_len)) {
-                       release_pathbuff(from_name);
-                       from_name = NULL;
-               } else if ((from_len + 1 + fromni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
-                       // if the path is not just "/", then append a "/"
-                       if (from_len > 2) {
-                               from_name[from_len-1] = '/';
-                       } else {
-                               from_len--;
-                       }
-                       strlcpy(&from_name[from_len], fromni.ni_cnd.cn_nameptr, MAXPATHLEN-from_len);
-                       from_len += fromni.ni_cnd.cn_namelen + 1;
-                       from_name[from_len] = '\0';
+               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;
-               to_len = MAXPATHLEN;
+               if (to_name) {
+                       to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated);
+               }
 
-               if (!to_name) {
-                       if (from_name) {
-                               release_pathbuff(from_name);
-                               from_name = NULL;
-                       }
-               } else if (vn_getpath(tdvp, to_name, &to_len)) {
-                       release_pathbuff(from_name);
-                       release_pathbuff(to_name);
-                       from_name = to_name = NULL;
-               } else if ((to_len + 1 + toni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
-                       // if the path is not just "/", then append a "/"
-                       if (to_len > 2) {
-                               to_name[to_len-1] = '/';
-                       } else {
-                               to_len--;
-                       }
-                       strlcpy(&to_name[to_len], toni.ni_cnd.cn_nameptr, MAXPATHLEN-to_len);
-                       to_len += toni.ni_cnd.cn_namelen + 1;
-                       to_name[to_len] = '\0';
+               if (from_truncated || to_truncated) {
+                       from_finfo.mode |= FSE_TRUNCATED_PATH;
                }
+
        } else {
                from_name = NULL;
                to_name   = NULL;
@@ -3166,6 +3166,9 @@ nfsrv_link(
                goto out;
 
        ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error)
@@ -3197,25 +3200,26 @@ nfsrv_link(
 #if CONFIG_FSE
        if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
                char *target_path = NULL;
-               int plen;
+               int plen, truncated=0;
                fse_info finfo;
 
                /* build the path to the new link file */
-               plen = MAXPATHLEN;
-               if ((target_path = get_pathbuff()) && !vn_getpath(dvp, target_path, &plen)) {
-                       if ((plen + 1 + ni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
-                               target_path[plen-1] = '/';
-                               strlcpy(&target_path[plen], ni.ni_cnd.cn_nameptr, MAXPATHLEN-plen);
-                               plen += ni.ni_cnd.cn_namelen;
-                       }
-                       if (get_fse_info(vp, &finfo, ctx) == 0)
+               target_path = get_pathbuff();
+               if (target_path) {
+                       plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
+
+                       if (get_fse_info(vp, &finfo, ctx) == 0) {
+                               if (truncated) {
+                                       finfo.mode |= FSE_TRUNCATED_PATH;
+                               }
                                add_fsevent(FSE_CREATE_FILE, ctx,
                                            FSE_ARG_STRING, plen, target_path,
                                            FSE_ARG_FINFO, &finfo,
                                            FSE_ARG_DONE);
-               }
-               if (target_path)
+                       }
+
                        release_pathbuff(target_path);
+               }
        }
 #endif
 
@@ -3279,7 +3283,7 @@ nfsrv_symlink(
        struct vnode_attr va, *vap = &va;
        struct nameidata ni;
        int error, dpreattrerr, dpostattrerr, postattrerr;
-       uint32_t len, linkdatalen;
+       uint32_t len = 0, linkdatalen, cnflags;
        uid_t saved_uid;
        char *linkdata;
        vnode_t vp, dvp, dirp;
@@ -3297,10 +3301,6 @@ nfsrv_symlink(
        linkdata = NULL;
        dirp = NULL;
 
-       /*
-        * Save the original credential UID in case they are
-        * mapped and we need to map the IDs in the attributes.
-        */
        saved_uid = kauth_cred_getuid(nd->nd_cr);
 
        ni.ni_cnd.cn_nameiop = 0;
@@ -3312,6 +3312,9 @@ nfsrv_symlink(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -3367,48 +3370,44 @@ nfsrv_symlink(
                goto out;
        }
 
-       /*
-        * If the credentials were mapped, we should
-        * map the same values in the attributes.
-        */
-       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)
-                       VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
-       }
        VATTR_SET(vap, va_type, VLNK);
        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 
+        * file system object creation. This has the nice side effect of
+        * enforcing BSD creation semantics
+        */
+       VATTR_CLEAR_ACTIVE(vap, va_uid);
+       VATTR_CLEAR_ACTIVE(vap, va_gid);
 
        /* authorize before creating */
        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 && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
-                       /*
-                        * Most NFS servers just ignore the UID/GID attributes, so we
-                        * try ignoring them if that'll help the request succeed.
-                        */
-                       VATTR_CLEAR_ACTIVE(vap, va_uid);
-                       VATTR_CLEAR_ACTIVE(vap, va_gid);
-                       error = vnode_authattr_new(dvp, vap, 0, ctx);
-               }
-       }
+
        if (!error)
                error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
 
        if (!error && (nd->nd_vers == NFS_VER3)) {
                if (vp == NULL) {
                        ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+                       ni.ni_op = OP_LOOKUP;
+#endif
                        ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
                        ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
                        ni.ni_cnd.cn_context = ctx;
                        ni.ni_startdir = dvp;
                        ni.ni_usedvp   = dvp;
-                       error = lookup(&ni);
+                       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;
                }
@@ -3493,6 +3492,7 @@ nfsmout:
 /*
  * nfs mkdir service
  */
 int
 nfsrv_mkdir(
        struct nfsrv_descript *nd,
@@ -3504,7 +3504,7 @@ nfsrv_mkdir(
        struct vnode_attr va, *vap = &va;
        struct nameidata ni;
        int error, dpreattrerr, dpostattrerr, postattrerr;
-       uint32_t len;
+       uint32_t len = 0;
        vnode_t vp, dvp, dirp;
        struct nfs_filehandle nfh;
        struct nfs_export *nx = NULL;
@@ -3518,10 +3518,6 @@ nfsrv_mkdir(
        nmreq = &nd->nd_nmreq;
        nfsm_chain_null(&nmrep);
 
-       /*
-        * Save the original credential UID in case they are
-        * mapped and we need to map the IDs in the attributes.
-        */
        saved_uid = kauth_cred_getuid(nd->nd_cr);
 
        ni.ni_cnd.cn_nameiop = 0;
@@ -3533,6 +3529,9 @@ nfsrv_mkdir(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_LINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -3578,17 +3577,6 @@ nfsrv_mkdir(
                goto out;
        }
 
-       /*
-        * If the credentials were mapped, we should
-        * map the same values in the attributes.
-        */
-       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)
-                       VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
-       }
-
        error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
 
        /* construct ACL and handle inheritance */
@@ -3602,22 +3590,33 @@ nfsrv_mkdir(
                if (!error && xacl != NULL)
                        VATTR_SET(vap, va_acl, xacl);
        }
+
        VATTR_CLEAR_ACTIVE(vap, va_data_size);
        VATTR_CLEAR_ACTIVE(vap, va_access_time);
+       /*
+        * 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. 
+        */
+       if (VATTR_IS_ACTIVE(vap, va_mode))
+               vap->va_mode &= ~S_ISGID;
+       /*
+        * 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
+        */
+       VATTR_CLEAR_ACTIVE(vap, va_uid);
+       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 && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
-                       /*
-                        * Most NFS servers just ignore the UID/GID attributes, so we
-                        * try ignoring them if that'll help the request succeed.
-                        */
-                       VATTR_CLEAR_ACTIVE(vap, va_uid);
-                       VATTR_CLEAR_ACTIVE(vap, va_gid);
-                       error = vnode_authattr_new(dvp, vap, 0, ctx);
-               }
-       }
+       /*
+        * 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)
+               error = EPERM;
 
        if (!error)
                error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
@@ -3716,7 +3715,7 @@ nfsrv_rmdir(
        mbuf_t *mrepp)
 {
        int error, dpreattrerr, dpostattrerr;
-       uint32_t len;
+       uint32_t len = 0;
        uid_t saved_uid;
        vnode_t vp, dvp, dirp;
        struct vnode_attr dpreattr, dpostattr;
@@ -3740,6 +3739,9 @@ nfsrv_rmdir(
        nfsmerr_if(error);
 
        ni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+       ni.ni_op = OP_UNLINK;
+#endif
        ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
        error = nfsm_chain_get_path_namei(nmreq, len, &ni);
        if (!error) {
@@ -3903,7 +3905,7 @@ nfsrv_readdir(
 
        error = 0;
        attrerr = ENOENT;
-       nentries = 0;
+       count = nentries = 0;
        nmreq = &nd->nd_nmreq;
        nfsm_chain_null(&nmrep);
        rbuf = NULL;
@@ -3923,7 +3925,7 @@ nfsrv_readdir(
 
        off = toff;
        siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
-       xfer = NFS_SRVMAXDATA(nd);
+       xfer = NFSRV_NDMAXDATA(nd);
        if (siz > xfer)
                siz = xfer;
        fullsiz = siz;
@@ -3940,8 +3942,12 @@ nfsrv_readdir(
        error = nfsrv_credcheck(nd, ctx, nx, nxo);
        nfsmerr_if(error);
 
+       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))
                vnopflag |= VNODE_READDIR_SEEKOFF32;
+
        if (nd->nd_vers == NFS_VER3) {
                nfsm_srv_vattr_init(&attr, NFS_VER3);
                error = attrerr = vnode_getattr(vp, &attr, ctx);
@@ -3974,7 +3980,6 @@ again:
        nfsmerr_if(error);
 
        if (uio_resid(auio) != 0) {
-               // LP64todo - fix this
                siz -= uio_resid(auio);
 
                /* If nothing read, return empty reply with eof set */
@@ -4139,7 +4144,7 @@ nfsrv_readdirplus(
        nfsmerr_if(error);
 
        off = toff;
-       xfer = NFS_SRVMAXDATA(nd);
+       xfer = NFSRV_NDMAXDATA(nd);
        dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
        if (dircount > xfer)
                dircount = xfer;
@@ -4163,6 +4168,9 @@ nfsrv_readdirplus(
        if (nxo->nxo_flags & NX_32BITCLIENTS)
                vnopflag |= VNODE_READDIR_SEEKOFF32;
 
+       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))
@@ -4191,7 +4199,6 @@ again:
        nfsmerr_if(error);
 
        if (uio_resid(auio) != 0) {
-               // LP64todo - fix this
                siz -= uio_resid(auio);
 
                /* If nothing read, return empty reply with eof set */
@@ -4574,7 +4581,7 @@ nfsmerr:
                maxsize = NFS_MAXDGRAMDATA;
                prefsize = NFS_PREFDGRAMDATA;
        } else
-               maxsize = prefsize = NFS_MAXDATA;
+               maxsize = prefsize = NFSRV_MAXDATA;
 
        nfsm_chain_add_32(error, &nmrep, maxsize);
        nfsm_chain_add_32(error, &nmrep, prefsize);
@@ -4797,7 +4804,7 @@ int (*nfsrv_procs[NFS_NPROCS])(struct nfsrv_descript *nd,
  * will return EPERM instead of EACCESS. EPERM is always an error.
  */
 
-static int
+int
 nfsrv_authorize(
        vnode_t vp,
        vnode_t dvp,