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