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)


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * Dispatch function for SMB2_QUERY_DIRECTORY
  30  *
  31  * Similar to smb_trans2_find.c (from SMB1)
  32  */
  33 
  34 #include <smbsrv/smb2_kproto.h>

  35 
  36 /*







  37  * Args (and other state) that we carry around among the
  38  * various functions involved in SMB2 Query Directory.
  39  */
  40 typedef struct smb2_find_args {
  41         uint32_t fa_maxdata;
  42         uint8_t fa_infoclass;
  43         uint8_t fa_fflags;
  44         uint16_t fa_maxcount;
  45         uint16_t fa_eos;        /* End Of Search */
  46         uint16_t fa_fixedsize;  /* size of fixed part of a returned entry */
  47         uint32_t fa_lastkey;    /* Last resume key */
  48         int fa_last_entry;      /* offset of last entry */






  49 } smb2_find_args_t;
  50 
  51 static uint32_t smb2_find_entries(smb_request_t *,
  52     smb_odir_t *, smb2_find_args_t *);
  53 static uint32_t smb2_find_mbc_encode(smb_request_t *,
  54     smb_fileinfo_t *, smb2_find_args_t *);
  55 
  56 /*
  57  * Tunable parameter to limit the maximum
  58  * number of entries to be returned.
  59  */
  60 uint16_t smb2_find_max = 128;
  61 
  62 smb_sdrc_t
  63 smb2_query_dir(smb_request_t *sr)
  64 {
  65         smb2_find_args_t                args;
  66         smb_odir_resume_t       odir_resume;
  67         smb_ofile_t *of = NULL;
  68         smb_odir_t *od = NULL;
  69         char *pattern = NULL;
  70         uint16_t StructSize;
  71         uint32_t FileIndex;
  72         uint16_t NameOffset;
  73         uint16_t NameLength;
  74         smb2fid_t smb2fid;


  82         bzero(&odir_resume, sizeof (odir_resume));
  83 
  84         /*
  85          * SMB2 Query Directory request
  86          */
  87         rc = smb_mbc_decodef(
  88             &sr->smb_data, "wbblqqwwl",
  89             &StructSize,            /* w */
  90             &args.fa_infoclass,             /* b */
  91             &args.fa_fflags,                /* b */
  92             &FileIndex,                     /* l */
  93             &smb2fid.persistent,    /* q */
  94             &smb2fid.temporal,              /* q */
  95             &NameOffset,            /* w */
  96             &NameLength,            /* w */
  97             &args.fa_maxdata);              /* l */
  98         if (rc || StructSize != 33)
  99                 return (SDRC_ERROR);
 100 
 101         status = smb2sr_lookup_fid(sr, &smb2fid);




 102         if (status)
 103                 goto errout;
 104         of = sr->fid_ofile;
 105 
 106         /*
 107          * If there's an input buffer (search pattern), decode it.
 108          * Two times MAXNAMELEN because it represents the UNICODE string
 109          * length in bytes.
 110          */
 111         if (NameLength >= (2 * MAXNAMELEN)) {
 112                 status = NT_STATUS_OBJECT_PATH_INVALID;
 113                 goto errout;
 114         }
 115         if (NameLength != 0) {
 116                 /*
 117                  * We're normally positioned at the pattern now,
 118                  * but there could be some padding before it.
 119                  */
 120                 skip = (sr->smb2_cmd_hdr + NameOffset) -
 121                     sr->smb_data.chain_offset;
 122                 if (skip < 0) {
 123                         status = NT_STATUS_OBJECT_PATH_INVALID;
 124                         goto errout;
 125                 }
 126                 if (skip > 0)
 127                         (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
 128                 rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
 129                     NameLength, &pattern);
 130                 if (rc || pattern == NULL) {
 131                         status = NT_STATUS_OBJECT_PATH_INVALID;
 132                         goto errout;
 133                 }
 134         } else
 135                 pattern = "*";
 136 
 137         /*
 138          * Setup the output buffer.
 139          */
 140         if (args.fa_maxdata > smb2_max_trans)
 141                 args.fa_maxdata = smb2_max_trans;
 142         sr->raw_data.max_bytes = args.fa_maxdata;
 143 
 144         /*
 145          * Get the mininum size of entries we will return, which
 146          * lets us estimate the number of entries we'll need.
 147          * This should be the size with a one character name.
 148          * Compare w/ smb2_find_get_maxdata().
 149          *
 150          * Also use this opportunity to validate fa_infoclass.
 151          */
 152 
 153         switch (args.fa_infoclass) {
 154         case FileDirectoryInformation:          /* 1 */
 155                 args.fa_fixedsize = 64;
 156                 break;
 157         case FileFullDirectoryInformation:      /* 2 */
 158                 args.fa_fixedsize = 68;
 159                 break;
 160         case FileBothDirectoryInformation:      /* 3 */
 161                 args.fa_fixedsize = 94;
 162                 break;
 163         case FileNamesInformation:              /* 12 */
 164                 args.fa_fixedsize = 12;
 165                 break;
 166         case FileIdBothDirectoryInformation:    /* 37 */
 167                 args.fa_fixedsize = 96;
 168                 break;
 169         case FileIdFullDirectoryInformation:    /* 38 */
 170                 args.fa_fixedsize = 84;
 171                 break;
 172         default:
 173                 status = NT_STATUS_INVALID_INFO_CLASS;
 174                 goto errout;
 175         }
 176 













 177         args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
 178         if (args.fa_maxcount == 0)
 179                 args.fa_maxcount = 1;
 180         if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
 181                 args.fa_maxcount = smb2_find_max;
 182         if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
 183                 args.fa_maxcount = 1;
 184 
 185         /*
 186          * If this ofile does not have an odir yet, get one.
 187          */
 188         mutex_enter(&of->f_mutex);
 189         if ((od = of->f_odir) == NULL) {
 190                 status = smb_odir_openfh(sr, pattern, sattr, &od);
 191                 of->f_odir = od;
 192         }
 193         mutex_exit(&of->f_mutex);
 194         if (od == NULL) {
 195                 if (status == 0)
 196                         status = NT_STATUS_INTERNAL_ERROR;


 209          */
 210         if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
 211                 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 212                 odir_resume.or_cookie = 0;
 213         } else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
 214                 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 215                 odir_resume.or_cookie = FileIndex;
 216         } else {
 217                 odir_resume.or_type = SMB_ODIR_RESUME_CONT;
 218         }
 219         smb_odir_resume_at(od, &odir_resume);
 220         of->f_seek_pos = od->d_offset;
 221 
 222         /*
 223          * The real work of readdir and format conversion.
 224          */
 225         status = smb2_find_entries(sr, od, &args);
 226 
 227         of->f_seek_pos = od->d_offset;
 228 
 229         if (status == NT_STATUS_NO_MORE_FILES) {
 230                 if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) {
 231                         status = NT_STATUS_NO_SUCH_FILE;
 232                         goto errout;
 233                 }





 234                 /*
 235                  * This is not an error, but a warning that can be
 236                  * used to tell the client that this data return
 237                  * is the last of the enumeration.  Returning this
 238                  * warning now (with the data) saves the client a
 239                  * round trip that would otherwise be needed to
 240                  * find out it's at the end.
 241                  */
 242                 sr->smb2_status = status;
 243                 status = 0;


 244         }
 245         if (status)
 246                 goto errout;
 247 
 248         /*
 249          * SMB2 Query Directory reply
 250          */
 251         StructSize = 9;
 252         DataOff = SMB2_HDR_SIZE + 8;
 253         DataLen = MBC_LENGTH(&sr->raw_data);
 254         rc = smb_mbc_encodef(
 255             &sr->reply, "wwlC",
 256             StructSize,         /* w */
 257             DataOff,            /* w */
 258             DataLen,            /* l */
 259             &sr->raw_data);      /* C */
 260         if (DataLen == 0)
 261                 (void) smb_mbc_encodef(&sr->reply, ".");
 262         if (rc == 0)
 263                 return (SDRC_SUCCESS);
 264         status = NT_STATUS_UNSUCCESSFUL;
 265 
 266 errout:
 267         smb2sr_put_error(sr, status);

 268         return (SDRC_SUCCESS);
 269 }
 270 
 271 /*
 272  * smb2_find_entries
 273  *
 274  * Find and encode up to args->fa_maxcount directory entries.
 275  *
 276  * Returns:
 277  *   NT status
 278  */
 279 static uint32_t
 280 smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
 281 {
 282         smb_fileinfo_t  fileinfo;
 283         smb_odir_resume_t odir_resume;


 284         uint16_t        count;
 285         uint16_t        minsize;
 286         uint32_t        status = 0;
 287         int             rc = -1;
 288 
 289         /*
 290          * Let's stop when the remaining space will not hold a
 291          * minimum size entry.  That's the fixed part plus the
 292          * storage size of a 1 char unicode string.
 293          */
 294         minsize = args->fa_fixedsize + 2;
 295 










 296         count = 0;
 297         while (count < args->fa_maxcount) {
 298 
 299                 if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
 300                         status = NT_STATUS_BUFFER_OVERFLOW;
 301                         break;
 302                 }
 303 
 304                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);

 305                 if (rc == ENOENT) {
 306                         status = NT_STATUS_NO_MORE_FILES;
 307                         break;
 308                 }
 309                 if (rc != 0) {
 310                         status = smb_errno2status(rc);
 311                         break;
 312                 }
 313                 if (args->fa_eos != 0) {
 314                         /* The readdir call hit the end. */
 315                         status = NT_STATUS_NO_MORE_FILES;
 316                         break;
 317                 }
 318 
 319                 status = smb2_find_mbc_encode(sr, &fileinfo, args);








 320                 if (status) {
 321                         /*
 322                          * We read a directory entry but failed to
 323                          * copy it into the output buffer.  Rewind
 324                          * the directory pointer so this will be
 325                          * the first entry read next time.
 326                          */
 327                         bzero(&odir_resume, sizeof (odir_resume));
 328                         odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 329                         odir_resume.or_cookie = args->fa_lastkey;
 330                         smb_odir_resume_at(od, &odir_resume);
 331                         break;
 332                 }
 333 
 334                 /*
 335                  * Save the offset of the next entry we'll read.
 336                  * If we fail copying, we'll need this offset.
 337                  */
 338                 args->fa_lastkey = fileinfo.fi_cookie;
 339                 ++count;
 340         }
 341 
 342         if (count == 0) {
 343                 ASSERT(status != 0);
 344         } else {
 345                 /*
 346                  * We copied some directory entries, but stopped for
 347                  * NT_STATUS_NO_MORE_FILES, or something.
 348                  *
 349                  * Per [MS-FSCC] sec. 2.4, the last entry in the
 350                  * enumeration MUST have its NextEntryOffset value
 351                  * set to zero.  Overwrite that in the last entry.
 352                  */
 353                 (void) smb_mbc_poke(&sr->raw_data,
 354                     args->fa_last_entry, "l", 0);
 355                 status = 0;
 356         }
 357 



 358         return (status);
 359 }
 360 
 361 /*
 362  * smb2_mbc_encode
 363  *
 364  * This function encodes the mbc for one directory entry.
 365  *
 366  * The function returns -1 when the max data requested by client
 367  * is reached. If the entry is valid and successful encoded, 0
 368  * will be returned; otherwise, 1 will be returned.
 369  *
 370  * We always null terminate the filename. The space for the null
 371  * is included in the maxdata calculation and is therefore included
 372  * in the next_entry_offset. namelen is the unterminated length of
 373  * the filename. For levels except STANDARD and EA_SIZE, if the
 374  * filename is ascii the name length returned to the client should
 375  * include the null terminator. Otherwise the length returned to
 376  * the client should not include the terminator.
 377  *
 378  * Returns: 0 - data successfully encoded
 379  *      NT status
 380  */
 381 static uint32_t
 382 smb2_find_mbc_encode(smb_request_t *sr, smb_fileinfo_t *fileinfo,
 383         smb2_find_args_t *args)
 384 {


 385         uint8_t         buf83[26];
 386         smb_msgbuf_t    mb;
 387         int             namelen, padsz;
 388         int             shortlen = 0;
 389         int             rc, starting_offset;
 390         uint32_t        next_entry_offset;
 391         uint32_t        mb_flags = SMB_MSGBUF_UNICODE;
 392         uint32_t        resume_key;
 393 
 394         namelen = smb_wcequiv_strlen(fileinfo->fi_name);
 395         if (namelen == -1)
 396                 return (NT_STATUS_INTERNAL_ERROR);
 397 
 398         /*
 399          * Keep track of where the last entry starts so we can
 400          * come back and poke the NextEntryOffset field.  Also,
 401          * after enumeration finishes, the caller uses this to
 402          * poke the last entry again with zero to mark it as
 403          * the end of the enumeration.
 404          */
 405         starting_offset = sr->raw_data.chain_offset;
 406 
 407         /*


 507                     &sr->raw_data, "llTTTTqqlllb.24c..q",
 508                     0,  /* NextEntryOffset (set later) */
 509                     resume_key,
 510                     &fileinfo->fi_crtime,
 511                     &fileinfo->fi_atime,
 512                     &fileinfo->fi_mtime,
 513                     &fileinfo->fi_ctime,
 514                     fileinfo->fi_size,               /* q */
 515                     fileinfo->fi_alloc_size, /* q */
 516                     fileinfo->fi_dosattr,    /* l */
 517                     namelen,                    /* l */
 518                     0L,         /* EaSize          l */
 519                     shortlen,                   /* b. */
 520                     buf83,                      /* 24c */
 521                     /* reserved                    .. */
 522                     fileinfo->fi_nodeid);    /* q */
 523 
 524                 smb_msgbuf_term(&mb);
 525                 break;
 526 









































 527         /* See also: SMB_FIND_FILE_NAMES_INFO */
 528         case FileNamesInformation:              /* 12 */
 529                 rc = smb_mbc_encodef(
 530                     &sr->raw_data, "lll",
 531                     0,  /* NextEntryOffset (set later) */
 532                     resume_key,
 533                     namelen);
 534                 break;
 535 
 536         default:
 537                 return (NT_STATUS_INVALID_INFO_CLASS);
 538         }
 539         if (rc) /* smb_mbc_encodef failed */
 540                 return (NT_STATUS_BUFFER_OVERFLOW);
 541 
 542         /*
 543          * At this point we have written all the fixed-size data
 544          * for the specified info. class.  Now put the name and
 545          * alignment padding, and then patch the NextEntryOffset.
 546          * Also store this offset for the caller so they can
 547          * patch this (again) to zero on the very last entry.
 548          */
 549         rc = smb_mbc_encodef(
 550             &sr->raw_data, "U",
 551             fileinfo->fi_name);
 552         if (rc)
 553                 return (NT_STATUS_BUFFER_OVERFLOW);
 554 
 555         /* Next entry needs to be 8-byte aligned. */
 556         padsz = sr->raw_data.chain_offset & 7;
 557         if (padsz) {
 558                 padsz = 8 - padsz;
 559                 (void) smb_mbc_encodef(&sr->raw_data, "#.", padsz);
 560         }
 561         next_entry_offset = sr->raw_data.chain_offset -      starting_offset;
 562         (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
 563             next_entry_offset);
 564         args->fa_last_entry = starting_offset;
 565 
 566         return (0);
 567 }


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 /*
  29  * Dispatch function for SMB2_QUERY_DIRECTORY
  30  *
  31  * Similar to smb_trans2_find.c (from SMB1)
  32  */
  33 
  34 #include <smbsrv/smb2_kproto.h>
  35 #include <smbsrv/smb2_aapl.h>
  36 
  37 /*
  38  * Internally defined info. level for MacOS support.
  39  * Make sure this does not conflict with real values in
  40  * FILE_INFORMATION_CLASS, and that it fits in 8-bits.
  41  */
  42 #define FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
  43 
  44 /*
  45  * Args (and other state) that we carry around among the
  46  * various functions involved in SMB2 Query Directory.
  47  */
  48 typedef struct smb2_find_args {
  49         uint32_t fa_maxdata;
  50         uint8_t fa_infoclass;
  51         uint8_t fa_fflags;
  52         uint16_t fa_maxcount;
  53         uint16_t fa_eos;        /* End Of Search */
  54         uint16_t fa_fixedsize;  /* size of fixed part of a returned entry */
  55         uint32_t fa_lastkey;    /* Last resume key */
  56         int fa_last_entry;      /* offset of last entry */
  57 
  58         /* Normal info, per dir. entry */
  59         smb_fileinfo_t fa_fi;
  60 
  61         /* MacOS AAPL extension stuff. */
  62         smb_macinfo_t fa_mi;
  63 } smb2_find_args_t;
  64 
  65 static uint32_t smb2_find_entries(smb_request_t *,
  66     smb_odir_t *, smb2_find_args_t *);
  67 static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);

  68 
  69 /*
  70  * Tunable parameter to limit the maximum
  71  * number of entries to be returned.
  72  */
  73 uint16_t smb2_find_max = 128;
  74 
  75 smb_sdrc_t
  76 smb2_query_dir(smb_request_t *sr)
  77 {
  78         smb2_find_args_t args;
  79         smb_odir_resume_t odir_resume;
  80         smb_ofile_t *of = NULL;
  81         smb_odir_t *od = NULL;
  82         char *pattern = NULL;
  83         uint16_t StructSize;
  84         uint32_t FileIndex;
  85         uint16_t NameOffset;
  86         uint16_t NameLength;
  87         smb2fid_t smb2fid;


  95         bzero(&odir_resume, sizeof (odir_resume));
  96 
  97         /*
  98          * SMB2 Query Directory request
  99          */
 100         rc = smb_mbc_decodef(
 101             &sr->smb_data, "wbblqqwwl",
 102             &StructSize,            /* w */
 103             &args.fa_infoclass,             /* b */
 104             &args.fa_fflags,                /* b */
 105             &FileIndex,                     /* l */
 106             &smb2fid.persistent,    /* q */
 107             &smb2fid.temporal,              /* q */
 108             &NameOffset,            /* w */
 109             &NameLength,            /* w */
 110             &args.fa_maxdata);              /* l */
 111         if (rc || StructSize != 33)
 112                 return (SDRC_ERROR);
 113 
 114         status = smb2sr_lookup_fid(sr, &smb2fid);
 115         of = sr->fid_ofile;
 116 
 117         DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
 118 
 119         if (status)
 120                 goto errout;

 121 
 122         /*
 123          * If there's an input buffer (search pattern), decode it.
 124          * Two times MAXNAMELEN because it represents the UNICODE string
 125          * length in bytes.
 126          */
 127         if (NameLength >= (2 * MAXNAMELEN)) {
 128                 status = NT_STATUS_OBJECT_PATH_INVALID;
 129                 goto errout;
 130         }
 131         if (NameLength != 0) {
 132                 /*
 133                  * We're normally positioned at the pattern now,
 134                  * but there could be some padding before it.
 135                  */
 136                 skip = (sr->smb2_cmd_hdr + NameOffset) -
 137                     sr->smb_data.chain_offset;
 138                 if (skip < 0) {
 139                         status = NT_STATUS_OBJECT_PATH_INVALID;
 140                         goto errout;
 141                 }
 142                 if (skip > 0)
 143                         (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
 144                 rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
 145                     NameLength, &pattern);
 146                 if (rc || pattern == NULL) {
 147                         status = NT_STATUS_OBJECT_PATH_INVALID;
 148                         goto errout;
 149                 }
 150         } else
 151                 pattern = "*";
 152 
 153         /*
 154          * Setup the output buffer.
 155          */
 156         if (args.fa_maxdata > smb2_max_trans)
 157                 args.fa_maxdata = smb2_max_trans;
 158         sr->raw_data.max_bytes = args.fa_maxdata;
 159 
 160         /*
 161          * Get the fixed size of entries we will return, which
 162          * lets us estimate the number of entries we'll need.


 163          *
 164          * Also use this opportunity to validate fa_infoclass.
 165          */
 166 
 167         switch (args.fa_infoclass) {
 168         case FileDirectoryInformation:          /* 1 */
 169                 args.fa_fixedsize = 64;
 170                 break;
 171         case FileFullDirectoryInformation:      /* 2 */
 172                 args.fa_fixedsize = 68;
 173                 break;
 174         case FileBothDirectoryInformation:      /* 3 */
 175                 args.fa_fixedsize = 94;
 176                 break;
 177         case FileNamesInformation:              /* 12 */
 178                 args.fa_fixedsize = 12;
 179                 break;
 180         case FileIdBothDirectoryInformation:    /* 37 */
 181                 args.fa_fixedsize = 96;
 182                 break;
 183         case FileIdFullDirectoryInformation:    /* 38 */
 184                 args.fa_fixedsize = 84;
 185                 break;
 186         default:
 187                 status = NT_STATUS_INVALID_INFO_CLASS;
 188                 goto errout;
 189         }
 190 
 191         /*
 192          * MacOS, when using the AAPL CreateContext extensions
 193          * and the "read dir attr" feature, uses a non-standard
 194          * information format for directory entries.  Internally
 195          * we'll use a fake info level to represent this case.
 196          * (Wish they had just defined a new info level.)
 197          */
 198         if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
 199             args.fa_infoclass == FileIdBothDirectoryInformation) {
 200                 args.fa_infoclass = FileIdMacOsDirectoryInformation;
 201                 args.fa_fixedsize = 96; /* yes, same size */
 202         }
 203 
 204         args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
 205         if (args.fa_maxcount == 0)
 206                 args.fa_maxcount = 1;
 207         if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
 208                 args.fa_maxcount = smb2_find_max;
 209         if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
 210                 args.fa_maxcount = 1;
 211 
 212         /*
 213          * If this ofile does not have an odir yet, get one.
 214          */
 215         mutex_enter(&of->f_mutex);
 216         if ((od = of->f_odir) == NULL) {
 217                 status = smb_odir_openfh(sr, pattern, sattr, &od);
 218                 of->f_odir = od;
 219         }
 220         mutex_exit(&of->f_mutex);
 221         if (od == NULL) {
 222                 if (status == 0)
 223                         status = NT_STATUS_INTERNAL_ERROR;


 236          */
 237         if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
 238                 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 239                 odir_resume.or_cookie = 0;
 240         } else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
 241                 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 242                 odir_resume.or_cookie = FileIndex;
 243         } else {
 244                 odir_resume.or_type = SMB_ODIR_RESUME_CONT;
 245         }
 246         smb_odir_resume_at(od, &odir_resume);
 247         of->f_seek_pos = od->d_offset;
 248 
 249         /*
 250          * The real work of readdir and format conversion.
 251          */
 252         status = smb2_find_entries(sr, od, &args);
 253 
 254         of->f_seek_pos = od->d_offset;
 255 
 256         if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
 257             status == NT_STATUS_NO_MORE_FILES) {
 258                 status = NT_STATUS_NO_SUCH_FILE;

 259         }
 260 
 261 errout:
 262         sr->smb2_status = status;
 263         DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
 264 
 265         /*
 266          * Note: NT_STATUS_NO_MORE_FILES is a warning
 267          * used to tell the client that this data return
 268          * is the last of the enumeration.  Returning this
 269          * warning now (with the data) saves the client a
 270          * round trip that would otherwise be needed to
 271          * find out it's at the end.
 272          */
 273         if (status != 0 &&
 274             status != NT_STATUS_NO_MORE_FILES) {
 275                 smb2sr_put_error(sr, status);
 276                 return (SDRC_SUCCESS);
 277         }


 278 
 279         /*
 280          * SMB2 Query Directory reply
 281          */
 282         StructSize = 9;
 283         DataOff = SMB2_HDR_SIZE + 8;
 284         DataLen = MBC_LENGTH(&sr->raw_data);
 285         rc = smb_mbc_encodef(
 286             &sr->reply, "wwlC",
 287             StructSize,         /* w */
 288             DataOff,            /* w */
 289             DataLen,            /* l */
 290             &sr->raw_data);      /* C */
 291         if (DataLen == 0)
 292                 (void) smb_mbc_encodef(&sr->reply, ".");



 293 
 294         if (rc)
 295                 sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
 296 
 297         return (SDRC_SUCCESS);
 298 }
 299 
 300 /*
 301  * smb2_find_entries
 302  *
 303  * Find and encode up to args->fa_maxcount directory entries.
 304  *
 305  * Returns:
 306  *   NT status
 307  */
 308 static uint32_t
 309 smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
 310 {

 311         smb_odir_resume_t odir_resume;
 312         char            *tbuf = NULL;
 313         size_t          tbuflen = 0;
 314         uint16_t        count;
 315         uint16_t        minsize;
 316         uint32_t        status = 0;
 317         int             rc = -1;
 318 
 319         /*
 320          * Let's stop when the remaining space will not hold a
 321          * minimum size entry.  That's the fixed part plus the
 322          * storage size of a 1 char unicode string.
 323          */
 324         minsize = args->fa_fixedsize + 2;
 325 
 326         /*
 327          * FileIdMacOsDirectoryInformation needs some buffer space
 328          * for composing directory entry + stream name for lookup.
 329          * Get the buffer now to avoid alloc/free per entry.
 330          */
 331         if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
 332                 tbuflen = 2 * MAXNAMELEN;
 333                 tbuf = kmem_alloc(tbuflen, KM_SLEEP);
 334         }
 335 
 336         count = 0;
 337         while (count < args->fa_maxcount) {
 338 
 339                 if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
 340                         status = NT_STATUS_BUFFER_OVERFLOW;
 341                         break;
 342                 }
 343 
 344                 rc = smb_odir_read_fileinfo(sr, od,
 345                     &args->fa_fi, &args->fa_eos);
 346                 if (rc == ENOENT) {
 347                         status = NT_STATUS_NO_MORE_FILES;
 348                         break;
 349                 }
 350                 if (rc != 0) {
 351                         status = smb_errno2status(rc);
 352                         break;
 353                 }
 354                 if (args->fa_eos != 0) {
 355                         /* The readdir call hit the end. */
 356                         status = NT_STATUS_NO_MORE_FILES;
 357                         break;
 358                 }
 359 
 360                 if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
 361                         (void) smb2_aapl_get_macinfo(sr, od,
 362                             &args->fa_fi, &args->fa_mi, tbuf, tbuflen);
 363 
 364                 if (smb2_aapl_use_file_ids == 0 &&
 365                     (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
 366                         args->fa_fi.fi_nodeid = 0;
 367 
 368                 status = smb2_find_mbc_encode(sr, args);
 369                 if (status) {
 370                         /*
 371                          * We read a directory entry but failed to
 372                          * copy it into the output buffer.  Rewind
 373                          * the directory pointer so this will be
 374                          * the first entry read next time.
 375                          */
 376                         bzero(&odir_resume, sizeof (odir_resume));
 377                         odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 378                         odir_resume.or_cookie = args->fa_lastkey;
 379                         smb_odir_resume_at(od, &odir_resume);
 380                         break;
 381                 }
 382 
 383                 /*
 384                  * Save the offset of the next entry we'll read.
 385                  * If we fail copying, we'll need this offset.
 386                  */
 387                 args->fa_lastkey = args->fa_fi.fi_cookie;
 388                 ++count;
 389         }
 390 
 391         if (count == 0) {
 392                 ASSERT(status != 0);
 393         } else {
 394                 /*
 395                  * We copied some directory entries, but stopped for
 396                  * NT_STATUS_NO_MORE_FILES, or something.
 397                  *
 398                  * Per [MS-FSCC] sec. 2.4, the last entry in the
 399                  * enumeration MUST have its NextEntryOffset value
 400                  * set to zero.  Overwrite that in the last entry.
 401                  */
 402                 (void) smb_mbc_poke(&sr->raw_data,
 403                     args->fa_last_entry, "l", 0);
 404                 status = 0;
 405         }
 406 
 407         if (tbuf != NULL)
 408                 kmem_free(tbuf, tbuflen);
 409 
 410         return (status);
 411 }
 412 
 413 /*
 414  * smb2_mbc_encode
 415  *
 416  * This function encodes the mbc for one directory entry.
 417  *
 418  * The function returns -1 when the max data requested by client
 419  * is reached. If the entry is valid and successful encoded, 0
 420  * will be returned; otherwise, 1 will be returned.
 421  *
 422  * We always null terminate the filename. The space for the null
 423  * is included in the maxdata calculation and is therefore included
 424  * in the next_entry_offset. namelen is the unterminated length of
 425  * the filename. For levels except STANDARD and EA_SIZE, if the
 426  * filename is ascii the name length returned to the client should
 427  * include the null terminator. Otherwise the length returned to
 428  * the client should not include the terminator.
 429  *
 430  * Returns: 0 - data successfully encoded
 431  *      NT status
 432  */
 433 static uint32_t
 434 smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)

 435 {
 436         smb_fileinfo_t  *fileinfo = &args->fa_fi;
 437         smb_macinfo_t   *macinfo = &args->fa_mi;
 438         uint8_t         buf83[26];
 439         smb_msgbuf_t    mb;
 440         int             namelen;
 441         int             shortlen = 0;
 442         int             rc, starting_offset;
 443         uint32_t        next_entry_offset;
 444         uint32_t        mb_flags = SMB_MSGBUF_UNICODE;
 445         uint32_t        resume_key;
 446 
 447         namelen = smb_wcequiv_strlen(fileinfo->fi_name);
 448         if (namelen == -1)
 449                 return (NT_STATUS_INTERNAL_ERROR);
 450 
 451         /*
 452          * Keep track of where the last entry starts so we can
 453          * come back and poke the NextEntryOffset field.  Also,
 454          * after enumeration finishes, the caller uses this to
 455          * poke the last entry again with zero to mark it as
 456          * the end of the enumeration.
 457          */
 458         starting_offset = sr->raw_data.chain_offset;
 459 
 460         /*


 560                     &sr->raw_data, "llTTTTqqlllb.24c..q",
 561                     0,  /* NextEntryOffset (set later) */
 562                     resume_key,
 563                     &fileinfo->fi_crtime,
 564                     &fileinfo->fi_atime,
 565                     &fileinfo->fi_mtime,
 566                     &fileinfo->fi_ctime,
 567                     fileinfo->fi_size,               /* q */
 568                     fileinfo->fi_alloc_size, /* q */
 569                     fileinfo->fi_dosattr,    /* l */
 570                     namelen,                    /* l */
 571                     0L,         /* EaSize          l */
 572                     shortlen,                   /* b. */
 573                     buf83,                      /* 24c */
 574                     /* reserved                    .. */
 575                     fileinfo->fi_nodeid);    /* q */
 576 
 577                 smb_msgbuf_term(&mb);
 578                 break;
 579 
 580         /*
 581          * MacOS, when using the AAPL extensions (see smb2_create)
 582          * uses modified directory listing responses where the
 583          * "EA size" field is replaced with "maximum access".
 584          * This avoids the need for MacOS Finder to come back
 585          * N times to get the maximum access for every file.
 586          */
 587         case FileIdMacOsDirectoryInformation:
 588                 rc = smb_mbc_encodef(
 589                     &sr->raw_data, "llTTTTqqll",
 590                     0,  /* NextEntryOffset (set later) */
 591                     resume_key,         /* a.k.a. file index */
 592                     &fileinfo->fi_crtime,
 593                     &fileinfo->fi_atime,
 594                     &fileinfo->fi_mtime,
 595                     &fileinfo->fi_ctime,
 596                     fileinfo->fi_size,               /* q */
 597                     fileinfo->fi_alloc_size, /* q */
 598                     fileinfo->fi_dosattr,    /* l */
 599                     namelen);                   /* l */
 600                 if (rc != 0)
 601                         break;
 602                 /*
 603                  * This where FileIdMacOsDirectoryInformation
 604                  * differs from FileIdBothDirectoryInformation
 605                  * Instead of: EaSize, ShortNameLen, ShortName;
 606                  * MacOS wants: MaxAccess, ResourceForkSize, and
 607                  * 16 bytes of "compressed finder info".
 608                  * mi_rforksize + mi_finderinfo falls where
 609                  * the 24 byte shortname would normally be.
 610                  */
 611                 rc = smb_mbc_encodef(
 612                     &sr->raw_data, "l..q16cwq",
 613                     macinfo->mi_maxaccess,   /* l */
 614                     /* short_name_len, reserved  (..) */
 615                     macinfo->mi_rforksize,   /* q */
 616                     macinfo->mi_finderinfo,  /* 16c */
 617                     macinfo->mi_unixmode,    /* w */
 618                     fileinfo->fi_nodeid);    /* q */
 619                 break;
 620 
 621         /* See also: SMB_FIND_FILE_NAMES_INFO */
 622         case FileNamesInformation:              /* 12 */
 623                 rc = smb_mbc_encodef(
 624                     &sr->raw_data, "lll",
 625                     0,  /* NextEntryOffset (set later) */
 626                     resume_key,
 627                     namelen);
 628                 break;
 629 
 630         default:
 631                 return (NT_STATUS_INVALID_INFO_CLASS);
 632         }
 633         if (rc) /* smb_mbc_encodef failed */
 634                 return (NT_STATUS_BUFFER_OVERFLOW);
 635 
 636         /*
 637          * At this point we have written all the fixed-size data
 638          * for the specified info. class.  Now put the name and
 639          * alignment padding, and then patch the NextEntryOffset.
 640          * Also store this offset for the caller so they can
 641          * patch this (again) to zero on the very last entry.
 642          */
 643         rc = smb_mbc_encodef(
 644             &sr->raw_data, "U",
 645             fileinfo->fi_name);
 646         if (rc)
 647                 return (NT_STATUS_BUFFER_OVERFLOW);
 648 
 649         /* Next entry needs to be 8-byte aligned. */
 650         (void) smb_mbc_put_align(&sr->raw_data, 8);
 651 



 652         next_entry_offset = sr->raw_data.chain_offset -      starting_offset;
 653         (void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
 654             next_entry_offset);
 655         args->fa_last_entry = starting_offset;
 656 
 657         return (0);
 658 }