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-3409 SMB2: OSX - cannot display nested folders in finder
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-3733 Want SMB2 Apple extensions
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-2604 Windows Explorer Stops Working / Crashes when Security Tab is accessed
NEX-2593 SMB2: unable to create folder with long filename on 4.0.3
NEX-1409 bcmp(NULL, NULL, 0) panics in DEBUG kernel
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Josef Sipek <josef.sipek@nexenta.com>
SMB-143 OS X 10.9.2 not working with SMB2
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
SMB-105 Codenomicon: SMB2 TC: 384126: Panic in smb2_query_dir with null search pattern
SMB-110 panic mapping a share from Nexentastor to the windows 2012 R2 client
SMB-109 Codenomicon: SMB TC: 409480 - Panic with SMB2_FIND request
SMB-96 Codenomicon: SMB2 TC: 141500 - Panic in smb2_decode_create_ctx
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)
        
*** 20,41 ****
   */
  /*
   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   *
!  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * Dispatch function for SMB2_QUERY_DIRECTORY
   *
   * Similar to smb_trans2_find.c (from SMB1)
   */
  
  #include <smbsrv/smb2_kproto.h>
  
  /*
   * Args (and other state) that we carry around among the
   * various functions involved in SMB2 Query Directory.
   */
  typedef struct smb2_find_args {
          uint32_t fa_maxdata;
--- 20,49 ----
   */
  /*
   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   *
!  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * Dispatch function for SMB2_QUERY_DIRECTORY
   *
   * Similar to smb_trans2_find.c (from SMB1)
   */
  
  #include <smbsrv/smb2_kproto.h>
+ #include <smbsrv/smb2_aapl.h>
  
  /*
+  * Internally defined info. level for MacOS support.
+  * Make sure this does not conflict with real values in
+  * FILE_INFORMATION_CLASS, and that it fits in 8-bits.
+  */
+ #define FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
+ 
+ /*
   * Args (and other state) that we carry around among the
   * various functions involved in SMB2 Query Directory.
   */
  typedef struct smb2_find_args {
          uint32_t fa_maxdata;
*** 44,59 ****
          uint16_t fa_maxcount;
          uint16_t fa_eos;        /* End Of Search */
          uint16_t fa_fixedsize;  /* size of fixed part of a returned entry */
          uint32_t fa_lastkey;    /* Last resume key */
          int fa_last_entry;      /* offset of last entry */
  } smb2_find_args_t;
  
  static uint32_t smb2_find_entries(smb_request_t *,
      smb_odir_t *, smb2_find_args_t *);
! static uint32_t smb2_find_mbc_encode(smb_request_t *,
!     smb_fileinfo_t *, smb2_find_args_t *);
  
  /*
   * Tunable parameter to limit the maximum
   * number of entries to be returned.
   */
--- 52,72 ----
          uint16_t fa_maxcount;
          uint16_t fa_eos;        /* End Of Search */
          uint16_t fa_fixedsize;  /* size of fixed part of a returned entry */
          uint32_t fa_lastkey;    /* Last resume key */
          int fa_last_entry;      /* offset of last entry */
+ 
+         /* Normal info, per dir. entry */
+         smb_fileinfo_t fa_fi;
+ 
+         /* MacOS AAPL extension stuff. */
+         smb_macinfo_t fa_mi;
  } smb2_find_args_t;
  
  static uint32_t smb2_find_entries(smb_request_t *,
      smb_odir_t *, smb2_find_args_t *);
! static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);
  
  /*
   * Tunable parameter to limit the maximum
   * number of entries to be returned.
   */
*** 97,109 ****
              &args.fa_maxdata);          /* l */
          if (rc || StructSize != 33)
                  return (SDRC_ERROR);
  
          status = smb2sr_lookup_fid(sr, &smb2fid);
          if (status)
                  goto errout;
-         of = sr->fid_ofile;
  
          /*
           * If there's an input buffer (search pattern), decode it.
           * Two times MAXNAMELEN because it represents the UNICODE string
           * length in bytes.
--- 110,125 ----
              &args.fa_maxdata);          /* l */
          if (rc || StructSize != 33)
                  return (SDRC_ERROR);
  
          status = smb2sr_lookup_fid(sr, &smb2fid);
+         of = sr->fid_ofile;
+ 
+         DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
+ 
          if (status)
                  goto errout;
  
          /*
           * If there's an input buffer (search pattern), decode it.
           * Two times MAXNAMELEN because it represents the UNICODE string
           * length in bytes.
*** 140,153 ****
          if (args.fa_maxdata > smb2_max_trans)
                  args.fa_maxdata = smb2_max_trans;
          sr->raw_data.max_bytes = args.fa_maxdata;
  
          /*
!          * Get the mininum size of entries we will return, which
           * lets us estimate the number of entries we'll need.
-          * This should be the size with a one character name.
-          * Compare w/ smb2_find_get_maxdata().
           *
           * Also use this opportunity to validate fa_infoclass.
           */
  
          switch (args.fa_infoclass) {
--- 156,167 ----
          if (args.fa_maxdata > smb2_max_trans)
                  args.fa_maxdata = smb2_max_trans;
          sr->raw_data.max_bytes = args.fa_maxdata;
  
          /*
!          * Get the fixed size of entries we will return, which
           * lets us estimate the number of entries we'll need.
           *
           * Also use this opportunity to validate fa_infoclass.
           */
  
          switch (args.fa_infoclass) {
*** 172,181 ****
--- 186,208 ----
          default:
                  status = NT_STATUS_INVALID_INFO_CLASS;
                  goto errout;
          }
  
+         /*
+          * MacOS, when using the AAPL CreateContext extensions
+          * and the "read dir attr" feature, uses a non-standard
+          * information format for directory entries.  Internally
+          * we'll use a fake info level to represent this case.
+          * (Wish they had just defined a new info level.)
+          */
+         if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
+             args.fa_infoclass == FileIdBothDirectoryInformation) {
+                 args.fa_infoclass = FileIdMacOsDirectoryInformation;
+                 args.fa_fixedsize = 96; /* yes, same size */
+         }
+ 
          args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
          if (args.fa_maxcount == 0)
                  args.fa_maxcount = 1;
          if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
                  args.fa_maxcount = smb2_find_max;
*** 224,251 ****
           */
          status = smb2_find_entries(sr, od, &args);
  
          of->f_seek_pos = od->d_offset;
  
!         if (status == NT_STATUS_NO_MORE_FILES) {
!                 if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) {
                          status = NT_STATUS_NO_SUCH_FILE;
-                         goto errout;
                  }
                  /*
!                  * This is not an error, but a warning that can be
                   * used to tell the client that this data return
                   * is the last of the enumeration.  Returning this
                   * warning now (with the data) saves the client a
                   * round trip that would otherwise be needed to
                   * find out it's at the end.
                   */
!                 sr->smb2_status = status;
!                 status = 0;
          }
-         if (status)
-                 goto errout;
  
          /*
           * SMB2 Query Directory reply
           */
          StructSize = 9;
--- 251,282 ----
           */
          status = smb2_find_entries(sr, od, &args);
  
          of->f_seek_pos = od->d_offset;
  
!         if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
!             status == NT_STATUS_NO_MORE_FILES) {
                  status = NT_STATUS_NO_SUCH_FILE;
          }
+ 
+ errout:
+         sr->smb2_status = status;
+         DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
+ 
          /*
!          * Note: NT_STATUS_NO_MORE_FILES is a warning
           * used to tell the client that this data return
           * is the last of the enumeration.  Returning this
           * warning now (with the data) saves the client a
           * round trip that would otherwise be needed to
           * find out it's at the end.
           */
!         if (status != 0 &&
!             status != NT_STATUS_NO_MORE_FILES) {
!                 smb2sr_put_error(sr, status);
!                 return (SDRC_SUCCESS);
          }
  
          /*
           * SMB2 Query Directory reply
           */
          StructSize = 9;
*** 257,272 ****
              DataOff,            /* w */
              DataLen,            /* l */
              &sr->raw_data);     /* C */
          if (DataLen == 0)
                  (void) smb_mbc_encodef(&sr->reply, ".");
-         if (rc == 0)
-                 return (SDRC_SUCCESS);
-         status = NT_STATUS_UNSUCCESSFUL;
  
! errout:
!         smb2sr_put_error(sr, status);
          return (SDRC_SUCCESS);
  }
  
  /*
   * smb2_find_entries
--- 288,301 ----
              DataOff,            /* w */
              DataLen,            /* l */
              &sr->raw_data);     /* C */
          if (DataLen == 0)
                  (void) smb_mbc_encodef(&sr->reply, ".");
  
!         if (rc)
!                 sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
! 
          return (SDRC_SUCCESS);
  }
  
  /*
   * smb2_find_entries
*** 277,288 ****
   *   NT status
   */
  static uint32_t
  smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
  {
-         smb_fileinfo_t  fileinfo;
          smb_odir_resume_t odir_resume;
          uint16_t        count;
          uint16_t        minsize;
          uint32_t        status = 0;
          int             rc = -1;
  
--- 306,318 ----
   *   NT status
   */
  static uint32_t
  smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
  {
          smb_odir_resume_t odir_resume;
+         char            *tbuf = NULL;
+         size_t          tbuflen = 0;
          uint16_t        count;
          uint16_t        minsize;
          uint32_t        status = 0;
          int             rc = -1;
  
*** 291,309 ****
           * minimum size entry.  That's the fixed part plus the
           * storage size of a 1 char unicode string.
           */
          minsize = args->fa_fixedsize + 2;
  
          count = 0;
          while (count < args->fa_maxcount) {
  
                  if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
                          status = NT_STATUS_BUFFER_OVERFLOW;
                          break;
                  }
  
!                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
                  if (rc == ENOENT) {
                          status = NT_STATUS_NO_MORE_FILES;
                          break;
                  }
                  if (rc != 0) {
--- 321,350 ----
           * minimum size entry.  That's the fixed part plus the
           * storage size of a 1 char unicode string.
           */
          minsize = args->fa_fixedsize + 2;
  
+         /*
+          * FileIdMacOsDirectoryInformation needs some buffer space
+          * for composing directory entry + stream name for lookup.
+          * Get the buffer now to avoid alloc/free per entry.
+          */
+         if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
+                 tbuflen = 2 * MAXNAMELEN;
+                 tbuf = kmem_alloc(tbuflen, KM_SLEEP);
+         }
+ 
          count = 0;
          while (count < args->fa_maxcount) {
  
                  if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
                          status = NT_STATUS_BUFFER_OVERFLOW;
                          break;
                  }
  
!                 rc = smb_odir_read_fileinfo(sr, od,
!                     &args->fa_fi, &args->fa_eos);
                  if (rc == ENOENT) {
                          status = NT_STATUS_NO_MORE_FILES;
                          break;
                  }
                  if (rc != 0) {
*** 314,324 ****
                          /* The readdir call hit the end. */
                          status = NT_STATUS_NO_MORE_FILES;
                          break;
                  }
  
!                 status = smb2_find_mbc_encode(sr, &fileinfo, args);
                  if (status) {
                          /*
                           * We read a directory entry but failed to
                           * copy it into the output buffer.  Rewind
                           * the directory pointer so this will be
--- 355,373 ----
                          /* The readdir call hit the end. */
                          status = NT_STATUS_NO_MORE_FILES;
                          break;
                  }
  
!                 if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
!                         (void) smb2_aapl_get_macinfo(sr, od,
!                             &args->fa_fi, &args->fa_mi, tbuf, tbuflen);
! 
!                 if (smb2_aapl_use_file_ids == 0 &&
!                     (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
!                         args->fa_fi.fi_nodeid = 0;
! 
!                 status = smb2_find_mbc_encode(sr, args);
                  if (status) {
                          /*
                           * We read a directory entry but failed to
                           * copy it into the output buffer.  Rewind
                           * the directory pointer so this will be
*** 333,343 ****
  
                  /*
                   * Save the offset of the next entry we'll read.
                   * If we fail copying, we'll need this offset.
                   */
!                 args->fa_lastkey = fileinfo.fi_cookie;
                  ++count;
          }
  
          if (count == 0) {
                  ASSERT(status != 0);
--- 382,392 ----
  
                  /*
                   * Save the offset of the next entry we'll read.
                   * If we fail copying, we'll need this offset.
                   */
!                 args->fa_lastkey = args->fa_fi.fi_cookie;
                  ++count;
          }
  
          if (count == 0) {
                  ASSERT(status != 0);
*** 353,362 ****
--- 402,414 ----
                  (void) smb_mbc_poke(&sr->raw_data,
                      args->fa_last_entry, "l", 0);
                  status = 0;
          }
  
+         if (tbuf != NULL)
+                 kmem_free(tbuf, tbuflen);
+ 
          return (status);
  }
  
  /*
   * smb2_mbc_encode
*** 377,392 ****
   *
   * Returns: 0 - data successfully encoded
   *      NT status
   */
  static uint32_t
! smb2_find_mbc_encode(smb_request_t *sr, smb_fileinfo_t *fileinfo,
!         smb2_find_args_t *args)
  {
          uint8_t         buf83[26];
          smb_msgbuf_t    mb;
!         int             namelen, padsz;
          int             shortlen = 0;
          int             rc, starting_offset;
          uint32_t        next_entry_offset;
          uint32_t        mb_flags = SMB_MSGBUF_UNICODE;
          uint32_t        resume_key;
--- 429,445 ----
   *
   * Returns: 0 - data successfully encoded
   *      NT status
   */
  static uint32_t
! smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)
  {
+         smb_fileinfo_t  *fileinfo = &args->fa_fi;
+         smb_macinfo_t   *macinfo = &args->fa_mi;
          uint8_t         buf83[26];
          smb_msgbuf_t    mb;
!         int             namelen;
          int             shortlen = 0;
          int             rc, starting_offset;
          uint32_t        next_entry_offset;
          uint32_t        mb_flags = SMB_MSGBUF_UNICODE;
          uint32_t        resume_key;
*** 522,531 ****
--- 575,625 ----
                      fileinfo->fi_nodeid);       /* q */
  
                  smb_msgbuf_term(&mb);
                  break;
  
+         /*
+          * MacOS, when using the AAPL extensions (see smb2_create)
+          * uses modified directory listing responses where the
+          * "EA size" field is replaced with "maximum access".
+          * This avoids the need for MacOS Finder to come back
+          * N times to get the maximum access for every file.
+          */
+         case FileIdMacOsDirectoryInformation:
+                 rc = smb_mbc_encodef(
+                     &sr->raw_data, "llTTTTqqll",
+                     0,  /* NextEntryOffset (set later) */
+                     resume_key,         /* a.k.a. file index */
+                     &fileinfo->fi_crtime,
+                     &fileinfo->fi_atime,
+                     &fileinfo->fi_mtime,
+                     &fileinfo->fi_ctime,
+                     fileinfo->fi_size,          /* q */
+                     fileinfo->fi_alloc_size,    /* q */
+                     fileinfo->fi_dosattr,       /* l */
+                     namelen);                   /* l */
+                 if (rc != 0)
+                         break;
+                 /*
+                  * This where FileIdMacOsDirectoryInformation
+                  * differs from FileIdBothDirectoryInformation
+                  * Instead of: EaSize, ShortNameLen, ShortName;
+                  * MacOS wants: MaxAccess, ResourceForkSize, and
+                  * 16 bytes of "compressed finder info".
+                  * mi_rforksize + mi_finderinfo falls where
+                  * the 24 byte shortname would normally be.
+                  */
+                 rc = smb_mbc_encodef(
+                     &sr->raw_data, "l..q16cwq",
+                     macinfo->mi_maxaccess,      /* l */
+                     /* short_name_len, reserved  (..) */
+                     macinfo->mi_rforksize,      /* q */
+                     macinfo->mi_finderinfo,     /* 16c */
+                     macinfo->mi_unixmode,       /* w */
+                     fileinfo->fi_nodeid);       /* q */
+                 break;
+ 
          /* See also: SMB_FIND_FILE_NAMES_INFO */
          case FileNamesInformation:              /* 12 */
                  rc = smb_mbc_encodef(
                      &sr->raw_data, "lll",
                      0,  /* NextEntryOffset (set later) */
*** 551,565 ****
              fileinfo->fi_name);
          if (rc)
                  return (NT_STATUS_BUFFER_OVERFLOW);
  
          /* Next entry needs to be 8-byte aligned. */
!         padsz = sr->raw_data.chain_offset & 7;
!         if (padsz) {
!                 padsz = 8 - padsz;
!                 (void) smb_mbc_encodef(&sr->raw_data, "#.", padsz);
!         }
          next_entry_offset = sr->raw_data.chain_offset - starting_offset;
          (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
              next_entry_offset);
          args->fa_last_entry = starting_offset;
  
--- 645,656 ----
              fileinfo->fi_name);
          if (rc)
                  return (NT_STATUS_BUFFER_OVERFLOW);
  
          /* Next entry needs to be 8-byte aligned. */
!         (void) smb_mbc_put_align(&sr->raw_data, 8);
! 
          next_entry_offset = sr->raw_data.chain_offset - starting_offset;
          (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
              next_entry_offset);
          args->fa_last_entry = starting_offset;