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,22 +20,30 @@
  */
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * 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,16 +52,21 @@
         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 *,
-    smb_fileinfo_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,13 +110,16 @@
             &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;
-        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.

@@ -140,14 +156,12 @@
         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
+         * Get the fixed 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) {

@@ -172,10 +186,23 @@
         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,28 +251,32 @@
          */
         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) {
+        if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
+            status == NT_STATUS_NO_MORE_FILES) {
                         status = NT_STATUS_NO_SUCH_FILE;
-                        goto errout;
                 }
+
+errout:
+        sr->smb2_status = status;
+        DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
+
                 /*
-                 * This is not an error, but a warning that can be
+         * 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.
                  */
-                sr->smb2_status = status;
-                status = 0;
+        if (status != 0 &&
+            status != NT_STATUS_NO_MORE_FILES) {
+                smb2sr_put_error(sr, status);
+                return (SDRC_SUCCESS);
         }
-        if (status)
-                goto errout;
 
         /*
          * SMB2 Query Directory reply
          */
         StructSize = 9;

@@ -257,16 +288,14 @@
             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);
+        if (rc)
+                sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
+
         return (SDRC_SUCCESS);
 }
 
 /*
  * smb2_find_entries

@@ -277,12 +306,13 @@
  *   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;
+        char            *tbuf = NULL;
+        size_t          tbuflen = 0;
         uint16_t        count;
         uint16_t        minsize;
         uint32_t        status = 0;
         int             rc = -1;
 

@@ -291,19 +321,30 @@
          * 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, &fileinfo, &args->fa_eos);
+                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,11 +355,19 @@
                         /* The readdir call hit the end. */
                         status = NT_STATUS_NO_MORE_FILES;
                         break;
                 }
 
-                status = smb2_find_mbc_encode(sr, &fileinfo, args);
+                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,11 +382,11 @@
 
                 /*
                  * 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;
+                args->fa_lastkey = args->fa_fi.fi_cookie;
                 ++count;
         }
 
         if (count == 0) {
                 ASSERT(status != 0);

@@ -353,10 +402,13 @@
                 (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,16 +429,17 @@
  *
  * 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)
+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, padsz;
+        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,10 +575,51 @@
                     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,15 +645,12 @@
             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);
-        }
+        (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;