1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 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 /*
30 * This module provides functions for TRANS2_FIND_FIRST2 and
31 * TRANS2_FIND_NEXT2 requests. The requests allow the client to search
32 * for the file(s) which match the file specification. The search is
33 * started with TRANS2_FIND_FIRST2 and can be continued if necessary with
34 * TRANS2_FIND_NEXT2. There are numerous levels of information which may be
35 * obtained for the returned files, the desired level is specified in the
36 * InformationLevel field of the requests.
37 *
38 * InformationLevel Name Value
39 * ================================= ================
40 *
41 * SMB_INFO_STANDARD 1
42 * SMB_INFO_QUERY_EA_SIZE 2
43 * SMB_INFO_QUERY_EAS_FROM_LIST 3
44 * SMB_FIND_FILE_DIRECTORY_INFO 0x101
45 * SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
46 * SMB_FIND_FILE_NAMES_INFO 0x103
47 * SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104
48 * SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO 0x105
49 * SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO 0x106
50 *
51 * The following sections detail the data returned for each
52 * InformationLevel. The requested information is placed in the Data
53 * portion of the transaction response. Note: a client which does not
54 * support long names can only request SMB_INFO_STANDARD.
55 *
56 * A four-byte resume key precedes each data item (described below) if bit
57 * 2 in the Flags field is set, i.e. if the request indicates the server
58 * should return resume keys. Note: it is not always the case. If the
59 * data item already includes the resume key, the resume key should not be
60 * added again.
61 *
62 * 4.3.4.1 SMB_INFO_STANDARD
63 *
64 * Response Field Description
65 * ================================ ==================================
66 *
67 * SMB_DATE CreationDate; Date when file was created
68 * SMB_TIME CreationTime; Time when file was created
69 * SMB_DATE LastAccessDate; Date of last file access
70 * SMB_TIME LastAccessTime; Time of last file access
71 * SMB_DATE LastWriteDate; Date of last write to the file
72 * SMB_TIME LastWriteTime; Time of last write to the file
73 * ULONG DataSize; File Size
74 * ULONG AllocationSize; Size of filesystem allocation unit
75 * USHORT Attributes; File Attributes
76 * UCHAR FileNameLength; Length of filename in bytes
77 * STRING FileName; Name of found file
78 *
79 * 4.3.4.2 SMB_INFO_QUERY_EA_SIZE
80 *
81 * Response Field Description
82 * ================================= ==================================
83 *
84 * SMB_DATE CreationDate; Date when file was created
85 * SMB_TIME CreationTime; Time when file was created
86 * SMB_DATE LastAccessDate; Date of last file access
87 * SMB_TIME LastAccessTime; Time of last file access
88 * SMB_DATE LastWriteDate; Date of last write to the file
89 * SMB_TIME LastWriteTime; Time of last write to the file
90 * ULONG DataSize; File Size
91 * ULONG AllocationSize; Size of filesystem allocation unit
92 * USHORT Attributes; File Attributes
93 * ULONG EaSize; Size of file's EA information
94 * UCHAR FileNameLength; Length of filename in bytes
95 * STRING FileName; Name of found file
96 *
97 * 4.3.4.3 SMB_INFO_QUERY_EAS_FROM_LIST
98 *
99 * This request returns the same information as SMB_INFO_QUERY_EA_SIZE, but
100 * only for files which have an EA list which match the EA information in
101 * the Data part of the request.
102 *
103 * 4.3.4.4 SMB_FIND_FILE_DIRECTORY_INFO
104 *
105 * Response Field Description
106 * ================================= ==================================
107 *
108 * ULONG NextEntryOffset; Offset from this structure to
109 * beginning of next one
110 * ULONG FileIndex;
111 * LARGE_INTEGER CreationTime; file creation time
112 * LARGE_INTEGER LastAccessTime; last access time
113 * LARGE_INTEGER LastWriteTime; last write time
114 * LARGE_INTEGER ChangeTime; last attribute change time
115 * LARGE_INTEGER EndOfFile; file size
116 * LARGE_INTEGER AllocationSize; size of filesystem allocation information
117 * ULONG ExtFileAttributes; Extended file attributes
118 * (see section 3.11)
119 * ULONG FileNameLength; Length of filename in bytes
120 * STRING FileName; Name of the file
121 *
122 * 4.3.4.5 SMB_FIND_FILE_FULL_DIRECTORY_INFO
123 *
124 * Response Field Description
125 * ================================= ==================================
126 *
127 * ULONG NextEntryOffset; Offset from this structure to
128 * beginning of next one
129 * ULONG FileIndex;
130 * LARGE_INTEGER CreationTime; file creation time
131 * LARGE_INTEGER LastAccessTime; last access time
132 * LARGE_INTEGER LastWriteTime; last write time
133 * LARGE_INTEGER ChangeTime; last attribute change time
134 * LARGE_INTEGER EndOfFile; file size
135 * LARGE_INTEGER AllocationSize; size of filesystem allocation information
136 * ULONG ExtFileAttributes; Extended file attributes
137 * (see section 3.11)
138 * ULONG FileNameLength; Length of filename in bytes
139 * ULONG EaSize; Size of file's extended attributes
140 * STRING FileName; Name of the file
141 *
142 *
143 * SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO
144 *
145 * This is the same as SMB_FIND_FILE_FULL_DIRECTORY_INFO but with
146 * FileId inserted after EaSize. FileId is preceded by a 4 byte
147 * alignment padding.
148 *
149 * Response Field Description
150 * ================================= ==================================
151 * ...
152 * ULONG EaSize; Size of file's extended attributes
153 * UCHAR Reserved[4]
154 * LARGE_INTEGER FileId Internal file system unique id.
155 * STRING FileName; Name of the file
156 *
157 * 4.3.4.6 SMB_FIND_FILE_BOTH_DIRECTORY_INFO
158 *
159 * Response Field Description
160 * ================================= ==================================
161 *
162 * ULONG NextEntryOffset; Offset from this structure to
163 * beginning of next one
164 * ULONG FileIndex;
165 * LARGE_INTEGER CreationTime; file creation time
166 * LARGE_INTEGER LastAccessTime; last access time
167 * LARGE_INTEGER LastWriteTime; last write time
168 * LARGE_INTEGER ChangeTime; last attribute change time
169 * LARGE_INTEGER EndOfFile; file size
170 * LARGE_INTEGER AllocationSize; size of filesystem allocation information
171 * ULONG ExtFileAttributes; Extended file attributes
172 * (see section 3.11)
173 * ULONG FileNameLength; Length of FileName in bytes
174 * ULONG EaSize; Size of file's extended attributes
175 * UCHAR ShortNameLength; Length of file's short name in bytes
176 * UCHAR Reserved
177 * WCHAR ShortName[12]; File's 8.3 conformant name in Unicode
178 * STRING FileName; Files full length name
179 *
180 *
181 * SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO
182 *
183 * This is the same as SMB_FIND_FILE_BOTH_DIRECTORY_INFO but with
184 * FileId inserted after ShortName. FileId is preceded by a 2 byte
185 * alignment pad.
186 *
187 * Response Field Description
188 * ================================= ==================================
189 * ...
190 * WCHAR ShortName[12]; File's 8.3 conformant name in Unicode
191 * UCHAR Reserved[2]
192 * LARGE_INTEGER FileId Internal file system unique id.
193 * STRING FileName; Files full length name
194 *
195 * 4.3.4.7 SMB_FIND_FILE_NAMES_INFO
196 *
197 * Response Field Description
198 * ================================= ==================================
199 *
200 * ULONG NextEntryOffset; Offset from this structure to
201 * beginning of next one
202 * ULONG FileIndex;
203 * ULONG FileNameLength; Length of FileName in bytes
204 * STRING FileName; Files full length name
205 */
206
207 #include <smbsrv/smb_kproto.h>
208 #include <smbsrv/msgbuf.h>
209 #include <smbsrv/smb_fsops.h>
210
211 /*
212 * Args (and other state) that we carry around among the
213 * various functions involved in FindFirst, FindNext.
214 */
215 typedef struct smb_find_args {
216 uint32_t fa_fixedsize;
217 uint16_t fa_infolev;
218 uint16_t fa_maxcount;
219 uint16_t fa_fflag;
220 uint16_t fa_eos; /* End Of Search */
221 uint16_t fa_lno; /* Last Name Offset */
222 uint32_t fa_lastkey; /* Last resume key */
223 char fa_lastname[MAXNAMELEN]; /* and name */
224 } smb_find_args_t;
225
226 static int smb_trans2_find_entries(smb_request_t *, smb_xa_t *,
227 smb_odir_t *, smb_find_args_t *);
228 static int smb_trans2_find_get_fixedsize(smb_request_t *, uint16_t, uint16_t);
229 static int smb_trans2_find_mbc_encode(smb_request_t *, smb_xa_t *,
230 smb_fileinfo_t *, smb_find_args_t *);
231
232 /*
233 * Tunable parameter to limit the maximum
234 * number of entries to be returned.
235 */
236 uint16_t smb_trans2_find_max = 128;
237
238 /*
239 * smb_com_trans2_find_first2
240 *
241 * Client Request Value
242 * ============================ ==================================
243 *
244 * UCHAR WordCount 15
245 * UCHAR TotalDataCount Total size of extended attribute list
246 * UCHAR SetupCount 1
247 * UCHAR Setup[0] TRANS2_FIND_FIRST2
248 *
249 * Parameter Block Encoding Description
250 * ============================ ==================================
251 * USHORT SearchAttributes;
252 * USHORT SearchCount; Maximum number of entries to return
253 * USHORT Flags; Additional information:
254 * Bit 0 - close search after this request
255 * Bit 1 - close search if end of search
256 * reached
257 * Bit 2 - return resume keys for each
258 * entry found
259 * Bit 3 - continue search from previous
260 * ending place
261 * Bit 4 - find with backup intent
262 * USHORT InformationLevel; See below
263 * ULONG SearchStorageType;
264 * STRING FileName; Pattern for the search
265 * UCHAR Data[ TotalDataCount ] FEAList if InformationLevel is
266 * QUERY_EAS_FROM_LIST
267 *
268 * Response Parameter Block Description
269 * ============================ ==================================
270 *
271 * USHORT Sid; Search handle
272 * USHORT SearchCount; Number of entries returned
273 * USHORT EndOfSearch; Was last entry returned?
274 * USHORT EaErrorOffset; Offset into EA list if EA error
275 * USHORT LastNameOffset; Offset into data to file name of last
276 * entry, if server needs it to resume
277 * search; else 0
278 * UCHAR Data[ TotalDataCount ] Level dependent info about the matches
279 * found in the search
280 */
281 smb_sdrc_t
smb_com_trans2_find_first2(smb_request_t * sr,smb_xa_t * xa)282 smb_com_trans2_find_first2(smb_request_t *sr, smb_xa_t *xa)
283 {
284 int count;
285 uint16_t sattr;
286 smb_pathname_t *pn;
287 smb_odir_t *od;
288 smb_find_args_t args;
289 uint32_t status;
290 uint32_t odir_flags = 0;
291
292 bzero(&args, sizeof (smb_find_args_t));
293
294 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
295 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
296 ERRDOS, ERROR_ACCESS_DENIED);
297 return (SDRC_ERROR);
298 }
299
300 pn = &sr->arg.dirop.fqi.fq_path;
301
302 if (smb_mbc_decodef(&xa->req_param_mb, "%wwww4.u", sr, &sattr,
303 &args.fa_maxcount, &args.fa_fflag, &args.fa_infolev,
304 &pn->pn_path) != 0) {
305 return (SDRC_ERROR);
306 }
307
308 smb_pathname_init(sr, pn, pn->pn_path);
309 if (!smb_pathname_validate(sr, pn))
310 return (-1);
311
312 if (smb_is_stream_name(pn->pn_path)) {
313 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
314 ERRDOS, ERROR_INVALID_NAME);
315 return (SDRC_ERROR);
316 }
317
318 if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT) {
319 sr->user_cr = smb_user_getprivcred(sr->uid_user);
320 odir_flags = SMB_ODIR_OPENF_BACKUP_INTENT;
321 }
322
323 args.fa_fixedsize =
324 smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
325 if (args.fa_fixedsize == 0)
326 return (SDRC_ERROR);
327
328 status = smb_odir_openpath(sr, pn->pn_path, sattr, odir_flags, &od);
329 if (status != 0) {
330 smbsr_error(sr, status, 0, 0);
331 return (SDRC_ERROR);
332 }
333 if (od == NULL)
334 return (SDRC_ERROR);
335
336 count = smb_trans2_find_entries(sr, xa, od, &args);
337
338 if (count == -1) {
339 smb_odir_close(od);
340 smb_odir_release(od);
341 return (SDRC_ERROR);
342 }
343
344 if (count == 0) {
345 smb_odir_close(od);
346 smb_odir_release(od);
347 smbsr_status(sr, NT_STATUS_NO_SUCH_FILE,
348 ERRDOS, ERROR_FILE_NOT_FOUND);
349 return (SDRC_ERROR);
350 }
351
352 if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
353 (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
354 smb_odir_close(od);
355 } /* else leave odir open for trans2_find_next2 */
356
357 (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
358 od->d_odid, /* Search ID */
359 count, /* Search Count */
360 args.fa_eos, /* End Of Search */
361 0, /* EA Error Offset */
362 args.fa_lno); /* Last Name Offset */
363
364 smb_odir_release(od);
365
366 return (SDRC_SUCCESS);
367 }
368
369 /*
370 * smb_com_trans2_find_next2
371 *
372 * Client Request Value
373 * ================================== =================================
374 *
375 * WordCount 15
376 * SetupCount 1
377 * Setup[0] TRANS2_FIND_NEXT2
378 *
379 * Parameter Block Encoding Description
380 * ================================== =================================
381 *
382 * USHORT Sid; Search handle
383 * USHORT SearchCount; Maximum number of entries to
384 * return
385 * USHORT InformationLevel; Levels described in
386 * TRANS2_FIND_FIRST2 request
387 * ULONG ResumeKey; Value returned by previous find2
388 * call
389 * USHORT Flags; Additional information: bit set-
390 * 0 - close search after this
391 * request
392 * 1 - close search if end of search
393 * reached
394 * 2 - return resume keys for each
395 * entry found
396 * 3 - resume/continue from previous
397 * ending place
398 * 4 - find with backup intent
399 * STRING FileName; Resume file name
400 *
401 * Sid is the value returned by a previous successful TRANS2_FIND_FIRST2
402 * call. If Bit3 of Flags is set, then FileName may be the NULL string,
403 * since the search is continued from the previous TRANS2_FIND request.
404 * Otherwise, FileName must not be more than 256 characters long.
405 *
406 * Response Field Description
407 * ================================== =================================
408 *
409 * USHORT SearchCount; Number of entries returned
410 * USHORT EndOfSearch; Was last entry returned?
411 * USHORT EaErrorOffset; Offset into EA list if EA error
412 * USHORT LastNameOffset; Offset into data to file name of
413 * last entry, if server needs it to
414 * resume search; else 0
415 * UCHAR Data[TotalDataCount] Level dependent info about the
416 * matches found in the search
417 *
418 *
419 * The last parameter in the request is a filename, which is a
420 * null-terminated unicode string.
421 *
422 * smb_mbc_decodef(&xa->req_param_mb, "%www lwu", sr,
423 * &odid, &fa_maxcount, &fa_infolev, &cookie, &fa_fflag, &fname)
424 *
425 * The filename parameter is not currently decoded because we
426 * expect a 2-byte null but Mac OS 10 clients send a 1-byte null,
427 * which leads to a decode error.
428 * Thus, we do not support resume by filename. We treat a request
429 * to resume by filename as SMB_FIND_CONTINUE_FROM_LAST.
430 */
431 smb_sdrc_t
smb_com_trans2_find_next2(smb_request_t * sr,smb_xa_t * xa)432 smb_com_trans2_find_next2(smb_request_t *sr, smb_xa_t *xa)
433 {
434 int count;
435 uint16_t odid;
436 smb_odir_t *od;
437 smb_find_args_t args;
438 smb_odir_resume_t odir_resume;
439
440 bzero(&args, sizeof (args));
441 bzero(&odir_resume, sizeof (odir_resume));
442
443 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
444 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
445 ERRDOS, ERROR_ACCESS_DENIED);
446 return (SDRC_ERROR);
447 }
448
449 if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr,
450 &odid, &args.fa_maxcount, &args.fa_infolev,
451 &odir_resume.or_cookie, &args.fa_fflag,
452 &odir_resume.or_fname) != 0) {
453 return (SDRC_ERROR);
454 }
455
456 if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
457 sr->user_cr = smb_user_getprivcred(sr->uid_user);
458
459 args.fa_fixedsize =
460 smb_trans2_find_get_fixedsize(sr, args.fa_infolev, args.fa_fflag);
461 if (args.fa_fixedsize == 0)
462 return (SDRC_ERROR);
463
464 od = smb_tree_lookup_odir(sr, odid);
465 if (od == NULL) {
466 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
467 ERRDOS, ERROR_INVALID_HANDLE);
468 return (SDRC_ERROR);
469 }
470
471 /*
472 * Set the correct position in the directory.
473 *
474 * "Continue from last" is easy, but due to a history of
475 * buggy server implementations, most clients don't use
476 * that method. The most widely used (and reliable) is
477 * resume by file name. Unfortunately, that can't really
478 * be fully supported unless your file system stores all
479 * directory entries in some sorted order (like NTFS).
480 * We can partially support resume by name, where the only
481 * name we're ever asked to resume on is the same as the
482 * most recent we returned. That's always what the client
483 * gives us as the resume name, so we can simply remember
484 * the last name/offset pair and use that to position on
485 * the following FindNext call. In the unlikely event
486 * that the client asks to resume somewhere else, we'll
487 * use the numeric resume key, and hope the client gives
488 * correctly uses one of the resume keys we provided.
489 */
490 if (args.fa_fflag & SMB_FIND_CONTINUE_FROM_LAST) {
491 odir_resume.or_type = SMB_ODIR_RESUME_CONT;
492 } else {
493 odir_resume.or_type = SMB_ODIR_RESUME_FNAME;
494 }
495 smb_odir_resume_at(od, &odir_resume);
496
497 count = smb_trans2_find_entries(sr, xa, od, &args);
498 if (count == -1) {
499 smb_odir_close(od);
500 smb_odir_release(od);
501 return (SDRC_ERROR);
502 }
503
504 if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
505 (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
506 smb_odir_close(od);
507 } /* else leave odir open for trans2_find_next2 */
508
509 smb_odir_release(od);
510
511 (void) smb_mbc_encodef(&xa->rep_param_mb, "wwww",
512 count, /* Search Count */
513 args.fa_eos, /* End Of Search */
514 0, /* EA Error Offset */
515 args.fa_lno); /* Last Name Offset */
516
517 return (SDRC_SUCCESS);
518 }
519
520
521 /*
522 * smb_trans2_find_entries
523 *
524 * Find and encode up to args->fa_maxcount directory entries.
525 * For compatibilty with Windows, if args->fa_maxcount is zero treat it as 1.
526 *
527 * Returns:
528 * count - count of entries encoded
529 * *eos = B_TRUE if no more directory entries
530 * -1 - error
531 */
532 static int
smb_trans2_find_entries(smb_request_t * sr,smb_xa_t * xa,smb_odir_t * od,smb_find_args_t * args)533 smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
534 smb_find_args_t *args)
535 {
536 smb_fileinfo_t fileinfo;
537 smb_odir_resume_t odir_resume;
538 uint16_t count, maxcount;
539 int rc = -1;
540 int LastEntryOffset = 0;
541 boolean_t need_rewind = B_FALSE;
542
543 /*
544 * EAs are not current supported, so a search for level
545 * SMB_INFO_QUERY_EAS_FROM_LIST should always return an
546 * empty list. Returning zero for this case gives the
547 * client an empty response, which is better than an
548 * NT_STATUS_INVALID_LEVEL return (and test failures).
549 *
550 * If and when we do support EAs, this level will modify
551 * the search here, and then return results just like
552 * SMB_INFO_QUERY_EA_SIZE, but only including files
553 * that have an EA in the provided list.
554 */
555 if (args->fa_infolev == SMB_INFO_QUERY_EAS_FROM_LIST)
556 return (0);
557
558 if ((maxcount = args->fa_maxcount) == 0)
559 maxcount = 1;
560
561 if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
562 maxcount = smb_trans2_find_max;
563
564 count = 0;
565 while (count < maxcount) {
566 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
567 if (rc != 0 || args->fa_eos != 0)
568 break;
569
570 LastEntryOffset = xa->rep_data_mb.chain_offset;
571 rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
572 if (rc == -1)
573 return (-1); /* fatal encoding error */
574 if (rc == 1) {
575 need_rewind = B_TRUE;
576 break; /* output space exhausted */
577 }
578
579 /*
580 * Save the info about the last file returned.
581 */
582 args->fa_lastkey = fileinfo.fi_cookie;
583 bcopy(fileinfo.fi_name, args->fa_lastname, MAXNAMELEN);
584
585 ++count;
586 }
587 if (args->fa_eos != 0 && rc == ENOENT)
588 rc = 0;
589
590 /*
591 * All but the ancient info levels start with NextEntryOffset.
592 * That's supposed to be zero in the last entry returned.
593 */
594 if (args->fa_infolev >= SMB_FIND_FILE_DIRECTORY_INFO) {
595 (void) smb_mbc_poke(&xa->rep_data_mb,
596 LastEntryOffset, "l", 0);
597 }
598
599 /* save the last cookie returned to client */
600 if (count != 0)
601 smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname);
602
603 /*
604 * If all retrieved entries have been successfully encoded
605 * and eos has not already been detected, check if there are
606 * any more entries. eos will be set if there are no more.
607 */
608 if ((rc == 0) && (args->fa_eos == 0)) {
609 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
610 /*
611 * If rc == ENOENT, we did not read any additional data.
612 * if rc != 0, there's no need to rewind.
613 */
614 if (rc == 0)
615 need_rewind = B_TRUE;
616 }
617
618 /*
619 * When the last entry we read from the directory did not
620 * fit in the return buffer, we will have read one entry
621 * that will not be returned in this call. That, and the
622 * check for EOS just above both can leave the directory
623 * position incorrect for the next call. Fix that now.
624 */
625 if (need_rewind) {
626 bzero(&odir_resume, sizeof (odir_resume));
627 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
628 odir_resume.or_cookie = args->fa_lastkey;
629 smb_odir_resume_at(od, &odir_resume);
630 }
631
632 return (count);
633 }
634
635 /*
636 * smb_trans2_find_get_fixedsize
637 *
638 * Calculate the sizeof the fixed part of the response for the
639 * specified information level.
640 *
641 * A non-zero return value provides the fixed size.
642 * A return value of zero indicates an unknown information level.
643 */
644 static int
smb_trans2_find_get_fixedsize(smb_request_t * sr,uint16_t infolev,uint16_t fflag)645 smb_trans2_find_get_fixedsize(smb_request_t *sr, uint16_t infolev,
646 uint16_t fflag)
647 {
648 int maxdata = 0;
649
650 switch (infolev) {
651 case SMB_INFO_STANDARD :
652 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
653 maxdata += sizeof (int32_t);
654 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
655 break;
656
657 case SMB_INFO_QUERY_EA_SIZE:
658 case SMB_INFO_QUERY_EAS_FROM_LIST:
659 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
660 maxdata += sizeof (int32_t);
661 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
662 break;
663
664 case SMB_FIND_FILE_DIRECTORY_INFO:
665 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4;
666 break;
667
668 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
669 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4;
670 break;
671
672 case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
673 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 8;
674 break;
675
676 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
677 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24;
678 break;
679
680 case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
681 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24
682 + 2 + 8;
683 break;
684
685 case SMB_FIND_FILE_NAMES_INFO:
686 maxdata += 4 + 4 + 4;
687 break;
688
689 case SMB_MAC_FIND_BOTH_HFS_INFO:
690 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 1 + 1 + 2 +
691 4 + 32 + 4 + 1 + 1 + 24 + 4;
692 break;
693
694 default:
695 maxdata = 0;
696 smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
697 ERRDOS, ERROR_INVALID_LEVEL);
698 }
699
700 return (maxdata);
701 }
702
703 /*
704 * This is an experimental feature that allows us to return zero
705 * for all numeric resume keys, to match Windows behavior with an
706 * NTFS share. Setting this variable to zero does that.
707 *
708 * It's possible we could remove this variable and always set
709 * numeric resume keys to zero, but that would leave us unable
710 * to handle a FindNext call with an arbitrary start position.
711 * In practice we never see these, but in theory we could.
712 *
713 * See the long comment above smb_com_trans2_find_next2() for
714 * more details about resume key / resume name handling.
715 */
716 int smbd_use_resume_keys = 1;
717
718 /*
719 * smb_trans2_mbc_encode
720 *
721 * This function encodes the mbc for one directory entry.
722 *
723 * The function returns -1 when the max data requested by client
724 * is reached. If the entry is valid and successful encoded, 0
725 * will be returned; otherwise, 1 will be returned.
726 *
727 * We always null terminate the filename. The space for the null
728 * is included in the maxdata calculation and is therefore included
729 * in the next_entry_offset. namelen is the unterminated length of
730 * the filename. For levels except STANDARD and EA_SIZE, if the
731 * filename is ascii the name length returned to the client should
732 * include the null terminator. Otherwise the length returned to
733 * the client should not include the terminator.
734 *
735 * Returns: 0 - data successfully encoded
736 * 1 - client request's maxdata limit reached
737 * -1 - error
738 */
739 static int
smb_trans2_find_mbc_encode(smb_request_t * sr,smb_xa_t * xa,smb_fileinfo_t * fileinfo,smb_find_args_t * args)740 smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
741 smb_fileinfo_t *fileinfo, smb_find_args_t *args)
742 {
743 int namelen, shortlen;
744 uint32_t next_entry_offset;
745 uint32_t dsize32, asize32;
746 uint32_t mb_flags = 0;
747 uint32_t resume_key;
748 char buf83[26];
749 smb_msgbuf_t mb;
750 int pad = 0;
751
752 namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
753 if (namelen == -1)
754 return (-1);
755
756 if (args->fa_infolev < SMB_FIND_FILE_DIRECTORY_INFO) {
757 /*
758 * Ancient info levels don't have a NextEntryOffset
759 * field, so there's no padding for alignment.
760 * The client expects a null after the file name,
761 * and then the next entry. The namelength field
762 * never includes the null for these old levels.
763 * Using the pad value to write the null because
764 * we don't want to add that to namelen.
765 * [MS-CIFS] sec. 2.8.1.{1-3}
766 */
767 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
768 pad = 2; /* Unicode null */
769 else
770 pad = 1; /* ascii null */
771 next_entry_offset = args->fa_fixedsize + namelen + pad;
772 if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
773 return (1);
774 } else {
775 /*
776 * Later info levels: The file name is written WITH
777 * null termination, and the size of that null _is_
778 * included in the namelen field. There may also
779 * be padding, and we pad to align(4) like Windows.
780 * Don't include the padding in the "room for" test
781 * because we want to ignore any error writing the
782 * pad bytes after the last element.
783 */
784 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0)
785 namelen += 2;
786 else
787 namelen += 1;
788 next_entry_offset = args->fa_fixedsize + namelen;
789 if (!MBC_ROOM_FOR(&xa->rep_data_mb, next_entry_offset))
790 return (1);
791 if ((next_entry_offset & 3) != 0) {
792 pad = 4 - (next_entry_offset & 3);
793 next_entry_offset += pad;
794 }
795 }
796
797 mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
798 dsize32 = (fileinfo->fi_size > UINT_MAX) ?
799 UINT_MAX : (uint32_t)fileinfo->fi_size;
800 asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
801 UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;
802
803 resume_key = fileinfo->fi_cookie;
804 if (smbd_use_resume_keys == 0)
805 resume_key = 0;
806
807 /*
808 * This switch handles all the "information levels" (formats)
809 * that we support. Note that all formats have the file name
810 * placed after some fixed-size data, and the code to write
811 * the file name is factored out at the end of this switch.
812 */
813 switch (args->fa_infolev) {
814 case SMB_INFO_STANDARD:
815 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
816 (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
817 resume_key);
818
819 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr,
820 smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
821 smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
822 smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
823 dsize32,
824 asize32,
825 fileinfo->fi_dosattr,
826 namelen);
827 break;
828
829 case SMB_INFO_QUERY_EA_SIZE:
830 case SMB_INFO_QUERY_EAS_FROM_LIST:
831 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
832 (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
833 resume_key);
834
835 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr,
836 smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
837 smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
838 smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
839 dsize32,
840 asize32,
841 fileinfo->fi_dosattr,
842 0L, /* EA Size */
843 namelen);
844 break;
845
846 case SMB_FIND_FILE_DIRECTORY_INFO:
847 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr,
848 next_entry_offset,
849 resume_key,
850 &fileinfo->fi_crtime,
851 &fileinfo->fi_atime,
852 &fileinfo->fi_mtime,
853 &fileinfo->fi_ctime,
854 fileinfo->fi_size,
855 fileinfo->fi_alloc_size,
856 fileinfo->fi_dosattr,
857 namelen);
858 break;
859
860 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
861 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll", sr,
862 next_entry_offset,
863 resume_key,
864 &fileinfo->fi_crtime,
865 &fileinfo->fi_atime,
866 &fileinfo->fi_mtime,
867 &fileinfo->fi_ctime,
868 fileinfo->fi_size,
869 fileinfo->fi_alloc_size,
870 fileinfo->fi_dosattr,
871 namelen,
872 0L);
873 break;
874
875 case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
876 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlll4.q", sr,
877 next_entry_offset,
878 resume_key,
879 &fileinfo->fi_crtime,
880 &fileinfo->fi_atime,
881 &fileinfo->fi_mtime,
882 &fileinfo->fi_ctime,
883 fileinfo->fi_size,
884 fileinfo->fi_alloc_size,
885 fileinfo->fi_dosattr,
886 namelen,
887 0L,
888 fileinfo->fi_nodeid);
889 break;
890
891 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
892 bzero(buf83, sizeof (buf83));
893 smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
894 mb_flags);
895 if (smb_msgbuf_encode(&mb, "U", fileinfo->fi_shortname) < 0) {
896 smb_msgbuf_term(&mb);
897 return (-1);
898 }
899 shortlen = smb_wcequiv_strlen(fileinfo->fi_shortname);
900
901 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqlllb.24c",
902 sr,
903 next_entry_offset,
904 resume_key,
905 &fileinfo->fi_crtime,
906 &fileinfo->fi_atime,
907 &fileinfo->fi_mtime,
908 &fileinfo->fi_ctime,
909 fileinfo->fi_size,
910 fileinfo->fi_alloc_size,
911 fileinfo->fi_dosattr,
912 namelen,
913 0L,
914 shortlen,
915 buf83);
916
917 smb_msgbuf_term(&mb);
918 break;
919
920 case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
921 bzero(buf83, sizeof (buf83));
922 smb_msgbuf_init(&mb, (uint8_t *)buf83, sizeof (buf83),
923 mb_flags);
924 if (smb_msgbuf_encode(&mb, "u", fileinfo->fi_shortname) < 0) {
925 smb_msgbuf_term(&mb);
926 return (-1);
927 }
928 shortlen = smb_ascii_or_unicode_strlen(sr,
929 fileinfo->fi_shortname);
930
931 (void) smb_mbc_encodef(&xa->rep_data_mb,
932 "%llTTTTqqlllb.24c2.q",
933 sr,
934 next_entry_offset,
935 resume_key,
936 &fileinfo->fi_crtime,
937 &fileinfo->fi_atime,
938 &fileinfo->fi_mtime,
939 &fileinfo->fi_ctime,
940 fileinfo->fi_size,
941 fileinfo->fi_alloc_size,
942 fileinfo->fi_dosattr,
943 namelen,
944 0L,
945 shortlen,
946 buf83,
947 fileinfo->fi_nodeid);
948
949 smb_msgbuf_term(&mb);
950 break;
951
952 case SMB_FIND_FILE_NAMES_INFO:
953 (void) smb_mbc_encodef(&xa->rep_data_mb, "%lll", sr,
954 next_entry_offset,
955 resume_key,
956 namelen);
957 break;
958
959 default:
960 /* invalid info. level */
961 return (-1);
962 }
963
964 /*
965 * At this point we have written all the fixed-size data
966 * for the specified info. level, and we're about to put
967 * the file name string in the message. We may later
968 * need the offset in the trans2 data where this string
969 * is placed, so save the message position now. Note:
970 * We also need to account for the alignment padding
971 * that may precede the unicode string.
972 */
973 args->fa_lno = xa->rep_data_mb.chain_offset;
974 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 &&
975 (args->fa_lno & 1) != 0)
976 args->fa_lno++;
977
978 (void) smb_mbc_encodef(&xa->rep_data_mb, "%#u", sr,
979 namelen, fileinfo->fi_name);
980
981 if (pad)
982 (void) smb_mbc_encodef(&xa->rep_data_mb, "#.", pad);
983
984 return (0);
985 }
986
987 /*
988 * Close a search started by a Trans2FindFirst2 request.
989 */
990 smb_sdrc_t
smb_pre_find_close2(smb_request_t * sr)991 smb_pre_find_close2(smb_request_t *sr)
992 {
993 DTRACE_SMB_START(op__FindClose2, smb_request_t *, sr);
994 return (SDRC_SUCCESS);
995 }
996
997 void
smb_post_find_close2(smb_request_t * sr)998 smb_post_find_close2(smb_request_t *sr)
999 {
1000 DTRACE_SMB_DONE(op__FindClose2, smb_request_t *, sr);
1001 }
1002
1003 smb_sdrc_t
smb_com_find_close2(smb_request_t * sr)1004 smb_com_find_close2(smb_request_t *sr)
1005 {
1006 uint16_t odid;
1007 smb_odir_t *od;
1008
1009 if (smbsr_decode_vwv(sr, "w", &odid) != 0)
1010 return (SDRC_ERROR);
1011
1012 od = smb_tree_lookup_odir(sr, odid);
1013 if (od == NULL) {
1014 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
1015 ERRDOS, ERROR_INVALID_HANDLE);
1016 return (SDRC_ERROR);
1017 }
1018
1019 smb_odir_close(od);
1020 smb_odir_release(od);
1021
1022 if (smbsr_encode_empty_result(sr))
1023 return (SDRC_ERROR);
1024
1025 return (SDRC_SUCCESS);
1026 }
1027