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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 #include <smbsrv/smb_kproto.h>
  27 
  28 /*
  29  * smb_com_search
  30  * smb_com_find, smb_com_find_close
  31  * smb_find_unique
  32  *
  33  * These commands are used for directory searching. They share the same
  34  * message formats, defined below:
  35  *
  36  * Client Request                     Description
  37  * ---------------------------------- ---------------------------------
  38  *
  39  * UCHAR WordCount;                   Count of parameter words = 2
  40  * USHORT MaxCount;                   Number of dir. entries to return
  41  * USHORT SearchAttributes;
  42  * USHORT ByteCount;                  Count of data bytes;  min = 5
  43  * UCHAR BufferFormat1;               0x04 -- ASCII
  44  * UCHAR FileName[];                  File name, may be null
  45  * UCHAR BufferFormat2;               0x05 -- Variable block
  46  * USHORT ResumeKeyLength;            Length of resume key, may be 0
  47  * UCHAR ResumeKey[];                 Resume key
  48  *
  49  * FileName specifies the file to be sought.  SearchAttributes indicates
  50  * the attributes that the file must have.  If  SearchAttributes is
  51  * zero then only normal files are returned.  If the system file, hidden or
  52  * directory attributes are specified then the search is inclusive - both the
  53  * specified type(s) of files and normal files are returned.  If the volume
  54  * label attribute is specified then the search is exclusive, and only the
  55  * volume label entry is returned.
  56  *
  57  * MaxCount specifies the number of directory entries to be returned.
  58  *
  59  * Server Response                    Description
  60  * ---------------------------------- ---------------------------------
  61  *
  62  * UCHAR WordCount;                   Count of parameter words = 1
  63  * USHORT Count;                      Number of entries returned
  64  * USHORT ByteCount;                  Count of data bytes;  min = 3
  65  * UCHAR BufferFormat;                0x05 -- Variable block
  66  * USHORT DataLength;                 Length of data
  67  * UCHAR DirectoryInformationData[];  Data
  68  *
  69  * The response will contain one or more directory entries as determined by
  70  * the Count field.  No more than MaxCount entries will be returned.  Only
  71  * entries that match the sought FileName and SearchAttributes combination
  72  * will be returned.
  73  *
  74  * ResumeKey must be null (length = 0) on the initial search request.
  75  * Subsequent search requests intended to continue a search must contain
  76  * the ResumeKey field extracted from the last directory entry of the
  77  * previous response.  ResumeKey is self-contained, for calls containing
  78  * a non-zero ResumeKey neither the SearchAttributes or FileName fields
  79  * will be valid in the request.  ResumeKey has the following format:
  80  *
  81  * Resume Key Field                   Description
  82  * ---------------------------------- ---------------------------------
  83  *
  84  * UCHAR Reserved;                    bit 7 - consumer use
  85  *                                    bits 5,6 - system use (must preserve)
  86  *                                    bits 0-4 - server use (must preserve)
  87  * UCHAR FileName[11];                Name of the returned file
  88  * UCHAR ReservedForServer[5];        Client must not modify
  89  *                                    byte 0 - uniquely identifies find
  90  *                                    through find_close
  91  *                                    bytes 1-4 - available for server use
  92  *                                    (must be non-zero)
  93  * UCHAR ReservedForConsumer[4];      Server must not modify
  94  *
  95  * FileName is 8.3 format, with the three character extension left
  96  * justified into FileName[9-11].
  97  *
  98  * There may be multiple matching entries in response to a single request
  99  * as wildcards are supported in the last component of FileName of the
 100  * initial request.
 101  *
 102  * Returned directory entries in the DirectoryInformationData field of the
 103  * response each have the following format:
 104  *
 105  * Directory Information Field        Description
 106  * ---------------------------------- ---------------------------------
 107  *
 108  * SMB_RESUME_KEY ResumeKey;          Described above
 109  * UCHAR FileAttributes;              Attributes of the found file
 110  * SMB_TIME LastWriteTime;            Time file was last written
 111  * SMB_DATE LastWriteDate;            Date file was last written
 112  * ULONG FileSize;                    Size of the file
 113  * UCHAR FileName[13];                ASCII, space-filled null terminated
 114  *
 115  * FileName must conform to 8.3 rules, and is padded after the extension
 116  * with 0x20 characters if necessary.
 117  *
 118  * As can be seen from the above structure, these commands cannot return
 119  * long filenames, and cannot return UNICODE filenames.
 120  *
 121  * Files which have a size greater than 2^32 bytes should have the least
 122  * significant 32 bits of their size returned in FileSize.
 123  *
 124  * smb_com_search
 125  * --------------
 126  *
 127  * If the client is prior to the LANMAN1.0 dialect, the returned FileName
 128  * should be uppercased.
 129  * If the client has negotiated a dialect prior to the LANMAN1.0 dialect,
 130  * or if bit0 of the Flags2 SMB header field of the request is clear,
 131  * the returned FileName should be uppercased.
 132  *
 133  * SMB_COM_SEARCH terminates when either the requested maximum number of
 134  * entries that match the named file are found, or the end of directory is
 135  * reached without the maximum number of matches being found.  A response
 136  * containing no entries indicates that no matching entries were found
 137  * between the starting point of the search and the end of directory.
 138  *
 139  *
 140  * The find, find_close and find_unique protocols may be used in place of
 141  * the core "search" protocol when LANMAN 1.0 dialect has been negotiated.
 142  *
 143  * smb_com_find
 144  * ------------
 145  *
 146  * The find protocol is used to match the find OS/2 system call.
 147  *
 148  * The format of the find protocol is the same as the core "search" protocol.
 149  * The difference is that the directory is logically Opened with a find protocol
 150  * and logically closed with the find close protocol.
 151  * As is true of a failing open, if a find request (find "first" request where
 152  * resume_key is null) fails (no entries are found), no find close protocol is
 153  * expected.
 154  *
 155  * If no global characters are present, a "find unique" protocol should be used
 156  * (only one entry is expected and find close need not be sent).
 157  *
 158  * A find request will terminate when either the requested maximum number of
 159  * entries that match the named file are found, or the end of directory is
 160  * reached without the maximum number of matches being found. A response
 161  * containing no entries indicates that no matching entries were found between
 162  * the starting point of the search and the end of directory.
 163  *
 164  * If a find requests more data than can be placed in a message of the
 165  * max-xmit-size for the TID specified, the server will return only the number
 166  * of entries which will fit.
 167  *
 168  *
 169  * smb_com_find_close
 170  * ------------------
 171  *
 172  * The find close protocol is used to match the find close OS/2 system call.
 173  *
 174  * Whereas the first find protocol logically opens the directory, subsequent
 175  * find  protocols presenting a resume_key further "read" the directory, the
 176  * find close  protocol "closes" the  directory allowing the server to free any
 177  * resources held in support of the directory search.
 178  *
 179  * In our implementation this translates to closing the odir.
 180  *
 181  *
 182  * smb_com_find_unique
 183  * -------------------
 184  *
 185  * The format of the find unique protocol is the same as the core "search"
 186  * protocol. The difference is that the directory is logically opened, any
 187  * matching entries returned, and then the directory is logically closed.
 188  *
 189  * The resume search key key will be returned as in the find protocol and
 190  * search protocol however it may NOT be returned to continue the search.
 191  * Only one buffer of entries is expected and find close need not be sent.
 192  *
 193  * If a find unique requests more data than can be placed in a message of the
 194  * max-xmit-size for the TID specified, the server will abort the virtual
 195  * circuit to the consumer.
 196  */
 197 
 198 #define SMB_NAME83_BUFLEN       12
 199 static void smb_name83(const char *, char *, size_t);
 200 
 201 /* *** smb_com_search *** */
 202 
 203 smb_sdrc_t
 204 smb_pre_search(smb_request_t *sr)
 205 {
 206         DTRACE_SMB_START(op__Search, smb_request_t *, sr);
 207         return (SDRC_SUCCESS);
 208 }
 209 
 210 void
 211 smb_post_search(smb_request_t *sr)
 212 {
 213         DTRACE_SMB_DONE(op__Search, smb_request_t *, sr);
 214 }
 215 
 216 smb_sdrc_t
 217 smb_com_search(smb_request_t *sr)
 218 {
 219         int                     rc;
 220         uint16_t                count, maxcount, index;
 221         uint16_t                sattr, odid;
 222         uint16_t                key_len;
 223         uint32_t                client_key;
 224         char                    name[SMB_SHORTNAMELEN];
 225         char                    name83[SMB_SHORTNAMELEN];
 226         smb_pathname_t          *pn;
 227         unsigned char           resume_char;
 228         unsigned char           type;
 229         boolean_t               find_first, to_upper;
 230         smb_tree_t              *tree;
 231         smb_odir_t              *od;
 232         smb_fileinfo_t          fileinfo;
 233         smb_odir_resume_t       odir_resume;
 234         uint32_t                status;
 235         uint16_t                eos;
 236 
 237         to_upper = B_FALSE;
 238         if ((sr->session->dialect <= LANMAN1_0) ||
 239             ((sr->smb_flg2 & SMB_FLAGS2_KNOWS_LONG_NAMES) == 0)) {
 240                 to_upper = B_TRUE;
 241         }
 242 
 243         /* We only handle 8.3 name here */
 244         sr->smb_flg2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES;
 245         sr->smb_flg &= ~SMB_FLAGS_CASE_INSENSITIVE;
 246 
 247         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 248                 return (SDRC_ERROR);
 249 
 250         pn = &sr->arg.dirop.fqi.fq_path;
 251         rc = smbsr_decode_data(sr, "%Abw", sr, &pn->pn_path, &type, &key_len);
 252         if ((rc != 0) || (type != 0x05))
 253                 return (SDRC_ERROR);
 254 
 255         smb_pathname_init(sr, pn, pn->pn_path);
 256         if (!smb_pathname_validate(sr, pn) ||
 257             smb_is_stream_name(pn->pn_path)) {
 258                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 259                     ERRDOS, ERROR_NO_MORE_FILES);
 260                 return (SDRC_ERROR);
 261         }
 262 
 263         tree = sr->tid_tree;
 264 
 265         /* Volume information only */
 266         if ((sattr == FILE_ATTRIBUTE_VOLUME) && (key_len != 21)) {
 267                 (void) memset(name, ' ', sizeof (name));
 268                 (void) strncpy(name, tree->t_volume, sizeof (name));
 269 
 270                 if (key_len >= 21) {
 271                         (void) smb_mbc_decodef(&sr->smb_data, "17.l",
 272                             &client_key);
 273                 } else {
 274                         client_key = 0;
 275                 }
 276 
 277                 (void) smb_mbc_encodef(&sr->reply, "bwwbwb11c5.lb8.13c",
 278                     1, 0, VAR_BCC, 5, 0, 0, pn->pn_path+1,
 279                     client_key, sattr, name);
 280 
 281                 rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8;
 282                 (void) smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
 283                     1, 1, rc+3, 5, rc);
 284 
 285                 return (SDRC_SUCCESS);
 286         }
 287 
 288         if ((key_len != 0) && (key_len != 21))
 289                 return (SDRC_ERROR);
 290 
 291         find_first = (key_len == 0);
 292         resume_char = 0;
 293         client_key = 0;
 294 
 295         if (find_first) {
 296                 status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
 297                 if (status != 0) {
 298                         if (status == NT_STATUS_ACCESS_DENIED)
 299                                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 300                                     ERRDOS, ERROR_NO_MORE_FILES);
 301                         return (SDRC_ERROR);
 302                 }
 303                 odid = od->d_odid;
 304         } else {
 305                 if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 306                     &resume_char, &index, &odid, &client_key) != 0) {
 307                         return (SDRC_ERROR);
 308                 }
 309                 od = smb_tree_lookup_odir(sr, odid);
 310         }
 311 
 312         if (od == NULL) {
 313                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 314                     ERRDOS, ERROR_INVALID_HANDLE);
 315                 return (SDRC_ERROR);
 316         }
 317 
 318         if (!find_first) {
 319                 if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
 320                         od->d_eof = B_TRUE;
 321                 } else {
 322                         odir_resume.or_type = SMB_ODIR_RESUME_IDX;
 323                         odir_resume.or_idx = index;
 324                         smb_odir_resume_at(od, &odir_resume);
 325                 }
 326         }
 327 
 328         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 329 
 330         rc = 0;
 331         index = 0;
 332         count = 0;
 333         if (maxcount > SMB_MAX_SEARCH)
 334                 maxcount = SMB_MAX_SEARCH;
 335 
 336         while (count < maxcount) {
 337                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 338                 if (rc != 0 || eos != 0)
 339                         break;
 340 
 341                 if (*fileinfo.fi_shortname == '\0') {
 342                         if (smb_needs_mangled(fileinfo.fi_name))
 343                                 continue;
 344                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 345                             SMB_SHORTNAMELEN - 1);
 346                         if (to_upper)
 347                                 (void) smb_strupr(fileinfo.fi_shortname);
 348                 }
 349                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 350 
 351                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 352                     resume_char, name83, index, odid, client_key,
 353                     fileinfo.fi_dosattr & 0xff,
 354                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 355                     (int32_t)fileinfo.fi_size,
 356                     fileinfo.fi_shortname);
 357 
 358                 smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
 359 
 360                 count++;
 361                 index++;
 362         }
 363         if (eos && rc == ENOENT)
 364                 rc = 0;
 365 
 366         if (rc != 0) {
 367                 smb_odir_close(od);
 368                 smb_odir_release(od);
 369                 return (SDRC_ERROR);
 370         }
 371 
 372         if (count == 0 && find_first) {
 373                 smb_odir_close(od);
 374                 smb_odir_release(od);
 375                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 376                     ERRDOS, ERROR_NO_MORE_FILES);
 377                 return (SDRC_ERROR);
 378         }
 379 
 380         rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8;
 381         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
 382             1, count, rc+3, 5, rc) < 0) {
 383                 smb_odir_close(od);
 384                 smb_odir_release(od);
 385                 return (SDRC_ERROR);
 386         }
 387 
 388         smb_odir_release(od);
 389         return (SDRC_SUCCESS);
 390 }
 391 
 392 
 393 /* *** smb_com_find *** */
 394 
 395 smb_sdrc_t
 396 smb_pre_find(smb_request_t *sr)
 397 {
 398         DTRACE_SMB_START(op__Find, smb_request_t *, sr);
 399         return (SDRC_SUCCESS);
 400 }
 401 
 402 void
 403 smb_post_find(smb_request_t *sr)
 404 {
 405         DTRACE_SMB_DONE(op__Find, smb_request_t *, sr);
 406 }
 407 
 408 smb_sdrc_t
 409 smb_com_find(smb_request_t *sr)
 410 {
 411         int                     rc;
 412         uint16_t                count, maxcount, index;
 413         uint16_t                sattr, odid;
 414         uint16_t                key_len;
 415         uint32_t                client_key;
 416         char                    name83[SMB_SHORTNAMELEN];
 417         smb_odir_t              *od;
 418         smb_fileinfo_t          fileinfo;
 419         uint32_t                status;
 420         uint16_t                eos;
 421 
 422         smb_pathname_t          *pn;
 423         unsigned char           resume_char;
 424         unsigned char           type;
 425         boolean_t               find_first = B_TRUE;
 426         smb_odir_resume_t       odir_resume;
 427 
 428         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 429                 return (SDRC_ERROR);
 430 
 431         pn = &sr->arg.dirop.fqi.fq_path;
 432         rc = smbsr_decode_data(sr, "%Abw", sr, &pn->pn_path, &type, &key_len);
 433         if ((rc != 0) || (type != 0x05))
 434                 return (SDRC_ERROR);
 435 
 436         if ((key_len != 0) && (key_len != 21))
 437                 return (SDRC_ERROR);
 438 
 439         smb_pathname_init(sr, pn, pn->pn_path);
 440         if (!smb_pathname_validate(sr, pn))
 441                 return (SDRC_ERROR);
 442 
 443         if (smb_is_stream_name(pn->pn_path)) {
 444                 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 445                     ERRDOS, ERROR_INVALID_NAME);
 446                 return (SDRC_ERROR);
 447         }
 448 
 449         find_first = (key_len == 0);
 450         resume_char = 0;
 451         client_key = 0;
 452 
 453         if (find_first) {
 454                 status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
 455                 if (status != 0) {
 456                         smbsr_error(sr, status, 0, 0);
 457                         return (SDRC_ERROR);
 458                 }
 459                 odid = od->d_odid;
 460         } else {
 461                 if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 462                     &resume_char, &index, &odid, &client_key) != 0) {
 463                         return (SDRC_ERROR);
 464                 }
 465                 od = smb_tree_lookup_odir(sr, odid);
 466         }
 467 
 468         if (od == NULL) {
 469                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 470                     ERRDOS, ERROR_INVALID_HANDLE);
 471                 return (SDRC_ERROR);
 472         }
 473 
 474         if (!find_first) {
 475                 if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
 476                         od->d_eof = B_TRUE;
 477                 } else {
 478                         odir_resume.or_type = SMB_ODIR_RESUME_IDX;
 479                         odir_resume.or_idx = index;
 480                         smb_odir_resume_at(od, &odir_resume);
 481                 }
 482         }
 483 
 484         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 485 
 486         rc = 0;
 487         index = 0;
 488         count = 0;
 489         if (maxcount > SMB_MAX_SEARCH)
 490                 maxcount = SMB_MAX_SEARCH;
 491 
 492         while (count < maxcount) {
 493                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 494                 if (rc != 0 || eos != 0)
 495                         break;
 496 
 497                 if (*fileinfo.fi_shortname == '\0') {
 498                         if (smb_needs_mangled(fileinfo.fi_name))
 499                                 continue;
 500                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 501                             SMB_SHORTNAMELEN - 1);
 502                 }
 503                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 504 
 505                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 506                     resume_char, name83, index, odid, client_key,
 507                     fileinfo.fi_dosattr & 0xff,
 508                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 509                     (int32_t)fileinfo.fi_size,
 510                     fileinfo.fi_shortname);
 511 
 512                 smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
 513 
 514                 count++;
 515                 index++;
 516         }
 517         if (eos && rc == ENOENT)
 518                 rc = 0;
 519 
 520         if (rc != 0) {
 521                 smb_odir_close(od);
 522                 smb_odir_release(od);
 523                 return (SDRC_ERROR);
 524         }
 525 
 526         if (count == 0 && find_first) {
 527                 smb_odir_close(od);
 528                 smb_odir_release(od);
 529                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 530                     ERRDOS, ERROR_NO_MORE_FILES);
 531                 return (SDRC_ERROR);
 532         }
 533 
 534         rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
 535         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
 536             1, count, rc+3, 5, rc) < 0) {
 537                 smb_odir_close(od);
 538                 smb_odir_release(od);
 539                 return (SDRC_ERROR);
 540         }
 541 
 542         smb_odir_release(od);
 543         return (SDRC_SUCCESS);
 544 }
 545 
 546 
 547 /* *** smb_com_find_close *** */
 548 
 549 smb_sdrc_t
 550 smb_pre_find_close(smb_request_t *sr)
 551 {
 552         DTRACE_SMB_START(op__FindClose, smb_request_t *, sr);
 553         return (SDRC_SUCCESS);
 554 }
 555 
 556 void
 557 smb_post_find_close(smb_request_t *sr)
 558 {
 559         DTRACE_SMB_DONE(op__FindClose, smb_request_t *, sr);
 560 }
 561 
 562 smb_sdrc_t
 563 smb_com_find_close(smb_request_t *sr)
 564 {
 565         int             rc;
 566         uint16_t        maxcount, index;
 567         uint16_t        sattr, odid;
 568         uint16_t        key_len;
 569         uint32_t        client_key;
 570         char            *path;
 571         unsigned char   resume_char;
 572         unsigned char   type;
 573         smb_odir_t      *od;
 574 
 575         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 576                 return (SDRC_ERROR);
 577 
 578         rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
 579         if ((rc != 0) || (type != 0x05))
 580                 return (SDRC_ERROR);
 581 
 582         if (key_len == 0) {
 583                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 584                     ERRDOS, ERROR_INVALID_HANDLE);
 585                 return (SDRC_ERROR);
 586         } else if (key_len != 21) {
 587                 return (SDRC_ERROR);
 588         }
 589 
 590         odid = 0;
 591         if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 592             &resume_char, &index, &odid, &client_key) != 0) {
 593                 return (SDRC_ERROR);
 594         }
 595 
 596         od = smb_tree_lookup_odir(sr, odid);
 597         if (od == NULL) {
 598                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 599                     ERRDOS, ERROR_INVALID_HANDLE);
 600                 return (SDRC_ERROR);
 601         }
 602 
 603         smb_odir_close(od);
 604         smb_odir_release(od);
 605 
 606         if (smbsr_encode_result(sr, 1, 3, "bwwbw", 1, 0, 3, 5, 0))
 607                 return (SDRC_ERROR);
 608 
 609         return (SDRC_SUCCESS);
 610 }
 611 
 612 
 613 /* *** smb_com_find_unique *** */
 614 
 615 smb_sdrc_t
 616 smb_pre_find_unique(smb_request_t *sr)
 617 {
 618         DTRACE_SMB_START(op__FindUnique, smb_request_t *, sr);
 619         return (SDRC_SUCCESS);
 620 }
 621 
 622 void
 623 smb_post_find_unique(smb_request_t *sr)
 624 {
 625         DTRACE_SMB_DONE(op__FindUnique, smb_request_t *, sr);
 626 }
 627 
 628 smb_sdrc_t
 629 smb_com_find_unique(struct smb_request *sr)
 630 {
 631         int                     rc;
 632         uint16_t                count, maxcount, index;
 633         uint16_t                sattr;
 634         smb_pathname_t          *pn;
 635         unsigned char           resume_char = '\0';
 636         uint32_t                client_key = 0;
 637         char                    name83[SMB_SHORTNAMELEN];
 638         smb_odir_t              *od;
 639         smb_fileinfo_t          fileinfo;
 640         uint32_t                status;
 641         uint16_t                eos;
 642         smb_vdb_t               *vdb;
 643 
 644         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 645                 return (SDRC_ERROR);
 646 
 647         pn = &sr->arg.dirop.fqi.fq_path;
 648         vdb = kmem_alloc(sizeof (smb_vdb_t), KM_SLEEP);
 649         if ((smbsr_decode_data(sr, "%AV", sr, &pn->pn_path, vdb) != 0) ||
 650             (vdb->vdb_len != 0)) {
 651                 kmem_free(vdb, sizeof (smb_vdb_t));
 652                 return (SDRC_ERROR);
 653         }
 654         kmem_free(vdb, sizeof (smb_vdb_t));
 655 
 656         smb_pathname_init(sr, pn, pn->pn_path);
 657         if (!smb_pathname_validate(sr, pn))
 658                 return (SDRC_ERROR);
 659 
 660         if (smb_is_stream_name(pn->pn_path)) {
 661                 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 662                     ERRDOS, ERROR_INVALID_NAME);
 663                 return (SDRC_ERROR);
 664         }
 665 
 666         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 667 
 668         status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
 669         if (status != 0) {
 670                 smbsr_error(sr, status, 0, 0);
 671                 return (SDRC_ERROR);
 672         }
 673         if (od == NULL)
 674                 return (SDRC_ERROR);
 675 
 676         rc = 0;
 677         count = 0;
 678         index = 0;
 679         if (maxcount > SMB_MAX_SEARCH)
 680                 maxcount = SMB_MAX_SEARCH;
 681 
 682         while (count < maxcount) {
 683                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 684                 if (rc != 0 || eos != 0)
 685                         break;
 686 
 687                 if (*fileinfo.fi_shortname == '\0') {
 688                         if (smb_needs_mangled(fileinfo.fi_name))
 689                                 continue;
 690                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 691                             SMB_SHORTNAMELEN - 1);
 692                 }
 693                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 694 
 695                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 696                     resume_char, name83, index, od->d_odid, client_key,
 697                     fileinfo.fi_dosattr & 0xff,
 698                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 699                     (int32_t)fileinfo.fi_size,
 700                     fileinfo.fi_shortname);
 701 
 702                 count++;
 703                 index++;
 704         }
 705         if (eos && rc == ENOENT)
 706                 rc = 0;
 707 
 708         smb_odir_close(od);
 709         smb_odir_release(od);
 710 
 711         if (rc != 0)
 712                 return (SDRC_ERROR);
 713 
 714         if (count == 0) {
 715                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 716                     ERRDOS, ERROR_NO_MORE_FILES);
 717                 return (SDRC_ERROR);
 718         }
 719 
 720         rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
 721         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset,
 722             "bwwbw", 1, count, rc+3, 5, rc) < 0) {
 723                 return (SDRC_ERROR);
 724         }
 725 
 726         return (SDRC_SUCCESS);
 727 }
 728 
 729 /*
 730  * smb_name83
 731  *
 732  * Format the filename for inclusion in the resume key. The filename
 733  * returned in the resume key is 11 bytes:
 734  * - up to 8 bytes of filename, space padded to 8 bytes
 735  * - up to 3 bytes of ext, space padded to 3 bytes
 736  *
 737  * The name passed to smb_name83 should be a shortname or a name that
 738  * doesn't require mangling.
 739  *
 740  * Examples:
 741  *      "fname.txt"    -> "FNAME   TXT"
 742  *      "fname.tx"     -> "FNAME   TX "
 743  *      "filename"     -> "FILENAME   "
 744  *      "filename.txt" -> "FILENAMETXT"
 745  *      "FILE~1.TXT"   -> "FILE~1  TXT"
 746  */
 747 static void
 748 smb_name83(const char *name, char *buf, size_t buflen)
 749 {
 750         const char *p;
 751         char *pbuf;
 752         int i;
 753 
 754         ASSERT(name && buf && (buflen >= SMB_NAME83_BUFLEN));
 755 
 756         (void) strlcpy(buf, "           ", SMB_NAME83_BUFLEN);
 757 
 758         /* Process "." and ".." up front */
 759         if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
 760                 (void) strncpy(buf, name, strlen(name));
 761                 return;
 762         }
 763 
 764         ASSERT(smb_needs_mangled(name) == B_FALSE);
 765 
 766         /* Process basename */
 767         for (i = 0, p = name, pbuf = buf;
 768             (i < SMB_NAME83_BASELEN) && (*p != '\0') && (*p != '.'); ++i)
 769                 *pbuf++ = *p++;
 770 
 771         /* Process the extension from the last dot in name */
 772         if ((p = strchr(name, '.')) != NULL) {
 773                 ++p;
 774                 pbuf = &buf[SMB_NAME83_BASELEN];
 775                 for (i = 0; (i < SMB_NAME83_EXTLEN) && (*p != '\0'); ++i)
 776                         *pbuf++ = *p++;
 777         }
 778 
 779         (void) smb_strupr(buf);
 780 }