--- old/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c	Mon Aug  5 17:26:13 2019
+++ new/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c	Mon Aug  5 17:26:13 2019
@@ -33,8 +33,9 @@
  */
 
 /*
- * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
+ * Portions Copyright (C) 2001 - 2014 Apple Inc. All rights reserved.
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -49,6 +50,7 @@
 #include <sys/uio.h>
 #include <sys/random.h>
 #include <sys/note.h>
+#include <sys/errno.h>
 #include <sys/cmn_err.h>
 
 #include <netsmb/smb_osdep.h>
@@ -61,15 +63,26 @@
 
 #define	STYPE_LEN	8	/* share type strings */
 
-/*
- * Largest size to use with LARGE_READ/LARGE_WRITE.
- * Specs say up to 64k data bytes, but Windows traffic
- * uses 60k... no doubt for some good reason.
- * (Probably to keep 4k block alignment.)
- * XXX: Move to smb.h maybe?
- */
-#define	SMB_MAX_LARGE_RW_SIZE (60*1024)
+struct smb_dialect {
+	int		d_id;
+	const char 	*d_name;
+};
 
+static struct smb_dialect smb_dialects[3] = {
+	{SMB_DIALECT_NTLM0_12,	"NT LANMAN 1.0"},
+	{SMB_DIALECT_NTLM0_12,	"NT LM 0.12"},
+#define	NDIALECT_SMB1	2
+	{SMB_DIALECT_SMB2_FF,	"SMB 2.???"},
+#define	NDIALECT_SMB2	3
+};
+
+static const uint32_t smb_clnt_caps_mask =
+    SMB_CAP_UNICODE |
+    SMB_CAP_LARGE_FILES |
+    SMB_CAP_NT_SMBS |
+    SMB_CAP_STATUS32 |
+    SMB_CAP_EXT_SECURITY;
+
 /*
  * Default timeout values, all in seconds.
  * Make these tunable (only via mdb for now).
@@ -76,21 +89,527 @@
  */
 int smb_timo_notice = 15;
 int smb_timo_default = 30;	/* was SMB_DEFRQTIMO */
+int smb_timo_logon = 45;
 int smb_timo_open = 45;
 int smb_timo_read = 45;
 int smb_timo_write = 60;	/* was SMBWRTTIMO */
 int smb_timo_append = 90;
 
-static int smb_smb_read(struct smb_share *ssp, uint16_t fid,
-	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
-static int smb_smb_write(struct smb_share *ssp, uint16_t fid,
-	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
+int
+smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
+{
+	smb_sopt_t *sv = &vcp->vc_sopt;
+	smbioc_ssn_work_t *wk = &vcp->vc_work;
+	struct smb_rq *rqp = NULL;
+	struct mbchain *mbp = NULL;
+	struct mdchain *mdp = NULL;
+	struct smb_dialect *dp;
+	int err, sblen, tlen;
+	uint8_t wc, eklen;
+	uint16_t dindex, bc;
+	uint16_t ndialects;
+	boolean_t will_sign = B_FALSE;
 
-static int smb_smb_readx(struct smb_share *ssp, uint16_t fid,
-	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
-static int smb_smb_writex(struct smb_share *ssp, uint16_t fid,
-	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
+	/*
+	 * Initialize: vc_hflags and vc_hflags2.
+	 * Note: vcp->vc_hflags* are copied into the
+	 * (per request) rqp->rq_hflags* by smb_rq_init.
+	 *
+	 * Like Windows, set FLAGS2_UNICODE in our first request,
+	 * even though technically we don't yet know whether the
+	 * server supports Unicode.  Will clear this flag below
+	 * if we find out it doesn't.  Need to do this because
+	 * some servers reject all non-Unicode requests.
+	 */
+	vcp->vc_hflags =
+	    SMB_FLAGS_CASELESS |
+	    SMB_FLAGS_CANONICAL_PATHNAMES;
+	vcp->vc_hflags2 =
+	    SMB_FLAGS2_KNOWS_LONG_NAMES |
+	    SMB_FLAGS2_KNOWS_EAS |
+	    SMB_FLAGS2_IS_LONG_NAME |
+	    SMB_FLAGS2_EXT_SEC |
+	    SMB_FLAGS2_ERR_STATUS |
+	    SMB_FLAGS2_UNICODE;
 
+	/*
+	 * The initial UID needs to be zero,
+	 */
+	vcp->vc_smbuid = 0;
+
+	/*
+	 * (Re)init negotiated values
+	 */
+	bzero(sv, sizeof (*sv));
+	sv->sv_maxmux = 1;
+	sv->sv_maxvcs = 1;
+	sv->sv_maxtx = 1024;
+
+	/*
+	 * Should we offer the magic SMB2 dialect?
+	 */
+	if (vcp->vc_ssn.ssn_maxver >= SMB2_DIALECT_BASE)
+		ndialects = NDIALECT_SMB2;
+	else
+		ndialects = NDIALECT_SMB1;
+
+	err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
+	if (err)
+		return (err);
+
+	/*
+	 * Build the SMB request.
+	 */
+	smb_rq_getrequest(rqp, &mbp);
+	smb_rq_wstart(rqp);
+	smb_rq_wend(rqp);
+	smb_rq_bstart(rqp);
+	for (dindex = 0; dindex < ndialects; dindex++) {
+		dp = &smb_dialects[dindex];
+		mb_put_uint8(mbp, SMB_DT_DIALECT);
+		tlen = strlen(dp->d_name) + 1;
+		mb_put_mem(mbp, dp->d_name, tlen, MB_MSYSTEM);
+	}
+	smb_rq_bend(rqp);
+
+	/*
+	 * Do the OTW call.
+	 */
+	err = smb_rq_internal(rqp, smb_timo_default);
+	/*
+	 * If it's an SMB1-to-SMB2 negotiate response,
+	 * call the special handler and then skip the
+	 * whole rest of this function.
+	 */
+	if (err == EPROTO) {
+		err = smb2_parse_smb1nego_resp(rqp);
+		smb_rq_done(rqp);
+		return (err);
+	}
+	if (err) {
+		SMBSDEBUG("smb_rq_internal, err %d", err);
+		goto errout;
+	}
+	/* Should only get status success. */
+	if (rqp->sr_error != NT_STATUS_SUCCESS) {
+		err = ENOTSUP;
+		goto errout;
+	}
+
+	/*
+	 * Decode the response
+	 *
+	 * Comments to right show names as described in
+	 * The Microsoft SMB Protocol spec. [MS-SMB]
+	 * section 2.2.3
+	 */
+	smb_rq_getreply(rqp, &mdp);
+	(void) md_get_uint8(mdp, &wc);
+	err = md_get_uint16le(mdp, &dindex);
+	if (err != 0)
+		goto errout;
+	if (dindex >= ndialects) {
+		SMBERROR("Invalid dialect index from server: %s\n",
+		    vcp->vc_srvname);
+		err = EBADRPC;
+		goto errout;
+	}
+	dp = smb_dialects + dindex;
+	sv->sv_proto = dp->d_id;
+	SMBSDEBUG("Dialect %s", dp->d_name);
+	if (dp->d_id < SMB_DIALECT_NTLM0_12) {
+		SMBSDEBUG("old dialect %s", dp->d_name);
+		goto errout;
+	}
+	if (wc != 17) {
+		SMBSDEBUG("bad wc %d", (int)wc);
+		goto errout;
+	}
+	md_get_uint8(mdp, &sv->sv_sm);		/* SecurityMode */
+	md_get_uint16le(mdp, &sv->sv_maxmux);	/* MaxMpxCount */
+	md_get_uint16le(mdp, &sv->sv_maxvcs);	/* MaxCountVCs */
+	md_get_uint32le(mdp, &sv->sv_maxtx);	/* MaxBufferSize */
+	md_get_uint32le(mdp, &sv->sv_maxraw);	/* MaxRawSize */
+	md_get_uint32le(mdp, &sv->sv_skey);	/* SessionKey */
+	md_get_uint32le(mdp, &sv->sv_caps);	/* Capabilities */
+	md_get_mem(mdp, NULL, 8, MB_MSYSTEM);	/* SystemTime(s) */
+	md_get_uint16le(mdp, (uint16_t *)&sv->sv_tz);
+	md_get_uint8(mdp, &eklen);	/* EncryptionKeyLength */
+	err = md_get_uint16le(mdp, &bc);	/* ByteCount */
+	if (err)
+		goto errout;
+
+	/* BEGIN CSTYLED */
+	/*
+	 * Will we do SMB signing?  Or block the connection?
+	 * The table below describes this logic.  References:
+	 * [Windows Server Protocols: MS-SMB, sec. 3.2.4.2.3]
+	 * http://msdn.microsoft.com/en-us/library/cc212511.aspx
+	 * http://msdn.microsoft.com/en-us/library/cc212929.aspx
+	 *
+	 * Srv/Cli     | Required | Enabled    | If Required | Disabled
+	 * ------------+----------+------------+-------------+-----------
+	 * Required    | Signed   | Signed     | Signed      | Blocked [1]
+	 * ------------+----------+------------+-------------+-----------
+	 * Enabled     | Signed   | Signed     | Not Signed  | Not Signed
+	 * ------------+----------+------------+-------------+-----------
+	 * If Required | Signed   | Not Signed | Not Signed  | Not Signed
+	 * ------------+----------+------------+-------------+-----------
+	 * Disabled    | Blocked  | Not Signed | Not Signed  | Not Signed
+	 *
+	 * [1] Like Windows 2003 and later, we don't really implement
+	 * the "Disabled" setting.  Instead we implement "If Required",
+	 * so we always sign if the server requires signing.
+	 */
+	/* END CSTYLED */
+
+	if (sv->sv_sm & SMB_SM_SIGS_REQUIRE) {
+		/*
+		 * Server requires signing.  We will sign,
+		 * even if local setting is "disabled".
+		 */
+		will_sign = B_TRUE;
+	} else if (sv->sv_sm & SMB_SM_SIGS) {
+		/*
+		 * Server enables signing (client's option).
+		 * If enabled locally, do signing.
+		 */
+		if (vcp->vc_vopt & SMBVOPT_SIGNING_ENABLED)
+			will_sign = B_TRUE;
+		/* else not signing. */
+	} else {
+		/*
+		 * Server does not support signing.
+		 * If we "require" it, bail now.
+		 */
+		if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) {
+			SMBERROR("Client requires signing "
+			    "but server has it disabled.");
+			err = EBADRPC;
+			goto errout;
+		}
+	}
+
+	/*
+	 * Anonymous sessions can't sign.
+	 */
+	if (vcp->vc_vopt & SMBVOPT_ANONYMOUS) {
+		will_sign = B_FALSE;
+	}
+
+	SMBSDEBUG("Security signatures: %d", (int)will_sign);
+	if (will_sign) {
+		vcp->vc_flags |= SMBV_SIGNING;
+		vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+
+		/*
+		 * MS-SMB 2.2.4.5 says that when SMB signing is enabled,
+		 * we should NOT use "large read/write" even though the
+		 * server might offer those capabilities.
+		 */
+		sv->sv_caps &= ~(SMB_CAP_LARGE_READX | SMB_CAP_LARGE_WRITEX);
+	}
+
+	/* See comment above re. FLAGS2_UNICODE */
+	if ((sv->sv_caps & SMB_CAP_UNICODE) != 0)
+		vcp->vc_flags |= SMBV_UNICODE;
+	else
+		vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE;
+
+	if ((sv->sv_caps & SMB_CAP_STATUS32) == 0) {
+		/* They don't do NT error codes. */
+		vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS;
+	}
+
+	/*
+	 * Warn if they don't support SMB_CAP_NT_SMBS
+	 * (We'll try to use NtCreate anyway)
+	 */
+	if ((sv->sv_caps & SMB_CAP_NT_SMBS) == 0) {
+		cmn_err(CE_NOTE, "%s does not support SMB_CAP_NT_SMBS",
+		    vcp->vc_srvname);
+	}
+
+	/*
+	 * The rest of the message varies depending on
+	 * whether we've negotiated "extended security".
+	 *
+	 * With extended security, we have:
+	 *	Server_GUID	(length 16)
+	 *	Security_BLOB
+	 * Otherwise we have:
+	 *	EncryptionKey (length is eklen)
+	 *	PrimaryDomain
+	 */
+	if (sv->sv_caps & SMB_CAP_EXT_SECURITY) {
+		SMBSDEBUG("Ext.Security: yes");
+
+		/*
+		 * Skip the server GUID.
+		 */
+		err = md_get_mem(mdp, NULL, SMB_GUIDLEN, MB_MSYSTEM);
+		if (err)
+			goto errout;
+		/*
+		 * Remainder is the security blob.
+		 * Note: eklen "must be ignored" [MS-SMB]
+		 */
+		sblen = (int)bc - SMB_GUIDLEN;
+		if (sblen < 0)
+			goto errout;
+		/* Security blob (hint) is next */
+	} else {
+		SMBSDEBUG("Ext.Security: no");
+		err = ENOTSUP;
+		goto errout;
+	}
+
+	/*
+	 * Copy the security blob out to user space.
+	 * Buffer addr,size in vc_auth_rbuf,rlen
+	 */
+	if (wk->wk_u_auth_rlen < sblen) {
+		SMBSDEBUG("vc_auth_rbuf too small");
+		/* Give caller required size. */
+		wk->wk_u_auth_rlen = sblen;
+		err = EMSGSIZE;
+		goto errout;
+	}
+	wk->wk_u_auth_rlen = sblen;
+	err = md_get_mem(mdp, wk->wk_u_auth_rbuf.lp_ptr, sblen, MB_MUSER);
+	if (err)
+		goto errout;
+
+	/*
+	 * A few sanity checks on what we received,
+	 * becuse we will send these in ssnsetup.
+	 *
+	 * Maximum outstanding requests (we care),
+	 * and Max. VCs (we only use one).  Also,
+	 * MaxBufferSize lower limit per spec.
+	 */
+	if (sv->sv_maxmux < 1)
+		sv->sv_maxmux = 1;
+	if (sv->sv_maxvcs < 1)
+		sv->sv_maxvcs = 1;
+	if (sv->sv_maxtx < 1024)
+		sv->sv_maxtx = 1024;
+
+	/*
+	 * Maximum transfer size.
+	 * Sanity checks:
+	 *
+	 * Let's be conservative about an upper limit here.
+	 * Win2k uses 16644 (and others) so 32k should be a
+	 * reasonable sanity limit for this value.
+	 *
+	 * Note that this limit does NOT affect READX/WRITEX
+	 * with CAP_LARGE_..., which we nearly always use.
+	 */
+	vcp->vc_txmax = sv->sv_maxtx;
+	if (vcp->vc_txmax > 0x8000)
+		vcp->vc_txmax = 0x8000;
+
+	/*
+	 * Max read/write sizes, WITHOUT overhead.
+	 * This is just the payload size, so we must
+	 * leave room for the SMB headers, etc.
+	 * This is just the ct_txmax value, but
+	 * reduced and rounded down.  Tricky bit:
+	 *
+	 * Servers typically give us a value that's
+	 * some nice "round" number, i.e 0x4000 plus
+	 * some overhead, i.e. Win2k: 16644==0x4104
+	 * Subtract for the SMB header (32) and the
+	 * SMB command word and byte vectors (34?),
+	 * then round down to a 512 byte multiple.
+	 */
+	tlen = vcp->vc_txmax - 68;
+	tlen &= 0xFE00;
+
+	vcp->vc_rwmax = tlen;
+	vcp->vc_rxmax = tlen;
+	vcp->vc_wxmax = tlen;
+
+	/*
+	 * Most of the "capability" bits we offer in session setup
+	 * are just copied from those offered by the server.
+	 */
+	sv->sv_caps &= smb_clnt_caps_mask;
+
+	smb_rq_done(rqp);
+	return (0);
+
+errout:
+	smb_rq_done(rqp);
+	if (err == 0)
+		err = EBADRPC;
+	return (err);
+}
+
+static const char NativeOS[] = "illumos";
+static const char LanMan[] = "NETSMB";
+
+int
+smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
+{
+	smb_sopt_t *sv = &vcp->vc_sopt;
+	smbioc_ssn_work_t *wk = &vcp->vc_work;
+	struct smb_rq *rqp = NULL;
+	struct mbchain *mbp = NULL;
+	struct mdchain *mdp = NULL;
+	char *sb;
+	int err, ret;
+	uint32_t caps;
+	uint16_t action, bc, sblen;
+	uint8_t wc;
+
+	caps = sv->sv_caps;
+	sb = wk->wk_u_auth_wbuf.lp_ptr;
+	sblen = (uint16_t)wk->wk_u_auth_wlen;
+
+	err = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX,
+	    scred, &rqp);
+	if (err != 0) {
+		ret = err;
+		goto out;
+	}
+
+	/*
+	 * Build the SMB Session Setup request.
+	 * Always extended security form.
+	 */
+	mbp = &rqp->sr_rq;
+	smb_rq_wstart(rqp);
+	mb_put_uint16le(mbp, 0xff);		/* 0: AndXCommand */
+	mb_put_uint16le(mbp, 0);		/* 1: AndXOffset */
+	mb_put_uint16le(mbp, sv->sv_maxtx);	/* 2: MaxBufferSize */
+	mb_put_uint16le(mbp, sv->sv_maxmux);	/* 3: MaxMpxCount */
+	mb_put_uint16le(mbp, 1);		/* 4: VcNumber */
+	mb_put_uint32le(mbp, sv->sv_skey);	/* 5,6: Session Key */
+	mb_put_uint16le(mbp, sblen);	/* 7: Sec. Blob Len */
+	mb_put_uint32le(mbp, 0);	/* 8,9: reserved */
+	mb_put_uint32le(mbp, caps);	/* 10,11: Capabilities */
+	smb_rq_wend(rqp);		/* 12: Byte Count */
+	smb_rq_bstart(rqp);
+	err = mb_put_mem(mbp, sb, sblen, MB_MUSER);
+	if (err != 0) {
+		ret = err;
+		goto out;
+	}
+	(void) smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE);
+	(void) smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE);
+	smb_rq_bend(rqp);
+
+	/*
+	 * Run the request.  The return value here should be the
+	 * return from this function, unless we fail decoding.
+	 * Note: NT_STATUS_MORE_PROCESSING_REQUIRED is OK, and
+	 * the caller expects EINPROGRESS for that case.
+	 */
+	ret = smb_rq_internal(rqp, smb_timo_logon);
+	if (ret != 0)
+		goto out;
+	switch (rqp->sr_error) {
+	case NT_STATUS_SUCCESS:
+		break;
+	case NT_STATUS_MORE_PROCESSING_REQUIRED:
+		/* Keep going, but return... */
+		ret = EINPROGRESS;
+		break;
+	default:
+		ret = EAUTH;
+		goto out;
+	}
+
+	if (vcp->vc_smbuid == 0)
+		vcp->vc_smbuid = rqp->sr_rpuid;
+
+	/*
+	 * Parse the reply
+	 */
+	smb_rq_getreply(rqp, &mdp);
+
+	err = md_get_uint8(mdp, &wc);
+	if (err != 0)
+		wc = 0;
+	if (wc != 4) {
+		ret = EBADRPC;
+		goto out;
+	}
+	md_get_uint16le(mdp, NULL);	/* secondary cmd */
+	md_get_uint16le(mdp, NULL);	/* andxoffset */
+	md_get_uint16le(mdp, &action);	/* action XXX */
+	md_get_uint16le(mdp, &sblen);	/* sec. blob len */
+	md_get_uint16le(mdp, &bc);	/* byte count */
+	/*
+	 * Get the security blob, after
+	 * sanity-checking the length.
+	 */
+	if (sblen == 0 || sblen > bc) {
+		ret = EBADRPC;
+		goto out;
+	}
+	if (sblen > wk->wk_u_auth_rlen) {
+		ret = EBADRPC;
+		goto out;
+	}
+	sb = wk->wk_u_auth_rbuf.lp_ptr;
+	err = md_get_mem(mdp, sb, sblen, MB_MUSER);
+	if (err) {
+		ret = EBADRPC;
+		goto out;
+	}
+
+	/*
+	 * Native OS, LANMGR, & Domain follow here.
+	 * We don't need them and don't parse them.
+	 */
+
+out:
+	if (err != 0 && err != EINPROGRESS) {
+		/* UID no longer valid. */
+		vcp->vc_smbuid = 0;
+	}
+	if (rqp)
+		smb_rq_done(rqp);
+
+	return (ret);
+}
+
+int
+smb_smb_logoff(struct smb_vc *vcp, struct smb_cred *scred)
+{
+	struct smb_rq *rqp;
+	struct mbchain *mbp;
+	int error;
+
+	if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
+		return (0);
+
+	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
+	if (error)
+		return (error);
+	mbp = &rqp->sr_rq;
+	smb_rq_wstart(rqp);
+	mb_put_uint8(mbp, 0xff);
+	mb_put_uint8(mbp, 0);
+	mb_put_uint16le(mbp, 0);
+	smb_rq_wend(rqp);
+	smb_rq_bstart(rqp);
+	smb_rq_bend(rqp);
+
+	/*
+	 * Run this with a relatively short timeout. (5 sec.)
+	 * We don't really care about the result here.
+	 * Also, don't reconnect for this, of course!
+	 */
+	rqp->sr_flags |= SMBR_NORECONNECT;
+	error = smb_rq_internal(rqp, 5);
+	smb_rq_done(rqp);
+	return (error);
+}
+
 /*
  * Get the string representation of a share "use" type,
  * as needed for the "service" in tree connect.
@@ -352,7 +871,7 @@
  * Modern create/open of file or directory.
  */
 int
-smb_smb_ntcreate(
+smb1_smb_ntcreate(
 	struct smb_share *ssp,
 	struct mbchain	*name_mb,
 	uint32_t cr_flags,	/* create flags */
@@ -475,7 +994,7 @@
 }
 
 int
-smb_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
+smb1_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
 	struct smb_cred *scrp)
 {
 	struct smb_rq rq, *rqp = &rq;
@@ -600,101 +1119,11 @@
 	return (error);
 }
 
-/*
- * Common function for read/write with UIO.
- * Called by netsmb smb_usr_rw,
- *  smbfs_readvnode, smbfs_writevnode
- */
 int
-smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw,
+smb_smb_readx(smb_fh_t *fhp, uint32_t *lenp,
 	uio_t *uiop, smb_cred_t *scred, int timo)
 {
-	struct smb_vc *vcp = SSTOVC(ssp);
-	ssize_t  save_resid;
-	uint32_t len, rlen, maxlen;
-	int error = 0;
-	int (*iofun)(struct smb_share *, uint16_t, uint32_t *,
-	    uio_t *, smb_cred_t *, int);
-
-	/*
-	 * Determine which function to use,
-	 * and the transfer size per call.
-	 */
-	if (SMB_DIALECT(vcp) >= SMB_DIALECT_NTLM0_12) {
-		/*
-		 * Using NT LM 0.12, so readx, writex.
-		 * Make sure we can represent the offset.
-		 */
-		if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0 &&
-		    (uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
-			return (EFBIG);
-
-		if (rw == UIO_READ) {
-			iofun = smb_smb_readx;
-			if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
-				maxlen = SMB_MAX_LARGE_RW_SIZE;
-			else
-				maxlen = vcp->vc_rxmax;
-		} else { /* UIO_WRITE */
-			iofun = smb_smb_writex;
-			if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
-				maxlen = SMB_MAX_LARGE_RW_SIZE;
-			else
-				maxlen = vcp->vc_wxmax;
-		}
-	} else {
-		/*
-		 * Using the old SMB_READ and SMB_WRITE so
-		 * we're limited to 32-bit offsets, etc.
-		 * XXX: Someday, punt the old dialects.
-		 */
-		if ((uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
-			return (EFBIG);
-
-		if (rw == UIO_READ) {
-			iofun = smb_smb_read;
-			maxlen = vcp->vc_rxmax;
-		} else { /* UIO_WRITE */
-			iofun = smb_smb_write;
-			maxlen = vcp->vc_wxmax;
-		}
-	}
-
-	save_resid = uiop->uio_resid;
-	while (uiop->uio_resid > 0) {
-		/* Lint: uio_resid may be 64-bits */
-		rlen = len = (uint32_t)min(maxlen, uiop->uio_resid);
-		error = (*iofun)(ssp, fid, &rlen, uiop, scred, timo);
-
-		/*
-		 * Note: the iofun called uio_update, so
-		 * not doing that here as one might expect.
-		 *
-		 * Quit the loop either on error, or if we
-		 * transferred less then requested.
-		 */
-		if (error || (rlen < len))
-			break;
-
-		timo = 0; /* only first I/O should wait */
-	}
-	if (error && (save_resid != uiop->uio_resid)) {
-		/*
-		 * Stopped on an error after having
-		 * successfully transferred data.
-		 * Suppress this error.
-		 */
-		SMBSDEBUG("error %d suppressed\n", error);
-		error = 0;
-	}
-
-	return (error);
-}
-
-static int
-smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
-	uio_t *uiop, smb_cred_t *scred, int timo)
-{
+	struct smb_share *ssp = FHTOSS(fhp);
 	struct smb_rq *rqp;
 	struct mbchain *mbp;
 	struct mdchain *mdp;
@@ -716,7 +1145,7 @@
 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
 	mb_put_uint8(mbp, 0);		/* MBZ */
 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
-	mb_put_uint16le(mbp, fid);
+	mb_put_uint16le(mbp, fhp->fh_fid1);
 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
 	mb_put_uint16le(mbp, lenlo);	/* MaxCount */
 	mb_put_uint16le(mbp, 1);	/* MinCount */
@@ -786,10 +1215,11 @@
 	return (error);
 }
 
-static int
-smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
+int
+smb_smb_writex(smb_fh_t *fhp, uint32_t *lenp,
 	uio_t *uiop, smb_cred_t *scred, int timo)
 {
+	struct smb_share *ssp = FHTOSS(fhp);
 	struct smb_rq *rqp;
 	struct mbchain *mbp;
 	struct mdchain *mdp;
@@ -811,7 +1241,7 @@
 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
 	mb_put_uint8(mbp, 0);		/* MBZ */
 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
-	mb_put_uint16le(mbp, fid);
+	mb_put_uint16le(mbp, fhp->fh_fid1);
 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
 	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
 	mb_put_uint16le(mbp, 0);	/* !write-thru */
@@ -859,148 +1289,13 @@
 	return (error);
 }
 
-static int
-smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
-	uio_t *uiop, smb_cred_t *scred, int timo)
-{
-	struct smb_rq *rqp;
-	struct mbchain *mbp;
-	struct mdchain *mdp;
-	int error;
-	uint32_t off32;
-	uint16_t bc, cnt, dlen, rcnt, todo;
-	uint8_t wc;
 
-	ASSERT(uiop->uio_loffset <= UINT32_MAX);
-	off32 = (uint32_t)uiop->uio_loffset;
-	ASSERT(*lenp <= UINT16_MAX);
-	cnt = (uint16_t)*lenp;
-	/* This next is an "estimate" of planned reads. */
-	todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
-
-	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
-	if (error)
-		return (error);
-	smb_rq_getrequest(rqp, &mbp);
-	smb_rq_wstart(rqp);
-	mb_put_uint16le(mbp, fid);
-	mb_put_uint16le(mbp, cnt);
-	mb_put_uint32le(mbp, off32);
-	mb_put_uint16le(mbp, todo);
-	smb_rq_wend(rqp);
-	smb_rq_bstart(rqp);
-	smb_rq_bend(rqp);
-
-	if (timo == 0)
-		timo = smb_timo_read;
-	error = smb_rq_simple_timed(rqp, timo);
-	if (error)
-		goto out;
-	smb_rq_getreply(rqp, &mdp);
-	error = md_get_uint8(mdp, &wc);
-	if (error)
-		goto out;
-	if (wc != 5) {
-		error = EBADRPC;
-		goto out;
-	}
-	md_get_uint16le(mdp, &rcnt);		/* ret. count */
-	md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);  /* res. */
-	md_get_uint16le(mdp, &bc);		/* byte count */
-	md_get_uint8(mdp, NULL);		/* buffer format */
-	error = md_get_uint16le(mdp, &dlen);	/* data len */
-	if (error)
-		goto out;
-	if (dlen < rcnt) {
-		SMBSDEBUG("oops: dlen=%d rcnt=%d\n",
-		    (int)dlen, (int)rcnt);
-		rcnt = dlen;
-	}
-	if (rcnt == 0) {
-		*lenp = 0;
-		goto out;
-	}
-	/* paranoid */
-	if (rcnt > cnt) {
-		SMBSDEBUG("bad server! rcnt %d, cnt %d\n",
-		    (int)rcnt, (int)cnt);
-		rcnt = cnt;
-	}
-	error = md_get_uio(mdp, uiop, (int)rcnt);
-	if (error)
-		goto out;
-
-	/* success */
-	*lenp = (int)rcnt;
-
-out:
-	smb_rq_done(rqp);
-	return (error);
-}
-
-static int
-smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
-	uio_t *uiop, smb_cred_t *scred, int timo)
-{
-	struct smb_rq *rqp;
-	struct mbchain *mbp;
-	struct mdchain *mdp;
-	int error;
-	uint32_t off32;
-	uint16_t cnt, rcnt, todo;
-	uint8_t wc;
-
-	ASSERT(uiop->uio_loffset <= UINT32_MAX);
-	off32 = (uint32_t)uiop->uio_loffset;
-	ASSERT(*lenp <= UINT16_MAX);
-	cnt = (uint16_t)*lenp;
-	/* This next is an "estimate" of planned writes. */
-	todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
-
-	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
-	if (error)
-		return (error);
-	smb_rq_getrequest(rqp, &mbp);
-	smb_rq_wstart(rqp);
-	mb_put_uint16le(mbp, fid);
-	mb_put_uint16le(mbp, cnt);
-	mb_put_uint32le(mbp, off32);
-	mb_put_uint16le(mbp, todo);
-	smb_rq_wend(rqp);
-	smb_rq_bstart(rqp);
-	mb_put_uint8(mbp, SMB_DT_DATA);
-	mb_put_uint16le(mbp, cnt);
-
-	error = mb_put_uio(mbp, uiop, *lenp);
-	if (error)
-		goto out;
-	smb_rq_bend(rqp);
-	if (timo == 0)
-		timo = smb_timo_write;
-	error = smb_rq_simple_timed(rqp, timo);
-	if (error)
-		goto out;
-	smb_rq_getreply(rqp, &mdp);
-	error = md_get_uint8(mdp, &wc);
-	if (error)
-		goto out;
-	if (wc != 1) {
-		error = EBADRPC;
-		goto out;
-	}
-	error = md_get_uint16le(mdp, &rcnt);
-	if (error)
-		goto out;
-	*lenp = rcnt;
-
-out:
-	smb_rq_done(rqp);
-	return (error);
-}
-
-
 static u_int32_t	smbechoes = 0;
 
+/*
+ * Note: the IOD calls this, so this request must not wait for
+ * connection state changes, etc. (uses smb_rq_internal)
+ */
 int
 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
 {
@@ -1018,13 +1313,8 @@
 	smb_rq_bstart(rqp);
 	mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
 	smb_rq_bend(rqp);
-	/*
-	 * Note: the IOD calls this, so
-	 * this request must not wait for
-	 * connection state changes, etc.
-	 */
 	rqp->sr_flags |= SMBR_NORECONNECT;
-	error = smb_rq_simple_timed(rqp, timo);
+	error = smb_rq_internal(rqp, timo);
 	SMBSDEBUG("%d\n", error);
 	smb_rq_done(rqp);
 	return (error);
