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