xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_query_dir.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
1a90cf9f2SGordon Ross /*
2a90cf9f2SGordon Ross  * CDDL HEADER START
3a90cf9f2SGordon Ross  *
4a90cf9f2SGordon Ross  * The contents of this file are subject to the terms of the
5a90cf9f2SGordon Ross  * Common Development and Distribution License (the "License").
6a90cf9f2SGordon Ross  * You may not use this file except in compliance with the License.
7a90cf9f2SGordon Ross  *
8a90cf9f2SGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a90cf9f2SGordon Ross  * or http://www.opensolaris.org/os/licensing.
10a90cf9f2SGordon Ross  * See the License for the specific language governing permissions
11a90cf9f2SGordon Ross  * and limitations under the License.
12a90cf9f2SGordon Ross  *
13a90cf9f2SGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14a90cf9f2SGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a90cf9f2SGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16a90cf9f2SGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17a90cf9f2SGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18a90cf9f2SGordon Ross  *
19a90cf9f2SGordon Ross  * CDDL HEADER END
20a90cf9f2SGordon Ross  */
21a90cf9f2SGordon Ross /*
22a90cf9f2SGordon Ross  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23a90cf9f2SGordon Ross  * Use is subject to license terms.
24a90cf9f2SGordon Ross  *
25*93bc28dbSGordon Ross  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26a90cf9f2SGordon Ross  */
27a90cf9f2SGordon Ross 
28a90cf9f2SGordon Ross /*
29a90cf9f2SGordon Ross  * Dispatch function for SMB2_QUERY_DIRECTORY
30a90cf9f2SGordon Ross  *
31a90cf9f2SGordon Ross  * Similar to smb_trans2_find.c (from SMB1)
32a90cf9f2SGordon Ross  */
33a90cf9f2SGordon Ross 
34a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
35d082c877SGordon Ross #include <smbsrv/smb2_aapl.h>
36d082c877SGordon Ross 
37d082c877SGordon Ross /*
38d082c877SGordon Ross  * Internally defined info. level for MacOS support.
39d082c877SGordon Ross  * Make sure this does not conflict with real values in
40d082c877SGordon Ross  * FILE_INFORMATION_CLASS, and that it fits in 8-bits.
41d082c877SGordon Ross  */
42d082c877SGordon Ross #define	FileIdMacOsDirectoryInformation (FileMaximumInformation + 10)
43a90cf9f2SGordon Ross 
44a90cf9f2SGordon Ross /*
45a90cf9f2SGordon Ross  * Args (and other state) that we carry around among the
46a90cf9f2SGordon Ross  * various functions involved in SMB2 Query Directory.
47a90cf9f2SGordon Ross  */
48a90cf9f2SGordon Ross typedef struct smb2_find_args {
49a90cf9f2SGordon Ross 	uint32_t fa_maxdata;
50a90cf9f2SGordon Ross 	uint8_t fa_infoclass;
51a90cf9f2SGordon Ross 	uint8_t fa_fflags;
52a90cf9f2SGordon Ross 	uint16_t fa_maxcount;
53a90cf9f2SGordon Ross 	uint16_t fa_eos;	/* End Of Search */
54a90cf9f2SGordon Ross 	uint16_t fa_fixedsize;	/* size of fixed part of a returned entry */
55a90cf9f2SGordon Ross 	uint32_t fa_lastkey;	/* Last resume key */
56a90cf9f2SGordon Ross 	int fa_last_entry;	/* offset of last entry */
57d082c877SGordon Ross 
58d082c877SGordon Ross 	/* Normal info, per dir. entry */
59d082c877SGordon Ross 	smb_fileinfo_t fa_fi;
60d082c877SGordon Ross 
61d082c877SGordon Ross 	/* MacOS AAPL extension stuff. */
62d082c877SGordon Ross 	smb_macinfo_t fa_mi;
63a90cf9f2SGordon Ross } smb2_find_args_t;
64a90cf9f2SGordon Ross 
65a90cf9f2SGordon Ross static uint32_t smb2_find_entries(smb_request_t *,
66a90cf9f2SGordon Ross     smb_odir_t *, smb2_find_args_t *);
67d082c877SGordon Ross static uint32_t smb2_find_mbc_encode(smb_request_t *, smb2_find_args_t *);
68a90cf9f2SGordon Ross 
69a90cf9f2SGordon Ross /*
70a90cf9f2SGordon Ross  * Tunable parameter to limit the maximum
71a90cf9f2SGordon Ross  * number of entries to be returned.
72a90cf9f2SGordon Ross  */
73a90cf9f2SGordon Ross uint16_t smb2_find_max = 128;
74a90cf9f2SGordon Ross 
75a90cf9f2SGordon Ross smb_sdrc_t
76a90cf9f2SGordon Ross smb2_query_dir(smb_request_t *sr)
77a90cf9f2SGordon Ross {
78a90cf9f2SGordon Ross 	smb2_find_args_t args;
79a90cf9f2SGordon Ross 	smb_odir_resume_t odir_resume;
80a90cf9f2SGordon Ross 	smb_ofile_t *of = NULL;
81a90cf9f2SGordon Ross 	smb_odir_t *od = NULL;
82a90cf9f2SGordon Ross 	char *pattern = NULL;
83a90cf9f2SGordon Ross 	uint16_t StructSize;
84a90cf9f2SGordon Ross 	uint32_t FileIndex;
85a90cf9f2SGordon Ross 	uint16_t NameOffset;
86a90cf9f2SGordon Ross 	uint16_t NameLength;
87a90cf9f2SGordon Ross 	smb2fid_t smb2fid;
88a90cf9f2SGordon Ross 	uint16_t sattr = SMB_SEARCH_ATTRIBUTES;
89a90cf9f2SGordon Ross 	uint16_t DataOff;
90a90cf9f2SGordon Ross 	uint32_t DataLen;
91a90cf9f2SGordon Ross 	uint32_t status;
92a90cf9f2SGordon Ross 	int skip, rc = 0;
93a90cf9f2SGordon Ross 
94a90cf9f2SGordon Ross 	bzero(&args, sizeof (args));
95a90cf9f2SGordon Ross 	bzero(&odir_resume, sizeof (odir_resume));
96a90cf9f2SGordon Ross 
97a90cf9f2SGordon Ross 	/*
98a90cf9f2SGordon Ross 	 * SMB2 Query Directory request
99a90cf9f2SGordon Ross 	 */
100a90cf9f2SGordon Ross 	rc = smb_mbc_decodef(
101a90cf9f2SGordon Ross 	    &sr->smb_data, "wbblqqwwl",
102a90cf9f2SGordon Ross 	    &StructSize,		/* w */
103a90cf9f2SGordon Ross 	    &args.fa_infoclass,		/* b */
104a90cf9f2SGordon Ross 	    &args.fa_fflags,		/* b */
105a90cf9f2SGordon Ross 	    &FileIndex,			/* l */
106a90cf9f2SGordon Ross 	    &smb2fid.persistent,	/* q */
107a90cf9f2SGordon Ross 	    &smb2fid.temporal,		/* q */
108a90cf9f2SGordon Ross 	    &NameOffset,		/* w */
109a90cf9f2SGordon Ross 	    &NameLength,		/* w */
110a90cf9f2SGordon Ross 	    &args.fa_maxdata);		/* l */
111a90cf9f2SGordon Ross 	if (rc || StructSize != 33)
112a90cf9f2SGordon Ross 		return (SDRC_ERROR);
113a90cf9f2SGordon Ross 
114a90cf9f2SGordon Ross 	status = smb2sr_lookup_fid(sr, &smb2fid);
115*93bc28dbSGordon Ross 	of = sr->fid_ofile;
116*93bc28dbSGordon Ross 
117*93bc28dbSGordon Ross 	DTRACE_SMB2_START(op__QueryDirectory, smb_request_t *, sr);
118*93bc28dbSGordon Ross 
119a90cf9f2SGordon Ross 	if (status)
120a90cf9f2SGordon Ross 		goto errout;
121a90cf9f2SGordon Ross 
122a90cf9f2SGordon Ross 	/*
123a90cf9f2SGordon Ross 	 * If there's an input buffer (search pattern), decode it.
124a90cf9f2SGordon Ross 	 * Two times MAXNAMELEN because it represents the UNICODE string
125a90cf9f2SGordon Ross 	 * length in bytes.
126a90cf9f2SGordon Ross 	 */
127a90cf9f2SGordon Ross 	if (NameLength >= (2 * MAXNAMELEN)) {
128a90cf9f2SGordon Ross 		status = NT_STATUS_OBJECT_PATH_INVALID;
129a90cf9f2SGordon Ross 		goto errout;
130a90cf9f2SGordon Ross 	}
131a90cf9f2SGordon Ross 	if (NameLength != 0) {
132a90cf9f2SGordon Ross 		/*
133a90cf9f2SGordon Ross 		 * We're normally positioned at the pattern now,
134a90cf9f2SGordon Ross 		 * but there could be some padding before it.
135a90cf9f2SGordon Ross 		 */
136a90cf9f2SGordon Ross 		skip = (sr->smb2_cmd_hdr + NameOffset) -
137a90cf9f2SGordon Ross 		    sr->smb_data.chain_offset;
138a90cf9f2SGordon Ross 		if (skip < 0) {
139a90cf9f2SGordon Ross 			status = NT_STATUS_OBJECT_PATH_INVALID;
140a90cf9f2SGordon Ross 			goto errout;
141a90cf9f2SGordon Ross 		}
142a90cf9f2SGordon Ross 		if (skip > 0)
143a90cf9f2SGordon Ross 			(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
144a90cf9f2SGordon Ross 		rc = smb_mbc_decodef(&sr->smb_data, "%#U", sr,
145a90cf9f2SGordon Ross 		    NameLength, &pattern);
146a90cf9f2SGordon Ross 		if (rc || pattern == NULL) {
147a90cf9f2SGordon Ross 			status = NT_STATUS_OBJECT_PATH_INVALID;
148a90cf9f2SGordon Ross 			goto errout;
149a90cf9f2SGordon Ross 		}
150a90cf9f2SGordon Ross 	} else
151a90cf9f2SGordon Ross 		pattern = "*";
152a90cf9f2SGordon Ross 
153a90cf9f2SGordon Ross 	/*
154a90cf9f2SGordon Ross 	 * Setup the output buffer.
155a90cf9f2SGordon Ross 	 */
156a90cf9f2SGordon Ross 	if (args.fa_maxdata > smb2_max_trans)
157a90cf9f2SGordon Ross 		args.fa_maxdata = smb2_max_trans;
158a90cf9f2SGordon Ross 	sr->raw_data.max_bytes = args.fa_maxdata;
159a90cf9f2SGordon Ross 
160a90cf9f2SGordon Ross 	/*
161d082c877SGordon Ross 	 * Get the fixed size of entries we will return, which
162a90cf9f2SGordon Ross 	 * lets us estimate the number of entries we'll need.
163a90cf9f2SGordon Ross 	 *
164a90cf9f2SGordon Ross 	 * Also use this opportunity to validate fa_infoclass.
165a90cf9f2SGordon Ross 	 */
166a90cf9f2SGordon Ross 
167a90cf9f2SGordon Ross 	switch (args.fa_infoclass) {
168a90cf9f2SGordon Ross 	case FileDirectoryInformation:		/* 1 */
169a90cf9f2SGordon Ross 		args.fa_fixedsize = 64;
170a90cf9f2SGordon Ross 		break;
171a90cf9f2SGordon Ross 	case FileFullDirectoryInformation:	/* 2 */
172a90cf9f2SGordon Ross 		args.fa_fixedsize = 68;
173a90cf9f2SGordon Ross 		break;
174a90cf9f2SGordon Ross 	case FileBothDirectoryInformation:	/* 3 */
175a90cf9f2SGordon Ross 		args.fa_fixedsize = 94;
176a90cf9f2SGordon Ross 		break;
177a90cf9f2SGordon Ross 	case FileNamesInformation:		/* 12 */
178a90cf9f2SGordon Ross 		args.fa_fixedsize = 12;
179a90cf9f2SGordon Ross 		break;
180a90cf9f2SGordon Ross 	case FileIdBothDirectoryInformation:	/* 37 */
181a90cf9f2SGordon Ross 		args.fa_fixedsize = 96;
182a90cf9f2SGordon Ross 		break;
183a90cf9f2SGordon Ross 	case FileIdFullDirectoryInformation:	/* 38 */
184a90cf9f2SGordon Ross 		args.fa_fixedsize = 84;
185a90cf9f2SGordon Ross 		break;
186a90cf9f2SGordon Ross 	default:
187a90cf9f2SGordon Ross 		status = NT_STATUS_INVALID_INFO_CLASS;
188a90cf9f2SGordon Ross 		goto errout;
189a90cf9f2SGordon Ross 	}
190a90cf9f2SGordon Ross 
191d082c877SGordon Ross 	/*
192d082c877SGordon Ross 	 * MacOS, when using the AAPL CreateContext extensions
193d082c877SGordon Ross 	 * and the "read dir attr" feature, uses a non-standard
194d082c877SGordon Ross 	 * information format for directory entries.  Internally
195d082c877SGordon Ross 	 * we'll use a fake info level to represent this case.
196d082c877SGordon Ross 	 * (Wish they had just defined a new info level.)
197d082c877SGordon Ross 	 */
198d082c877SGordon Ross 	if ((sr->session->s_flags & SMB_SSN_AAPL_READDIR) != 0 &&
199d082c877SGordon Ross 	    args.fa_infoclass == FileIdBothDirectoryInformation) {
200d082c877SGordon Ross 		args.fa_infoclass = FileIdMacOsDirectoryInformation;
201d082c877SGordon Ross 		args.fa_fixedsize = 96; /* yes, same size */
202d082c877SGordon Ross 	}
203d082c877SGordon Ross 
204a90cf9f2SGordon Ross 	args.fa_maxcount = args.fa_maxdata / (args.fa_fixedsize + 4);
205a90cf9f2SGordon Ross 	if (args.fa_maxcount == 0)
206a90cf9f2SGordon Ross 		args.fa_maxcount = 1;
207a90cf9f2SGordon Ross 	if ((smb2_find_max != 0) && (args.fa_maxcount > smb2_find_max))
208a90cf9f2SGordon Ross 		args.fa_maxcount = smb2_find_max;
209a90cf9f2SGordon Ross 	if (args.fa_fflags & SMB2_QDIR_FLAG_SINGLE)
210a90cf9f2SGordon Ross 		args.fa_maxcount = 1;
211a90cf9f2SGordon Ross 
212a90cf9f2SGordon Ross 	/*
213a90cf9f2SGordon Ross 	 * If this ofile does not have an odir yet, get one.
214a90cf9f2SGordon Ross 	 */
215a90cf9f2SGordon Ross 	mutex_enter(&of->f_mutex);
216a90cf9f2SGordon Ross 	if ((od = of->f_odir) == NULL) {
217a90cf9f2SGordon Ross 		status = smb_odir_openfh(sr, pattern, sattr, &od);
218a90cf9f2SGordon Ross 		of->f_odir = od;
219a90cf9f2SGordon Ross 	}
220a90cf9f2SGordon Ross 	mutex_exit(&of->f_mutex);
221a90cf9f2SGordon Ross 	if (od == NULL) {
222a90cf9f2SGordon Ross 		if (status == 0)
223a90cf9f2SGordon Ross 			status = NT_STATUS_INTERNAL_ERROR;
224a90cf9f2SGordon Ross 		goto errout;
225a90cf9f2SGordon Ross 	}
226a90cf9f2SGordon Ross 
227a90cf9f2SGordon Ross 	/*
228a90cf9f2SGordon Ross 	 * "Reopen" sets a new pattern and restart.
229a90cf9f2SGordon Ross 	 */
230a90cf9f2SGordon Ross 	if (args.fa_fflags & SMB2_QDIR_FLAG_REOPEN) {
231a90cf9f2SGordon Ross 		smb_odir_reopen(od, pattern, sattr);
232a90cf9f2SGordon Ross 	}
233a90cf9f2SGordon Ross 
234a90cf9f2SGordon Ross 	/*
235a90cf9f2SGordon Ross 	 * Set the correct position in the directory.
236a90cf9f2SGordon Ross 	 */
237a90cf9f2SGordon Ross 	if (args.fa_fflags & SMB2_QDIR_FLAG_RESTART) {
238a90cf9f2SGordon Ross 		odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
239a90cf9f2SGordon Ross 		odir_resume.or_cookie = 0;
240a90cf9f2SGordon Ross 	} else if (args.fa_fflags & SMB2_QDIR_FLAG_INDEX) {
241a90cf9f2SGordon Ross 		odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
242a90cf9f2SGordon Ross 		odir_resume.or_cookie = FileIndex;
243a90cf9f2SGordon Ross 	} else {
244a90cf9f2SGordon Ross 		odir_resume.or_type = SMB_ODIR_RESUME_CONT;
245a90cf9f2SGordon Ross 	}
246a90cf9f2SGordon Ross 	smb_odir_resume_at(od, &odir_resume);
247a90cf9f2SGordon Ross 	of->f_seek_pos = od->d_offset;
248a90cf9f2SGordon Ross 
249a90cf9f2SGordon Ross 	/*
250a90cf9f2SGordon Ross 	 * The real work of readdir and format conversion.
251a90cf9f2SGordon Ross 	 */
252a90cf9f2SGordon Ross 	status = smb2_find_entries(sr, od, &args);
253a90cf9f2SGordon Ross 
254a90cf9f2SGordon Ross 	of->f_seek_pos = od->d_offset;
255a90cf9f2SGordon Ross 
256*93bc28dbSGordon Ross 	if ((args.fa_fflags & SMB2_QDIR_FLAG_SINGLE) &&
257*93bc28dbSGordon Ross 	    status == NT_STATUS_NO_MORE_FILES) {
258a90cf9f2SGordon Ross 		status = NT_STATUS_NO_SUCH_FILE;
259a90cf9f2SGordon Ross 	}
260*93bc28dbSGordon Ross 
261*93bc28dbSGordon Ross errout:
262*93bc28dbSGordon Ross 	sr->smb2_status = status;
263*93bc28dbSGordon Ross 	DTRACE_SMB2_DONE(op__QueryDirectory, smb_request_t *, sr);
264*93bc28dbSGordon Ross 
265a90cf9f2SGordon Ross 	/*
266*93bc28dbSGordon Ross 	 * Note: NT_STATUS_NO_MORE_FILES is a warning
267a90cf9f2SGordon Ross 	 * used to tell the client that this data return
268a90cf9f2SGordon Ross 	 * is the last of the enumeration.  Returning this
269a90cf9f2SGordon Ross 	 * warning now (with the data) saves the client a
270a90cf9f2SGordon Ross 	 * round trip that would otherwise be needed to
271a90cf9f2SGordon Ross 	 * find out it's at the end.
272a90cf9f2SGordon Ross 	 */
273*93bc28dbSGordon Ross 	if (status != 0 &&
274*93bc28dbSGordon Ross 	    status != NT_STATUS_NO_MORE_FILES) {
275*93bc28dbSGordon Ross 		smb2sr_put_error(sr, status);
276*93bc28dbSGordon Ross 		return (SDRC_SUCCESS);
277a90cf9f2SGordon Ross 	}
278a90cf9f2SGordon Ross 
279a90cf9f2SGordon Ross 	/*
280a90cf9f2SGordon Ross 	 * SMB2 Query Directory reply
281a90cf9f2SGordon Ross 	 */
282a90cf9f2SGordon Ross 	StructSize = 9;
283a90cf9f2SGordon Ross 	DataOff = SMB2_HDR_SIZE + 8;
284a90cf9f2SGordon Ross 	DataLen = MBC_LENGTH(&sr->raw_data);
285a90cf9f2SGordon Ross 	rc = smb_mbc_encodef(
286a90cf9f2SGordon Ross 	    &sr->reply, "wwlC",
287a90cf9f2SGordon Ross 	    StructSize,		/* w */
288a90cf9f2SGordon Ross 	    DataOff,		/* w */
289a90cf9f2SGordon Ross 	    DataLen,		/* l */
290a90cf9f2SGordon Ross 	    &sr->raw_data);	/* C */
291a90cf9f2SGordon Ross 	if (DataLen == 0)
292a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(&sr->reply, ".");
293a90cf9f2SGordon Ross 
294*93bc28dbSGordon Ross 	if (rc)
295*93bc28dbSGordon Ross 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
296*93bc28dbSGordon Ross 
297a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
298a90cf9f2SGordon Ross }
299a90cf9f2SGordon Ross 
300a90cf9f2SGordon Ross /*
301a90cf9f2SGordon Ross  * smb2_find_entries
302a90cf9f2SGordon Ross  *
303a90cf9f2SGordon Ross  * Find and encode up to args->fa_maxcount directory entries.
304a90cf9f2SGordon Ross  *
305a90cf9f2SGordon Ross  * Returns:
306a90cf9f2SGordon Ross  *   NT status
307a90cf9f2SGordon Ross  */
308a90cf9f2SGordon Ross static uint32_t
309a90cf9f2SGordon Ross smb2_find_entries(smb_request_t *sr, smb_odir_t *od, smb2_find_args_t *args)
310a90cf9f2SGordon Ross {
311a90cf9f2SGordon Ross 	smb_odir_resume_t odir_resume;
312d082c877SGordon Ross 	char 		*tbuf = NULL;
313d082c877SGordon Ross 	size_t		tbuflen = 0;
314a90cf9f2SGordon Ross 	uint16_t	count;
315a90cf9f2SGordon Ross 	uint16_t	minsize;
316a90cf9f2SGordon Ross 	uint32_t	status = 0;
317a90cf9f2SGordon Ross 	int		rc = -1;
318a90cf9f2SGordon Ross 
319a90cf9f2SGordon Ross 	/*
320a90cf9f2SGordon Ross 	 * Let's stop when the remaining space will not hold a
321a90cf9f2SGordon Ross 	 * minimum size entry.  That's the fixed part plus the
322a90cf9f2SGordon Ross 	 * storage size of a 1 char unicode string.
323a90cf9f2SGordon Ross 	 */
324a90cf9f2SGordon Ross 	minsize = args->fa_fixedsize + 2;
325a90cf9f2SGordon Ross 
326d082c877SGordon Ross 	/*
327d082c877SGordon Ross 	 * FileIdMacOsDirectoryInformation needs some buffer space
328d082c877SGordon Ross 	 * for composing directory entry + stream name for lookup.
329d082c877SGordon Ross 	 * Get the buffer now to avoid alloc/free per entry.
330d082c877SGordon Ross 	 */
331d082c877SGordon Ross 	if (args->fa_infoclass == FileIdMacOsDirectoryInformation) {
332d082c877SGordon Ross 		tbuflen = 2 * MAXNAMELEN;
333d082c877SGordon Ross 		tbuf = kmem_alloc(tbuflen, KM_SLEEP);
334d082c877SGordon Ross 	}
335d082c877SGordon Ross 
336a90cf9f2SGordon Ross 	count = 0;
337a90cf9f2SGordon Ross 	while (count < args->fa_maxcount) {
338a90cf9f2SGordon Ross 
339a90cf9f2SGordon Ross 		if (!MBC_ROOM_FOR(&sr->raw_data, minsize)) {
340a90cf9f2SGordon Ross 			status = NT_STATUS_BUFFER_OVERFLOW;
341a90cf9f2SGordon Ross 			break;
342a90cf9f2SGordon Ross 		}
343a90cf9f2SGordon Ross 
344d082c877SGordon Ross 		rc = smb_odir_read_fileinfo(sr, od,
345d082c877SGordon Ross 		    &args->fa_fi, &args->fa_eos);
346a90cf9f2SGordon Ross 		if (rc == ENOENT) {
347a90cf9f2SGordon Ross 			status = NT_STATUS_NO_MORE_FILES;
348a90cf9f2SGordon Ross 			break;
349a90cf9f2SGordon Ross 		}
350a90cf9f2SGordon Ross 		if (rc != 0) {
351a90cf9f2SGordon Ross 			status = smb_errno2status(rc);
352a90cf9f2SGordon Ross 			break;
353a90cf9f2SGordon Ross 		}
354a90cf9f2SGordon Ross 		if (args->fa_eos != 0) {
355a90cf9f2SGordon Ross 			/* The readdir call hit the end. */
356a90cf9f2SGordon Ross 			status = NT_STATUS_NO_MORE_FILES;
357a90cf9f2SGordon Ross 			break;
358a90cf9f2SGordon Ross 		}
359a90cf9f2SGordon Ross 
360d082c877SGordon Ross 		if (args->fa_infoclass == FileIdMacOsDirectoryInformation)
361d082c877SGordon Ross 			(void) smb2_aapl_get_macinfo(sr, od,
362d082c877SGordon Ross 			    &args->fa_fi, &args->fa_mi, tbuf, tbuflen);
363d082c877SGordon Ross 
364d082c877SGordon Ross 		if (smb2_aapl_use_file_ids == 0 &&
365d082c877SGordon Ross 		    (sr->session->s_flags & SMB_SSN_AAPL_CCEXT) != 0)
366d082c877SGordon Ross 			args->fa_fi.fi_nodeid = 0;
367d082c877SGordon Ross 
368d082c877SGordon Ross 		status = smb2_find_mbc_encode(sr, args);
369a90cf9f2SGordon Ross 		if (status) {
370a90cf9f2SGordon Ross 			/*
371a90cf9f2SGordon Ross 			 * We read a directory entry but failed to
372a90cf9f2SGordon Ross 			 * copy it into the output buffer.  Rewind
373a90cf9f2SGordon Ross 			 * the directory pointer so this will be
374a90cf9f2SGordon Ross 			 * the first entry read next time.
375a90cf9f2SGordon Ross 			 */
376a90cf9f2SGordon Ross 			bzero(&odir_resume, sizeof (odir_resume));
377a90cf9f2SGordon Ross 			odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
378a90cf9f2SGordon Ross 			odir_resume.or_cookie = args->fa_lastkey;
379a90cf9f2SGordon Ross 			smb_odir_resume_at(od, &odir_resume);
380a90cf9f2SGordon Ross 			break;
381a90cf9f2SGordon Ross 		}
382a90cf9f2SGordon Ross 
383a90cf9f2SGordon Ross 		/*
384a90cf9f2SGordon Ross 		 * Save the offset of the next entry we'll read.
385a90cf9f2SGordon Ross 		 * If we fail copying, we'll need this offset.
386a90cf9f2SGordon Ross 		 */
387d082c877SGordon Ross 		args->fa_lastkey = args->fa_fi.fi_cookie;
388a90cf9f2SGordon Ross 		++count;
389a90cf9f2SGordon Ross 	}
390a90cf9f2SGordon Ross 
391a90cf9f2SGordon Ross 	if (count == 0) {
392a90cf9f2SGordon Ross 		ASSERT(status != 0);
393a90cf9f2SGordon Ross 	} else {
394a90cf9f2SGordon Ross 		/*
395a90cf9f2SGordon Ross 		 * We copied some directory entries, but stopped for
396a90cf9f2SGordon Ross 		 * NT_STATUS_NO_MORE_FILES, or something.
397a90cf9f2SGordon Ross 		 *
398a90cf9f2SGordon Ross 		 * Per [MS-FSCC] sec. 2.4, the last entry in the
399a90cf9f2SGordon Ross 		 * enumeration MUST have its NextEntryOffset value
400a90cf9f2SGordon Ross 		 * set to zero.  Overwrite that in the last entry.
401a90cf9f2SGordon Ross 		 */
402a90cf9f2SGordon Ross 		(void) smb_mbc_poke(&sr->raw_data,
403a90cf9f2SGordon Ross 		    args->fa_last_entry, "l", 0);
404a90cf9f2SGordon Ross 		status = 0;
405a90cf9f2SGordon Ross 	}
406a90cf9f2SGordon Ross 
407d082c877SGordon Ross 	if (tbuf != NULL)
408d082c877SGordon Ross 		kmem_free(tbuf, tbuflen);
409d082c877SGordon Ross 
410a90cf9f2SGordon Ross 	return (status);
411a90cf9f2SGordon Ross }
412a90cf9f2SGordon Ross 
413a90cf9f2SGordon Ross /*
414a90cf9f2SGordon Ross  * smb2_mbc_encode
415a90cf9f2SGordon Ross  *
416a90cf9f2SGordon Ross  * This function encodes the mbc for one directory entry.
417a90cf9f2SGordon Ross  *
418a90cf9f2SGordon Ross  * The function returns -1 when the max data requested by client
419a90cf9f2SGordon Ross  * is reached. If the entry is valid and successful encoded, 0
420a90cf9f2SGordon Ross  * will be returned; otherwise, 1 will be returned.
421a90cf9f2SGordon Ross  *
422a90cf9f2SGordon Ross  * We always null terminate the filename. The space for the null
423a90cf9f2SGordon Ross  * is included in the maxdata calculation and is therefore included
424a90cf9f2SGordon Ross  * in the next_entry_offset. namelen is the unterminated length of
425a90cf9f2SGordon Ross  * the filename. For levels except STANDARD and EA_SIZE, if the
426a90cf9f2SGordon Ross  * filename is ascii the name length returned to the client should
427a90cf9f2SGordon Ross  * include the null terminator. Otherwise the length returned to
428a90cf9f2SGordon Ross  * the client should not include the terminator.
429a90cf9f2SGordon Ross  *
430a90cf9f2SGordon Ross  * Returns: 0 - data successfully encoded
431a90cf9f2SGordon Ross  *      NT status
432a90cf9f2SGordon Ross  */
433a90cf9f2SGordon Ross static uint32_t
434d082c877SGordon Ross smb2_find_mbc_encode(smb_request_t *sr, smb2_find_args_t *args)
435a90cf9f2SGordon Ross {
436d082c877SGordon Ross 	smb_fileinfo_t	*fileinfo = &args->fa_fi;
437d082c877SGordon Ross 	smb_macinfo_t	*macinfo = &args->fa_mi;
438a90cf9f2SGordon Ross 	uint8_t		buf83[26];
439a90cf9f2SGordon Ross 	smb_msgbuf_t	mb;
440a90cf9f2SGordon Ross 	int		namelen, padsz;
441a90cf9f2SGordon Ross 	int		shortlen = 0;
442a90cf9f2SGordon Ross 	int		rc, starting_offset;
443a90cf9f2SGordon Ross 	uint32_t	next_entry_offset;
444a90cf9f2SGordon Ross 	uint32_t	mb_flags = SMB_MSGBUF_UNICODE;
445a90cf9f2SGordon Ross 	uint32_t	resume_key;
446a90cf9f2SGordon Ross 
447a90cf9f2SGordon Ross 	namelen = smb_wcequiv_strlen(fileinfo->fi_name);
448a90cf9f2SGordon Ross 	if (namelen == -1)
449a90cf9f2SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
450a90cf9f2SGordon Ross 
451a90cf9f2SGordon Ross 	/*
452a90cf9f2SGordon Ross 	 * Keep track of where the last entry starts so we can
453a90cf9f2SGordon Ross 	 * come back and poke the NextEntryOffset field.  Also,
454a90cf9f2SGordon Ross 	 * after enumeration finishes, the caller uses this to
455a90cf9f2SGordon Ross 	 * poke the last entry again with zero to mark it as
456a90cf9f2SGordon Ross 	 * the end of the enumeration.
457a90cf9f2SGordon Ross 	 */
458a90cf9f2SGordon Ross 	starting_offset = sr->raw_data.chain_offset;
459a90cf9f2SGordon Ross 
460a90cf9f2SGordon Ross 	/*
461a90cf9f2SGordon Ross 	 * Technically (per MS-SMB2) resume keys are optional.
462a90cf9f2SGordon Ross 	 * Windows doesn't need them, but MacOS does.
463a90cf9f2SGordon Ross 	 */
464a90cf9f2SGordon Ross 	resume_key = fileinfo->fi_cookie;
465a90cf9f2SGordon Ross 
466a90cf9f2SGordon Ross 	/*
467a90cf9f2SGordon Ross 	 * This switch handles all the "information levels" (formats)
468a90cf9f2SGordon Ross 	 * that we support.  Note that all formats have the file name
469a90cf9f2SGordon Ross 	 * placed after some fixed-size data, and the code to write
470a90cf9f2SGordon Ross 	 * the file name is factored out at the end of this switch.
471a90cf9f2SGordon Ross 	 */
472a90cf9f2SGordon Ross 	switch (args->fa_infoclass) {
473a90cf9f2SGordon Ross 
474a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_DIRECTORY_INFO */
475a90cf9f2SGordon Ross 	case FileDirectoryInformation:		/* 1 */
476a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
477a90cf9f2SGordon Ross 		    &sr->raw_data, "llTTTTqqll",
478a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
479a90cf9f2SGordon Ross 		    resume_key,
480a90cf9f2SGordon Ross 		    &fileinfo->fi_crtime,
481a90cf9f2SGordon Ross 		    &fileinfo->fi_atime,
482a90cf9f2SGordon Ross 		    &fileinfo->fi_mtime,
483a90cf9f2SGordon Ross 		    &fileinfo->fi_ctime,
484a90cf9f2SGordon Ross 		    fileinfo->fi_size,
485a90cf9f2SGordon Ross 		    fileinfo->fi_alloc_size,
486a90cf9f2SGordon Ross 		    fileinfo->fi_dosattr,
487a90cf9f2SGordon Ross 		    namelen);
488a90cf9f2SGordon Ross 		break;
489a90cf9f2SGordon Ross 
490a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_FULL_DIRECTORY_INFO */
491a90cf9f2SGordon Ross 	case FileFullDirectoryInformation:	/* 2 */
492a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
493a90cf9f2SGordon Ross 		    &sr->raw_data, "llTTTTqqlll",
494a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
495a90cf9f2SGordon Ross 		    resume_key,
496a90cf9f2SGordon Ross 		    &fileinfo->fi_crtime,
497a90cf9f2SGordon Ross 		    &fileinfo->fi_atime,
498a90cf9f2SGordon Ross 		    &fileinfo->fi_mtime,
499a90cf9f2SGordon Ross 		    &fileinfo->fi_ctime,
500a90cf9f2SGordon Ross 		    fileinfo->fi_size,
501a90cf9f2SGordon Ross 		    fileinfo->fi_alloc_size,
502a90cf9f2SGordon Ross 		    fileinfo->fi_dosattr,
503a90cf9f2SGordon Ross 		    namelen,
504a90cf9f2SGordon Ross 		    0L);	/* EaSize */
505a90cf9f2SGordon Ross 		break;
506a90cf9f2SGordon Ross 
507a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO */
508a90cf9f2SGordon Ross 	case FileIdFullDirectoryInformation:	/* 38 */
509a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
510a90cf9f2SGordon Ross 		    &sr->raw_data, "llTTTTqqllllq",
511a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
512a90cf9f2SGordon Ross 		    resume_key,
513a90cf9f2SGordon Ross 		    &fileinfo->fi_crtime,
514a90cf9f2SGordon Ross 		    &fileinfo->fi_atime,
515a90cf9f2SGordon Ross 		    &fileinfo->fi_mtime,
516a90cf9f2SGordon Ross 		    &fileinfo->fi_ctime,
517a90cf9f2SGordon Ross 		    fileinfo->fi_size,
518a90cf9f2SGordon Ross 		    fileinfo->fi_alloc_size,
519a90cf9f2SGordon Ross 		    fileinfo->fi_dosattr,
520a90cf9f2SGordon Ross 		    namelen,
521a90cf9f2SGordon Ross 		    0L,		/* EaSize */
522a90cf9f2SGordon Ross 		    0L,		/* reserved */
523a90cf9f2SGordon Ross 		    fileinfo->fi_nodeid);
524a90cf9f2SGordon Ross 		break;
525a90cf9f2SGordon Ross 
526a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_BOTH_DIRECTORY_INFO */
527a90cf9f2SGordon Ross 	case FileBothDirectoryInformation:	/* 3 */
528a90cf9f2SGordon Ross 		bzero(buf83, sizeof (buf83));
529a90cf9f2SGordon Ross 		smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
530a90cf9f2SGordon Ross 		if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname))
531a90cf9f2SGordon Ross 			shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
532a90cf9f2SGordon Ross 
533a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
534a90cf9f2SGordon Ross 		    &sr->raw_data, "llTTTTqqlllb.24c",
535a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
536a90cf9f2SGordon Ross 		    resume_key,
537a90cf9f2SGordon Ross 		    &fileinfo->fi_crtime,
538a90cf9f2SGordon Ross 		    &fileinfo->fi_atime,
539a90cf9f2SGordon Ross 		    &fileinfo->fi_mtime,
540a90cf9f2SGordon Ross 		    &fileinfo->fi_ctime,
541a90cf9f2SGordon Ross 		    fileinfo->fi_size,
542a90cf9f2SGordon Ross 		    fileinfo->fi_alloc_size,
543a90cf9f2SGordon Ross 		    fileinfo->fi_dosattr,
544a90cf9f2SGordon Ross 		    namelen,
545a90cf9f2SGordon Ross 		    0L,		/* EaSize */
546a90cf9f2SGordon Ross 		    shortlen,
547a90cf9f2SGordon Ross 		    buf83);
548a90cf9f2SGordon Ross 
549a90cf9f2SGordon Ross 		smb_msgbuf_term(&mb);
550a90cf9f2SGordon Ross 		break;
551a90cf9f2SGordon Ross 
552a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO */
553a90cf9f2SGordon Ross 	case FileIdBothDirectoryInformation:	/* 37 */
554a90cf9f2SGordon Ross 		bzero(buf83, sizeof (buf83));
555a90cf9f2SGordon Ross 		smb_msgbuf_init(&mb, buf83, sizeof (buf83), mb_flags);
556a90cf9f2SGordon Ross 		if (!smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname))
557a90cf9f2SGordon Ross 			shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
558a90cf9f2SGordon Ross 
559a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
560a90cf9f2SGordon Ross 		    &sr->raw_data, "llTTTTqqlllb.24c..q",
561a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
562a90cf9f2SGordon Ross 		    resume_key,
563a90cf9f2SGordon Ross 		    &fileinfo->fi_crtime,
564a90cf9f2SGordon Ross 		    &fileinfo->fi_atime,
565a90cf9f2SGordon Ross 		    &fileinfo->fi_mtime,
566a90cf9f2SGordon Ross 		    &fileinfo->fi_ctime,
567a90cf9f2SGordon Ross 		    fileinfo->fi_size,		/* q */
568a90cf9f2SGordon Ross 		    fileinfo->fi_alloc_size,	/* q */
569a90cf9f2SGordon Ross 		    fileinfo->fi_dosattr,	/* l */
570a90cf9f2SGordon Ross 		    namelen,			/* l */
571a90cf9f2SGordon Ross 		    0L,		/* EaSize	   l */
572a90cf9f2SGordon Ross 		    shortlen,			/* b. */
573a90cf9f2SGordon Ross 		    buf83,			/* 24c */
574a90cf9f2SGordon Ross 		    /* reserved			   .. */
575a90cf9f2SGordon Ross 		    fileinfo->fi_nodeid);	/* q */
576a90cf9f2SGordon Ross 
577a90cf9f2SGordon Ross 		smb_msgbuf_term(&mb);
578a90cf9f2SGordon Ross 		break;
579a90cf9f2SGordon Ross 
580d082c877SGordon Ross 	/*
581d082c877SGordon Ross 	 * MacOS, when using the AAPL extensions (see smb2_create)
582d082c877SGordon Ross 	 * uses modified directory listing responses where the
583d082c877SGordon Ross 	 * "EA size" field is replaced with "maximum access".
584d082c877SGordon Ross 	 * This avoids the need for MacOS Finder to come back
585d082c877SGordon Ross 	 * N times to get the maximum access for every file.
586d082c877SGordon Ross 	 */
587d082c877SGordon Ross 	case FileIdMacOsDirectoryInformation:
588d082c877SGordon Ross 		rc = smb_mbc_encodef(
589d082c877SGordon Ross 		    &sr->raw_data, "llTTTTqqll",
590d082c877SGordon Ross 		    0,	/* NextEntryOffset (set later) */
591d082c877SGordon Ross 		    resume_key,		/* a.k.a. file index */
592d082c877SGordon Ross 		    &fileinfo->fi_crtime,
593d082c877SGordon Ross 		    &fileinfo->fi_atime,
594d082c877SGordon Ross 		    &fileinfo->fi_mtime,
595d082c877SGordon Ross 		    &fileinfo->fi_ctime,
596d082c877SGordon Ross 		    fileinfo->fi_size,		/* q */
597d082c877SGordon Ross 		    fileinfo->fi_alloc_size,	/* q */
598d082c877SGordon Ross 		    fileinfo->fi_dosattr,	/* l */
599d082c877SGordon Ross 		    namelen);			/* l */
600d082c877SGordon Ross 		if (rc != 0)
601d082c877SGordon Ross 			break;
602d082c877SGordon Ross 		/*
603d082c877SGordon Ross 		 * This where FileIdMacOsDirectoryInformation
604d082c877SGordon Ross 		 * differs from FileIdBothDirectoryInformation
605d082c877SGordon Ross 		 * Instead of: EaSize, ShortNameLen, ShortName;
606d082c877SGordon Ross 		 * MacOS wants: MaxAccess, ResourceForkSize, and
607d082c877SGordon Ross 		 * 16 bytes of "compressed finder info".
608d082c877SGordon Ross 		 * mi_rforksize + mi_finderinfo falls where
609d082c877SGordon Ross 		 * the 24 byte shortname would normally be.
610d082c877SGordon Ross 		 */
611d082c877SGordon Ross 		rc = smb_mbc_encodef(
612d082c877SGordon Ross 		    &sr->raw_data, "l..q16cwq",
613d082c877SGordon Ross 		    macinfo->mi_maxaccess,	/* l */
614d082c877SGordon Ross 		    /* short_name_len, reserved  (..) */
615d082c877SGordon Ross 		    macinfo->mi_rforksize,	/* q */
616d082c877SGordon Ross 		    macinfo->mi_finderinfo,	/* 16c */
617d082c877SGordon Ross 		    macinfo->mi_unixmode,	/* w */
618d082c877SGordon Ross 		    fileinfo->fi_nodeid);	/* q */
619d082c877SGordon Ross 		break;
620d082c877SGordon Ross 
621a90cf9f2SGordon Ross 	/* See also: SMB_FIND_FILE_NAMES_INFO */
622a90cf9f2SGordon Ross 	case FileNamesInformation:		/* 12 */
623a90cf9f2SGordon Ross 		rc = smb_mbc_encodef(
624a90cf9f2SGordon Ross 		    &sr->raw_data, "lll",
625a90cf9f2SGordon Ross 		    0,	/* NextEntryOffset (set later) */
626a90cf9f2SGordon Ross 		    resume_key,
627a90cf9f2SGordon Ross 		    namelen);
628a90cf9f2SGordon Ross 		break;
629a90cf9f2SGordon Ross 
630a90cf9f2SGordon Ross 	default:
631a90cf9f2SGordon Ross 		return (NT_STATUS_INVALID_INFO_CLASS);
632a90cf9f2SGordon Ross 	}
633a90cf9f2SGordon Ross 	if (rc)	/* smb_mbc_encodef failed */
634a90cf9f2SGordon Ross 		return (NT_STATUS_BUFFER_OVERFLOW);
635a90cf9f2SGordon Ross 
636a90cf9f2SGordon Ross 	/*
637a90cf9f2SGordon Ross 	 * At this point we have written all the fixed-size data
638a90cf9f2SGordon Ross 	 * for the specified info. class.  Now put the name and
639a90cf9f2SGordon Ross 	 * alignment padding, and then patch the NextEntryOffset.
640a90cf9f2SGordon Ross 	 * Also store this offset for the caller so they can
641a90cf9f2SGordon Ross 	 * patch this (again) to zero on the very last entry.
642a90cf9f2SGordon Ross 	 */
643a90cf9f2SGordon Ross 	rc = smb_mbc_encodef(
644a90cf9f2SGordon Ross 	    &sr->raw_data, "U",
645a90cf9f2SGordon Ross 	    fileinfo->fi_name);
646a90cf9f2SGordon Ross 	if (rc)
647a90cf9f2SGordon Ross 		return (NT_STATUS_BUFFER_OVERFLOW);
648a90cf9f2SGordon Ross 
649a90cf9f2SGordon Ross 	/* Next entry needs to be 8-byte aligned. */
650a90cf9f2SGordon Ross 	padsz = sr->raw_data.chain_offset & 7;
651a90cf9f2SGordon Ross 	if (padsz) {
652a90cf9f2SGordon Ross 		padsz = 8 - padsz;
653a90cf9f2SGordon Ross 		(void) smb_mbc_encodef(&sr->raw_data, "#.", padsz);
654a90cf9f2SGordon Ross 	}
655a90cf9f2SGordon Ross 	next_entry_offset = sr->raw_data.chain_offset -	starting_offset;
656a90cf9f2SGordon Ross 	(void) smb_mbc_poke(&sr->raw_data, starting_offset, "l",
657a90cf9f2SGordon Ross 	    next_entry_offset);
658a90cf9f2SGordon Ross 	args->fa_last_entry = starting_offset;
659a90cf9f2SGordon Ross 
660a90cf9f2SGordon Ross 	return (0);
661a90cf9f2SGordon Ross }
662