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