xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_trans2_find.c (revision 22f5594a529d50114d839d4ddecc2c499731a3d7)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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  *
48  * The following sections detail the data returned for each
49  * InformationLevel. The requested information is placed in the Data
50  * portion of the transaction response. Note: a client which does not
51  * support long names can only request SMB_INFO_STANDARD.
52  *
53  * A four-byte resume key precedes each data item (described below) if bit
54  * 2 in the Flags field is set, i.e. if the request indicates the server
55  * should return resume keys. Note: it is not always the case. If the
56  * data item already includes the resume key, the resume key should not be
57  * added again.
58  *
59  * 4.3.4.1   SMB_INFO_STANDARD
60  *
61  *  Response Field                    Description
62  *  ================================  ==================================
63  *
64  *  SMB_DATE CreationDate;            Date when file was created
65  *  SMB_TIME CreationTime;            Time when file was created
66  *  SMB_DATE LastAccessDate;          Date of last file access
67  *  SMB_TIME LastAccessTime;          Time of last file access
68  *  SMB_DATE LastWriteDate;           Date of last write to the file
69  *  SMB_TIME LastWriteTime;           Time of last write to the file
70  *  ULONG  DataSize;                  File Size
71  *  ULONG AllocationSize;             Size of filesystem allocation unit
72  *  USHORT Attributes;                File Attributes
73  *  UCHAR FileNameLength;             Length of filename in bytes
74  *  STRING FileName;                  Name of found file
75  *
76  * 4.3.4.2   SMB_INFO_QUERY_EA_SIZE
77  *
78  *  Response Field                     Description
79  *  =================================  ==================================
80  *
81  *   SMB_DATE CreationDate;            Date when file was created
82  *   SMB_TIME CreationTime;            Time when file was created
83  *   SMB_DATE LastAccessDate;          Date of last file access
84  *   SMB_TIME LastAccessTime;          Time of last file access
85  *   SMB_DATE LastWriteDate;           Date of last write to the file
86  *   SMB_TIME LastWriteTime;           Time of last write to the file
87  *   ULONG DataSize;                   File Size
88  *   ULONG AllocationSize;             Size of filesystem allocation unit
89  *   USHORT Attributes;                File Attributes
90  *   ULONG EaSize;                     Size of file's EA information
91  *   UCHAR FileNameLength;             Length of filename in bytes
92  *   STRING FileName;                  Name of found file
93  *
94  * 4.3.4.3   SMB_INFO_QUERY_EAS_FROM_LIST
95  *
96  * This request returns the same information as SMB_INFO_QUERY_EA_SIZE, but
97  * only for files which have an EA list which match the EA information in
98  * the Data part of the request.
99  *
100  * 4.3.4.4   SMB_FIND_FILE_DIRECTORY_INFO
101  *
102  *  Response Field                     Description
103  *  =================================  ==================================
104  *
105  *  ULONG NextEntryOffset;             Offset from this structure to
106  *					beginning of next one
107  *  ULONG FileIndex;
108  *  LARGE_INTEGER CreationTime;        file creation time
109  *  LARGE_INTEGER LastAccessTime;      last access time
110  *  LARGE_INTEGER LastWriteTime;       last write time
111  *  LARGE_INTEGER ChangeTime;          last attribute change time
112  *  LARGE_INTEGER EndOfFile;           file size
113  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
114  *  ULONG ExtFileAttributes;           Extended file attributes
115  *					(see section 3.11)
116  *  ULONG FileNameLength;              Length of filename in bytes
117  *  STRING FileName;                   Name of the file
118  *
119  * 4.3.4.5   SMB_FIND_FILE_FULL_DIRECTORY_INFO
120  *
121  *  Response Field                     Description
122  *  =================================  ==================================
123  *
124  *  ULONG NextEntryOffset;             Offset from this structure to
125  *					beginning of next one
126  *  ULONG FileIndex;
127  *  LARGE_INTEGER CreationTime;        file creation time
128  *  LARGE_INTEGER LastAccessTime;      last access time
129  *  LARGE_INTEGER LastWriteTime;       last write time
130  *  LARGE_INTEGER ChangeTime;          last attribute change time
131  *  LARGE_INTEGER EndOfFile;           file size
132  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
133  *  ULONG ExtFileAttributes;           Extended file attributes
134  *					(see section 3.11)
135  *  ULONG FileNameLength;              Length of filename in bytes
136  *  ULONG EaSize;                      Size of file's extended attributes
137  *  STRING FileName;                   Name of the file
138  *
139  * 4.3.4.6   SMB_FIND_FILE_BOTH_DIRECTORY_INFO
140  *
141  *  Response Field                     Description
142  *  =================================  ==================================
143  *
144  *  ULONG NextEntryOffset;             Offset from this structure to
145  *					beginning of next one
146  *  ULONG FileIndex;
147  *  LARGE_INTEGER CreationTime;        file creation time
148  *  LARGE_INTEGER LastAccessTime;      last access time
149  *  LARGE_INTEGER LastWriteTime;       last write time
150  *  LARGE_INTEGER ChangeTime;          last attribute change time
151  *  LARGE_INTEGER EndOfFile;           file size
152  *  LARGE_INTEGER AllocationSize;      size of filesystem allocation information
153  *  ULONG ExtFileAttributes;           Extended file attributes
154  *					(see section 3.11)
155  *  ULONG FileNameLength;              Length of FileName in bytes
156  *  ULONG EaSize;                      Size of file's extended attributes
157  *  UCHAR ShortNameLength;             Length of file's short name in bytes
158  *  UCHAR Reserved
159  *  WCHAR ShortName[12];               File's 8.3 conformant name in Unicode
160  *  STRING FileName;                   Files full length name
161  *
162  * 4.3.4.7   SMB_FIND_FILE_NAMES_INFO
163  *
164  *  Response Field                     Description
165  *  =================================  ==================================
166  *
167  *  ULONG NextEntryOffset;             Offset from this structure to
168  *                                     beginning of next one
169  *  ULONG FileIndex;
170  *  ULONG FileNameLength;              Length of FileName in bytes
171  *  STRING FileName;                   Files full length name
172  */
173 
174 #include <smbsrv/smb_incl.h>
175 #include <smbsrv/msgbuf.h>
176 #include <smbsrv/smbtrans.h>
177 #include <smbsrv/smb_fsops.h>
178 
179 static int smb_trans2_find_get_maxdata(smb_request_t *, uint16_t, uint16_t);
180 
181 int smb_trans2_find_get_dents(smb_request_t *, smb_xa_t *,
182     uint16_t, uint16_t, int, smb_node_t *,
183     uint16_t, uint16_t, int, char *, uint32_t *, int *, int *);
184 
185 int smb_gather_dents_info(char *, ino_t, int, char *, uint32_t, int32_t *,
186     smb_attr_t *, smb_node_t *, char *, char *);
187 
188 int smb_trans2_find_process_ients(smb_request_t *, smb_xa_t *,
189     smb_dent_info_hdr_t *, uint16_t, uint16_t, int,
190     smb_node_t *, int *, uint32_t *);
191 
192 int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *,
193     smb_dent_info_t *, int, uint16_t, uint16_t,
194     uint32_t, smb_node_t *, smb_node_t *);
195 
196 /*
197  * The UNIX characters below are considered illegal in Windows file names.
198  * The following character conversions are used to support sites in which
199  * Catia v4 is in use on UNIX and Catia v5 is in use on Windows.
200  *
201  * ---------------------------
202  * Unix-char	| Windows-char
203  * ---------------------------
204  *   "		| (0x00a8) Diaeresis
205  *   *		| (0x00a4) Currency Sign
206  *   :		| (0x00f7) Division Sign
207  *   <		| (0x00ab) Left-Pointing Double Angle Quotation Mark
208  *   >		| (0x00bb) Right-Pointing Double Angle Quotation Mark
209  *   ?		| (0x00bf) Inverted Question mark
210  *   \		| (0x00ff) Latin Small Letter Y with Diaeresis
211  *   |		| (0x00a6) Broken Bar
212  */
213 static int (*catia_callback)(uint8_t *, uint8_t *, int) = NULL;
214 void smb_register_catia_callback(
215     int (*catia_v4tov5)(uint8_t *, uint8_t *, int));
216 void smb_unregister_catia_callback();
217 
218 /*
219  * Tunable parameter to limit the maximum
220  * number of entries to be returned.
221  */
222 uint16_t smb_trans2_find_max = 128;
223 
224 /*
225  * smb_register_catia_callback
226  *
227  * This function will be invoked by the catia module to register its
228  * function that translates filename in version 4 to a format that is
229  * compatible to version 5.
230  */
231 void
232 smb_register_catia_callback(
233     int (*catia_v4tov5)(uint8_t *, uint8_t *, int))
234 {
235 	catia_callback = catia_v4tov5;
236 }
237 
238 /*
239  * smb_unregister_catia_callback
240  *
241  * This function will unregister the catia callback prior to the catia
242  * module gets unloaded.
243  */
244 void
245 smb_unregister_catia_callback()
246 {
247 	catia_callback = 0;
248 }
249 
250 /*
251  * smb_com_trans2_find_first2
252  *
253  *  Client Request                Value
254  *  ============================  ==================================
255  *
256  *  UCHAR  WordCount              15
257  *  UCHAR  TotalDataCount         Total size of extended attribute list
258  *  UCHAR  SetupCount             1
259  *  UCHAR  Setup[0]               TRANS2_FIND_FIRST2
260  *
261  *  Parameter Block Encoding      Description
262  *  ============================  ==================================
263  *  USHORT SearchAttributes;
264  *  USHORT SearchCount;           Maximum number of entries to return
265  *  USHORT Flags;                 Additional information:
266  *                                Bit 0 - close search after this request
267  *                                Bit 1 - close search if end of search
268  *                                reached
269  *                                Bit 2 - return resume keys for each
270  *                                entry found
271  *                                Bit 3 - continue search from previous
272  *                                ending place
273  *                                Bit 4 - find with backup intent
274  *  USHORT InformationLevel;      See below
275  *  ULONG SearchStorageType;
276  *  STRING FileName;              Pattern for the search
277  *  UCHAR Data[ TotalDataCount ]  FEAList if InformationLevel is
278  *                                QUERY_EAS_FROM_LIST
279  *
280  *  Response Parameter Block      Description
281  *  ============================  ==================================
282  *
283  *  USHORT Sid;                   Search handle
284  *  USHORT SearchCount;           Number of entries returned
285  *  USHORT EndOfSearch;           Was last entry returned?
286  *  USHORT EaErrorOffset;         Offset into EA list if EA error
287  *  USHORT LastNameOffset;        Offset into data to file name of last
288  *                                entry, if server needs it to resume
289  *                                search; else 0
290  *  UCHAR Data[ TotalDataCount ]  Level dependent info about the matches
291  *                                found in the search
292  */
293 smb_sdrc_t
294 smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
295 {
296 	int		more = 0, rc;
297 	uint16_t	sattr, fflag, infolev;
298 	uint16_t	maxcount = 0;
299 	int		maxdata;
300 	int		count, wildcards;
301 	uint32_t	cookie;
302 	char		*path;
303 	smb_node_t	*dir_snode;
304 	char		*pattern;
305 	uint16_t	sid;
306 
307 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
308 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
309 		    ERRDOS, ERROR_ACCESS_DENIED);
310 		return (SDRC_ERROR);
311 	}
312 
313 	if (smb_decode_mbc(&xa->req_param_mb, "%wwww4.u", sr,
314 	    &sattr, &maxcount, &fflag, &infolev, &path) != 0) {
315 		return (SDRC_ERROR);
316 	}
317 
318 	maxdata = smb_trans2_find_get_maxdata(sr, infolev, fflag);
319 	if (maxdata == 0) {
320 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
321 		    ERRDOS, ERROR_INVALID_LEVEL);
322 		return (SDRC_ERROR);
323 	}
324 
325 	/*
326 	 * When maxcount is zero Windows behaves as if it was 1.
327 	 */
328 	if (maxcount == 0)
329 		maxcount = 1;
330 
331 	if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
332 		maxcount = smb_trans2_find_max;
333 
334 	if (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
335 		(void) smb_convert_unicode_wildcards(path);
336 
337 	if (smb_rdir_open(sr, path, sattr) != 0)
338 		return (SDRC_ERROR);
339 
340 	/*
341 	 * Get a copy of information
342 	 */
343 	dir_snode = sr->sid_odir->d_dir_snode;
344 	pattern = kmem_alloc(MAXNAMELEN, KM_SLEEP);
345 	(void) strcpy(pattern, sr->sid_odir->d_pattern);
346 
347 	if (strcmp(pattern, "*.*") == 0)
348 		(void) strncpy(pattern, "*", sizeof (pattern));
349 
350 	wildcards = sr->sid_odir->d_wildcards;
351 	sattr = sr->sid_odir->d_sattr;
352 	cookie = 0;
353 
354 	rc = smb_trans2_find_get_dents(sr, xa, fflag, infolev, maxdata,
355 	    dir_snode, sattr, maxcount, wildcards,
356 	    pattern, &cookie, &more, &count);
357 
358 	if (!count)
359 		rc = ENOENT;
360 
361 	if (rc) {
362 		smb_rdir_close(sr);
363 		kmem_free(pattern, MAXNAMELEN);
364 		if (rc == ENOENT)
365 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
366 			    ERRDOS, ERROR_INVALID_NAME);
367 		else
368 			smbsr_errno(sr, rc);
369 		return (SDRC_ERROR);
370 	}
371 
372 	/*
373 	 * Save the sid here in case the search is closed below,
374 	 * which will invalidate sr->smb_sid.  We return the
375 	 * sid, even though the search has been closed, to be
376 	 * compatible with Windows.
377 	 */
378 	sid = sr->smb_sid;
379 
380 	if (fflag & SMB_FIND_CLOSE_AFTER_REQUEST ||
381 	    (!more && fflag & SMB_FIND_CLOSE_AT_EOS)) {
382 		smb_rdir_close(sr);
383 	} else {
384 		mutex_enter(&sr->sid_odir->d_mutex);
385 		sr->sid_odir->d_cookie = cookie;
386 		mutex_exit(&sr->sid_odir->d_mutex);
387 	}
388 
389 	(void) smb_encode_mbc(&xa->rep_param_mb, "wwwww",
390 	    sid, count, (more ? 0 : 1), 0, 0);
391 
392 	kmem_free(pattern, MAXNAMELEN);
393 	return (SDRC_SUCCESS);
394 }
395 
396 /*
397  * smb_com_trans2_find_next2
398  *
399  *  Client Request                     Value
400  *  ================================== =================================
401  *
402  *  WordCount                          15
403  *  SetupCount                         1
404  *  Setup[0]                           TRANS2_FIND_NEXT2
405  *
406  *  Parameter Block Encoding           Description
407  *  ================================== =================================
408  *
409  *  USHORT Sid;                        Search handle
410  *  USHORT SearchCount;                Maximum number of entries to
411  *                                      return
412  *  USHORT InformationLevel;           Levels described in
413  *                                      TRANS2_FIND_FIRST2 request
414  *  ULONG ResumeKey;                   Value returned by previous find2
415  *                                      call
416  *  USHORT Flags;                      Additional information: bit set-
417  *                                      0 - close search after this
418  *                                      request
419  *                                      1 - close search if end of search
420  *                                      reached
421  *                                      2 - return resume keys for each
422  *                                      entry found
423  *                                      3 - resume/continue from previous
424  *                                      ending place
425  *                                      4 - find with backup intent
426  *  STRING FileName;                   Resume file name
427  *
428  * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2
429  * call.  If Bit3 of Flags is set, then FileName may be the NULL string,
430  * since the search is continued from the previous TRANS2_FIND request.
431  * Otherwise, FileName must not be more than 256 characters long.
432  *
433  *  Response Field                     Description
434  *  ================================== =================================
435  *
436  *  USHORT SearchCount;                Number of entries returned
437  *  USHORT EndOfSearch;                Was last entry returned?
438  *  USHORT EaErrorOffset;              Offset into EA list if EA error
439  *  USHORT LastNameOffset;             Offset into data to file name of
440  *                                      last entry, if server needs it to
441  *                                      resume search; else 0
442  *  UCHAR Data[TotalDataCount]         Level dependent info about the
443  *                                      matches found in the search
444  */
445 smb_sdrc_t
446 smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
447 {
448 	uint16_t fflag, infolev;
449 	int	maxdata, count, wildcards, more = 0, rc;
450 	uint32_t cookie;
451 	uint16_t maxcount = 0;
452 	smb_node_t *dir_snode;
453 	char *pattern;
454 	uint16_t sattr;
455 
456 	/*
457 	 * The last parameter in the request is a path, which is a
458 	 * null-terminated unicode string.
459 	 *
460 	 * smb_decode_mbc(&xa->req_param_mb, "%www lwu", sr,
461 	 *    &sr->smb_sid, &maxcount, &infolev, &cookie, &fflag, &path)
462 	 *
463 	 * We don't reference this parameter and it is not currently
464 	 * decoded because we a expect 2-byte null but Mac OS 10
465 	 * clients send a 1-byte null, which leads to a decode error.
466 	 */
467 	if (smb_decode_mbc(&xa->req_param_mb, "%www lw", sr,
468 	    &sr->smb_sid, &maxcount, &infolev, &cookie, &fflag) != 0) {
469 		return (SDRC_ERROR);
470 	}
471 
472 	sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, sr->smb_sid);
473 	if (sr->sid_odir == NULL) {
474 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
475 		return (SDRC_ERROR);
476 	}
477 
478 	maxdata = smb_trans2_find_get_maxdata(sr, infolev, fflag);
479 	if (maxdata == 0) {
480 		smb_rdir_close(sr);
481 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
482 		    ERRDOS, ERROR_INVALID_LEVEL);
483 		return (SDRC_ERROR);
484 	}
485 
486 	/*
487 	 * When maxcount is zero Windows behaves as if it was 1.
488 	 */
489 	if (maxcount == 0)
490 		maxcount = 1;
491 
492 	if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
493 		maxcount = smb_trans2_find_max;
494 
495 	/*
496 	 * Get a copy of information
497 	 */
498 	dir_snode = sr->sid_odir->d_dir_snode;
499 	pattern = kmem_alloc(MAXNAMELEN, KM_SLEEP);
500 	(void) strcpy(pattern, sr->sid_odir->d_pattern);
501 	wildcards = sr->sid_odir->d_wildcards;
502 	sattr = sr->sid_odir->d_sattr;
503 	if (fflag & SMB_FIND_CONTINUE_FROM_LAST) {
504 		mutex_enter(&sr->sid_odir->d_mutex);
505 		cookie = sr->sid_odir->d_cookie;
506 		mutex_exit(&sr->sid_odir->d_mutex);
507 	}
508 
509 	rc = smb_trans2_find_get_dents(sr, xa, fflag, infolev, maxdata,
510 	    dir_snode, sattr, maxcount, wildcards, pattern, &cookie,
511 	    &more, &count);
512 
513 	if (rc) {
514 		smb_rdir_close(sr);
515 		kmem_free(pattern, MAXNAMELEN);
516 		smbsr_errno(sr, rc);
517 		return (SDRC_ERROR);
518 	}
519 
520 	if (fflag & SMB_FIND_CLOSE_AFTER_REQUEST ||
521 	    (!more && fflag & SMB_FIND_CLOSE_AT_EOS))
522 		smb_rdir_close(sr);
523 	else {
524 		mutex_enter(&sr->sid_odir->d_mutex);
525 		sr->sid_odir->d_cookie = cookie;
526 		mutex_exit(&sr->sid_odir->d_mutex);
527 	}
528 
529 	(void) smb_encode_mbc(&xa->rep_param_mb, "wwww",
530 	    count, (more ? 0 : 1), 0, 0);
531 
532 	kmem_free(pattern, MAXNAMELEN);
533 	return (SDRC_SUCCESS);
534 }
535 
536 /*
537  * smb_trans2_find_get_maxdata
538  *
539  * Calculate the minimum response space required for the specified
540  * information level.
541  *
542  * A non-zero return value provides the minimum space required.
543  * A return value of zero indicates an unknown information level.
544  */
545 static int
546 smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
547 {
548 	int maxdata;
549 
550 	maxdata = smb_ascii_or_unicode_null_len(sr);
551 
552 	switch (infolev) {
553 	case SMB_INFO_STANDARD :
554 		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
555 			maxdata += sizeof (int32_t);
556 		maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
557 		break;
558 
559 	case SMB_INFO_QUERY_EA_SIZE:
560 		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
561 			maxdata += sizeof (int32_t);
562 		maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
563 		break;
564 
565 	case SMB_FIND_FILE_DIRECTORY_INFO:
566 		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4;
567 		break;
568 
569 	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
570 		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24;
571 		break;
572 
573 	case SMB_FIND_FILE_NAMES_INFO:
574 		maxdata += 4 + 4 + 4;
575 		break;
576 
577 	case SMB_MAC_FIND_BOTH_HFS_INFO:
578 		maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 +
579 		    4 + 32 + 4 + 1 + 1 + 24 + 4;
580 		break;
581 
582 	default:
583 		maxdata = 0;
584 	}
585 
586 	return (maxdata);
587 }
588 
589 /*
590  * smb_trans2_find_get_dents
591  *
592  * This function will get all the directory entry information and mbc
593  * encode it in the xa. If there is an error, it will be returned;
594  * otherwise, 0 is returned.
595  *
596  * The more field will be updated. If the value returned is one, it means
597  * there are more entries; otherwise, the returned value will be zero. The
598  * cookie will also be updated to indicate the next start point for the
599  * search. The count value will also be updated to stores the total entries
600  * encoded.
601  */
602 int smb_trans2_find_get_dents(
603     smb_request_t	*sr,
604     smb_xa_t		*xa,
605     uint16_t		fflag,
606     uint16_t		infolev,
607     int			maxdata,
608     smb_node_t		*dir_snode,
609     uint16_t		sattr,
610     uint16_t		maxcount,
611     int			wildcards,
612     char		*pattern,
613     uint32_t		*cookie,
614     int			*more,
615     int			*count)
616 {
617 	smb_dent_info_hdr_t	*ihdr;
618 	smb_dent_info_t		*ient;
619 	int			dent_buf_size;
620 	int			i;
621 	int			total;
622 	int			maxentries;
623 	int			rc;
624 
625 	ihdr = kmem_zalloc(sizeof (smb_dent_info_hdr_t), KM_SLEEP);
626 	*count = 0;
627 
628 	if (!wildcards)
629 		maxentries = maxcount = 1;
630 	else {
631 		maxentries = (xa->rep_data_mb.max_bytes -
632 		    xa->rep_data_mb.chain_offset) / maxdata;
633 		if (maxcount > SMB_MAX_DENTS_IOVEC)
634 			maxcount = SMB_MAX_DENTS_IOVEC;
635 		if (maxentries > maxcount)
636 			maxentries = maxcount;
637 	}
638 
639 	/* Each entry will need to be aligned so add _POINTER_ALIGNMENT */
640 	dent_buf_size =
641 	    maxentries * (SMB_MAX_DENT_INFO_SIZE + _POINTER_ALIGNMENT);
642 	ihdr->iov->iov_base = kmem_alloc(dent_buf_size, KM_SLEEP);
643 
644 	ihdr->sattr = sattr;
645 	ihdr->pattern = pattern;
646 	ihdr->sr = sr;
647 
648 	ihdr->uio.uio_iovcnt = maxcount;
649 	ihdr->uio.uio_resid = dent_buf_size;
650 	ihdr->uio.uio_iov = ihdr->iov;
651 	ihdr->uio.uio_loffset = 0;
652 
653 	rc = smb_get_dents(sr, cookie, dir_snode, wildcards, ihdr, more);
654 	if (rc != 0) {
655 		goto out;
656 	}
657 
658 	if (ihdr->iov->iov_len == 0)
659 		*count = 0;
660 	else
661 		*count = smb_trans2_find_process_ients(sr, xa, ihdr, fflag,
662 		    infolev, maxdata, dir_snode, more, cookie);
663 	rc = 0;
664 
665 out:
666 
667 	total = maxcount - ihdr->uio.uio_iovcnt;
668 	ASSERT((total >= 0) && (total <= SMB_MAX_DENTS_IOVEC));
669 	for (i = 0; i < total; i++) {
670 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
671 		ient = (smb_dent_info_t *)ihdr->iov[i].iov_base;
672 		ASSERT(ient);
673 		smb_node_release(ient->snode);
674 	}
675 
676 	kmem_free(ihdr->iov->iov_base, dent_buf_size);
677 	kmem_free(ihdr, sizeof (smb_dent_info_hdr_t));
678 	return (0);
679 }
680 
681 
682 
683 /*
684  * smb_get_dents
685  *
686  * This function utilizes "smb_fsop_getdents()" to get dir entries.
687  * The "smb_gather_dents_info()" is the call back function called
688  * inside the file system. It is very important that the function
689  * does not sleep or yield since it is processed inside a file
690  * system transaction.
691  *
692  * The function returns 0 when successful and error code when failed.
693  * If more is provided, the return value of 1 is returned indicating
694  * more entries; otherwise, 0 is returned.
695  */
696 int smb_get_dents(
697     smb_request_t	*sr,
698     uint32_t		*cookie,
699     smb_node_t		*dir_snode,
700     uint32_t		wildcards,
701     smb_dent_info_hdr_t	*ihdr,
702     int			*more)
703 {
704 	int		rc;
705 	char		*namebuf;
706 	smb_node_t	*snode;
707 	smb_attr_t	file_attr;
708 	uint32_t	maxcnt = ihdr->uio.uio_iovcnt;
709 	char		shortname[MANGLE_NAMELEN], name83[MANGLE_NAMELEN];
710 	fsvol_attr_t	vol_attr;
711 
712 	namebuf = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
713 	if (more)
714 		*more = 0;
715 
716 	if ((rc = fsd_getattr(&sr->tid_tree->t_fsd, &vol_attr)) != 0) {
717 		kmem_free(namebuf, MAXNAMELEN);
718 		return (rc);
719 	}
720 
721 	if (!wildcards) {
722 		/* Already found entry? */
723 		if (*cookie != 0)
724 			return (0);
725 		shortname[0] = '\0';
726 
727 		rc = smb_fsop_lookup(sr, sr->user_cr, 0, sr->tid_tree->t_snode,
728 		    dir_snode, ihdr->pattern, &snode, &file_attr, shortname,
729 		    name83);
730 
731 		if (rc) {
732 			kmem_free(namebuf, MAXNAMELEN);
733 			return (rc);
734 		}
735 
736 		(void) strlcpy(namebuf, ihdr->pattern, MAXNAMELEN);
737 
738 		/*
739 		 * It is not necessary to set the "force" flag (i.e. to
740 		 * take into account mangling for case-insensitive collisions)
741 		 */
742 
743 		if (shortname[0] == '\0')
744 			(void) smb_mangle_name(snode->attr.sa_vattr.va_nodeid,
745 			    namebuf, shortname, name83, 0);
746 		(void) smb_gather_dents_info((char *)ihdr,
747 		    snode->attr.sa_vattr.va_nodeid,
748 		    strlen(namebuf), namebuf, -1, (int *)&maxcnt,
749 		    &snode->attr, snode, shortname, name83);
750 		kmem_free(namebuf, MAXNAMELEN);
751 		return (0);
752 	}
753 
754 	if ((rc = smb_fsop_getdents(sr, sr->user_cr, dir_snode, cookie,
755 	    0, (int *)&maxcnt, (char *)ihdr, ihdr->pattern)) != 0) {
756 		if (rc == ENOENT) {
757 			kmem_free(namebuf, MAXNAMELEN);
758 			return (0);
759 		}
760 		kmem_free(namebuf, MAXNAMELEN);
761 		return (rc);
762 	}
763 
764 	if (*cookie != 0x7FFFFFFF && more)
765 		*more = 1;
766 
767 	kmem_free(namebuf, MAXNAMELEN);
768 	return (0);
769 }
770 
771 
772 
773 
774 /*
775  * smb_gather_dents_info
776  *
777  * The function will accept information of each directory entry and put
778  * the needed information in the buffer. It is passed as the call back
779  * function for smb_fsop_getdents() to gather trans2 find info.
780  *
781  * If the buffer space is not enough, -1 will be returned. Regardless
782  * of valid entry or not, 0 will be returned; however, only valid entry
783  * will be stored in the buffer.
784  */
785 int /*ARGSUSED*/
786 smb_gather_dents_info(
787     char	*args,
788     ino_t	fileid,
789     int		namelen,
790     char	*name,
791     uint32_t	cookie,
792     int32_t	*countp,
793     smb_attr_t	*attr,
794     smb_node_t	*snode,
795     char	*shortname,
796     char	*name83)
797 {
798 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
799 	smb_dent_info_hdr_t	*ihdr = (smb_dent_info_hdr_t *)args;
800 	smb_dent_info_t		*ient;
801 	uint8_t			*v5_name = NULL;
802 	uint8_t			*np = (uint8_t *)name;
803 	int			reclen = sizeof (smb_dent_info_t) + namelen;
804 
805 	v5_name = kmem_alloc(MAXNAMELEN-1, KM_SLEEP);
806 
807 	if (!ihdr->uio.uio_iovcnt || ihdr->uio.uio_resid < reclen) {
808 		kmem_free(v5_name, MAXNAMELEN-1);
809 		smb_node_release(snode);
810 		return (-1);
811 	}
812 
813 	if (!smb_sattr_check(attr, name, ihdr->sattr)) {
814 		kmem_free(v5_name, MAXNAMELEN-1);
815 		smb_node_release(snode);
816 		return (0);
817 	}
818 
819 	if (catia_callback) {
820 		catia_callback(v5_name, (uint8_t *)name,  MAXNAMELEN-1);
821 		np = v5_name;
822 		reclen = sizeof (smb_dent_info_t) + strlen((char *)v5_name);
823 	}
824 
825 	ASSERT(snode);
826 	ASSERT(snode->n_magic == SMB_NODE_MAGIC);
827 	ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
828 
829 	/*
830 	 * Each entry needs to be properly aligned or we may get an alignment
831 	 * fault on sparc.
832 	 */
833 	ihdr->uio.uio_loffset = (offset_t)PTRALIGN(ihdr->uio.uio_loffset);
834 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
835 	ient = (smb_dent_info_t *)&ihdr->iov->iov_base[ihdr->uio.uio_loffset];
836 
837 	ient->cookie = cookie;
838 	ient->attr = *attr;
839 	ient->snode = snode;
840 
841 	(void) strcpy(ient->name, (char *)np);
842 	(void) strcpy(ient->shortname, shortname);
843 	(void) strcpy(ient->name83, name83);
844 	ihdr->uio.uio_iov->iov_base = (char *)ient;
845 	ihdr->uio.uio_iov->iov_len = reclen;
846 
847 	ihdr->uio.uio_iov++;
848 	ihdr->uio.uio_iovcnt--;
849 	ihdr->uio.uio_resid -= reclen;
850 	ihdr->uio.uio_loffset += reclen;
851 
852 	kmem_free(v5_name, MAXNAMELEN-1);
853 	return (0);
854 }
855 
856 
857 
858 /*
859  * smb_trans2_find_process_ients
860  *
861  * This function encodes the directory entry information store in
862  * the iov structure of the ihdr structure.
863  *
864  * The total entries encoded will be returned. If the entries encoded
865  * is less than the total entries in the iov, the more field will
866  * be updated to 1. Also, the next cookie wil be updated as well.
867  */
868 int
869 smb_trans2_find_process_ients(
870     smb_request_t	*sr,
871     smb_xa_t		*xa,
872     smb_dent_info_hdr_t	*ihdr,
873     uint16_t		fflag,
874     uint16_t		infolev,
875     int			maxdata,
876     smb_node_t		*dir_snode,
877     int			*more,
878     uint32_t		*cookie)
879 {
880 	int i, err = 0;
881 	smb_dent_info_t *ient;
882 	uint32_t mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE)
883 	    ? SMB_MSGBUF_UNICODE : 0;
884 
885 	for (i = 0; i < SMB_MAX_DENTS_IOVEC; i++) {
886 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
887 		if ((ient = (smb_dent_info_t *)ihdr->iov[i].iov_base) == 0)
888 			break;
889 
890 		/*
891 		 * Observed differences between our response and Windows
892 		 * response, which hasn't caused a problem yet!
893 		 *
894 		 * 1. The NextEntryOffset field for the last entry should
895 		 * be 0.  This code always calculate the record length
896 		 * and puts the result in the NextEntryOffset field.
897 		 *
898 		 * 2. The FileIndex field is always 0.  This code puts
899 		 * the cookie in the FileIndex field.
900 		 */
901 		err = smb_trans2_find_mbc_encode(sr, xa, ient, maxdata, infolev,
902 		    fflag, mb_flags, dir_snode, NULL);
903 
904 		if (err)
905 			break;
906 	}
907 
908 	/*
909 	 * Not enough space to store all the entries returned,
910 	 * which is indicated by setting more.
911 	 */
912 	if (more && err < 0) {
913 		*more = 1;
914 
915 		/*
916 		 * Assume the space will be at least enough for 1 entry.
917 		 */
918 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
919 		ient = (smb_dent_info_t *)ihdr->iov[i-1].iov_base;
920 		*cookie = ient->cookie;
921 	}
922 	return (i);
923 }
924 
925 /*
926  * smb_trans2_find_mbc_encode
927  *
928  * This function encodes the mbc for one directory entry.
929  *
930  * The function returns -1 when the max data requested by client
931  * is reached. If the entry is valid and successful encoded, 0
932  * will be returned; otherwise, 1 will be returned.
933  */
934 int /*ARGSUSED*/
935 smb_trans2_find_mbc_encode(
936     smb_request_t	*sr,
937     smb_xa_t		*xa,
938     smb_dent_info_t	*ient,
939     int			maxdata,
940     uint16_t		infolev,
941     uint16_t		fflag,
942     uint32_t		mb_flags,
943     smb_node_t		*dir_snode,
944     smb_node_t		*sd_snode)
945 {
946 	int uni_namelen;
947 	int shortlen;
948 	uint32_t next_entry_offset;
949 	char buf83[26];
950 	smb_msgbuf_t mb;
951 	uint32_t dattr = 0;
952 	uint32_t dsize32 = 0;
953 	uint32_t asize32 = 0;
954 	u_offset_t datasz = 0;
955 	u_offset_t allocsz = 0;
956 	smb_node_t *lnk_snode;
957 	smb_attr_t lnkattr;
958 	int rc;
959 
960 	uni_namelen = smb_ascii_or_unicode_strlen(sr, ient->name);
961 	if (uni_namelen == -1)
962 		return (1);
963 
964 	next_entry_offset = maxdata + uni_namelen;
965 
966 	if (MBC_ROOM_FOR(&xa->rep_data_mb, (maxdata + uni_namelen)) == 0)
967 		return (-1);
968 
969 	if (ient->attr.sa_vattr.va_type == VLNK) {
970 		rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
971 		    sr->tid_tree->t_snode, dir_snode, ient->name, &lnk_snode,
972 		    &lnkattr, 0, 0);
973 
974 		/*
975 		 * We normally want to resolve the object to which a symlink
976 		 * refers so that CIFS clients can access sub-directories and
977 		 * find the correct association for files. This causes a
978 		 * problem, however, if a symlink in a sub-directory points
979 		 * to a parent directory (some UNIX GUI's create a symlink in
980 		 * $HOME/.desktop that points to the user's home directory).
981 		 * Some Windows applications (i.e. virus scanning) loop/hang
982 		 * trying to follow this recursive path and there is little
983 		 * we can do because the path is constructed on the client.
984 		 * skc_dirsymlink_enable allows an end-user to disable
985 		 * symlinks to directories. Symlinks to other object types
986 		 * should be unaffected.
987 		 */
988 		if (rc == 0) {
989 			if (smb_dirsymlink_enable ||
990 			    (lnkattr.sa_vattr.va_type != VDIR)) {
991 				smb_node_release(ient->snode);
992 				ient->snode = lnk_snode;
993 				ient->attr = lnkattr;
994 			} else {
995 				smb_node_release(lnk_snode);
996 			}
997 		}
998 	}
999 
1000 	if (infolev != SMB_FIND_FILE_NAMES_INFO) {
1001 		/* data size */
1002 		datasz = smb_node_get_size(ient->snode, &ient->attr);
1003 		dsize32 = (datasz > UINT_MAX) ? UINT_MAX : (uint32_t)datasz;
1004 
1005 		/* allocation size */
1006 		allocsz = ient->attr.sa_vattr.va_nblocks * DEV_BSIZE;
1007 		asize32 = (allocsz > UINT_MAX) ? UINT_MAX : (uint32_t)allocsz;
1008 
1009 		dattr = smb_mode_to_dos_attributes(&ient->attr);
1010 	}
1011 
1012 	switch (infolev) {
1013 	case SMB_INFO_STANDARD:
1014 		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
1015 			(void) smb_encode_mbc(&xa->rep_data_mb, "l",
1016 			    ient->cookie);
1017 
1018 		(void) smb_encode_mbc(&xa->rep_data_mb, "%yyyllwbu", sr,
1019 		    ient->attr.sa_crtime.tv_sec ? ient->attr.sa_crtime.tv_sec :
1020 		    ient->attr.sa_vattr.va_mtime.tv_sec,
1021 		    ient->attr.sa_vattr.va_atime.tv_sec,
1022 		    ient->attr.sa_vattr.va_mtime.tv_sec,
1023 		    dsize32,
1024 		    asize32,
1025 		    dattr,
1026 		    uni_namelen,
1027 		    ient->name);
1028 		break;
1029 
1030 	case SMB_INFO_QUERY_EA_SIZE:
1031 		if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
1032 			(void) smb_encode_mbc(&xa->rep_data_mb, "l",
1033 			    ient->cookie);
1034 
1035 		(void) smb_encode_mbc(&xa->rep_data_mb, "%yyyllwlbu", sr,
1036 		    ient->attr.sa_crtime.tv_sec ? ient->attr.sa_crtime.tv_sec :
1037 		    ient->attr.sa_vattr.va_mtime.tv_sec,
1038 		    ient->attr.sa_vattr.va_atime.tv_sec,
1039 		    ient->attr.sa_vattr.va_mtime.tv_sec,
1040 		    dsize32,
1041 		    asize32,
1042 		    dattr,
1043 		    0L,		/* EA Size */
1044 		    uni_namelen,
1045 		    ient->name);
1046 		break;
1047 
1048 	case SMB_FIND_FILE_DIRECTORY_INFO:
1049 		(void) smb_encode_mbc(&xa->rep_data_mb, "%llTTTTqqllu", sr,
1050 		    next_entry_offset,
1051 		    ient->cookie,
1052 		    ient->attr.sa_crtime.tv_sec ? &ient->attr.sa_crtime :
1053 		    &ient->attr.sa_vattr.va_mtime,
1054 		    &ient->attr.sa_vattr.va_atime,
1055 		    &ient->attr.sa_vattr.va_mtime,
1056 		    &ient->attr.sa_vattr.va_ctime,
1057 		    (uint64_t)datasz,
1058 		    (uint64_t)allocsz,
1059 		    dattr,
1060 		    uni_namelen,
1061 		    ient->name);
1062 		break;
1063 
1064 	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
1065 		bzero(buf83, sizeof (buf83));
1066 		smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
1067 		    mb_flags);
1068 		if (smb_msgbuf_encode(&mb, "u", ient->shortname) < 0) {
1069 			smb_msgbuf_term(&mb);
1070 			return (-1);
1071 		}
1072 		shortlen = smb_ascii_or_unicode_strlen(sr, ient->shortname);
1073 
1074 		(void) smb_encode_mbc(&xa->rep_data_mb, "%llTTTTqqlllb.24cu",
1075 		    sr,
1076 		    next_entry_offset,
1077 		    ient->cookie,
1078 		    ient->attr.sa_crtime.tv_sec ? &ient->attr.sa_crtime :
1079 		    &ient->attr.sa_vattr.va_mtime,
1080 		    &ient->attr.sa_vattr.va_atime,
1081 		    &ient->attr.sa_vattr.va_mtime,
1082 		    &ient->attr.sa_vattr.va_ctime,
1083 		    (uint64_t)datasz,
1084 		    (uint64_t)allocsz,
1085 		    dattr,
1086 		    uni_namelen,
1087 		    0L,
1088 		    shortlen,
1089 		    buf83,
1090 		    ient->name);
1091 
1092 		smb_msgbuf_term(&mb);
1093 		break;
1094 
1095 	case SMB_FIND_FILE_NAMES_INFO:
1096 		(void) smb_encode_mbc(&xa->rep_data_mb, "%lllu", sr,
1097 		    next_entry_offset,
1098 		    ient->cookie,
1099 		    uni_namelen,
1100 		    ient->name);
1101 		break;
1102 	}
1103 
1104 	return (0);
1105 }
1106 
1107 /*
1108  * Close a search started by a Trans2FindFirst2 request.
1109  */
1110 smb_sdrc_t
1111 smb_pre_find_close2(smb_request_t *sr)
1112 {
1113 	int rc;
1114 
1115 	rc = smbsr_decode_vwv(sr, "w", &sr->smb_sid);
1116 
1117 	DTRACE_SMB_1(op__FindClose2__start, smb_request_t *, sr);
1118 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1119 }
1120 
1121 void
1122 smb_post_find_close2(smb_request_t *sr)
1123 {
1124 	DTRACE_SMB_1(op__FindClose2__done, smb_request_t *, sr);
1125 }
1126 
1127 smb_sdrc_t
1128 smb_com_find_close2(smb_request_t *sr)
1129 {
1130 	sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree, sr->smb_sid);
1131 	if (sr->sid_odir == NULL) {
1132 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
1133 		return (SDRC_ERROR);
1134 	}
1135 
1136 	smb_rdir_close(sr);
1137 
1138 	if (smbsr_encode_empty_result(sr))
1139 		return (SDRC_ERROR);
1140 
1141 	return (SDRC_SUCCESS);
1142 }
1143