1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   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 2015 Nexenta Systems, Inc.  All rights reserved.
  26  */
  27 
  28 
  29 /*
  30  * This module provides functions for TRANS2_FIND_FIRST2 and
  31  * TRANS2_FIND_NEXT2 requests. The requests allow the client to search
  32  * for the file(s) which match the file specification.  The search is
  33  * started with TRANS2_FIND_FIRST2 and can be continued if necessary with
  34  * TRANS2_FIND_NEXT2. There are numerous levels of information which may be
  35  * obtained for the returned files, the desired level is specified in the
  36  * InformationLevel field of the requests.
  37  *
  38  *  InformationLevel Name              Value
  39  *  =================================  ================
  40  *
  41  *  SMB_INFO_STANDARD                  1
  42  *  SMB_INFO_QUERY_EA_SIZE             2
  43  *  SMB_INFO_QUERY_EAS_FROM_LIST       3
  44  *  SMB_FIND_FILE_DIRECTORY_INFO       0x101
  45  *  SMB_FIND_FILE_FULL_DIRECTORY_INFO  0x102
  46  *  SMB_FIND_FILE_NAMES_INFO           0x103
  47  *  SMB_FIND_FILE_BOTH_DIRECTORY_INFO  0x104
  48  *  SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO  0x105
  49  *  SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO  0x106
  50  *
  51  * The following sections detail the data returned for each
  52  * InformationLevel. The requested information is placed in the Data
  53  * portion of the transaction response. Note: a client which does not
  54  * support long names can only request SMB_INFO_STANDARD.
  55  *
  56  * A four-byte resume key precedes each data item (described below) if bit
  57  * 2 in the Flags field is set, i.e. if the request indicates the server
  58  * should return resume keys. Note: it is not always the case. If the
  59  * data item already includes the resume key, the resume key should not be
  60  * added again.
  61  *
  62  * 4.3.4.1   SMB_INFO_STANDARD
  63  *
  64  *  Response Field                    Description
  65  *  ================================  ==================================
  66  *
  67  *  SMB_DATE CreationDate;            Date when file was created
  68  *  SMB_TIME CreationTime;            Time when file was created
  69  *  SMB_DATE LastAccessDate;          Date of last file access
  70  *  SMB_TIME LastAccessTime;          Time of last file access
  71  *  SMB_DATE LastWriteDate;           Date of last write to the file
  72  *  SMB_TIME LastWriteTime;           Time of last write to the file
  73  *  ULONG  DataSize;                  File Size
  74  *  ULONG AllocationSize;             Size of filesystem allocation unit
  75  *  USHORT Attributes;                File Attributes
  76  *  UCHAR FileNameLength;             Length of filename in bytes
  77  *  STRING FileName;                  Name of found file
  78  *
  79  * 4.3.4.2   SMB_INFO_QUERY_EA_SIZE
  80  *
  81  *  Response Field                     Description
  82  *  =================================  ==================================
  83  *
  84  *   SMB_DATE CreationDate;            Date when file was created
  85  *   SMB_TIME CreationTime;            Time when file was created
  86  *   SMB_DATE LastAccessDate;          Date of last file access
  87  *   SMB_TIME LastAccessTime;          Time of last file access
  88  *   SMB_DATE LastWriteDate;           Date of last write to the file
  89  *   SMB_TIME LastWriteTime;           Time of last write to the file
  90  *   ULONG DataSize;                   File Size
  91  *   ULONG AllocationSize;             Size of filesystem allocation unit
  92  *   USHORT Attributes;                File Attributes
  93  *   ULONG EaSize;                     Size of file's EA information
  94  *   UCHAR FileNameLength;             Length of filename in bytes
  95  *   STRING FileName;                  Name of found file
  96  *
  97  * 4.3.4.3   SMB_INFO_QUERY_EAS_FROM_LIST
  98  *
  99  * This request returns the same information as SMB_INFO_QUERY_EA_SIZE, but
 100  * only for files which have an EA list which match the EA information in
 101  * the Data part of the request.
 102  *
 103  * 4.3.4.4   SMB_FIND_FILE_DIRECTORY_INFO
 104  *
 105  *  Response Field                     Description
 106  *  =================================  ==================================
 107  *
 108  *  ULONG NextEntryOffset;             Offset from this structure to
 109  *                                      beginning of next one
 110  *  ULONG FileIndex;
 111  *  LARGE_INTEGER CreationTime;        file creation time
 112  *  LARGE_INTEGER LastAccessTime;      last access time
 113  *  LARGE_INTEGER LastWriteTime;       last write time
 114  *  LARGE_INTEGER ChangeTime;          last attribute change time
 115  *  LARGE_INTEGER EndOfFile;           file size
 116  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
 117  *  ULONG ExtFileAttributes;           Extended file attributes
 118  *                                      (see section 3.11)
 119  *  ULONG FileNameLength;              Length of filename in bytes
 120  *  STRING FileName;                   Name of the file
 121  *
 122  * 4.3.4.5   SMB_FIND_FILE_FULL_DIRECTORY_INFO
 123  *
 124  *  Response Field                     Description
 125  *  =================================  ==================================
 126  *
 127  *  ULONG NextEntryOffset;             Offset from this structure to
 128  *                                      beginning of next one
 129  *  ULONG FileIndex;
 130  *  LARGE_INTEGER CreationTime;        file creation time
 131  *  LARGE_INTEGER LastAccessTime;      last access time
 132  *  LARGE_INTEGER LastWriteTime;       last write time
 133  *  LARGE_INTEGER ChangeTime;          last attribute change time
 134  *  LARGE_INTEGER EndOfFile;           file size
 135  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
 136  *  ULONG ExtFileAttributes;           Extended file attributes
 137  *                                      (see section 3.11)
 138  *  ULONG FileNameLength;              Length of filename in bytes
 139  *  ULONG EaSize;                      Size of file's extended attributes
 140  *  STRING FileName;                   Name of the file
 141  *
 142  *
 143  *  SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO
 144  *
 145  *  This is the same as SMB_FIND_FILE_FULL_DIRECTORY_INFO but with
 146  *  FileId inserted after EaSize. FileId is preceded by a 4 byte
 147  *  alignment padding.
 148  *
 149  *  Response Field                     Description
 150  *  =================================  ==================================
 151  *  ...
 152  *  ULONG EaSize;                      Size of file's extended attributes
 153  *  UCHAR Reserved[4]
 154  *  LARGE_INTEGER FileId               Internal file system unique id.
 155  *  STRING FileName;                   Name of the file
 156  *
 157  * 4.3.4.6   SMB_FIND_FILE_BOTH_DIRECTORY_INFO
 158  *
 159  *  Response Field                     Description
 160  *  =================================  ==================================
 161  *
 162  *  ULONG NextEntryOffset;             Offset from this structure to
 163  *                                      beginning of next one
 164  *  ULONG FileIndex;
 165  *  LARGE_INTEGER CreationTime;        file creation time
 166  *  LARGE_INTEGER LastAccessTime;      last access time
 167  *  LARGE_INTEGER LastWriteTime;       last write time
 168  *  LARGE_INTEGER ChangeTime;          last attribute change time
 169  *  LARGE_INTEGER EndOfFile;           file size
 170  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
 171  *  ULONG ExtFileAttributes;           Extended file attributes
 172  *                                      (see section 3.11)
 173  *  ULONG FileNameLength;              Length of FileName in bytes
 174  *  ULONG EaSize;                      Size of file's extended attributes
 175  *  UCHAR ShortNameLength;             Length of file's short name in bytes
 176  *  UCHAR Reserved
 177  *  WCHAR ShortName[12];               File's 8.3 conformant name in Unicode
 178  *  STRING FileName;                   Files full length name
 179  *
 180  *
 181  *  SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO
 182  *
 183  *  This is the same as SMB_FIND_FILE_BOTH_DIRECTORY_INFO but with
 184  *  FileId inserted after ShortName. FileId is preceded by a 2 byte
 185  *  alignment pad.
 186  *
 187  *  Response Field                     Description
 188  *  =================================  ==================================
 189  *  ...
 190  *  WCHAR ShortName[12];               File's 8.3 conformant name in Unicode
 191  *  UCHAR Reserved[2]
 192  *  LARGE_INTEGER FileId               Internal file system unique id.
 193  *  STRING FileName;                   Files full length name
 194  *
 195  * 4.3.4.7   SMB_FIND_FILE_NAMES_INFO
 196  *
 197  *  Response Field                     Description
 198  *  =================================  ==================================
 199  *
 200  *  ULONG NextEntryOffset;             Offset from this structure to
 201  *                                     beginning of next one
 202  *  ULONG FileIndex;
 203  *  ULONG FileNameLength;              Length of FileName in bytes
 204  *  STRING FileName;                   Files full length name
 205  */
 206 
 207 #include <smbsrv/smb_kproto.h>
 208 #include <smbsrv/msgbuf.h>
 209 #include <smbsrv/smb_fsops.h>
 210 
 211 /*
 212  * Args (and other state) that we carry around among the
 213  * various functions involved in FindFirst, FindNext.
 214  */
 215 typedef struct smb_find_args {
 216         uint32_t fa_maxdata;
 217         uint16_t fa_infolev;
 218         uint16_t fa_maxcount;
 219         uint16_t fa_fflag;
 220         uint16_t fa_eos;        /* End Of Search */
 221         uint16_t fa_lno;        /* Last Name Offset */
 222         uint32_t fa_lastkey;    /* Last resume key */
 223         char fa_lastname[MAXNAMELEN]; /* and name */
 224 } smb_find_args_t;
 225 
 226 static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *,
 227     smb_odir_t *, smb_find_args_t *);
 228 static int smb_trans2_find_get_maxdata(smb_request_t *, uint16_t, uint16_t);
 229 static int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *,
 230     smb_fileinfo_t *, smb_find_args_t *);
 231 
 232 /*
 233  * Tunable parameter to limit the maximum
 234  * number of entries to be returned.
 235  */
 236 uint16_t smb_trans2_find_max = 128;
 237 
 238 /*
 239  * smb_com_trans2_find_first2
 240  *
 241  *  Client Request                Value
 242  *  ============================  ==================================
 243  *
 244  *  UCHAR  WordCount              15
 245  *  UCHAR  TotalDataCount         Total size of extended attribute list
 246  *  UCHAR  SetupCount             1
 247  *  UCHAR  Setup[0]               TRANS2_FIND_FIRST2
 248  *
 249  *  Parameter Block Encoding      Description
 250  *  ============================  ==================================
 251  *  USHORT SearchAttributes;
 252  *  USHORT SearchCount;           Maximum number of entries to return
 253  *  USHORT Flags;                 Additional information:
 254  *                                Bit 0 - close search after this request
 255  *                                Bit 1 - close search if end of search
 256  *                                reached
 257  *                                Bit 2 - return resume keys for each
 258  *                                entry found
 259  *                                Bit 3 - continue search from previous
 260  *                                ending place
 261  *                                Bit 4 - find with backup intent
 262  *  USHORT InformationLevel;      See below
 263  *  ULONG SearchStorageType;
 264  *  STRING FileName;              Pattern for the search
 265  *  UCHAR Data[ TotalDataCount ]  FEAList if InformationLevel is
 266  *                                QUERY_EAS_FROM_LIST
 267  *
 268  *  Response Parameter Block      Description
 269  *  ============================  ==================================
 270  *
 271  *  USHORT Sid;                   Search handle
 272  *  USHORT SearchCount;           Number of entries returned
 273  *  USHORT EndOfSearch;           Was last entry returned?
 274  *  USHORT EaErrorOffset;         Offset into EA list if EA error
 275  *  USHORT LastNameOffset;        Offset into data to file name of last
 276  *                                entry, if server needs it to resume
 277  *                                search; else 0
 278  *  UCHAR Data[ TotalDataCount ]  Level dependent info about the matches
 279  *                                found in the search
 280  */
 281 smb_sdrc_t
 282 smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
 283 {
 284         int             count;
 285         uint16_t        sattr;
 286         smb_pathname_t  *pn;
 287         smb_odir_t      *od;
 288         smb_find_args_t args;
 289         uint32_t        status;
 290         uint32_t        odir_flags = 0;
 291 
 292         bzero(&args, sizeof (smb_find_args_t));
 293 
 294         if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
 295                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
 296                     ERRDOS, ERROR_ACCESS_DENIED);
 297                 return (SDRC_ERROR);
 298         }
 299 
 300         pn = &sr->arg.dirop.fqi.fq_path;
 301 
 302         if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr,
 303             &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev,
 304             &pn->pn_path) != 0) {
 305                 return (SDRC_ERROR);
 306         }
 307 
 308         smb_pathname_init(sr, pn, pn->pn_path);
 309         if (!smb_pathname_validate(sr, pn))
 310                 return (-1);
 311 
 312         if (smb_is_stream_name(pn->pn_path)) {
 313                 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 314                     ERRDOS, ERROR_INVALID_NAME);
 315                 return (SDRC_ERROR);
 316         }
 317 
 318         if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) {
 319                 sr->user_cr = smb_user_getprivcred(sr->uid_user);
 320                 odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
 321         }
 322 
 323         args.fa_maxdata =
 324             smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
 325         if (args.fa_maxdata == 0)
 326                 return (SDRC_ERROR);
 327 
 328         status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
 329         if (status != 0) {
 330                 smbsr_error(sr, status, 0, 0);
 331                 return (SDRC_ERROR);
 332         }
 333         if (od == NULL)
 334                 return (SDRC_ERROR);
 335 
 336         count = smb_trans2_find_entries(sr, xa, od, &args);
 337 
 338         if (count == -1) {
 339                 smb_odir_close(od);
 340                 smb_odir_release(od);
 341                 return (SDRC_ERROR);
 342         }
 343 
 344         if (count == 0) {
 345                 smb_odir_close(od);
 346                 smb_odir_release(od);
 347                 smbsr_errno(sr, ENOENT);
 348                 return (SDRC_ERROR);
 349         }
 350 
 351         if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
 352             (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
 353                 smb_odir_close(od);
 354         } /* else leave odir open for trans2_find_next2 */
 355 
 356         (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
 357             od->d_odid,      /* Search ID */
 358             count,      /* Search Count */
 359             args.fa_eos, /* End Of Search */
 360             0,          /* EA Error Offset */
 361             args.fa_lno); /* Last Name Offset */
 362 
 363         smb_odir_release(od);
 364 
 365         return (SDRC_SUCCESS);
 366 }
 367 
 368 /*
 369  * smb_com_trans2_find_next2
 370  *
 371  *  Client Request                     Value
 372  *  ================================== =================================
 373  *
 374  *  WordCount                          15
 375  *  SetupCount                         1
 376  *  Setup[0]                           TRANS2_FIND_NEXT2
 377  *
 378  *  Parameter Block Encoding           Description
 379  *  ================================== =================================
 380  *
 381  *  USHORT Sid;                        Search handle
 382  *  USHORT SearchCount;                Maximum number of entries to
 383  *                                      return
 384  *  USHORT InformationLevel;           Levels described in
 385  *                                      TRANS2_FIND_FIRST2 request
 386  *  ULONG ResumeKey;                   Value returned by previous find2
 387  *                                      call
 388  *  USHORT Flags;                      Additional information: bit set-
 389  *                                      0 - close search after this
 390  *                                      request
 391  *                                      1 - close search if end of search
 392  *                                      reached
 393  *                                      2 - return resume keys for each
 394  *                                      entry found
 395  *                                      3 - resume/continue from previous
 396  *                                      ending place
 397  *                                      4 - find with backup intent
 398  *  STRING FileName;                   Resume file name
 399  *
 400  * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2
 401  * call.  If Bit3 of Flags is set, then FileName may be the NULL string,
 402  * since the search is continued from the previous TRANS2_FIND request.
 403  * Otherwise, FileName must not be more than 256 characters long.
 404  *
 405  *  Response Field                     Description
 406  *  ================================== =================================
 407  *
 408  *  USHORT SearchCount;                Number of entries returned
 409  *  USHORT EndOfSearch;                Was last entry returned?
 410  *  USHORT EaErrorOffset;              Offset into EA list if EA error
 411  *  USHORT LastNameOffset;             Offset into data to file name of
 412  *                                      last entry, if server needs it to
 413  *                                      resume search; else 0
 414  *  UCHAR Data[TotalDataCount]         Level dependent info about the
 415  *                                      matches found in the search
 416  *
 417  *
 418  * The last parameter in the request is a filename, which is a
 419  * null-terminated unicode string.
 420  *
 421  * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr,
 422  *    &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname)
 423  *
 424  * The filename parameter is not currently decoded because we
 425  * expect a 2-byte null but Mac OS 10 clients send a 1-byte null,
 426  * which leads to a decode error.
 427  * Thus, we do not support resume by filename.  We treat a request
 428  * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST.
 429  */
 430 smb_sdrc_t
 431 smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
 432 {
 433         int                     count;
 434         uint16_t                odid;
 435         smb_odir_t              *od;
 436         smb_find_args_t         args;
 437         smb_odir_resume_t       odir_resume;
 438 
 439         bzero(&args, sizeof (args));
 440         bzero(&odir_resume, sizeof (odir_resume));
 441 
 442         if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
 443                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
 444                     ERRDOS, ERROR_ACCESS_DENIED);
 445                 return (SDRC_ERROR);
 446         }
 447 
 448         if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr,
 449             &odid, &args.fa_maxcount, &args.fa_infolev,
 450             &odir_resume.or_cookie, &args.fa_fflag,
 451             &odir_resume.or_fname) != 0) {
 452                 return (SDRC_ERROR);
 453         }
 454 
 455         if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
 456                 sr->user_cr = smb_user_getprivcred(sr->uid_user);
 457 
 458         args.fa_maxdata =
 459             smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
 460         if (args.fa_maxdata == 0)
 461                 return (SDRC_ERROR);
 462 
 463         od = smb_tree_lookup_odir(sr, odid);
 464         if (od == NULL) {
 465                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 466                     ERRDOS, ERROR_INVALID_HANDLE);
 467                 return (SDRC_ERROR);
 468         }
 469 
 470         /*
 471          * Set the correct position in the directory.
 472          *
 473          * "Continue from last" is easy, but due to a history of
 474          * buggy server implementations, most clients don't use
 475          * that method.  The most widely used (and reliable) is
 476          * resume by file name.  Unfortunately, that can't really
 477          * be fully supported unless your file system stores all
 478          * directory entries in some sorted order (like NTFS).
 479          * We can partially support resume by name, where the only
 480          * name we're ever asked to resume on is the same as the
 481          * most recent we returned.  That's always what the client
 482          * gives us as the resume name, so we can simply remember
 483          * the last name/offset pair and use that to position on
 484          * the following FindNext call.  In the unlikely event
 485          * that the client asks to resume somewhere else, we'll
 486          * use the numeric resume key, and hope the client gives
 487          * correctly uses one of the resume keys we provided.
 488          */
 489         if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) {
 490                 odir_resume.or_type = SMB_ODIR_RESUME_CONT;
 491         } else {
 492                 odir_resume.or_type = SMB_ODIR_RESUME_FNAME;
 493         }
 494         smb_odir_resume_at(od, &odir_resume);
 495 
 496         count = smb_trans2_find_entries(sr, xa, od, &args);
 497         if (count == -1) {
 498                 smb_odir_close(od);
 499                 smb_odir_release(od);
 500                 return (SDRC_ERROR);
 501         }
 502 
 503         if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
 504             (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
 505                 smb_odir_close(od);
 506         } /* else leave odir open for trans2_find_next2 */
 507 
 508         smb_odir_release(od);
 509 
 510         (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
 511             count,      /* Search Count */
 512             args.fa_eos, /* End Of Search */
 513             0,          /* EA Error Offset */
 514             args.fa_lno); /* Last Name Offset */
 515 
 516         return (SDRC_SUCCESS);
 517 }
 518 
 519 
 520 /*
 521  * smb_trans2_find_entries
 522  *
 523  * Find and encode up to args->fa_maxcount directory entries.
 524  * For compatibilty with Windows, if args->fa_maxcount is zero treat it as 1.
 525  *
 526  * Returns:
 527  *   count - count of entries encoded
 528  *           *eos = B_TRUE if no more directory entries
 529  *      -1 - error
 530  */
 531 static int
 532 smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
 533     smb_find_args_t *args)
 534 {
 535         smb_fileinfo_t  fileinfo;
 536         smb_odir_resume_t odir_resume;
 537         uint16_t        count, maxcount;
 538         int             rc = -1;
 539         boolean_t       need_rewind = B_FALSE;
 540 
 541         if ((maxcount = args->fa_maxcount) == 0)
 542                 maxcount = 1;
 543 
 544         if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
 545                 maxcount = smb_trans2_find_max;
 546 
 547         count = 0;
 548         while (count < maxcount) {
 549                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
 550                 if (rc != 0 || args->fa_eos != 0)
 551                         break;
 552 
 553                 rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
 554                 if (rc == -1)
 555                         return (-1); /* fatal encoding error */
 556                 if (rc == 1) {
 557                         need_rewind = B_TRUE;
 558                         break; /* output space exhausted */
 559                 }
 560 
 561                 /*
 562                  * Save the info about the last file returned.
 563                  */
 564                 args->fa_lastkey = fileinfo.fi_cookie;
 565                 bcopy(fileinfo.fi_name, args->fa_lastname, MAXNAMELEN);
 566 
 567                 ++count;
 568         }
 569         if (args->fa_eos != 0 && rc == ENOENT)
 570                 rc = 0;
 571 
 572         /* save the last cookie returned to client */
 573         if (count != 0)
 574                 smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname);
 575 
 576         /*
 577          * If all retrieved entries have been successfully encoded
 578          * and eos has not already been detected, check if there are
 579          * any more entries. eos will be set if there are no more.
 580          */
 581         if ((rc == 0) && (args->fa_eos == 0)) {
 582                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
 583                 /*
 584                  * If rc == ENOENT, we did not read any additional data.
 585                  * if rc != 0, there's no need to rewind.
 586                  */
 587                 if (rc == 0)
 588                         need_rewind = B_TRUE;
 589         }
 590 
 591         /*
 592          * When the last entry we read from the directory did not
 593          * fit in the return buffer, we will have read one entry
 594          * that will not be returned in this call.  That, and the
 595          * check for EOS just above both can leave the directory
 596          * position incorrect for the next call.  Fix that now.
 597          */
 598         if (need_rewind) {
 599                 bzero(&odir_resume, sizeof (odir_resume));
 600                 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
 601                 odir_resume.or_cookie = args->fa_lastkey;
 602                 smb_odir_resume_at(od, &odir_resume);
 603         }
 604 
 605         return (count);
 606 }
 607 
 608 /*
 609  * smb_trans2_find_get_maxdata
 610  *
 611  * Calculate the minimum response space required for the specified
 612  * information level.
 613  *
 614  * A non-zero return value provides the minimum space required.
 615  * A return value of zero indicates an unknown information level.
 616  */
 617 static int
 618 smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
 619 {
 620         int maxdata;
 621 
 622         maxdata = smb_ascii_or_unicode_null_len(sr);
 623 
 624         switch (infolev) {
 625         case SMB_INFO_STANDARD :
 626                 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
 627                         maxdata += sizeof (int32_t);
 628                 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
 629                 break;
 630 
 631         case SMB_INFO_QUERY_EA_SIZE:
 632                 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
 633                         maxdata += sizeof (int32_t);
 634                 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
 635                 break;
 636 
 637         case SMB_FIND_FILE_DIRECTORY_INFO:
 638                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4;
 639                 break;
 640 
 641         case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
 642                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4;
 643                 break;
 644 
 645         case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
 646                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 8;
 647                 break;
 648 
 649         case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
 650                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24;
 651                 break;
 652 
 653         case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
 654                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24
 655                     + 2 + 8;
 656                 break;
 657 
 658         case SMB_FIND_FILE_NAMES_INFO:
 659                 maxdata += 4 + 4 + 4;
 660                 break;
 661 
 662         case SMB_MAC_FIND_BOTH_HFS_INFO:
 663                 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 +
 664                     4 + 32 + 4 + 1 + 1 + 24 + 4;
 665                 break;
 666 
 667         default:
 668                 maxdata = 0;
 669                 smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
 670                     ERRDOS, ERROR_INVALID_LEVEL);
 671         }
 672 
 673         return (maxdata);
 674 }
 675 
 676 /*
 677  * This is an experimental feature that allows us to return zero
 678  * for all numeric resume keys, to match Windows behavior with an
 679  * NTFS share.  Setting this variable to zero does that.
 680  *
 681  * It's possible we could remove this variable and always set
 682  * numeric resume keys to zero, but that would leave us unable
 683  * to handle a FindNext call with an arbitrary start position.
 684  * In practice we never see these, but in theory we could.
 685  *
 686  * See the long comment above smb_com_trans2_find_next2() for
 687  * more details about resume key / resume name handling.
 688  */
 689 int smbd_use_resume_keys = 1;
 690 
 691 /*
 692  * smb_trans2_mbc_encode
 693  *
 694  * This function encodes the mbc for one directory entry.
 695  *
 696  * The function returns -1 when the max data requested by client
 697  * is reached. If the entry is valid and successful encoded, 0
 698  * will be returned; otherwise, 1 will be returned.
 699  *
 700  * We always null terminate the filename. The space for the null
 701  * is included in the maxdata calculation and is therefore included
 702  * in the next_entry_offset. namelen is the unterminated length of
 703  * the filename. For levels except STANDARD and EA_SIZE, if the
 704  * filename is ascii the name length returned to the client should
 705  * include the null terminator. Otherwise the length returned to
 706  * the client should not include the terminator.
 707  *
 708  * Returns: 0 - data successfully encoded
 709  *          1 - client request's maxdata limit reached
 710  *         -1 - error
 711  */
 712 static int
 713 smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
 714     smb_fileinfo_t *fileinfo, smb_find_args_t *args)
 715 {
 716         int             namelen, shortlen;
 717         uint32_t        next_entry_offset;
 718         uint32_t        dsize32, asize32;
 719         uint32_t        mb_flags = 0;
 720         uint32_t        resume_key;
 721         char            buf83[26];
 722         smb_msgbuf_t    mb;
 723 
 724         namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
 725         if (namelen == -1)
 726                 return (-1);
 727 
 728         /*
 729          * If ascii the filename length returned to the client should
 730          * include the null terminator for levels except STANDARD and
 731          * EASIZE.
 732          */
 733         if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
 734                 if ((args->fa_infolev != SMB_INFO_STANDARD) &&
 735                     (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
 736                         namelen += 1;
 737         }
 738 
 739         next_entry_offset = args->fa_maxdata + namelen;
 740 
 741         if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
 742                 return (1);
 743 
 744         mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
 745         dsize32 = (fileinfo->fi_size > UINT_MAX) ?
 746             UINT_MAX : (uint32_t)fileinfo->fi_size;
 747         asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
 748             UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;
 749 
 750         resume_key = fileinfo->fi_cookie;
 751         if (smbd_use_resume_keys == 0)
 752                 resume_key = 0;
 753 
 754         /*
 755          * This switch handles all the "information levels" (formats)
 756          * that we support.  Note that all formats have the file name
 757          * placed after some fixed-size data, and the code to write
 758          * the file name is factored out at the end of this switch.
 759          */
 760         switch (args->fa_infolev) {
 761         case SMB_INFO_STANDARD:
 762                 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
 763                         (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
 764                             resume_key);
 765 
 766                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr,
 767                     smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
 768                     smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
 769                     smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
 770                     dsize32,
 771                     asize32,
 772                     fileinfo->fi_dosattr,
 773                     namelen);
 774                 break;
 775 
 776         case SMB_INFO_QUERY_EA_SIZE:
 777                 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
 778                         (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
 779                             resume_key);
 780 
 781                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr,
 782                     smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
 783                     smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
 784                     smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
 785                     dsize32,
 786                     asize32,
 787                     fileinfo->fi_dosattr,
 788                     0L,         /* EA Size */
 789                     namelen);
 790                 break;
 791 
 792         case SMB_FIND_FILE_DIRECTORY_INFO:
 793                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr,
 794                     next_entry_offset,
 795                     resume_key,
 796                     &fileinfo->fi_crtime,
 797                     &fileinfo->fi_atime,
 798                     &fileinfo->fi_mtime,
 799                     &fileinfo->fi_ctime,
 800                     fileinfo->fi_size,
 801                     fileinfo->fi_alloc_size,
 802                     fileinfo->fi_dosattr,
 803                     namelen);
 804                 break;
 805 
 806         case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
 807                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll", sr,
 808                     next_entry_offset,
 809                     resume_key,
 810                     &fileinfo->fi_crtime,
 811                     &fileinfo->fi_atime,
 812                     &fileinfo->fi_mtime,
 813                     &fileinfo->fi_ctime,
 814                     fileinfo->fi_size,
 815                     fileinfo->fi_alloc_size,
 816                     fileinfo->fi_dosattr,
 817                     namelen,
 818                     0L);
 819                 break;
 820 
 821         case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
 822                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.q", sr,
 823                     next_entry_offset,
 824                     resume_key,
 825                     &fileinfo->fi_crtime,
 826                     &fileinfo->fi_atime,
 827                     &fileinfo->fi_mtime,
 828                     &fileinfo->fi_ctime,
 829                     fileinfo->fi_size,
 830                     fileinfo->fi_alloc_size,
 831                     fileinfo->fi_dosattr,
 832                     namelen,
 833                     0L,
 834                     fileinfo->fi_nodeid);
 835                 break;
 836 
 837         case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
 838                 bzero(buf83, sizeof (buf83));
 839                 smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
 840                     mb_flags);
 841                 if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) {
 842                         smb_msgbuf_term(&mb);
 843                         return (-1);
 844                 }
 845                 shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
 846 
 847                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c",
 848                     sr,
 849                     next_entry_offset,
 850                     resume_key,
 851                     &fileinfo->fi_crtime,
 852                     &fileinfo->fi_atime,
 853                     &fileinfo->fi_mtime,
 854                     &fileinfo->fi_ctime,
 855                     fileinfo->fi_size,
 856                     fileinfo->fi_alloc_size,
 857                     fileinfo->fi_dosattr,
 858                     namelen,
 859                     0L,
 860                     shortlen,
 861                     buf83);
 862 
 863                 smb_msgbuf_term(&mb);
 864                 break;
 865 
 866         case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
 867                 bzero(buf83, sizeof (buf83));
 868                 smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
 869                     mb_flags);
 870                 if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) {
 871                         smb_msgbuf_term(&mb);
 872                         return (-1);
 873                 }
 874                 shortlen = smb_ascii_or_unicode_strlen(sr,
 875                     fileinfo->fi_shortname);
 876 
 877                 (void) smb_mbc_encodef(&xa->rep_data_mb,
 878                     "%llTTTTqqlllb.24c2.q",
 879                     sr,
 880                     next_entry_offset,
 881                     resume_key,
 882                     &fileinfo->fi_crtime,
 883                     &fileinfo->fi_atime,
 884                     &fileinfo->fi_mtime,
 885                     &fileinfo->fi_ctime,
 886                     fileinfo->fi_size,
 887                     fileinfo->fi_alloc_size,
 888                     fileinfo->fi_dosattr,
 889                     namelen,
 890                     0L,
 891                     shortlen,
 892                     buf83,
 893                     fileinfo->fi_nodeid);
 894 
 895                 smb_msgbuf_term(&mb);
 896                 break;
 897 
 898         case SMB_FIND_FILE_NAMES_INFO:
 899                 (void) smb_mbc_encodef(&xa->rep_data_mb, "%lll", sr,
 900                     next_entry_offset,
 901                     resume_key,
 902                     namelen);
 903                 break;
 904 
 905         default:
 906                 /* invalid info. level */
 907                 return (-1);
 908         }
 909 
 910         /*
 911          * At this point we have written all the fixed-size data
 912          * for the specified info. level, and we're about to put
 913          * the file name string in the message.  We may later
 914          * need the offset in the trans2 data where this string
 915          * is placed, so save the message position now.  Note:
 916          * We also need to account for the alignment padding
 917          * that may precede the unicode string.
 918          */
 919         args->fa_lno = xa->rep_data_mb.chain_offset;
 920         if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 &&
 921             (args->fa_lno & 1) != 0)
 922                 args->fa_lno++;
 923 
 924         (void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
 925             fileinfo->fi_name);
 926 
 927         return (0);
 928 }
 929 
 930 /*
 931  * Close a search started by a Trans2FindFirst2 request.
 932  */
 933 smb_sdrc_t
 934 smb_pre_find_close2(smb_request_t *sr)
 935 {
 936         DTRACE_SMB_1(op__FindClose2__start, smb_request_t *, sr);
 937         return (SDRC_SUCCESS);
 938 }
 939 
 940 void
 941 smb_post_find_close2(smb_request_t *sr)
 942 {
 943         DTRACE_SMB_1(op__FindClose2__done, smb_request_t *, sr);
 944 }
 945 
 946 smb_sdrc_t
 947 smb_com_find_close2(smb_request_t *sr)
 948 {
 949         uint16_t        odid;
 950         smb_odir_t      *od;
 951 
 952         if (smbsr_decode_vwv(sr, "w", &odid) != 0)
 953                 return (SDRC_ERROR);
 954 
 955         od = smb_tree_lookup_odir(sr, odid);
 956         if (od == NULL) {
 957                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 958                     ERRDOS, ERROR_INVALID_HANDLE);
 959                 return (SDRC_ERROR);
 960         }
 961 
 962         smb_odir_close(od);
 963         smb_odir_release(od);
 964 
 965         if (smbsr_encode_empty_result(sr))
 966                 return (SDRC_ERROR);
 967 
 968         return (SDRC_SUCCESS);
 969 }