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,30 ****
   */
  /*
   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   *
!  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
   */
  
  
  /*
   * This module provides functions for TRANS2_FIND_FIRST2 and
--- 20,30 ----
   */
  /*
   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   *
!  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   */
  
  
  /*
   * This module provides functions for TRANS2_FIND_FIRST2 and
*** 211,221 ****
  /*
   * 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;
          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 */
--- 211,221 ----
  /*
   * Args (and other state) that we carry around among the
   * various functions involved in FindFirst, FindNext.
   */
  typedef struct smb_find_args {
!         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,233 ****
          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_mbc_encode(smb_request_t *, smb_xa_t *,
      smb_fileinfo_t *, smb_find_args_t *);
  
  /*
   * Tunable parameter to limit the maximum
--- 223,233 ----
          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_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,330 ****
          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)
                  return (SDRC_ERROR);
  
          status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
          if (status != 0) {
                  smbsr_error(sr, status, 0, 0);
--- 318,330 ----
          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_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,352 ****
          }
  
          if (count == 0) {
                  smb_odir_close(od);
                  smb_odir_release(od);
!                 smbsr_errno(sr, ENOENT);
                  return (SDRC_ERROR);
          }
  
          if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
              (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
--- 342,353 ----
          }
  
          if (count == 0) {
                  smb_odir_close(od);
                  smb_odir_release(od);
!                 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,465 ****
          }
  
          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)
                  return (SDRC_ERROR);
  
          od = smb_tree_lookup_odir(sr, odid);
          if (od == NULL) {
                  smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
--- 454,466 ----
          }
  
          if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
                  sr->user_cr = smb_user_getprivcred(sr->uid_user);
  
!         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,545 ****
--- 535,562 ----
  {
          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,557 ****
--- 565,575 ----
          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,576 ****
--- 585,603 ----
                  ++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,636 ****
  
          return (count);
  }
  
  /*
!  * smb_trans2_find_get_maxdata
   *
!  * Calculate the minimum response space required for the specified
!  * information level.
   *
!  * A non-zero return value provides the minimum space required.
   * 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)
  {
!         int maxdata;
  
-         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:
                  if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
                          maxdata += sizeof (int32_t);
                  maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
                  break;
  
--- 631,663 ----
  
          return (count);
  }
  
  /*
!  * smb_trans2_find_get_fixedsize
   *
!  * Calculate the sizeof the fixed part of the response for the
!  * specified information level.
   *
!  * 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_fixedsize(smb_request_t *sr, uint16_t infolev,
!         uint16_t fflag)
  {
!         int maxdata = 0;
  
          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,748 ****
          uint32_t        dsize32, asize32;
          uint32_t        mb_flags = 0;
          uint32_t        resume_key;
          char            buf83[26];
          smb_msgbuf_t    mb;
  
          namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
          if (namelen == -1)
                  return (-1);
  
          /*
!          * If ascii the filename length returned to the client should
!          * include the null terminator for levels except STANDARD and
!          * EASIZE.
           */
!         if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
!                 if ((args->fa_infolev != SMB_INFO_STANDARD) &&
!                     (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
                          namelen += 1;
          }
  
-         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;
--- 745,801 ----
          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) {
                  /*
!                  * 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) != 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;
                  }
+         }
  
          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,781 ****
--- 825,835 ----
                      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,948 ****
          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);
  
          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);
          return (SDRC_SUCCESS);
  }
  
  void
  smb_post_find_close2(smb_request_t *sr)
  {
!         DTRACE_SMB_1(op__FindClose2__done, smb_request_t *, sr);
  }
  
  smb_sdrc_t
  smb_com_find_close2(smb_request_t *sr)
  {
--- 973,1005 ----
          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,
!             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_START(op__FindClose2, smb_request_t *, sr);
          return (SDRC_SUCCESS);
  }
  
  void
  smb_post_find_close2(smb_request_t *sr)
  {
!         DTRACE_SMB_DONE(op__FindClose2, smb_request_t *, sr);
  }
  
  smb_sdrc_t
  smb_com_find_close2(smb_request_t *sr)
  {