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)

@@ -31,13 +31,14 @@
  *
  * $Id: smbfs_subr.c,v 1.18 2005/02/02 00:22:23 lindak Exp $
  */
 
 /*
- * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/time.h>

@@ -45,10 +46,11 @@
 #include <sys/sunddi.h>
 
 #include <netsmb/smb_osdep.h>
 
 #include <netsmb/smb.h>
+#include <netsmb/smb2.h>
 #include <netsmb/smb_conn.h>
 #include <netsmb/smb_subr.h>
 #include <netsmb/smb_rq.h>
 
 #include <smbfs/smbfs.h>

@@ -71,14 +73,15 @@
 {
         int caseopt = SMB_CS_NONE;
         int unicode = (SMB_UNICODE_STRINGS(vcp)) ? 1 : 0;
         int error;
 
-        if (SMB_DIALECT(vcp) < SMB_DIALECT_LANMAN1_0)
-                caseopt |= SMB_CS_UPPER;
-
-        if (unicode) {
+        /*
+         * SMB1 may need an alignment pad before (not SMB2)
+         */
+        if (((vcp)->vc_flags & SMBV_SMB2) == 0 &&
+            ((vcp)->vc_hflags2 & SMB_FLAGS2_UNICODE) != 0) {
                 error = mb_put_padbyte(mbp);
                 if (error)
                         return (error);
         }
 

@@ -120,15 +123,18 @@
                 error = smb_put_dmem(mbp, vcp,
                     name, nmlen, caseopt, NULL);
                 if (error)
                         return (error);
         }
-        /* Put NULL termination. */
+
+        /* SMB1 wants NULL termination. */
+        if (((vcp)->vc_flags & SMBV_SMB2) == 0) {
         if (unicode)
                 error = mb_put_uint16le(mbp, 0);
         else
                 error = mb_put_uint8(mbp, 0);
+        }
 
         return (error);
 }
 
 /*

@@ -180,6 +186,274 @@
          * Conversion failed, but our caller does not
          * deal with errors here, so just put a "?".
          * Don't expect to ever see this.
          */
         (void) strlcpy(ctx->f_name, "?", ctx->f_namesz);
+}
+
+/*
+ * Decode a directory entry from OtW form into ctx->f_attr
+ *
+ * Caller already put some (wire-format) directory entries
+ * into ctx->f_mdchain and we expect to find one.
+ *
+ * Advancing correctly through the buffer can be tricky if one
+ * tries to add up the size of an entry as you go (which is how
+ * the darwin code this is derived from did it).  The easiest way
+ * to correctly advance the position is to get a whole dirent
+ * into another mdchain (entry_mdc) based on NextEntryOffset,
+ * and then scan the data from that mdchain.  On the last entry,
+ * we don't know the entire length, so just scan directly from
+ * what remains of the multi-entry buffer instead of trying to
+ * figure out the length to copy into a separate mdchain.
+ */
+int
+smbfs_decode_dirent(struct smbfs_fctx *ctx)
+{
+        struct mdchain entry_mdc;
+        struct mdchain *mdp = &ctx->f_mdchain;
+        size_t nmlen;
+        uint64_t llongint;
+        uint32_t nmsize, dattr;
+        uint32_t nextoff = 0;
+        int error;
+
+        /* In case we error out... */
+        ctx->f_nmlen = 0;
+        ctx->f_rkey = (uint32_t)-1;
+        bzero(&entry_mdc, sizeof (entry_mdc));
+
+        /*
+         * Setup mdp to point to an mbchain holding
+         * what should be a single directory entry.
+         */
+        error = md_get_uint32le(mdp, &nextoff);
+        if (error != 0)
+                goto errout;
+        if (nextoff >= 4) {
+                /*
+                 * More entries follow.  Make a new mbchain
+                 * holding just this one entry, then advance.
+                 */
+                mblk_t *m = NULL;
+                error = md_get_mbuf(mdp, nextoff - 4, &m);
+                if (error != 0)
+                        goto errout;
+                md_initm(&entry_mdc, m);
+                mdp = &entry_mdc;
+                ctx->f_eofs += nextoff;
+        } else {
+                /* Scan directly from ctx->f_mdchain */
+                ctx->f_eofs = ctx->f_left;
+        }
+
+        /*
+         * Decode the fixed-size parts
+         */
+        switch (ctx->f_infolevel) {
+        case FileFullDirectoryInformation:
+        case SMB_FIND_FULL_DIRECTORY_INFO:
+                md_get_uint32le(mdp, &ctx->f_rkey);     /* resume key (idx) */
+                md_get_uint64le(mdp, &llongint);        /* creation time */
+                smb_time_NT2local(llongint, &ctx->f_attr.fa_createtime);
+                md_get_uint64le(mdp, &llongint);
+                smb_time_NT2local(llongint, &ctx->f_attr.fa_atime);
+                md_get_uint64le(mdp, &llongint);
+                smb_time_NT2local(llongint, &ctx->f_attr.fa_mtime);
+                md_get_uint64le(mdp, &llongint);
+                smb_time_NT2local(llongint, &ctx->f_attr.fa_ctime);
+                md_get_uint64le(mdp, &llongint);        /* file size */
+                ctx->f_attr.fa_size = llongint;
+                md_get_uint64le(mdp, &llongint);        /* alloc. size */
+                ctx->f_attr.fa_allocsz = llongint;
+                md_get_uint32le(mdp, &dattr);   /* ext. file attributes */
+                ctx->f_attr.fa_attr = dattr;
+                error = md_get_uint32le(mdp, &nmsize);  /* name size (otw) */
+                if (error)
+                        goto errout;
+                md_get_uint32le(mdp, NULL);     /* Ea size */
+                break;
+
+        case FileStreamInformation:
+                error = md_get_uint32le(mdp, &nmsize);  /* name size (otw) */
+                md_get_uint64le(mdp, &llongint);        /* file size */
+                ctx->f_attr.fa_size = llongint;
+                md_get_uint64le(mdp, &llongint);        /* alloc. size */
+                ctx->f_attr.fa_allocsz = llongint;
+                /*
+                 * Stream names start with a ':' that we want to skip.
+                 * This is the easiest place to take care of that.
+                 * Always unicode here.
+                 */
+                if (nmsize >= 2) {
+                        struct mdchain save_mdc;
+                        uint16_t wch;
+                        save_mdc = *mdp;
+                        md_get_uint16le(mdp, &wch);
+                        if (wch == ':') {
+                                /* OK, we skipped the ':' */
+                                nmsize -= 2;
+                        } else {
+                                SMBVDEBUG("No leading : in stream?\n");
+                                /* restore position */
+                                *mdp = save_mdc;
+                        }
+                }
+                break;
+
+        default:
+                SMBVDEBUG("unexpected info level %d\n", ctx->f_infolevel);
+                error = EINVAL;
+                goto errout;
+        }
+
+        /*
+         * Get the filename, and convert to utf-8
+         * Allocated f_name in findopen
+         */
+        nmlen = ctx->f_namesz;
+        error = smb_get_dstring(mdp, SSTOVC(ctx->f_ssp),
+            ctx->f_name, &nmlen, nmsize);
+        if (error != 0)
+                goto errout;
+        ctx->f_nmlen = (int)nmlen;
+        md_done(&entry_mdc);
+        return (0);
+
+errout:
+        /*
+         * Something bad has happened and we ran out of data
+         * before we could parse all f_ecnt entries expected.
+         * Give up on the current buffer.
+         */
+        SMBVDEBUG("ran out of data\n");
+        ctx->f_eofs = ctx->f_left;
+        md_done(&entry_mdc);
+        return (error);
+}
+
+/*
+ * Decode FileAllInformation
+ *
+ * The data is a concatenation of:
+ *      FileBasicInformation
+ *      FileStandardInformation
+ *      FileInternalInformation
+ *      FileEaInformation
+ *      FilePositionInformation
+ *      FileModeInformation
+ *      FileAlignmentInformation
+ *      FileNameInformation
+ */
+/*ARGSUSED*/
+int
+smbfs_decode_file_all_info(struct smb_share *ssp,
+        struct mdchain *mdp, struct smbfattr *fap)
+{
+        uint64_t llongint, lsize;
+        uint32_t dattr;
+        int error;
+
+        /*
+         * This part is: FileBasicInformation
+         */
+
+        /* creation time */
+        md_get_uint64le(mdp, &llongint);
+        smb_time_NT2local(llongint, &fap->fa_createtime);
+
+        /* last access time */
+        md_get_uint64le(mdp, &llongint);
+        smb_time_NT2local(llongint, &fap->fa_atime);
+
+        /* last write time */
+        md_get_uint64le(mdp, &llongint);
+        smb_time_NT2local(llongint, &fap->fa_mtime);
+
+        /* last change time */
+        md_get_uint64le(mdp, &llongint);
+        smb_time_NT2local(llongint, &fap->fa_ctime);
+
+        /* attributes */
+        md_get_uint32le(mdp, &dattr);
+        fap->fa_attr = dattr;
+
+        /* reserved */
+        md_get_uint32le(mdp, NULL);
+
+        /*
+         * This part is: FileStandardInformation
+         */
+
+        /* allocation size */
+        md_get_uint64le(mdp, &lsize);
+        fap->fa_allocsz = lsize;
+
+        /* File size */
+        error = md_get_uint64le(mdp, &lsize);
+        fap->fa_size = lsize;
+
+        /*
+         * There's more after this but we don't need it:
+         * Remainder of FileStandardInformation
+         *      NumLlinks, DeletOnClose, IsDir, reserved.
+         * Then:
+         *      FileInternalInformation
+         *      FileEaInformation
+         *      FilePositionInformation
+         *      FileModeInformation
+         *      FileAlignmentInformation
+         *      FileNameInformation
+         */
+
+        return (error);
+}
+
+/*
+ * Decode FileFsAttributeInformation
+ *
+ *    ULONG FileSystemAttributes;
+ *    LONG MaximumComponentNameLength;
+ *    ULONG FileSystemNameLength;
+ *    WCHAR FileSystemName[1];
+ */
+int
+smbfs_decode_fs_attr_info(struct smb_share *ssp,
+        struct mdchain *mdp, struct smb_fs_attr_info *fsa)
+{
+        struct smb_vc *vcp = SSTOVC(ssp);
+        uint32_t nlen;
+        int error;
+
+        md_get_uint32le(mdp, &fsa->fsa_aflags);
+        md_get_uint32le(mdp, &fsa->fsa_maxname);
+        error = md_get_uint32le(mdp, &nlen);    /* fs name length */
+        if (error)
+                goto out;
+
+        /*
+         * Get the FS type name.
+         */
+        bzero(fsa->fsa_tname, FSTYPSZ);
+        if (SMB_UNICODE_STRINGS(vcp)) {
+                uint16_t tmpbuf[FSTYPSZ];
+                size_t tmplen, outlen;
+
+                if (nlen > sizeof (tmpbuf))
+                        nlen = sizeof (tmpbuf);
+                error = md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM);
+                if (error != 0)
+                        goto out;
+                tmplen = nlen / 2;      /* UCS-2 chars */
+                outlen = FSTYPSZ - 1;
+                error = uconv_u16tou8(tmpbuf, &tmplen,
+                    (uchar_t *)fsa->fsa_tname, &outlen,
+                    UCONV_IN_LITTLE_ENDIAN);
+        } else {
+                if (nlen > (FSTYPSZ - 1))
+                        nlen = FSTYPSZ - 1;
+                error = md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM);
+        }
+
+out:
+        return (error);
 }