Print this page
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
re #11128 nsmb_close locking and teardown deadlock

@@ -31,12 +31,13 @@
  *
  * $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $
  */
 
 /*
- * 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.
  */
 
 /*
  * various SMB requests. Most of the routines merely packs data into mbufs.
  */

@@ -47,10 +48,11 @@
 #include <sys/lock.h>
 #include <sys/socket.h>
 #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>
 
 #include <netsmb/smb.h>

@@ -59,40 +61,557 @@
 #include <netsmb/smb_subr.h>
 #include <netsmb/smb_tran.h>
 
 #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).
  */
 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.
  */
 static const char *

@@ -350,11 +869,11 @@
 
 /*
  * 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 */
         uint32_t req_acc,       /* requested access */
         uint32_t efa,           /* ext. file attrs (DOS attr +) */

@@ -473,11 +992,11 @@
 
         return (0);
 }
 
 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;
         struct mbchain *mbp;
         long time;

@@ -598,105 +1117,15 @@
         error = smb_rq_simple(rqp);
         smb_rq_done(rqp);
         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;
         int error;
         uint32_t offlo, offhi, rlen;

@@ -714,11 +1143,11 @@
         smb_rq_getrequest(rqp, &mbp);
         smb_rq_wstart(rqp);
         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 */
                                         /* (only indicates blocking) */
         mb_put_uint32le(mbp, lenhi);    /* MaxCountHigh */

@@ -784,14 +1213,15 @@
 out:
         smb_rq_done(rqp);
         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;
         int error;
         uint32_t offlo, offhi, rlen;

@@ -809,11 +1239,11 @@
         smb_rq_getrequest(rqp, &mbp);
         smb_rq_wstart(rqp);
         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 */
         mb_put_uint16le(mbp, 0);
         mb_put_uint16le(mbp, lenhi);

@@ -857,152 +1287,17 @@
 out:
         smb_rq_done(rqp);
         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)
 {
         struct smb_rq *rqp;
         struct mbchain *mbp;

@@ -1016,16 +1311,11 @@
         mb_put_uint16le(mbp, 1); /* echo count */
         smb_rq_wend(rqp);
         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);
 }