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 */
215typedef 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
226static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *,
227    smb_odir_t *, smb_find_args_t *);
228static int smb_trans2_find_get_fixedsize(smb_request_t *, uint16_t, uint16_t);
229static 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 */
236uint16_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 */
281smb_sdrc_t
282smb_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 */
431smb_sdrc_t
432smb_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 */
532static int
533smb_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 */
644static int
645smb_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 */
716int 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 */
739static int
740smb_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 */
990smb_sdrc_t
991smb_pre_find_close2(smb_request_t *sr)
992{
993	DTRACE_SMB_START(op__FindClose2, smb_request_t *, sr);
994	return (SDRC_SUCCESS);
995}
996
997void
998smb_post_find_close2(smb_request_t *sr)
999{
1000	DTRACE_SMB_DONE(op__FindClose2, smb_request_t *, sr);
1001}
1002
1003smb_sdrc_t
1004smb_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