+ /* get mbuf list to hold read data */
+ error = nfsm_mbuf_get_list(count, &mread, &mreadcnt);
+ nfsmerr_if(error);
+ MALLOC(uio_bufp, char *, UIO_SIZEOF(mreadcnt), M_TEMP, M_WAITOK);
+ if (uio_bufp)
+ auio = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
+ UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt));
+ if (!uio_bufp || !auio) {
+ error = ENOMEM;
+ goto errorexit;
+ }
+ for (m = mread; m; m = mbuf_next(m))
+ uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
+ error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx);
+ } else {
+ auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+ if (!auio) {
+ error = ENOMEM;
+ goto errorexit;
+ }
+ }
+
+errorexit:
+ if (!error || (nd->nd_vers == NFS_VER3)) {
+ nfsm_srv_vattr_init(vap, nd->nd_vers);
+ attrerr = vnode_getattr(vp, vap, ctx);
+ if (!error && (nd->nd_vers == NFS_VER2))
+ error = attrerr; /* NFSv2 must have attributes to return */
+ }
+ nfsmerr_if(error);
+
+ vnode_put(vp);
+ vp = NULL;
+
+ /* trim off any data not actually read */
+ len -= uio_resid(auio);
+ tlen = nfsm_rndup(len);
+ if (count != tlen || tlen != len)
+ nfsm_adj(mread, count - tlen, tlen - len);
+
+nfsmerr:
+ /* assemble reply */
+ nd->nd_repstat = error;
+ error = nfsrv_rephead(nd, slp, &nmrep, NFSX_POSTOPORFATTR(nd->nd_vers) + 3 * NFSX_UNSIGNED);
+ nfsmout_if(error);
+ *mrepp = nmrep.nmc_mhead;
+ nfsmout_on_status(nd, error);
+ if (nd->nd_vers == NFS_VER3)
+ nfsm_chain_add_postop_attr(error, nd, &nmrep, attrerr, vap);
+ if (error || nd->nd_repstat) {
+ nfsm_chain_build_done(error, &nmrep);
+ goto nfsmout;
+ }
+ if (nd->nd_vers == NFS_VER3) {
+ nfsm_chain_add_32(error, &nmrep, len);
+ nfsm_chain_add_32(error, &nmrep, (len < reqlen) ? TRUE : FALSE);
+ } else {
+ error = nfsm_chain_add_fattr(nd, &nmrep, vap);
+ }
+ nfsm_chain_add_32(error, &nmrep, len);
+ nfsm_chain_build_done(error, &nmrep);
+ nfsmout_if(error);
+ error = mbuf_setnext(nmrep.nmc_mcur, mread);
+ if (!error)
+ mread = NULL;
+
+ /* update export stats */
+ NFSStatAdd64(&nx->nx_stats.bytes_read, len);
+
+ /* update active user stats */
+ nfsrv_update_user_stat(nx, nd, saved_uid, 1, len, 0);
+nfsmout:
+ if (vp)
+ vnode_put(vp);
+ if (mread)
+ mbuf_freem(mread);
+ if (uio_bufp != NULL)
+ FREE(uio_bufp, M_TEMP);
+ if (error) {
+ nfsm_chain_cleanup(&nmrep);
+ *mrepp = NULL;
+ }
+ return (error);
+}
+
+#if CONFIG_FSE
+/*
+ * NFS File modification reporting
+ *
+ * When the contents of a file are changed, a "content modified"
+ * fsevent needs to be issued. Normally this would be done at
+ * file close time. This is difficult for NFS because the protocol
+ * has no "close" operation. The client sends a stream of write
+ * requests that just stop. So we keep a hash table full of
+ * vnodes that have been written to recently, and issue a
+ * "content modified" fsevent only if there are no writes to
+ * a vnode for nfsrv_fmod_pendtime milliseconds.
+ */
+int nfsrv_fmod_pending; /* count of vnodes being written to */
+int nfsrv_fmod_pendtime = 1000; /* msec to wait */
+int nfsrv_fmod_min_interval = 100; /* msec min interval between callbacks */
+
+/*
+ * This function is called via the kernel's callout
+ * mechanism. Calls are made only when there are
+ * vnodes pending a fsevent creation, and no more
+ * frequently than every nfsrv_fmod_min_interval ms.
+ */
+void
+nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
+{
+ struct nfsrv_fmod_hashhead *headp, firehead;
+ struct nfsrv_fmod *fp, *nfp, *pfp;
+ uint64_t timenow, next_deadline;
+ int interval = 0, i, fmod_fire;
+
+ 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);
+
+ /*
+ * 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.
+ */
+ headp = &nfsrv_fmod_hashtbl[i];
+ LIST_FOREACH(fp, headp, fm_link) {
+ if (timenow >= fp->fm_deadline)
+ break;
+ if (fp->fm_deadline < next_deadline)
+ next_deadline = fp->fm_deadline;
+ }
+