Print this page
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-4458 Incorrect directory listing response for non-UNICODE clients
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-4540 SMB server declines EA support incorrectly
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4053 Customer DIR command hangs with smb2 enabled
NEX-4080 SMB_ODIR_FLAG_WILDCARDS can be incorrectly inherited
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
re #10733 Windows 7 directory listing keeps restarting (fix lint)
re #10733 Windows 7 directory listing keeps restarting

@@ -20,11 +20,11 @@
  */
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 
 /*
  * This module provides functions for TRANS2_FIND_FIRST2 and

@@ -211,11 +211,11 @@
 /*
  * Args (and other state) that we carry around among the
  * various functions involved in FindFirst, FindNext.
  */
 typedef struct smb_find_args {
-        uint32_t fa_maxdata;
+        uint32_t fa_fixedsize;
         uint16_t fa_infolev;
         uint16_t fa_maxcount;
         uint16_t fa_fflag;
         uint16_t fa_eos;        /* End Of Search */
         uint16_t fa_lno;        /* Last Name Offset */

@@ -223,11 +223,11 @@
         char fa_lastname[MAXNAMELEN]; /* and name */
 } smb_find_args_t;
 
 static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *,
     smb_odir_t *, smb_find_args_t *);
-static int smb_trans2_find_get_maxdata(smb_request_t *, uint16_t, uint16_t);
+static int smb_trans2_find_get_fixedsize(smb_request_t *, uint16_t, uint16_t);
 static int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *,
     smb_fileinfo_t *, smb_find_args_t *);
 
 /*
  * Tunable parameter to limit the maximum

@@ -318,13 +318,13 @@
         if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) {
                 sr->user_cr = smb_user_getprivcred(sr->uid_user);
                 odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
         }
 
-        args.fa_maxdata =
-            smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
-        if (args.fa_maxdata == 0)
+        args.fa_fixedsize =
+            smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
+        if (args.fa_fixedsize == 0)
                 return (SDRC_ERROR);
 
         status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
         if (status != 0) {
                 smbsr_error(sr, status, 0, 0);

@@ -342,11 +342,12 @@
         }
 
         if (count == 0) {
                 smb_odir_close(od);
                 smb_odir_release(od);
-                smbsr_errno(sr, ENOENT);
+                smbsr_status(sr, NT_STATUS_NO_SUCH_FILE,
+                    ERRDOS, ERROR_FILE_NOT_FOUND);
                 return (SDRC_ERROR);
         }
 
         if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
             (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {

@@ -453,13 +454,13 @@
         }
 
         if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
                 sr->user_cr = smb_user_getprivcred(sr->uid_user);
 
-        args.fa_maxdata =
-            smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
-        if (args.fa_maxdata == 0)
+        args.fa_fixedsize =
+            smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
+        if (args.fa_fixedsize == 0)
                 return (SDRC_ERROR);
 
         od = smb_tree_lookup_odir(sr, odid);
         if (od == NULL) {
                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,

@@ -534,12 +535,28 @@
 {
         smb_fileinfo_t  fileinfo;
         smb_odir_resume_t odir_resume;
         uint16_t        count, maxcount;
         int             rc = -1;
+        int             LastEntryOffset = 0;
         boolean_t       need_rewind = B_FALSE;
 
+        /*
+         * EAs are not current supported, so a search for level
+         * SMB_INFO_QUERY_EAS_FROM_LIST should always return an
+         * empty list.  Returning zero for this case gives the
+         * client an empty response, which is better than an
+         * NT_STATUS_INVALID_LEVEL return (and test failures).
+         *
+         * If and when we do support EAs, this level will modify
+         * the search here, and then return results just like
+         * SMB_INFO_QUERY_EA_SIZE, but only including files
+         * that have an EA in the provided list.
+         */
+        if (args->fa_infolev == SMB_INFO_QUERY_EAS_FROM_LIST)
+                return (0);
+
         if ((maxcount = args->fa_maxcount) == 0)
                 maxcount = 1;
 
         if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
                 maxcount = smb_trans2_find_max;

@@ -548,10 +565,11 @@
         while (count < maxcount) {
                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
                 if (rc != 0 || args->fa_eos != 0)
                         break;
 
+                LastEntryOffset = xa->rep_data_mb.chain_offset;
                 rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
                 if (rc == -1)
                         return (-1); /* fatal encoding error */
                 if (rc == 1) {
                         need_rewind = B_TRUE;

@@ -567,10 +585,19 @@
                 ++count;
         }
         if (args->fa_eos != 0 && rc == ENOENT)
                 rc = 0;
 
+        /*
+         * All but the ancient info levels start with NextEntryOffset.
+         * That's supposed to be zero in the last entry returned.
+         */
+        if (args->fa_infolev >= SMB_FIND_FILE_DIRECTORY_INFO) {
+                (void) smb_mbc_poke(&xa->rep_data_mb,
+                    LastEntryOffset, "l", 0);
+        }
+
         /* save the last cookie returned to client */
         if (count != 0)
                 smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname);
 
         /*

@@ -604,33 +631,33 @@
 
         return (count);
 }
 
 /*
- * smb_trans2_find_get_maxdata
+ * smb_trans2_find_get_fixedsize
  *
- * Calculate the minimum response space required for the specified
- * information level.
+ * Calculate the sizeof the fixed part of the response for the
+ * specified information level.
  *
- * A non-zero return value provides the minimum space required.
+ * A non-zero return value provides the fixed size.
  * A return value of zero indicates an unknown information level.
  */
 static int
-smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
+smb_trans2_find_get_fixedsize(smb_request_t *sr, uint16_t infolev,
+        uint16_t fflag)
 {
-        int maxdata;
+        int maxdata = 0;
 
-        maxdata = smb_ascii_or_unicode_null_len(sr);
-
         switch (infolev) {
         case SMB_INFO_STANDARD :
                 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
                         maxdata += sizeof (int32_t);
                 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
                 break;
 
         case SMB_INFO_QUERY_EA_SIZE:
+        case SMB_INFO_QUERY_EAS_FROM_LIST:
                 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
                         maxdata += sizeof (int32_t);
                 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
                 break;
 

@@ -718,31 +745,57 @@
         uint32_t        dsize32, asize32;
         uint32_t        mb_flags = 0;
         uint32_t        resume_key;
         char            buf83[26];
         smb_msgbuf_t    mb;
+        int             pad = 0;
 
         namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
         if (namelen == -1)
                 return (-1);
 
+        if (args->fa_infolev < SMB_FIND_FILE_DIRECTORY_INFO) {
         /*
-         * If ascii the filename length returned to the client should
-         * include the null terminator for levels except STANDARD and
-         * EASIZE.
+                 * Ancient info levels don't have a NextEntryOffset
+                 * field, so there's no padding for alignment.
+                 * The client expects a null after the file name,
+                 * and then the next entry.  The namelength field
+                 * never includes the null for these old levels.
+                 * Using the pad value to write the null because
+                 * we don't want to add that to namelen.
+                 * [MS-CIFS] sec. 2.8.1.{1-3}
          */
-        if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
-                if ((args->fa_infolev != SMB_INFO_STANDARD) &&
-                    (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
+                if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
+                        pad = 2; /* Unicode null */
+                else
+                        pad = 1; /* ascii null */
+                next_entry_offset = args->fa_fixedsize + namelen + pad;
+                if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
+                        return (1);
+        } else {
+                /*
+                 * Later info levels: The file name is written WITH
+                 * null termination, and the size of that null _is_
+                 * included in the namelen field.  There may also
+                 * be padding, and we pad to align(4) like Windows.
+                 * Don't include the padding in the "room for" test
+                 * because we want to ignore any error writing the
+                 * pad bytes after the last element.
+                 */
+                if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
+                        namelen += 2;
+                else
                         namelen += 1;
+                next_entry_offset = args->fa_fixedsize + namelen;
+                if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
+                        return (1);
+                if ((next_entry_offset & 3) != 0) {
+                        pad = 4 - (next_entry_offset & 3);
+                        next_entry_offset += pad;
         }
+        }
 
-        next_entry_offset = args->fa_maxdata + namelen;
-
-        if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
-                return (1);
-
         mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
         dsize32 = (fileinfo->fi_size > UINT_MAX) ?
             UINT_MAX : (uint32_t)fileinfo->fi_size;
         asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
             UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;

@@ -772,10 +825,11 @@
                     fileinfo->fi_dosattr,
                     namelen);
                 break;
 
         case SMB_INFO_QUERY_EA_SIZE:
+        case SMB_INFO_QUERY_EAS_FROM_LIST:
                 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
                         (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
                             resume_key);
 
                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr,

@@ -919,30 +973,33 @@
         args->fa_lno = xa->rep_data_mb.chain_offset;
         if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 &&
             (args->fa_lno & 1) != 0)
                 args->fa_lno++;
 
-        (void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
-            fileinfo->fi_name);
+        (void) smb_mbc_encodef(&xa->rep_data_mb, "%#u", sr,
+            namelen, fileinfo->fi_name);
 
+        if (pad)
+                (void) smb_mbc_encodef(&xa->rep_data_mb, "#.", pad);
+
         return (0);
 }
 
 /*
  * Close a search started by a Trans2FindFirst2 request.
  */
 smb_sdrc_t
 smb_pre_find_close2(smb_request_t *sr)
 {
-        DTRACE_SMB_1(op__FindClose2__start, smb_request_t *, sr);
+        DTRACE_SMB_START(op__FindClose2, smb_request_t *, sr);
         return (SDRC_SUCCESS);
 }
 
 void
 smb_post_find_close2(smb_request_t *sr)
 {
-        DTRACE_SMB_1(op__FindClose2__done, smb_request_t *, sr);
+        DTRACE_SMB_DONE(op__FindClose2, smb_request_t *, sr);
 }
 
 smb_sdrc_t
 smb_com_find_close2(smb_request_t *sr)
 {