xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_find.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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	"@(#)smb_find.c	1.7	08/07/30 SMI"
27 
28 #include <smbsrv/smb_incl.h>
29 
30 
31 /*
32  * smb_com_find
33  *
34  * Request Format: (same as core Search Protocol - "Find First" form)
35  *
36  *  Client Request                     Description
37  *  ================================== =================================
38  *
39  *  BYTE  smb_wct;			value = 2
40  *  WORD  smb_count;			max number of entries to find
41  *  WORD  smb_attr;			search attribute
42  *  WORD  smb_bcc;			minimum value = 5
43  *  BYTE  smb_ident1;			ASCII  (04)
44  *  BYTE  smb_pathname[];		filename (may contain global characters)
45  *  BYTE  smb_ident2;			Variable Block (05)
46  *  WORD  smb_keylen;			resume key length (zero if "Find First")
47  *  BYTE  smb_resumekey[*];		"Find Next" key, * = value of smb_keylen
48  *
49  * Response Format: (same as core Search Protocol)
50  *
51  *  Server Response                    Description
52  *  ================================== =================================
53  *  BYTE  smb_wct;			value = 1
54  *  WORD  smb_count;			number of entries found
55  *  WORD  smb_bcc;			minimum value = 3
56  *  BYTE  smb_ident;			Variable Block (05)
57  *  WORD  smb_datalen;			data length
58  *  BYTE  smb_data[*];			directory entries
59  *
60  * Directory Information Entry (dir_info) Format: (same as core Search Protocol)
61  *
62  *  BYTE  find_buf_reserved[21];	reserved (resume_key)
63  *  BYTE  find_buf_attr;		attribute
64  *  WORD  find_buf_time;		modification time (hhhhh mmmmmm xxxxx)
65  *					 where 'xxxxx' is in 2 second increments
66  *  WORD  find_buf_date;		modification date (yyyyyyy mmmm ddddd)
67  *  DWORD  find_buf_size;		file size
68  *  STRING find_buf_pname[13];		file name -- ASCII (null terminated)
69  *
70  * The resume_key has the following format:
71  *
72  *  BYTE  sr_res;			reserved:
73  *					bit  7 - reserved for consumer use
74  *					bit  5,6 - reserved for system use
75  *					   (must be preserved)
76  *					bits 0-4 - reserved for server
77  *					   (must be preserved)
78  *  BYTE  sr_name[11];			pathname sought.
79  *					 Format: 1-8 character file name,
80  *					 left justified 0-3 character extension,
81  *  BYTE  sr_findid[1];			uniquely identifies find through
82  *					 find_close
83  *  BYTE  sr_server[4];			available for server use
84  *					 (must be non-zero)
85  *  BYTE  sr_res[4];			reserved for consumer use
86  *
87  * Service:
88  *
89  * The Find protocol finds the directory entry or group of entries matching the
90  * specified file pathname. The filename portion of the pathname may contain
91  * global (wild card) characters.
92  *
93  * The Find protocol is used to match the find OS/2 system call. The protocols
94  * "Find", "Find_Unique" and "Find_Close" are methods of reading (or searching)
95  * a directory. These protocols may be used in place of the core "Search"
96  * protocol when LANMAN 1.0 dialect has been negotiated. There may be cases
97  * where the Search protocol will still be used.
98  *
99  * The format of the Find protocol is the same as the core "Search" protocol.
100  * The difference is that the directory is logically Opened with a Find protocol
101  * and logically closed with the Find Close protocol. This allows the Server to
102  * make better use of its resources. Search buffers are thus held (allowing
103  * search resumption via presenting a "resume_key") until a Find Close protocol
104  * is received. The sr_findid field of each resume key is a unique identifier
105  * (within the session) of the search from "Find" through "Find close". Thus if
106  * the consumer does "Find ahead", any find buffers containing resume keys with
107  * the matching find id may be released when the Find Close is requested.
108  *
109  * As is true of a failing open, if a Find request (Find "first" request where
110  * resume_key is null) fails (no entries are found), no find close protocol is
111  * expected.
112  *
113  * If no global characters are present, a "Find Unique" protocol should be used
114  * (only one entry is expected and find close need not be sent).
115  *
116  * The file path name in the request specifies the file to be sought. The
117  * attribute field indicates the attributes that the file must have. If the
118  * attribute is zero then only normal files are returned. If the system file,
119  * hidden or directory attributes are specified then the search is inclusive --
120  * both the specified type(s) of files and normal files are returned. If the
121  * volume label attribute is specified then the search is exclusive, and only
122  * the volume label entry is returned
123  *
124  * The max-count field specifies the number of directory entries to be returned.
125  * The response will contain zero or more directory entries as determined by the
126  * count-returned field. No more than max-count entries will be returned. Only
127  * entries that match the sought filename/attribute will be returned.
128  *
129  * The resume_key field must be null (length = 0) on the initial ("Find First")
130  * find request. Subsequent find requests intended to continue a search must
131  * contain the resume_key field extracted from the last directory entry of the
132  * previous response. The resume_key field is self-contained, for on calls
133  * containing a resume_key neither the attribute or pathname fields will be
134  * valid in the request. A find request will terminate when either the
135  * requested maximum number of entries that match the named file are found, or
136  * the end of directory is reached without the maximum number of matches being
137  * found. A response containing no entries indicates that no matching entries
138  * were found between the starting point of the search and the end of directory.
139  *
140  * There may be multiple matching entries in response to a single request as
141  * Find supports "wild cards" in the file name (last component of the pathname).
142  * "?" is the wild single characters, "*" or "null" will match any number of
143  * filename characters within a single part of the filename component. The
144  * filename is divided into two parts -- an eight character name and a three
145  * character extension. The name and extension are divided by a ".".
146  *
147  * If a filename part commences with one or more "?"s then exactly that number
148  * of characters will be matched by the Wild Cards, e.g., "??x" will equal "abx"
149  * but not "abcx" or "ax". When a filename part has trailing "?"s then it will
150  * match the specified number of characters  or less, e.g., "x??" will match
151  * "xab", "xa" and "x", but not "xabc". If only "?"s are present in the filename
152  * part, then it is handled as for trailing "?"s "*" or "null" match entire
153  * pathname parts, thus "*.abc" or ".abc" will match any file with an extension
154  * of "abc". "*.*", "*" or "null" will match all files in a directory.
155  *
156  * Unprotected servers require the requester to have read permission on the
157  * subtree containing the directory searched (the share specifies read
158  * permission).
159  *
160  * Protected servers require the requester to have permission to search the
161  * specified directory.
162  *
163  * If a Find requests more data than can be placed in a message of the
164  * max-xmit-size for the TID specified, the server will return only the number
165  * of entries which will fit.
166  *
167  * The number of entries returned will be the minimum of:
168  *    1. The number of entries requested.
169  *    2. The number of (complete) entries that will fit in the negotiated SMB
170  *       buffer.
171  *    3. The number of entries that match the requested name pattern and
172  *       attributes.
173  *
174  * The error ERRnofiles set in smb_err field of the response header or a zero
175  * value in smb_count of the response indicates no matching entry was found.
176  *
177  * The resume search key returned along with each directory entry is a server
178  * defined key which when returned in the Find Next protocol, allows the
179  * directory search to be resumed at the directory entry fol lowing the one
180  * denoted by the resume search key.
181  *
182  * The date is in the following format:
183  *   bits:
184  *	1 1 1 1  1 1
185  *	5 4 3 2  1 0 9 8  7 6 5 4  3 2 1 0
186  *	y y y y  y y y m  m m m d  d d d d
187  *   where:
188  *	y - bit of year 0-119 (1980-2099)
189  *	m - bit of month 1-12
190  *	d - bit of day 1-31
191  *
192  * The time is in the following format:
193  *   bits:
194  *	1 1 1 1  1 1
195  *	5 4 3 2  1 0 9 8  7 6 5 4  3 2 1 0
196  *	h h h h  h m m m  m m m x  x x x x
197  *   where:
198  *	h - bit of hour (0-23)
199  *	m - bit of minute (0-59)
200  *	x - bit of 2 second increment
201  *
202  * Find may generate the following errors.
203  *	ERRDOS/ERRnofiles
204  *	ERRDOS/ERRbadpath
205  *	ERRDOS/ERRnoaccess
206  *	ERRDOS/ERRbadaccess
207  *	ERRDOS/ERRbadshare
208  *	ERRSRV/ERRerror
209  *	ERRSRV/ERRaccess
210  *	ERRSRV/ERRinvnid
211  */
212 smb_sdrc_t
213 smb_pre_find(smb_request_t *sr)
214 {
215 	DTRACE_SMB_1(op__Find__start, smb_request_t *, sr);
216 	return (SDRC_SUCCESS);
217 }
218 
219 void
220 smb_post_find(smb_request_t *sr)
221 {
222 	DTRACE_SMB_1(op__Find__done, smb_request_t *, sr);
223 }
224 
225 smb_sdrc_t
226 smb_com_find(smb_request_t *sr)
227 {
228 	int			rc;
229 	unsigned short		sattr, count, maxcount;
230 	char			*path;
231 	unsigned char		resume_char;
232 	uint32_t		client_key;
233 	uint16_t		index;
234 	uint32_t		cookie;
235 	struct smb_node		*node;
236 	unsigned char		type;
237 	unsigned short		key_len;
238 	smb_odir_context_t	*pc;
239 	boolean_t		find_first = B_TRUE;
240 
241 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
242 		return (SDRC_ERROR);
243 
244 	rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
245 	if ((rc != 0) || (type != 0x05))
246 		return (SDRC_ERROR);
247 
248 	if (key_len == 0) {		/* begin search */
249 		if (smb_rdir_open(sr, path, sattr) != 0)
250 			return (SDRC_ERROR);
251 		cookie = 0;
252 	} else if (key_len == 21) {
253 		sr->smb_sid = 0;
254 		if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
255 		    &resume_char, &index, &sr->smb_sid, &client_key) != 0) {
256 			/* We don't know which rdir to close */
257 			return (SDRC_ERROR);
258 		}
259 
260 		sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree,
261 		    sr->smb_sid);
262 		if (sr->sid_odir == NULL) {
263 			smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
264 			    ERRDOS, ERRbadfid);
265 			return (SDRC_ERROR);
266 		}
267 
268 		cookie = sr->sid_odir->d_cookies[index];
269 		if (cookie != 0)
270 			find_first = B_FALSE;
271 	} else {
272 		/* We don't know which rdir to close */
273 		return (SDRC_ERROR);
274 	}
275 
276 	(void) smb_mbc_encodef(&sr->reply, "bwwbw", 1, 0, VAR_BCC, 5, 0);
277 
278 	pc = kmem_zalloc(sizeof (smb_odir_context_t), KM_SLEEP);
279 	pc->dc_cookie = cookie;
280 	index = 0;
281 	count = 0;
282 	node = NULL;
283 	rc = 0;
284 
285 	if (maxcount > SMB_MAX_SEARCH)
286 		maxcount = SMB_MAX_SEARCH;
287 
288 	while (count < maxcount) {
289 		if ((rc = smb_rdir_next(sr, &node, pc)) != 0)
290 			break;
291 
292 		(void) smb_mbc_encodef(&sr->reply, "b8c3c.wwlbYl13c",
293 		    resume_char,
294 		    pc->dc_name83, pc->dc_name83+9,
295 		    index, sr->smb_sid, client_key,
296 		    pc->dc_dattr & 0xff,
297 		    smb_gmt2local(sr, pc->dc_attr.sa_vattr.va_mtime.tv_sec),
298 		    (int32_t)smb_node_get_size(node, &pc->dc_attr),
299 		    (*pc->dc_shortname) ? pc->dc_shortname :
300 		    pc->dc_name);
301 
302 		smb_node_release(node);
303 		node = NULL;
304 		sr->sid_odir->d_cookies[index] = pc->dc_cookie;
305 		count++;
306 		index++;
307 	}
308 
309 	kmem_free(pc, sizeof (smb_odir_context_t));
310 
311 	if ((rc != 0) && (rc != ENOENT)) {
312 		/* returned error by smb_rdir_next() */
313 		smb_rdir_close(sr);
314 		smbsr_errno(sr, rc);
315 		return (SDRC_ERROR);
316 	}
317 
318 	if (count == 0 && find_first) {
319 		smb_rdir_close(sr);
320 		smbsr_warn(sr, NT_STATUS_NO_MORE_FILES,
321 		    ERRDOS, ERROR_NO_MORE_FILES);
322 		return (SDRC_ERROR);
323 	}
324 
325 	rc = (MBC_LENGTH(&sr->reply) - sr->cur_reply_offset) - 8;
326 	if (smb_mbc_poke(&sr->reply, sr->cur_reply_offset, "bwwbw",
327 	    1, count, rc+3, 5, rc) < 0) {
328 		smb_rdir_close(sr);
329 		return (SDRC_ERROR);
330 	}
331 
332 	return (SDRC_SUCCESS);
333 }
334 
335 /*
336  * smb_com_find_close
337  *
338  * Request Format: (same as core Search Protocol - "Find Next" form)
339  *
340  *  Client Request                     Description
341  *  ================================== =================================
342  *
343  *  BYTE  smb_wct;			value = 2
344  *  WORD  smb_count;			max number of entries to find
345  *  WORD  smb_attr;			search attribute
346  *  WORD  smb_bcc;			minimum value = 5
347  *  BYTE  smb_ident1;			ASCII  (04)
348  *  BYTE  smb_pathname[];		null (may contain only null)
349  *  BYTE  smb_ident2;			Variable Block (05)
350  *  WORD  smb_keylen;			resume (close) key length
351  *					 (may not be zero)
352  *  BYTE  smb_resumekey[*];		"Find Close" key
353  *					 (* = value of smb_keylen)
354  *
355  * Response Format: (same format as core Search Protocol)
356  *
357  *  Server Response                    Description
358  *  ================================== =================================
359  *
360  *  BYTE  smb_wct;			value = 1
361  *  WORD  smb_reserved;			reserved
362  *  WORD  smb_bcc;			value = 3
363  *  BYTE  smb_ident;			Variable Block (05)
364  *  WORD  smb_datalen;			data length (value = 0)
365  *
366  *  The resume_key (or close key) has the following format:
367  *
368  *  BYTE  sr_res;			reserved:
369  * 					bit  7 - reserved for consumer use
370  *					bit  5,6 - reserved for system use
371  *					  (must be preserved)
372  *					bits 0-4 - rsvd for server
373  *					  (must be preserved by consumer)
374  *  BYTE  sr_name[11];			pathname sought.
375  * 					Format: 1-8 character file name,
376  *					left justified 0-3 character extension,
377  *					left justified (in last 3 chars)
378  *  BYTE  sr_findid[1];			uniquely identifies find
379  * 					through find_close
380  *  BYTE  sr_server[4];			available for server use
381  * 					(must be non-zero)
382  *  BYTE  sr_res[4];			reserved for consumer use
383  *
384  *  Service:
385  *
386  * The  Find_Close  protocol  closes  the  association  between  a  Find  id
387  * returned  (in  the  resume_key)  by  the Find protocol and the directory
388  * search.
389  *
390  * Whereas  the  First  Find  protocol  logically  opens  the  directory,
391  * subsequent  find  protocols  presenting  a resume_key  further "read" the
392  * directory,  the  Find  Close  protocol "closes" the  directory  allowing  the
393  * server to free any resources held in support of the directory search.
394  *
395  * The  Find  Close  protocol  is  used  to  match  the  find  Close  OS/2
396  * system call.  The  protocols "Find", "Find Unique" and "Find  Close" are
397  * methods  of reading  (or  searching)  a  directory.  These  protocols  may
398  * be used in place of the core "Search" protocol when LANMAN 1.0 dialect has
399  * been negotiated.  There may be cases where the Search protocol will still be
400  * used.
401  *
402  * Although  only  the  find  id  portion  the  resume  key  should  be
403  * required to  identify  the  search  being  ter minated,  the entire
404  * resume_key as returned in  the previous Find, either a "Find  First" or "Find
405  * Next" is sent to the server in this protocol.
406  *
407  * Find Close may generate the following errors:
408  *
409  *	ERRDOS/ERRbadfid
410  *	ERRSRV/ERRerror
411  *	ERRSRV/ERRinvnid
412  */
413 smb_sdrc_t
414 smb_pre_find_close(smb_request_t *sr)
415 {
416 	DTRACE_SMB_1(op__FindClose__start, smb_request_t *, sr);
417 	return (SDRC_SUCCESS);
418 }
419 
420 void
421 smb_post_find_close(smb_request_t *sr)
422 {
423 	DTRACE_SMB_1(op__FindClose__done, smb_request_t *, sr);
424 }
425 
426 smb_sdrc_t
427 smb_com_find_close(smb_request_t *sr)
428 {
429 	unsigned short		sattr, maxcount;
430 	char			*path;
431 	unsigned char		resume_char;
432 	uint32_t		resume_key;
433 	uint16_t		index;
434 	unsigned char		type;
435 	unsigned short		key_len;
436 	int			rc;
437 
438 	if (smbsr_decode_vwv(sr, "ww", &maxcount, &sattr) != 0)
439 		return (SDRC_ERROR);
440 
441 	rc = smbsr_decode_data(sr, "%Abw", sr, &path, &type, &key_len);
442 	if ((rc != 0) || (type != 0x05))
443 		return (SDRC_ERROR);
444 
445 	if (key_len == 0) {		/* begin search */
446 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
447 		return (SDRC_ERROR);
448 	}
449 
450 	if (key_len == 21) {
451 		sr->smb_sid = 0;
452 		if (smb_mbc_decodef(&sr->smb_data, "b12.wwl",
453 		    &resume_char, &index, &sr->smb_sid, &resume_key) != 0) {
454 			return (SDRC_ERROR);
455 		}
456 
457 		sr->sid_odir = smb_odir_lookup_by_sid(sr->tid_tree,
458 		    sr->smb_sid);
459 		if (sr->sid_odir == NULL) {
460 			smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
461 			    ERRDOS, ERRbadfid);
462 			return (SDRC_ERROR);
463 		}
464 	} else {
465 		return (SDRC_ERROR);
466 	}
467 
468 	smb_rdir_close(sr);
469 	if (smbsr_encode_result(sr, 1, 3, "bwwbw", 1, 0, 3, 5, 0))
470 		return (SDRC_ERROR);
471 	return (SDRC_SUCCESS);
472 }
473