+ return nfsm_rpchead2(nmp->nm_sotype, NFS_PROG, nfsvers, proc,
+ auth_type, auth_len, req->r_cred, req, mrest, xidp, mreqp);
+}
+
+int
+nfsm_rpchead2(int sotype, int prog, int vers, int proc, int auth_type, int auth_len,
+ kauth_cred_t cred, struct nfsreq *req, mbuf_t mrest, u_int64_t *xidp, mbuf_t *mreqp)
+{
+ mbuf_t mreq, mb;
+ int error, i, grpsiz, authsiz, reqlen;
+ size_t headlen;
+ struct timeval tv;
+ struct nfsm_chain nmreq;
+
+ /* allocate the packet */
+ authsiz = nfsm_rndup(auth_len);
+ headlen = authsiz + 10 * NFSX_UNSIGNED;
+ if (sotype == SOCK_STREAM) /* also include room for any RPC Record Mark */
+ headlen += NFSX_UNSIGNED;
+ if (headlen >= nfs_mbuf_minclsize) {
+ error = mbuf_getpacket(MBUF_WAITOK, &mreq);
+ } else {
+ error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &mreq);
+ if (!error) {
+ if (headlen < nfs_mbuf_mhlen)
+ mbuf_align_32(mreq, headlen);
+ else
+ mbuf_align_32(mreq, 8 * NFSX_UNSIGNED);
+ }
+ }
+ if (error) {
+ /* unable to allocate packet */
+ /* XXX should we keep statistics for these errors? */
+ return (error);
+ }
+
+ /*
+ * If the caller gave us a non-zero XID then use it because
+ * it may be a higher-level resend with a GSSAPI credential.
+ * Otherwise, allocate a new one.
+ */
+ if (*xidp == 0) {
+ lck_mtx_lock(nfs_request_mutex);
+ if (!nfs_xid) {
+ /*
+ * Derive initial xid from system time.
+ *
+ * Note: it's OK if this code inits nfs_xid to 0 (for example,
+ * due to a broken clock) because we immediately increment it
+ * and we guarantee to never use xid 0. So, nfs_xid should only
+ * ever be 0 the first time this function is called.
+ */
+ microtime(&tv);
+ nfs_xid = tv.tv_sec << 12;
+ }
+ if (++nfs_xid == 0) {
+ /* Skip zero xid if it should ever happen. */
+ nfs_xidwrap++;
+ nfs_xid++;
+ }
+ *xidp = nfs_xid + ((u_int64_t)nfs_xidwrap << 32);
+ lck_mtx_unlock(nfs_request_mutex);
+ }
+
+ /* build the header(s) */
+ nmreq.nmc_mcur = nmreq.nmc_mhead = mreq;
+ nmreq.nmc_ptr = mbuf_data(nmreq.nmc_mcur);
+ nmreq.nmc_left = mbuf_trailingspace(nmreq.nmc_mcur);
+
+ /* First, if it's a TCP stream insert space for an RPC record mark */
+ if (sotype == SOCK_STREAM)
+ nfsm_chain_add_32(error, &nmreq, 0);
+
+ /* Then the RPC header. */
+ nfsm_chain_add_32(error, &nmreq, (*xidp & 0xffffffff));
+ nfsm_chain_add_32(error, &nmreq, RPC_CALL);
+ nfsm_chain_add_32(error, &nmreq, RPC_VER2);
+ nfsm_chain_add_32(error, &nmreq, prog);
+ nfsm_chain_add_32(error, &nmreq, vers);
+ nfsm_chain_add_32(error, &nmreq, proc);
+
+add_cred:
+ switch (auth_type) {
+ case RPCAUTH_NULL:
+ nfsm_chain_add_32(error, &nmreq, RPCAUTH_NULL); /* auth */
+ nfsm_chain_add_32(error, &nmreq, 0); /* length */
+ nfsm_chain_add_32(error, &nmreq, RPCAUTH_NULL); /* verf */
+ nfsm_chain_add_32(error, &nmreq, 0); /* length */
+ nfsm_chain_build_done(error, &nmreq);
+ break;
+ case RPCAUTH_UNIX:
+ nfsm_chain_add_32(error, &nmreq, RPCAUTH_UNIX);
+ nfsm_chain_add_32(error, &nmreq, authsiz);
+ 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, cred->cr_groups[0]); /* GID */
+ grpsiz = (auth_len >> 2) - 5;
+ nfsm_chain_add_32(error, &nmreq, grpsiz);/* additional GIDs */
+ for (i = 1; i <= grpsiz; i++)
+ nfsm_chain_add_32(error, &nmreq, cred->cr_groups[i]);
+
+ /* And the verifier... */
+ nfsm_chain_add_32(error, &nmreq, RPCAUTH_NULL); /* flavor */
+ nfsm_chain_add_32(error, &nmreq, 0); /* length */
+ nfsm_chain_build_done(error, &nmreq);
+
+ /* Append the args mbufs */
+ if (!error)
+ error = mbuf_setnext(nmreq.nmc_mcur, mrest);
+ break;
+ case RPCAUTH_KRB5:
+ case RPCAUTH_KRB5I:
+ case RPCAUTH_KRB5P:
+ error = nfs_gss_clnt_cred_put(req, &nmreq, mrest);
+ if (error == ENEEDAUTH) {
+ /*
+ * Use sec=sys for this user
+ */
+ error = 0;
+ auth_type = RPCAUTH_UNIX;
+ goto add_cred;
+ }
+ break;
+ };
+
+ /* finish setting up the packet */
+ if (!error)
+ error = mbuf_pkthdr_setrcvif(mreq, 0);
+
+ if (error) {
+ mbuf_freem(mreq);
+ return (error);
+ }
+
+ /* Calculate the size of the request */
+ reqlen = 0;
+ for (mb = nmreq.nmc_mhead; mb; mb = mbuf_next(mb))
+ reqlen += mbuf_len(mb);
+
+ mbuf_pkthdr_setlen(mreq, reqlen);
+
+ /*
+ * If the request goes on a TCP stream,
+ * set its size in the RPC record mark.
+ * The record mark count doesn't include itself
+ * and the last fragment bit is set.
+ */
+ if (sotype == SOCK_STREAM)
+ nfsm_chain_set_recmark(error, &nmreq,
+ (reqlen - NFSX_UNSIGNED) | 0x80000000);
+
+ *mreqp = mreq;
+ return (0);