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