Home | History | Annotate | Download | only in smbsrv
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     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 /* *** smb_com_search *** */
    199 
    200 smb_sdrc_t
    201 smb_pre_search(smb_request_t *sr)
    202 {
    203 	DTRACE_SMB_1(op__Search__start, smb_request_t *, sr);
    204 	return (SDRC_SUCCESS);
    205 }
    206 
    207 void
    208 smb_post_search(smb_request_t *sr)
    209 {
    210 	DTRACE_SMB_1(op__Search__done, smb_request_t *, sr);
    211 }
    212 
    213 smb_sdrc_t
    214 smb_com_search(smb_request_t *sr)
    215 {
    216 	int			rc;
    217 	uint16_t		count, maxcount, index;
    218 	uint16_t		sattr, odid;
    219 	uint16_t		key_len;
    220 	uint32_t		client_key;
    221 	char			name[SMB_SHORTNAMELEN];
    222 	char			*path;
    223 	unsigned char		resume_char;
    224 	unsigned char		type;
    225 	boolean_t		find_first, to_upper;
    226 	smb_tree_t		*tree;
    227 	smb_odir_t		*od;
    228 	smb_fileinfo_t		fileinfo;
    229 	smb_odir_resume_t	odir_resume;
    230 	boolean_t		eos;
    231 
    232 	to_upper = B_FALSE;
    233 	if ((sr->session->dialect <= LANMAN1_0) ||
    234 	    ((sr->smb_flg2 & SMB_FLAGS2_KNOWS_LONG_NAMES) == 0)) {
    235 		to_upper = B_TRUE;
    236 	}
    237 
    238 	/* We only handle 8.3 name here */
    239 	sr->smb_flg2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES;
    240 	sr->smb_flg &= ~SMB_FLAGS_CASE_INSENSITIVE;
    241 
    242 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
    243 		return (SDRC_ERROR);
    244 
    245 	rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
    246 	if ((rc != 0) || (type != 0x05))
    247 		return (SDRC_ERROR);
    248 
    249 	tree = sr->tid_tree;
    250 
    251 	/* Volume information only */
    252 	if ((sattr == FILE_ATTRIBUTE_VOLUME) && (key_len != 21)) {
    253 		(void) memset(name, ' ', sizeof (name));
    254 		(void) strncpy(name, tree->t_volume, sizeof (name));
    255 
    256 		if (key_len >= 21) {
    257 			(void) smb_mbc_decodef(&sr->smb_data, "17.l",
    258 			    &client_key);
    259 		} else {
    260 			client_key = 0;
    261 		}
    262 
    263 		(void) smb_mbc_encodef(&sr->reply, "bwwbwb11c5.lb8.13c",
    264 		    1, 0, VAR_BCC, 5, 0, 0, path+1,
    265 		    client_key, sattr, name);
    266 
    267 		rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8;
    268 		(void) smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
    269 		    1, 1, rc+3, 5, rc);
    270 
    271 		return (SDRC_SUCCESS);
    272 	}
    273 
    274 	if ((key_len != 0) && (key_len != 21))
    275 		return (SDRC_ERROR);
    276 
    277 	find_first = (key_len == 0);
    278 	resume_char = 0;
    279 	client_key = 0;
    280 
    281 	if (find_first) {
    282 		/* NT interprets NULL filename as "\" */
    283 		if (strlen(path) == 0)
    284 			path = "\\";
    285 
    286 		odid = smb_odir_open(sr, path, sattr, 0);
    287 		if (odid == 0) {
    288 			if (sr->smb_error.status == NT_STATUS_ACCESS_DENIED)
    289 				smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
    290 				    ERRDOS, ERROR_NO_MORE_FILES);
    291 			return (SDRC_ERROR);
    292 		}
    293 	} else {
    294 		if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
    295 		    &resume_char, &index, &odid, &client_key) != 0) {
    296 			return (SDRC_ERROR);
    297 		}
    298 	}
    299 
    300 	od = smb_tree_lookup_odir(sr->tid_tree, odid);
    301 	if (od == NULL) {
    302 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
    303 		    ERRDOS, ERROR_INVALID_HANDLE);
    304 		return (SDRC_ERROR);
    305 	}
    306 
    307 	if (!find_first) {
    308 		odir_resume.or_type = SMB_ODIR_RESUME_IDX;
    309 		odir_resume.or_idx = index;
    310 		smb_odir_resume_at(od, &odir_resume);
    311 	}
    312 
    313 	(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
    314 
    315 	rc = 0;
    316 	index = 0;
    317 	count = 0;
    318 	if (maxcount > SMB_MAX_SEARCH)
    319 		maxcount = SMB_MAX_SEARCH;
    320 
    321 	while (count < maxcount) {
    322 		rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
    323 		if ((rc != 0 || (eos == B_TRUE)))
    324 			break;
    325 
    326 		(void) memset(name, ' ', sizeof (name));
    327 		if (*fileinfo.fi_shortname == '\0') {
    328 			(void) strlcpy(name, fileinfo.fi_name,
    329 			    SMB_SHORTNAMELEN - 1);
    330 			if (to_upper)
    331 				(void) smb_strupr(name);
    332 		} else {
    333 			(void) strlcpy(name, fileinfo.fi_shortname,
    334 			    SMB_SHORTNAMELEN - 1);
    335 		}
    336 
    337 		(void) smb_mbc_encodef(&sr->reply, "b8c3c.wwlbYl13c",
    338 		    resume_char,
    339 		    fileinfo.fi_name83, fileinfo.fi_name83+9,
    340 		    index, odid, client_key,
    341 		    fileinfo.fi_dosattr & 0xff,
    342 		    smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
    343 		    (int32_t)fileinfo.fi_size,
    344 		    name);
    345 
    346 		smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
    347 
    348 		count++;
    349 		index++;
    350 	}
    351 
    352 	if (rc != 0) {
    353 		smb_odir_close(od);
    354 		smb_odir_release(od);
    355 		return (SDRC_ERROR);
    356 	}
    357 
    358 	if (count == 0 && find_first) {
    359 		smb_odir_close(od);
    360 		smb_odir_release(od);
    361 		smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
    362 		    ERRDOS, ERROR_NO_MORE_FILES);
    363 		return (SDRC_ERROR);
    364 	}
    365 
    366 	rc = (sr->reply.chain_offset - sr->cur_reply_offset) - 8;
    367 	if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
    368 	    1, count, rc+3, 5, rc) < 0) {
    369 		smb_odir_close(od);
    370 		smb_odir_release(od);
    371 		return (SDRC_ERROR);
    372 	}
    373 
    374 	smb_odir_release(od);
    375 	return (SDRC_SUCCESS);
    376 }
    377 
    378 
    379 /* *** smb_com_find *** */
    380 
    381 smb_sdrc_t
    382 smb_pre_find(smb_request_t *sr)
    383 {
    384 	DTRACE_SMB_1(op__Find__start, smb_request_t *, sr);
    385 	return (SDRC_SUCCESS);
    386 }
    387 
    388 void
    389 smb_post_find(smb_request_t *sr)
    390 {
    391 	DTRACE_SMB_1(op__Find__done, smb_request_t *, sr);
    392 }
    393 
    394 smb_sdrc_t
    395 smb_com_find(smb_request_t *sr)
    396 {
    397 	int			rc;
    398 	uint16_t		count, maxcount, index;
    399 	uint16_t		sattr, odid;
    400 	uint16_t		key_len;
    401 	uint32_t		client_key;
    402 	char			name[SMB_SHORTNAMELEN];
    403 	smb_odir_t		*od;
    404 	smb_fileinfo_t		fileinfo;
    405 	boolean_t		eos;
    406 
    407 	char			*path;
    408 	unsigned char		resume_char;
    409 	unsigned char		type;
    410 	boolean_t		find_first = B_TRUE;
    411 	smb_odir_resume_t	odir_resume;
    412 
    413 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
    414 		return (SDRC_ERROR);
    415 
    416 	rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
    417 	if ((rc != 0) || (type != 0x05))
    418 		return (SDRC_ERROR);
    419 
    420 	if ((key_len != 0) && (key_len != 21))
    421 		return (SDRC_ERROR);
    422 
    423 	find_first = (key_len == 0);
    424 	resume_char = 0;
    425 	client_key = 0;
    426 
    427 	if (find_first) {
    428 		odid = smb_odir_open(sr, path, sattr, 0);
    429 		if (odid == 0)
    430 			return (SDRC_ERROR);
    431 	} else {
    432 		if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
    433 		    &resume_char, &index, &odid, &client_key) != 0) {
    434 			return (SDRC_ERROR);
    435 		}
    436 	}
    437 
    438 	od = smb_tree_lookup_odir(sr->tid_tree, odid);
    439 	if (od == NULL) {
    440 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
    441 		    ERRDOS, ERROR_INVALID_HANDLE);
    442 		return (SDRC_ERROR);
    443 	}
    444 
    445 	if (!find_first) {
    446 		odir_resume.or_type = SMB_ODIR_RESUME_IDX;
    447 		odir_resume.or_idx = index;
    448 		smb_odir_resume_at(od, &odir_resume);
    449 	}
    450 
    451 	(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
    452 
    453 	rc = 0;
    454 	index = 0;
    455 	count = 0;
    456 	if (maxcount > SMB_MAX_SEARCH)
    457 		maxcount = SMB_MAX_SEARCH;
    458 
    459 	while (count < maxcount) {
    460 		rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
    461 		if ((rc != 0 || (eos == B_TRUE)))
    462 			break;
    463 
    464 		(void) memset(name, ' ', sizeof (name));
    465 		if (*fileinfo.fi_shortname == '\0') {
    466 			(void) strlcpy(name, fileinfo.fi_name,
    467 			    SMB_SHORTNAMELEN - 1);
    468 		} else {
    469 			(void) strlcpy(name, fileinfo.fi_shortname,
    470 			    SMB_SHORTNAMELEN - 1);
    471 		}
    472 
    473 		(void) smb_mbc_encodef(&sr->reply, "b8c3c.wwlbYl13c",
    474 		    resume_char,
    475 		    fileinfo.fi_name83, fileinfo.fi_name83+9,
    476 		    index, odid, client_key,
    477 		    fileinfo.fi_dosattr & 0xff,
    478 		    smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
    479 		    (int32_t)fileinfo.fi_size,
    480 		    name);
    481 
    482 		smb_odir_save_cookie(od, index, fileinfo.fi_cookie);
    483 
    484 		count++;
    485 		index++;
    486 	}
    487 
    488 	if (rc != 0) {
    489 		smb_odir_close(od);
    490 		smb_odir_release(od);
    491 		return (SDRC_ERROR);
    492 	}
    493 
    494 	if (count == 0 && find_first) {
    495 		smb_odir_close(od);
    496 		smb_odir_release(od);
    497 		smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
    498 		    ERRDOS, ERROR_NO_MORE_FILES);
    499 		return (SDRC_ERROR);
    500 	}
    501 
    502 	rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
    503 	if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
    504 	    1, count, rc+3, 5, rc) < 0) {
    505 		smb_odir_close(od);
    506 		smb_odir_release(od);
    507 		return (SDRC_ERROR);
    508 	}
    509 
    510 	smb_odir_release(od);
    511 	return (SDRC_SUCCESS);
    512 }
    513 
    514 
    515 /* *** smb_com_find_close *** */
    516 
    517 smb_sdrc_t
    518 smb_pre_find_close(smb_request_t *sr)
    519 {
    520 	DTRACE_SMB_1(op__FindClose__start, smb_request_t *, sr);
    521 	return (SDRC_SUCCESS);
    522 }
    523 
    524 void
    525 smb_post_find_close(smb_request_t *sr)
    526 {
    527 	DTRACE_SMB_1(op__FindClose__done, smb_request_t *, sr);
    528 }
    529 
    530 smb_sdrc_t
    531 smb_com_find_close(smb_request_t *sr)
    532 {
    533 	int		rc;
    534 	uint16_t	maxcount, index;
    535 	uint16_t	sattr, odid;
    536 	uint16_t	key_len;
    537 	uint32_t	client_key;
    538 	char		*path;
    539 	unsigned char	resume_char;
    540 	unsigned char	type;
    541 	smb_odir_t	*od;
    542 
    543 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
    544 		return (SDRC_ERROR);
    545 
    546 	rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
    547 	if ((rc != 0) || (type != 0x05))
    548 		return (SDRC_ERROR);
    549 
    550 	if (key_len == 0) {
    551 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
    552 		    ERRDOS, ERROR_INVALID_HANDLE);
    553 		return (SDRC_ERROR);
    554 	} else if (key_len != 21) {
    555 		return (SDRC_ERROR);
    556 	}
    557 
    558 	odid = 0;
    559 	if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
    560 	    &resume_char, &index, &odid, &client_key) != 0) {
    561 		return (SDRC_ERROR);
    562 	}
    563 
    564 	od = smb_tree_lookup_odir(sr->tid_tree, odid);
    565 	if (od == NULL) {
    566 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
    567 		    ERRDOS, ERROR_INVALID_HANDLE);
    568 		return (SDRC_ERROR);
    569 	}
    570 
    571 	smb_odir_close(od);
    572 	smb_odir_release(od);
    573 
    574 	if (smbsr_encode_result(sr, 1, 3, "bwwbw", 1, 0, 3, 5, 0))
    575 		return (SDRC_ERROR);
    576 
    577 	return (SDRC_SUCCESS);
    578 }
    579 
    580 
    581 /* *** smb_com_find_unique *** */
    582 
    583 smb_sdrc_t
    584 smb_pre_find_unique(smb_request_t *sr)
    585 {
    586 	DTRACE_SMB_1(op__FindUnique__start, smb_request_t *, sr);
    587 	return (SDRC_SUCCESS);
    588 }
    589 
    590 void
    591 smb_post_find_unique(smb_request_t *sr)
    592 {
    593 	DTRACE_SMB_1(op__FindUnique__done, smb_request_t *, sr);
    594 }
    595 
    596 smb_sdrc_t
    597 smb_com_find_unique(struct smb_request *sr)
    598 {
    599 	int			rc;
    600 	uint16_t		count, maxcount, index;
    601 	uint16_t		sattr, odid;
    602 	char			*path;
    603 	unsigned char		resume_char = '\0';
    604 	uint32_t		client_key = 0;
    605 	char			name[SMB_SHORTNAMELEN];
    606 	smb_odir_t		*od;
    607 	smb_fileinfo_t		fileinfo;
    608 	boolean_t		eos;
    609 	smb_vdb_t		*vdb;
    610 
    611 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
    612 		return (SDRC_ERROR);
    613 
    614 	vdb = kmem_alloc(sizeof (smb_vdb_t), KM_SLEEP);
    615 	if ((smbsr_decode_data(sr, "%AV", sr, &path, vdb) != 0) ||
    616 	    (vdb->vdb_len != 0)) {
    617 		kmem_free(vdb, sizeof (smb_vdb_t));
    618 		return (SDRC_ERROR);
    619 	}
    620 	kmem_free(vdb, sizeof (smb_vdb_t));
    621 
    622 	(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
    623 
    624 	odid = smb_odir_open(sr, path, sattr, 0);
    625 	if (odid == 0)
    626 		return (SDRC_ERROR);
    627 	od = smb_tree_lookup_odir(sr->tid_tree, odid);
    628 	if (od == NULL)
    629 		return (SDRC_ERROR);
    630 
    631 	rc = 0;
    632 	count = 0;
    633 	index = 0;
    634 	if (maxcount > SMB_MAX_SEARCH)
    635 		maxcount = SMB_MAX_SEARCH;
    636 
    637 	while (count < maxcount) {
    638 		rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &eos);
    639 		if ((rc != 0 || (eos == B_TRUE)))
    640 			break;
    641 
    642 		(void) memset(name, ' ', sizeof (name));
    643 		if (*fileinfo.fi_shortname == '\0') {
    644 			(void) strlcpy(name, fileinfo.fi_name,
    645 			    SMB_SHORTNAMELEN - 1);
    646 		} else {
    647 			(void) strlcpy(name, fileinfo.fi_shortname,
    648 			    SMB_SHORTNAMELEN - 1);
    649 		}
    650 
    651 		(void) smb_mbc_encodef(&sr->reply, "b8c3c.wwlbYl13c",
    652 		    resume_char,
    653 		    fileinfo.fi_name83, fileinfo.fi_name83+9,
    654 		    index, odid, client_key,
    655 		    fileinfo.fi_dosattr & 0xff,
    656 		    smb_time_gmt_to_local(sr, fileinfo.fi_mtime.tv_sec),
    657 		    (int32_t)fileinfo.fi_size,
    658 		    name);
    659 
    660 		count++;
    661 		index++;
    662 	}
    663 
    664 	smb_odir_close(od);
    665 	smb_odir_release(od);
    666 
    667 	if (rc != 0)
    668 		return (SDRC_ERROR);
    669 
    670 	if (count == 0) {
    671 		smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
    672 		    ERRDOS, ERROR_NO_MORE_FILES);
    673 		return (SDRC_ERROR);
    674 	}
    675 
    676 	rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
    677 	if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset,
    678 	    "bwwbw", 1, count, rc+3, 5, rc) < 0) {
    679 		return (SDRC_ERROR);
    680 	}
    681 
    682 	return (SDRC_SUCCESS);
    683 }
    684