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 2013 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_1(op__Search__start, smb_request_t *, sr);
 207         return (SDRC_SUCCESS);
 208 }
 209 
 210 void
 211 smb_post_search(smb_request_t *sr)
 212 {
 213         DTRACE_SMB_1(op__Search__done, 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         } else {
 304                 if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 305                     &resume_char, &index, &odid, &client_key) != 0) {
 306                         return (SDRC_ERROR);
 307                 }
 308                 od = smb_tree_lookup_odir(sr, odid);
 309         }
 310 
 311         if (od == NULL) {
 312                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 313                     ERRDOS, ERROR_INVALID_HANDLE);
 314                 return (SDRC_ERROR);
 315         }
 316 
 317         if (!find_first) {
 318                 if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
 319                         od->d_eof = B_TRUE;
 320                 } else {
 321                         odir_resume.or_type = SMB_ODIR_RESUME_IDX;
 322                         odir_resume.or_idx = index;
 323                         smb_odir_resume_at(od, &odir_resume);
 324                 }
 325         }
 326 
 327         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 328 
 329         rc = 0;
 330         index = 0;
 331         count = 0;
 332         if (maxcount > SMB_MAX_SEARCH)
 333                 maxcount = SMB_MAX_SEARCH;
 334 
 335         while (count < maxcount) {
 336                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 337                 if (rc != 0 || eos != 0)
 338                         break;
 339 
 340                 if (*fileinfo.fi_shortname == '\0') {
 341                         if (smb_needs_mangled(fileinfo.fi_name))
 342                                 continue;
 343                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 344                             SMB_SHORTNAMELEN - 1);
 345                         if (to_upper)
 346                                 (void) smb_strupr(fileinfo.fi_shortname);
 347                 }
 348                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 349 
 350                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 351                     resume_char, name83, index, odid, client_key,
 352                     fileinfo.fi_dosattr & 0xff,
 353                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 354                     (int32_t)fileinfo.fi_size,
 355                     fileinfo.fi_shortname);
 356 
 357                 smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
 358 
 359                 count++;
 360                 index++;
 361         }
 362         if (eos && rc == ENOENT)
 363                 rc = 0;
 364 
 365         if (rc != 0) {
 366                 smb_odir_close(od);
 367                 smb_odir_release(od);
 368                 return (SDRC_ERROR);
 369         }
 370 
 371         if (count == 0 && find_first) {
 372                 smb_odir_close(od);
 373                 smb_odir_release(od);
 374                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 375                     ERRDOS, ERROR_NO_MORE_FILES);
 376                 return (SDRC_ERROR);
 377         }
 378 
 379         rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8;
 380         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
 381             1, count, rc+3, 5, rc) < 0) {
 382                 smb_odir_close(od);
 383                 smb_odir_release(od);
 384                 return (SDRC_ERROR);
 385         }
 386 
 387         smb_odir_release(od);
 388         return (SDRC_SUCCESS);
 389 }
 390 
 391 
 392 /* *** smb_com_find *** */
 393 
 394 smb_sdrc_t
 395 smb_pre_find(smb_request_t *sr)
 396 {
 397         DTRACE_SMB_1(op__Find__start, smb_request_t *, sr);
 398         return (SDRC_SUCCESS);
 399 }
 400 
 401 void
 402 smb_post_find(smb_request_t *sr)
 403 {
 404         DTRACE_SMB_1(op__Find__done, smb_request_t *, sr);
 405 }
 406 
 407 smb_sdrc_t
 408 smb_com_find(smb_request_t *sr)
 409 {
 410         int                     rc;
 411         uint16_t                count, maxcount, index;
 412         uint16_t                sattr, odid;
 413         uint16_t                key_len;
 414         uint32_t                client_key;
 415         char                    name83[SMB_SHORTNAMELEN];
 416         smb_odir_t              *od;
 417         smb_fileinfo_t          fileinfo;
 418         uint32_t                status;
 419         uint16_t                eos;
 420 
 421         smb_pathname_t          *pn;
 422         unsigned char           resume_char;
 423         unsigned char           type;
 424         boolean_t               find_first = B_TRUE;
 425         smb_odir_resume_t       odir_resume;
 426 
 427         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 428                 return (SDRC_ERROR);
 429 
 430         pn = &sr->arg.dirop.fqi.fq_path;
 431         rc = smbsr_decode_data(sr, "%Abw", sr, &pn->pn_path, &type, &key_len);
 432         if ((rc != 0) || (type != 0x05))
 433                 return (SDRC_ERROR);
 434 
 435         if ((key_len != 0) && (key_len != 21))
 436                 return (SDRC_ERROR);
 437 
 438         smb_pathname_init(sr, pn, pn->pn_path);
 439         if (!smb_pathname_validate(sr, pn))
 440                 return (SDRC_ERROR);
 441 
 442         if (smb_is_stream_name(pn->pn_path)) {
 443                 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 444                     ERRDOS, ERROR_INVALID_NAME);
 445                 return (SDRC_ERROR);
 446         }
 447 
 448         find_first = (key_len == 0);
 449         resume_char = 0;
 450         client_key = 0;
 451 
 452         if (find_first) {
 453                 status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
 454                 if (status != 0) {
 455                         smbsr_error(sr, status, 0, 0);
 456                         return (SDRC_ERROR);
 457                 }
 458         } else {
 459                 if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 460                     &resume_char, &index, &odid, &client_key) != 0) {
 461                         return (SDRC_ERROR);
 462                 }
 463                 od = smb_tree_lookup_odir(sr, odid);
 464         }
 465 
 466         if (od == NULL) {
 467                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 468                     ERRDOS, ERROR_INVALID_HANDLE);
 469                 return (SDRC_ERROR);
 470         }
 471 
 472         if (!find_first) {
 473                 if ((od->d_flags & SMB_ODIR_FLAG_WILDCARDS) == 0) {
 474                         od->d_eof = B_TRUE;
 475                 } else {
 476                         odir_resume.or_type = SMB_ODIR_RESUME_IDX;
 477                         odir_resume.or_idx = index;
 478                         smb_odir_resume_at(od, &odir_resume);
 479                 }
 480         }
 481 
 482         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 483 
 484         rc = 0;
 485         index = 0;
 486         count = 0;
 487         if (maxcount > SMB_MAX_SEARCH)
 488                 maxcount = SMB_MAX_SEARCH;
 489 
 490         while (count < maxcount) {
 491                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 492                 if (rc != 0 || eos != 0)
 493                         break;
 494 
 495                 if (*fileinfo.fi_shortname == '\0') {
 496                         if (smb_needs_mangled(fileinfo.fi_name))
 497                                 continue;
 498                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 499                             SMB_SHORTNAMELEN - 1);
 500                 }
 501                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 502 
 503                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 504                     resume_char, name83, index, odid, client_key,
 505                     fileinfo.fi_dosattr & 0xff,
 506                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 507                     (int32_t)fileinfo.fi_size,
 508                     fileinfo.fi_shortname);
 509 
 510                 smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
 511 
 512                 count++;
 513                 index++;
 514         }
 515         if (eos && rc == ENOENT)
 516                 rc = 0;
 517 
 518         if (rc != 0) {
 519                 smb_odir_close(od);
 520                 smb_odir_release(od);
 521                 return (SDRC_ERROR);
 522         }
 523 
 524         if (count == 0 && find_first) {
 525                 smb_odir_close(od);
 526                 smb_odir_release(od);
 527                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 528                     ERRDOS, ERROR_NO_MORE_FILES);
 529                 return (SDRC_ERROR);
 530         }
 531 
 532         rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
 533         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
 534             1, count, rc+3, 5, rc) < 0) {
 535                 smb_odir_close(od);
 536                 smb_odir_release(od);
 537                 return (SDRC_ERROR);
 538         }
 539 
 540         smb_odir_release(od);
 541         return (SDRC_SUCCESS);
 542 }
 543 
 544 
 545 /* *** smb_com_find_close *** */
 546 
 547 smb_sdrc_t
 548 smb_pre_find_close(smb_request_t *sr)
 549 {
 550         DTRACE_SMB_1(op__FindClose__start, smb_request_t *, sr);
 551         return (SDRC_SUCCESS);
 552 }
 553 
 554 void
 555 smb_post_find_close(smb_request_t *sr)
 556 {
 557         DTRACE_SMB_1(op__FindClose__done, smb_request_t *, sr);
 558 }
 559 
 560 smb_sdrc_t
 561 smb_com_find_close(smb_request_t *sr)
 562 {
 563         int             rc;
 564         uint16_t        maxcount, index;
 565         uint16_t        sattr, odid;
 566         uint16_t        key_len;
 567         uint32_t        client_key;
 568         char            *path;
 569         unsigned char   resume_char;
 570         unsigned char   type;
 571         smb_odir_t      *od;
 572 
 573         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 574                 return (SDRC_ERROR);
 575 
 576         rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
 577         if ((rc != 0) || (type != 0x05))
 578                 return (SDRC_ERROR);
 579 
 580         if (key_len == 0) {
 581                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 582                     ERRDOS, ERROR_INVALID_HANDLE);
 583                 return (SDRC_ERROR);
 584         } else if (key_len != 21) {
 585                 return (SDRC_ERROR);
 586         }
 587 
 588         odid = 0;
 589         if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
 590             &resume_char, &index, &odid, &client_key) != 0) {
 591                 return (SDRC_ERROR);
 592         }
 593 
 594         od = smb_tree_lookup_odir(sr, odid);
 595         if (od == NULL) {
 596                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
 597                     ERRDOS, ERROR_INVALID_HANDLE);
 598                 return (SDRC_ERROR);
 599         }
 600 
 601         smb_odir_close(od);
 602         smb_odir_release(od);
 603 
 604         if (smbsr_encode_result(sr, 1, 3, "bwwbw", 1, 0, 3, 5, 0))
 605                 return (SDRC_ERROR);
 606 
 607         return (SDRC_SUCCESS);
 608 }
 609 
 610 
 611 /* *** smb_com_find_unique *** */
 612 
 613 smb_sdrc_t
 614 smb_pre_find_unique(smb_request_t *sr)
 615 {
 616         DTRACE_SMB_1(op__FindUnique__start, smb_request_t *, sr);
 617         return (SDRC_SUCCESS);
 618 }
 619 
 620 void
 621 smb_post_find_unique(smb_request_t *sr)
 622 {
 623         DTRACE_SMB_1(op__FindUnique__done, smb_request_t *, sr);
 624 }
 625 
 626 smb_sdrc_t
 627 smb_com_find_unique(struct smb_request *sr)
 628 {
 629         int                     rc;
 630         uint16_t                count, maxcount, index;
 631         uint16_t                sattr;
 632         smb_pathname_t          *pn;
 633         unsigned char           resume_char = '\0';
 634         uint32_t                client_key = 0;
 635         char                    name83[SMB_SHORTNAMELEN];
 636         smb_odir_t              *od;
 637         smb_fileinfo_t          fileinfo;
 638         uint32_t                status;
 639         uint16_t                eos;
 640         smb_vdb_t               *vdb;
 641 
 642         if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
 643                 return (SDRC_ERROR);
 644 
 645         pn = &sr->arg.dirop.fqi.fq_path;
 646         vdb = kmem_alloc(sizeof (smb_vdb_t), KM_SLEEP);
 647         if ((smbsr_decode_data(sr, "%AV", sr, &pn->pn_path, vdb) != 0) ||
 648             (vdb->vdb_len != 0)) {
 649                 kmem_free(vdb, sizeof (smb_vdb_t));
 650                 return (SDRC_ERROR);
 651         }
 652         kmem_free(vdb, sizeof (smb_vdb_t));
 653 
 654         smb_pathname_init(sr, pn, pn->pn_path);
 655         if (!smb_pathname_validate(sr, pn))
 656                 return (SDRC_ERROR);
 657 
 658         if (smb_is_stream_name(pn->pn_path)) {
 659                 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
 660                     ERRDOS, ERROR_INVALID_NAME);
 661                 return (SDRC_ERROR);
 662         }
 663 
 664         (void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
 665 
 666         status = smb_odir_openpath(sr, pn->pn_path, sattr, 0, &od);
 667         if (status != 0) {
 668                 smbsr_error(sr, status, 0, 0);
 669                 return (SDRC_ERROR);
 670         }
 671         if (od == NULL)
 672                 return (SDRC_ERROR);
 673 
 674         rc = 0;
 675         count = 0;
 676         index = 0;
 677         if (maxcount > SMB_MAX_SEARCH)
 678                 maxcount = SMB_MAX_SEARCH;
 679 
 680         while (count < maxcount) {
 681                 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
 682                 if (rc != 0 || eos != 0)
 683                         break;
 684 
 685                 if (*fileinfo.fi_shortname == '\0') {
 686                         if (smb_needs_mangled(fileinfo.fi_name))
 687                                 continue;
 688                         (void) strlcpy(fileinfo.fi_shortname, fileinfo.fi_name,
 689                             SMB_SHORTNAMELEN - 1);
 690                 }
 691                 smb_name83(fileinfo.fi_shortname, name83, SMB_SHORTNAMELEN);
 692 
 693                 (void) smb_mbc_encodef(&sr->reply, "b11c.wwlbYl13c",
 694                     resume_char, name83, index, od->d_odid,
 695                     client_key, fileinfo.fi_dosattr & 0xff,
 696                     smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
 697                     (int32_t)fileinfo.fi_size,
 698                     fileinfo.fi_shortname);
 699 
 700                 count++;
 701                 index++;
 702         }
 703         if (eos && rc == ENOENT)
 704                 rc = 0;
 705 
 706         smb_odir_close(od);
 707         smb_odir_release(od);
 708 
 709         if (rc != 0)
 710                 return (SDRC_ERROR);
 711 
 712         if (count == 0) {
 713                 smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
 714                     ERRDOS, ERROR_NO_MORE_FILES);
 715                 return (SDRC_ERROR);
 716         }
 717 
 718         rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
 719         if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset,
 720             "bwwbw", 1, count, rc+3, 5, rc) < 0) {
 721                 return (SDRC_ERROR);
 722         }
 723 
 724         return (SDRC_SUCCESS);
 725 }
 726 
 727 /*
 728  * smb_name83
 729  *
 730  * Format the filename for inclusion in the resume key. The filename
 731  * returned in the resume key is 11 bytes:
 732  * - up to 8 bytes of filename, space padded to 8 bytes
 733  * - up to 3 bytes of ext, space padded to 3 bytes
 734  *
 735  * The name passed to smb_name83 should be a shortname or a name that
 736  * doesn't require mangling.
 737  *
 738  * Examples:
 739  *      "fname.txt"    -> "FNAME   TXT"
 740  *      "fname.tx"     -> "FNAME   TX "
 741  *      "filename"     -> "FILENAME   "
 742  *      "filename.txt" -> "FILENAMETXT"
 743  *      "FILE~1.TXT"   -> "FILE~1  TXT"
 744  */
 745 static void
 746 smb_name83(const char *name, char *buf, size_t buflen)
 747 {
 748         const char *p;
 749         char *pbuf;
 750         int i;
 751 
 752         ASSERT(name && buf && (buflen >= SMB_NAME83_BUFLEN));
 753 
 754         (void) strlcpy(buf, "           ", SMB_NAME83_BUFLEN);
 755 
 756         /* Process "." and ".." up front */
 757         if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
 758                 (void) strncpy(buf, name, strlen(name));
 759                 return;
 760         }
 761 
 762         ASSERT(smb_needs_mangled(name) == B_FALSE);
 763 
 764         /* Process basename */
 765         for (i = 0, p = name, pbuf = buf;
 766             (i < SMB_NAME83_BASELEN) && (*p != '\0') && (*p != '.'); ++i)
 767                 *pbuf++ = *p++;
 768 
 769         /* Process the extension from the last dot in name */
 770         if ((p = strchr(name, '.')) != NULL) {
 771                 ++p;
 772                 pbuf = &buf[SMB_NAME83_BASELEN];
 773                 for (i = 0; (i < SMB_NAME83_EXTLEN) && (*p != '\0'); ++i)
 774                         *pbuf++ = *p++;
 775         }
 776 
 777         (void) smb_strupr(buf);
 778 }