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)
NEX-6309 Update NT status codes
Update ntstatus.h from [MS-ERREF] October 2015
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>

@@ -32,10 +32,12 @@
  * $Id: smb_subr.c,v 1.27.108.1 2005/06/02 00:55:39 lindak Exp $
  */
 
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Portions Copyright (C) 2001 - 2013 Apple Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kmem.h>

@@ -55,54 +57,23 @@
 #include <netsmb/smb.h>
 #include <netsmb/smb_conn.h>
 #include <netsmb/smb_rq.h>
 #include <netsmb/smb_subr.h>
 
-/*
- * XXX:This conversion might not be fully MS-Compatible
- * for calculating hashes. The output length may differ
- * for some locales and needs to be handled from where
- * the call is made.
- */
-int
-smb_toupper(const char *inbuf, char *outbuf, size_t outlen)
-{
-        int err = 0;
-        size_t inlen, inrem, outrem;
-
-        inrem = inlen = strlen(inbuf);
-        outrem = outlen;
-        (void) u8_textprep_str((char *)inbuf, &inrem, outbuf, &outrem,
-            U8_TEXTPREP_TOUPPER, U8_UNICODE_LATEST, &err);
-        /* inrem, outrem are bytes unused, remaining */
-        if (inrem) {
-                SMBSDEBUG("input %d remains: %s\n", (int)inrem, inbuf);
-                inlen -= inrem;
-        }
-        if (outrem) {
-                outlen -= outrem;
-                outbuf[outlen] = '\0';
-        }
-        if (outlen > inlen) {
-                SMBSDEBUG("outlen > inlen! (%d > %d)\n",
-                    (int)outlen, (int)inlen);
-                /* Truncate to inlen here? */
-        }
-
-        return (err);
-}
-
 void
 smb_credinit(struct smb_cred *scred, cred_t *cr)
 {
         /* cr arg is optional */
         if (cr == NULL)
                 cr = ddi_get_cred();
+#ifdef  _KERNEL
         if (is_system_labeled()) {
                 cr = crdup(cr);
                 (void) setpflags(NET_MAC_AWARE, 1, cr);
-        } else {
+        } else
+#endif
+        {
                 crhold(cr);
         }
         scred->scr_cred = cr;
 }
 

@@ -113,10 +84,18 @@
                 crfree(scred->scr_cred);
                 scred->scr_cred = NULL;
         }
 }
 
+#ifndef _KERNEL
+/* ARGSUSED */
+void
+smb_debugmsg(const char *func, char *msg)
+{
+}
+#endif  /* _KERNEL */
+
 /*
  * Helper for the SMBERROR macro, etc.
  * This is also a good place for a breakpoint
  * or a dtrace probe, i.e. fbt:nsmb:smb_errmsg
  */

@@ -135,10 +114,13 @@
                  */
                 (void) vsnprintf(buf, sizeof (buf), fmt, adx);
                 DTRACE_PROBE2(debugmsg2,
                     (char *), func_name,
                     (char *), buf);
+#ifndef _KERNEL
+                smb_debugmsg(func_name, buf);
+#endif
         } else {
                 /*
                  * This is one of our xxxERROR macros.
                  * Add a prefix to the fmt string,
                  * then let vcmn_err do the args.

@@ -188,10 +170,13 @@
 #define ENOMEDIUM ENXIO
 #endif
 #ifndef ETIME
 #define ETIME ETIMEDOUT
 #endif
+#ifndef EMOREDATA
+#define EMOREDATA (0x7fff)
+#endif
 
 /*
  * Log any un-handled NT or DOS errors we encounter.
  * Make these log NOTICE in a debug build to ensure
  * they get noticed during tests.  In the field these

@@ -217,11 +202,13 @@
         {NT_STATUS_ACCOUNT_EXPIRED,             EACCES},
         {NT_STATUS_ACCOUNT_LOCKED_OUT,          EACCES},
         {NT_STATUS_ACCOUNT_RESTRICTION,         EACCES},
         {NT_STATUS_ADDRESS_ALREADY_EXISTS,      EADDRINUSE},
         {NT_STATUS_BAD_NETWORK_NAME,            ENOENT},
-        {NT_STATUS_BUFFER_TOO_SMALL,            EMOREDATA},
+        {NT_STATUS_BAD_NETWORK_PATH,            ENOENT},
+        {NT_STATUS_BUFFER_TOO_SMALL,            E2BIG},
+        {NT_STATUS_CANCELLED,                   ECANCELED},
         {NT_STATUS_CANNOT_DELETE,               EACCES},
         {NT_STATUS_CONFLICTING_ADDRESSES,       EADDRINUSE},
         {NT_STATUS_CONNECTION_ABORTED,          ECONNABORTED},
         {NT_STATUS_CONNECTION_DISCONNECTED,     ECONNABORTED},
         {NT_STATUS_CONNECTION_REFUSED,          ECONNREFUSED},

@@ -231,87 +218,118 @@
         {NT_STATUS_DEVICE_PROTOCOL_ERROR,       EPROTO},
         {NT_STATUS_DIRECTORY_NOT_EMPTY,         ENOTEMPTY},
         {NT_STATUS_DISK_FULL,                   ENOSPC},
         {NT_STATUS_DLL_NOT_FOUND,               ELIBACC},
         {NT_STATUS_DUPLICATE_NAME,              EINVAL},
+        {NT_STATUS_EAS_NOT_SUPPORTED,           ENOTSUP},
+        {NT_STATUS_EA_TOO_LARGE,                E2BIG},
         {NT_STATUS_END_OF_FILE,                 ENODATA},
+        {NT_STATUS_FILE_CLOSED,                 EBADF},
+        {NT_STATUS_FILE_DELETED,                ENOENT},
+        {NT_STATUS_FILE_INVALID,                EIO},
         {NT_STATUS_FILE_IS_A_DIRECTORY,         EISDIR},
         {NT_STATUS_FILE_LOCK_CONFLICT,          EAGAIN},
+        {NT_STATUS_FILE_RENAMED,                ENOENT},
         {NT_STATUS_FLOAT_INEXACT_RESULT,        ERANGE},
         {NT_STATUS_FLOAT_OVERFLOW,              ERANGE},
         {NT_STATUS_FLOAT_UNDERFLOW,             ERANGE},
         {NT_STATUS_HOST_UNREACHABLE,            EHOSTUNREACH},
-        {NT_STATUS_ILL_FORMED_PASSWORD,         EACCES},
+        {NT_STATUS_ILL_FORMED_PASSWORD,         EAUTH},
+        {NT_STATUS_INFO_LENGTH_MISMATCH,        EINVAL},
+        {NT_STATUS_INSUFFICIENT_RESOURCES,      EAGAIN},
+        {NT_STATUS_INSUFF_SERVER_RESOURCES,     EAGAIN},
         {NT_STATUS_INTEGER_OVERFLOW,            ERANGE},
-        {NT_STATUS_INVALID_ACCOUNT_NAME,        EACCES},
+        {NT_STATUS_INVALID_ACCOUNT_NAME,        EAUTH},
+        {NT_STATUS_INVALID_BUFFER_SIZE,         EIO},
+        {NT_STATUS_INVALID_DEVICE_REQUEST,      EINVAL},
         {NT_STATUS_INVALID_HANDLE,              EBADF},
+        {NT_STATUS_INVALID_INFO_CLASS,          EINVAL},
         {NT_STATUS_INVALID_LEVEL,               ENOTSUP},
-        {NT_STATUS_INVALID_LOGON_HOURS,         EACCES},
+        {NT_STATUS_INVALID_LOCK_SEQUENCE,       EINVAL},
+        {NT_STATUS_INVALID_LOGON_HOURS,         EAUTH},
         {NT_STATUS_INVALID_OWNER,               EINVAL},
         {NT_STATUS_INVALID_PARAMETER,           EINVAL},
         {NT_STATUS_INVALID_PIPE_STATE,          EPIPE},
         {NT_STATUS_INVALID_PRIMARY_GROUP,       EINVAL},
         {NT_STATUS_INVALID_WORKSTATION,         EACCES},
         {NT_STATUS_IN_PAGE_ERROR,               EFAULT},
+        {NT_STATUS_IO_DEVICE_ERROR,             EIO},
         {NT_STATUS_IO_TIMEOUT,                  ETIMEDOUT},
-        {NT_STATUS_IP_ADDRESS_CONFLICT1,        ENOTUNIQ},
-        {NT_STATUS_IP_ADDRESS_CONFLICT2,        ENOTUNIQ},
+        {NT_STATUS_IP_ADDRESS_CONFLICT1,        EADDRINUSE},
+        {NT_STATUS_IP_ADDRESS_CONFLICT2,        EADDRINUSE},
         {NT_STATUS_LICENSE_QUOTA_EXCEEDED,      EDQUOT},
         {NT_STATUS_LOCK_NOT_GRANTED,            EAGAIN},
-        {NT_STATUS_LOGIN_TIME_RESTRICTION,      EACCES},
-        {NT_STATUS_LOGON_FAILURE,               EACCES},
+        {NT_STATUS_LOGIN_TIME_RESTRICTION,      EAUTH},
+        {NT_STATUS_LOGON_FAILURE,               EAUTH},
+        {NT_STATUS_LOGON_TYPE_NOT_GRANTED,      EAUTH},
         {NT_STATUS_MEDIA_WRITE_PROTECTED,       EROFS},
         {NT_STATUS_MEMORY_NOT_ALLOCATED,        EFAULT},
+        {NT_STATUS_MORE_PROCESSING_REQUIRED,    EINPROGRESS},
         {NT_STATUS_NAME_TOO_LONG,               ENAMETOOLONG},
         {NT_STATUS_NETWORK_ACCESS_DENIED,       EACCES},
         {NT_STATUS_NETWORK_BUSY,                EBUSY},
+        {NT_STATUS_NETWORK_NAME_DELETED,        ENOENT},
         {NT_STATUS_NETWORK_UNREACHABLE,         ENETUNREACH},
         {NT_STATUS_NET_WRITE_FAULT,             ECOMM},
+        {NT_STATUS_NONEXISTENT_EA_ENTRY,        ENOENT},
         {NT_STATUS_NONEXISTENT_SECTOR,          ESPIPE},
         {NT_STATUS_NONE_MAPPED,                 EINVAL},
         {NT_STATUS_NOT_A_DIRECTORY,             ENOTDIR},
+        {NT_STATUS_NOT_FOUND,                   ENOENT},
         {NT_STATUS_NOT_IMPLEMENTED,             ENOTSUP},
+        {NT_STATUS_NOT_LOCKED,                  ENOLCK},
         {NT_STATUS_NOT_MAPPED_VIEW,             EINVAL},
         {NT_STATUS_NOT_SUPPORTED,               ENOTSUP},
+        {NT_STATUS_NO_EAS_ON_FILE,              ENOENT},
+        {NT_STATUS_NO_LOGON_SERVERS,            EAUTH},
         {NT_STATUS_NO_MEDIA,                    ENOMEDIUM},
         {NT_STATUS_NO_MEDIA_IN_DEVICE,          ENOMEDIUM},
         {NT_STATUS_NO_MEMORY,                   ENOMEM},
         {NT_STATUS_NO_SUCH_DEVICE,              ENODEV},
         {NT_STATUS_NO_SUCH_FILE,                ENOENT},
+        {NT_STATUS_NO_SUCH_LOGON_SESSION,       EAUTH},
+        {NT_STATUS_NO_SUCH_USER,                EAUTH},
+        {NT_STATUS_NO_TRUST_LSA_SECRET,         EAUTH},
+        {NT_STATUS_NO_TRUST_SAM_ACCOUNT,        EAUTH},
         {NT_STATUS_OBJECT_NAME_COLLISION,       EEXIST},
         {NT_STATUS_OBJECT_NAME_INVALID,         EINVAL},
         {NT_STATUS_OBJECT_NAME_NOT_FOUND,       ENOENT},
         {NT_STATUS_OBJECT_PATH_INVALID,         ENOTDIR},
         {NT_STATUS_OBJECT_PATH_NOT_FOUND,       ENOENT},
+        {NT_STATUS_OBJECT_PATH_SYNTAX_BAD,      EINVAL},
+        {NT_STATUS_OBJECT_TYPE_MISMATCH,        EBADF},
         {NT_STATUS_PAGEFILE_QUOTA,              EDQUOT},
-        {NT_STATUS_PASSWORD_EXPIRED,            EACCES},
-        {NT_STATUS_PASSWORD_MUST_CHANGE,        EACCES},
-        {NT_STATUS_PASSWORD_RESTRICTION,        EACCES},
+        {NT_STATUS_PASSWORD_EXPIRED,            EAUTH},
+        {NT_STATUS_PASSWORD_MUST_CHANGE,        EAUTH},
+        {NT_STATUS_PASSWORD_RESTRICTION,        EAUTH},
         {NT_STATUS_PATH_NOT_COVERED,            ENOENT},
         {NT_STATUS_PIPE_BROKEN,                 EPIPE},
         {NT_STATUS_PIPE_BUSY,                   EPIPE},
         {NT_STATUS_PIPE_CONNECTED,              EISCONN},
         {NT_STATUS_PIPE_DISCONNECTED,           EPIPE},
         {NT_STATUS_PIPE_NOT_AVAILABLE,          EBUSY},
         {NT_STATUS_PORT_CONNECTION_REFUSED,     ECONNREFUSED},
+        {NT_STATUS_PORT_DISCONNECTED,           EBADF},
         {NT_STATUS_PORT_MESSAGE_TOO_LONG,       EMSGSIZE},
         {NT_STATUS_PORT_UNREACHABLE,            EHOSTUNREACH},
         {NT_STATUS_PROTOCOL_UNREACHABLE,        ENOPROTOOPT},
         {NT_STATUS_QUOTA_EXCEEDED,              EDQUOT},
-        {NT_STATUS_RANGE_NOT_LOCKED,            EIO},
+        {NT_STATUS_RANGE_NOT_LOCKED,            EAGAIN}, /* like F_SETLK */
         {NT_STATUS_REGISTRY_QUOTA_LIMIT,        EDQUOT},
         {NT_STATUS_REMOTE_DISCONNECT,           ESHUTDOWN},
         {NT_STATUS_REMOTE_NOT_LISTENING,        ECONNREFUSED},
         {NT_STATUS_REQUEST_NOT_ACCEPTED,        EACCES},
         {NT_STATUS_RETRY,                       EAGAIN},
         {NT_STATUS_SHARING_VIOLATION,           EBUSY},
         {NT_STATUS_TIMER_NOT_CANCELED,          ETIME},
         {NT_STATUS_TOO_MANY_LINKS,              EMLINK},
         {NT_STATUS_TOO_MANY_OPENED_FILES,       EMFILE},
+        {NT_STATUS_TRUSTED_DOMAIN_FAILURE,      EAUTH},
+        {NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, EAUTH},
         {NT_STATUS_UNABLE_TO_FREE_VM,           EADDRINUSE},
         {NT_STATUS_UNSUCCESSFUL,                EINVAL},
-        {NT_STATUS_WRONG_PASSWORD,              EACCES},
+        {NT_STATUS_WRONG_PASSWORD,              EAUTH},
         {0,     0}
 };
 
 /*
  * Table for converting NT STATUS values to DOS class/code.

@@ -591,18 +609,10 @@
         {ERRDOS,        ERRnoaccess,    NT_STATUS_PROCESS_IS_TERMINATING},
         {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LOGON_TYPE},
         {ERRHRD,        ERRgeneral,     NT_STATUS_NO_GUID_TRANSLATION},
         {ERRHRD,        ERRgeneral,     NT_STATUS_CANNOT_IMPERSONATE},
         {ERRHRD,        ERRgeneral,     NT_STATUS_IMAGE_ALREADY_LOADED},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_NOT_PRESENT},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_LID_NOT_EXIST},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_LID_ALREADY_OWNED},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_NOT_LID_OWNER},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_COMMAND},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_LID},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE},
-        {ERRHRD,        ERRgeneral,     NT_STATUS_ABIOS_INVALID_SELECTOR},
         {ERRHRD,        ERRgeneral,     NT_STATUS_NO_LDT},
         {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_SIZE},
         {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_OFFSET},
         {ERRHRD,        ERRgeneral,     NT_STATUS_INVALID_LDT_DESCRIPTOR},
         {ERRDOS, ERROR_BAD_EXE_FORMAT,  NT_STATUS_INVALID_IMAGE_NE_FORMAT},

@@ -866,11 +876,24 @@
                 if (nt2d->nterr == nterr)
                         return (smb_maperror(nt2d->dclass, nt2d->derr));
         return (EIO);
 }
 
+uint_t
+smb_doserr2status(int dclass, int derr)
+{
+        const nt2doserr_t *nt2d;
 
+        if (dclass == 0 && derr == 0)
+                return (0);
+
+        for (nt2d = nt2doserr; nt2d->nterr; nt2d++)
+                if (nt2d->dclass == dclass && nt2d->derr == derr)
+                        return (nt2d->nterr);
+        return (NT_STATUS_UNSUCCESSFUL);
+}
+
 int
 smb_maperror(int eclass, int eno)
 {
         if (eclass == 0 && eno == 0)
                 return (0);

@@ -994,17 +1017,94 @@
         smb_errmsg(unknown_err_logpri, "smb_maperror",
             "Unknown DOS error %d/%d\n", eclass, eno);
         return (EIO);
 }
 
-#if defined(NOICONVSUPPORT) || defined(lint)
-extern int iconv_conv(void *handle, const char **inbuf,
-    size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
-#endif
-
 #define SMALL_CONV 256
 
+/*
+ * Decode an SMB OTW string (Unicode or OEM chars)
+ * converting to UTF-8 in the output buffer.
+ * outlen is in/out (max size on input)
+ * insize is the wire size (2 * chars if unicode)
+ * The output string is null terminated.
+ * Output length does not include the null.
+ */
+int
+smb_get_dstring(struct mdchain *mdc, struct smb_vc *vcp,
+        char *outbuf, size_t *outlen, int insize)
+{
+        uint16_t convbuf[SMALL_CONV];
+        uint16_t *cbuf;
+        size_t cbufalloc, inlen, outsize;
+        int error;
+
+        if (insize <= 0)
+                return (0);
+        /* Note: inlen is UTF-16 symbols. */
+        inlen = insize / 2;
+
+        if (*outlen < 2)
+                return (EINVAL);
+        outsize = *outlen - 1; /* room for null */
+
+        /*
+         * Get a buffer for the conversion and fill it.
+         * Use stack buffer if the string is
+         * small enough, else allocate.
+         */
+        if (insize < sizeof (convbuf)) {
+                cbufalloc = 0;
+                cbuf = convbuf;
+        } else {
+                cbufalloc = insize + 2;
+                cbuf = kmem_alloc(cbufalloc, KM_SLEEP);
+        }
+        error = md_get_mem(mdc, cbuf, insize, MB_MSYSTEM);
+        if (error != 0)
+                goto out;
+        cbuf[inlen] = 0;
+
+        /*
+         * Handle the easy case (non-unicode).
+         * XXX: Technically, we should convert
+         * the string to OEM codeset first...
+         * Modern servers all use Unicode, so
+         * this is good enough.
+         */
+        if (SMB_UNICODE_STRINGS(vcp) == 0) {
+                *outlen = strlcpy(outbuf, (char *)cbuf, outsize);
+                if (*outlen > outsize) {
+                        *outlen = outsize;
+                        error = E2BIG;
+                }
+        } else {
+                /*
+                 * Convert from UTF-16 to UTF-8
+                 */
+                error = uconv_u16tou8(cbuf, &inlen,
+                    (uchar_t *)outbuf, outlen,
+                    UCONV_IN_LITTLE_ENDIAN);
+                if (error == 0) {
+                        outbuf[*outlen] = '\0';
+                }
+        }
+
+        ASSERT(*outlen == strlen(outbuf));
+
+out:
+        if (cbufalloc != 0)
+                kmem_free(cbuf, cbufalloc);
+
+        return (error);
+}
+
+/*
+ * It's surprising that this function does utf8-ucs2 conversion.
+ * One would expect only smb_put_dstring to do that.
+ * Fixing that will require changing a bunch of callers. XXX
+ */
 /*ARGSUSED*/
 int
 smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src,
         int size, int caseopt, int *lenp)
 {

@@ -1077,6 +1177,133 @@
         error = smb_put_dmem(mbp, vcp, src, len, caseopt, NULL);
         if (error)
                 return (error);
 
         return (error);
+}
+int
+smb_smb_ntcreate(struct smb_share *ssp, struct mbchain *name_mb,
+        uint32_t crflag, uint32_t req_acc, uint32_t efa, uint32_t sh_acc,
+        uint32_t disp, uint32_t createopt,  uint32_t impersonate,
+        struct smb_cred *scrp, smb_fh_t *fhp,
+        uint32_t *cr_act_p, struct smbfattr *fap)
+{
+        int err;
+
+        if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
+                err = smb2_smb_ntcreate(ssp, name_mb, NULL, NULL,
+                   crflag, req_acc, efa, sh_acc, disp, createopt,
+                   impersonate, scrp, &fhp->fh_fid2, cr_act_p, fap);
+        } else {
+                err = smb1_smb_ntcreate(ssp, name_mb, crflag, req_acc,
+                    efa, sh_acc, disp, createopt,  impersonate, scrp,
+                    &fhp->fh_fid1, cr_act_p, fap);
+        }
+        return (err);
+}
+
+int
+smb_smb_close(struct smb_share *ssp, smb_fh_t *fhp,
+        struct smb_cred *scrp)
+{
+        int err;
+
+        if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
+                err = smb2_smb_close(ssp, &fhp->fh_fid2, scrp);
+        } else {
+                err = smb1_smb_close(ssp, fhp->fh_fid1, NULL, scrp);
+        }
+
+        return (err);
+}
+
+/*
+ * 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.)
+ */
+uint32_t smb1_large_io_max = (60*1024);
+
+/*
+ * Common function for read/write with UIO.
+ * Called by netsmb smb_usr_rw,
+ *  smbfs_readvnode, smbfs_writevnode
+ */
+int
+smb_rwuio(smb_fh_t *fhp, uio_rw_t rw,
+        uio_t *uiop, smb_cred_t *scred, int timo)
+{
+        struct smb_share *ssp = FHTOSS(fhp);
+        struct smb_vc *vcp = SSTOVC(ssp);
+        ssize_t  save_resid;
+        uint32_t len, rlen, maxlen;
+        int error = 0;
+        int (*iofun)(smb_fh_t *, uint32_t *,
+            uio_t *, smb_cred_t *, int);
+
+        /* After reconnect, the fid is invalid. */
+        if (fhp->fh_vcgenid != ssp->ss_vcgenid)
+                return (ESTALE);
+
+        if (SSTOVC(ssp)->vc_flags & SMBV_SMB2) {
+                if (rw == UIO_READ) {
+                        iofun = smb2_smb_read;
+                        maxlen = vcp->vc_sopt.sv2_maxread;
+                } else { /* UIO_WRITE */
+                        iofun = smb2_smb_write;
+                        maxlen = vcp->vc_sopt.sv2_maxwrite;
+                }
+        } else {
+                /*
+                 * 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 = smb1_large_io_max;
+                        else
+                                maxlen = vcp->vc_rxmax;
+                } else { /* UIO_WRITE */
+                        iofun = smb_smb_writex;
+                        if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
+                                maxlen = smb1_large_io_max;
+                        else
+                                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)(fhp, &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);
 }