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