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