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 2015 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
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_maxdata;
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_maxdata(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 *
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_maxdata =
324 smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
325 if (args.fa_maxdata == 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_errno(sr, ENOENT);
348 return (SDRC_ERROR);
349 }
350
351 if ((args.fa_fflag & SMB_FIND_CLOSE_AFTER_REQUEST) ||
352 (args.fa_eos && (args.fa_fflag & SMB_FIND_CLOSE_AT_EOS))) {
353 smb_odir_close(od);
354 } /* else leave odir open for trans2_find_next2 */
355
356 (void) smb_mbc_encodef(&xa->rep_param_mb, "wwwww",
357 od->d_odid, /* Search ID */
358 count, /* Search Count */
359 args.fa_eos, /* End Of Search */
360 0, /* EA Error Offset */
361 args.fa_lno); /* Last Name Offset */
362
363 smb_odir_release(od);
364
365 return (SDRC_SUCCESS);
366 }
367
438
439 bzero(&args, sizeof (args));
440 bzero(&odir_resume, sizeof (odir_resume));
441
442 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
443 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
444 ERRDOS, ERROR_ACCESS_DENIED);
445 return (SDRC_ERROR);
446 }
447
448 if (smb_mbc_decodef(&xa->req_param_mb, "%wwwlwu", sr,
449 &odid, &args.fa_maxcount, &args.fa_infolev,
450 &odir_resume.or_cookie, &args.fa_fflag,
451 &odir_resume.or_fname) != 0) {
452 return (SDRC_ERROR);
453 }
454
455 if (args.fa_fflag & SMB_FIND_WITH_BACKUP_INTENT)
456 sr->user_cr = smb_user_getprivcred(sr->uid_user);
457
458 args.fa_maxdata =
459 smb_trans2_find_get_maxdata(sr, args.fa_infolev, args.fa_fflag);
460 if (args.fa_maxdata == 0)
461 return (SDRC_ERROR);
462
463 od = smb_tree_lookup_odir(sr, odid);
464 if (od == NULL) {
465 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
466 ERRDOS, ERROR_INVALID_HANDLE);
467 return (SDRC_ERROR);
468 }
469
470 /*
471 * Set the correct position in the directory.
472 *
473 * "Continue from last" is easy, but due to a history of
474 * buggy server implementations, most clients don't use
475 * that method. The most widely used (and reliable) is
476 * resume by file name. Unfortunately, that can't really
477 * be fully supported unless your file system stores all
478 * directory entries in some sorted order (like NTFS).
479 * We can partially support resume by name, where the only
480 * name we're ever asked to resume on is the same as the
519
520 /*
521 * smb_trans2_find_entries
522 *
523 * Find and encode up to args->fa_maxcount directory entries.
524 * For compatibilty with Windows, if args->fa_maxcount is zero treat it as 1.
525 *
526 * Returns:
527 * count - count of entries encoded
528 * *eos = B_TRUE if no more directory entries
529 * -1 - error
530 */
531 static int
532 smb_trans2_find_entries(smb_request_t *sr, smb_xa_t *xa, smb_odir_t *od,
533 smb_find_args_t *args)
534 {
535 smb_fileinfo_t fileinfo;
536 smb_odir_resume_t odir_resume;
537 uint16_t count, maxcount;
538 int rc = -1;
539 boolean_t need_rewind = B_FALSE;
540
541 if ((maxcount = args->fa_maxcount) == 0)
542 maxcount = 1;
543
544 if ((smb_trans2_find_max != 0) && (maxcount > smb_trans2_find_max))
545 maxcount = smb_trans2_find_max;
546
547 count = 0;
548 while (count < maxcount) {
549 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
550 if (rc != 0 || args->fa_eos != 0)
551 break;
552
553 rc = smb_trans2_find_mbc_encode(sr, xa, &fileinfo, args);
554 if (rc == -1)
555 return (-1); /* fatal encoding error */
556 if (rc == 1) {
557 need_rewind = B_TRUE;
558 break; /* output space exhausted */
559 }
560
561 /*
562 * Save the info about the last file returned.
563 */
564 args->fa_lastkey = fileinfo.fi_cookie;
565 bcopy(fileinfo.fi_name, args->fa_lastname, MAXNAMELEN);
566
567 ++count;
568 }
569 if (args->fa_eos != 0 && rc == ENOENT)
570 rc = 0;
571
572 /* save the last cookie returned to client */
573 if (count != 0)
574 smb_odir_save_fname(od, args->fa_lastkey, args->fa_lastname);
575
576 /*
577 * If all retrieved entries have been successfully encoded
578 * and eos has not already been detected, check if there are
579 * any more entries. eos will be set if there are no more.
580 */
581 if ((rc == 0) && (args->fa_eos == 0)) {
582 rc = smb_odir_read_fileinfo(sr, od, &fileinfo, &args->fa_eos);
583 /*
584 * If rc == ENOENT, we did not read any additional data.
585 * if rc != 0, there's no need to rewind.
586 */
587 if (rc == 0)
588 need_rewind = B_TRUE;
589 }
590
591 /*
592 * When the last entry we read from the directory did not
593 * fit in the return buffer, we will have read one entry
594 * that will not be returned in this call. That, and the
595 * check for EOS just above both can leave the directory
596 * position incorrect for the next call. Fix that now.
597 */
598 if (need_rewind) {
599 bzero(&odir_resume, sizeof (odir_resume));
600 odir_resume.or_type = SMB_ODIR_RESUME_COOKIE;
601 odir_resume.or_cookie = args->fa_lastkey;
602 smb_odir_resume_at(od, &odir_resume);
603 }
604
605 return (count);
606 }
607
608 /*
609 * smb_trans2_find_get_maxdata
610 *
611 * Calculate the minimum response space required for the specified
612 * information level.
613 *
614 * A non-zero return value provides the minimum space required.
615 * A return value of zero indicates an unknown information level.
616 */
617 static int
618 smb_trans2_find_get_maxdata(smb_request_t *sr, uint16_t infolev, uint16_t fflag)
619 {
620 int maxdata;
621
622 maxdata = smb_ascii_or_unicode_null_len(sr);
623
624 switch (infolev) {
625 case SMB_INFO_STANDARD :
626 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
627 maxdata += sizeof (int32_t);
628 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 1;
629 break;
630
631 case SMB_INFO_QUERY_EA_SIZE:
632 if (fflag & SMB_FIND_RETURN_RESUME_KEYS)
633 maxdata += sizeof (int32_t);
634 maxdata += 2 + 2 + 2 + 4 + 4 + 2 + 4 + 1;
635 break;
636
637 case SMB_FIND_FILE_DIRECTORY_INFO:
638 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4;
639 break;
640
641 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
642 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4;
643 break;
644
645 case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
646 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 8;
647 break;
648
649 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
650 maxdata += 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 2 + 24;
651 break;
703 * the filename. For levels except STANDARD and EA_SIZE, if the
704 * filename is ascii the name length returned to the client should
705 * include the null terminator. Otherwise the length returned to
706 * the client should not include the terminator.
707 *
708 * Returns: 0 - data successfully encoded
709 * 1 - client request's maxdata limit reached
710 * -1 - error
711 */
712 static int
713 smb_trans2_find_mbc_encode(smb_request_t *sr, smb_xa_t *xa,
714 smb_fileinfo_t *fileinfo, smb_find_args_t *args)
715 {
716 int namelen, shortlen;
717 uint32_t next_entry_offset;
718 uint32_t dsize32, asize32;
719 uint32_t mb_flags = 0;
720 uint32_t resume_key;
721 char buf83[26];
722 smb_msgbuf_t mb;
723
724 namelen = smb_ascii_or_unicode_strlen(sr, fileinfo->fi_name);
725 if (namelen == -1)
726 return (-1);
727
728 /*
729 * If ascii the filename length returned to the client should
730 * include the null terminator for levels except STANDARD and
731 * EASIZE.
732 */
733 if (!(sr->smb_flg2 & SMB_FLAGS2_UNICODE)) {
734 if ((args->fa_infolev != SMB_INFO_STANDARD) &&
735 (args->fa_infolev != SMB_INFO_QUERY_EA_SIZE))
736 namelen += 1;
737 }
738
739 next_entry_offset = args->fa_maxdata + namelen;
740
741 if (MBC_ROOM_FOR(&xa->rep_data_mb, (args->fa_maxdata + namelen)) == 0)
742 return (1);
743
744 mb_flags = (sr->smb_flg2 & SMB_FLAGS2_UNICODE) ? SMB_MSGBUF_UNICODE : 0;
745 dsize32 = (fileinfo->fi_size > UINT_MAX) ?
746 UINT_MAX : (uint32_t)fileinfo->fi_size;
747 asize32 = (fileinfo->fi_alloc_size > UINT_MAX) ?
748 UINT_MAX : (uint32_t)fileinfo->fi_alloc_size;
749
750 resume_key = fileinfo->fi_cookie;
751 if (smbd_use_resume_keys == 0)
752 resume_key = 0;
753
754 /*
755 * This switch handles all the "information levels" (formats)
756 * that we support. Note that all formats have the file name
757 * placed after some fixed-size data, and the code to write
758 * the file name is factored out at the end of this switch.
759 */
760 switch (args->fa_infolev) {
761 case SMB_INFO_STANDARD:
762 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
763 (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
764 resume_key);
765
766 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwb", sr,
767 smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
768 smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
769 smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
770 dsize32,
771 asize32,
772 fileinfo->fi_dosattr,
773 namelen);
774 break;
775
776 case SMB_INFO_QUERY_EA_SIZE:
777 if (args->fa_fflag & SMB_FIND_RETURN_RESUME_KEYS)
778 (void) smb_mbc_encodef(&xa->rep_data_mb, "l",
779 resume_key);
780
781 (void) smb_mbc_encodef(&xa->rep_data_mb, "%yyyllwlb", sr,
782 smb_time_gmt_to_local(sr, fileinfo->fi_crtime.tv_sec),
783 smb_time_gmt_to_local(sr, fileinfo->fi_atime.tv_sec),
784 smb_time_gmt_to_local(sr, fileinfo->fi_mtime.tv_sec),
785 dsize32,
786 asize32,
787 fileinfo->fi_dosattr,
788 0L, /* EA Size */
789 namelen);
790 break;
791
792 case SMB_FIND_FILE_DIRECTORY_INFO:
793 (void) smb_mbc_encodef(&xa->rep_data_mb, "%llTTTTqqll", sr,
794 next_entry_offset,
795 resume_key,
796 &fileinfo->fi_crtime,
904
905 default:
906 /* invalid info. level */
907 return (-1);
908 }
909
910 /*
911 * At this point we have written all the fixed-size data
912 * for the specified info. level, and we're about to put
913 * the file name string in the message. We may later
914 * need the offset in the trans2 data where this string
915 * is placed, so save the message position now. Note:
916 * We also need to account for the alignment padding
917 * that may precede the unicode string.
918 */
919 args->fa_lno = xa->rep_data_mb.chain_offset;
920 if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0 &&
921 (args->fa_lno & 1) != 0)
922 args->fa_lno++;
923
924 (void) smb_mbc_encodef(&xa->rep_data_mb, "%u", sr,
925 fileinfo->fi_name);
926
927 return (0);
928 }
929
930 /*
931 * Close a search started by a Trans2FindFirst2 request.
932 */
933 smb_sdrc_t
934 smb_pre_find_close2(smb_request_t *sr)
935 {
936 DTRACE_SMB_1(op__FindClose2__start, smb_request_t *, sr);
937 return (SDRC_SUCCESS);
938 }
939
940 void
941 smb_post_find_close2(smb_request_t *sr)
942 {
943 DTRACE_SMB_1(op__FindClose2__done, smb_request_t *, sr);
944 }
945
946 smb_sdrc_t
947 smb_com_find_close2(smb_request_t *sr)
948 {
949 uint16_t odid;
950 smb_odir_t *od;
951
952 if (smbsr_decode_vwv(sr, "w", &odid) != 0)
953 return (SDRC_ERROR);
954
955 od = smb_tree_lookup_odir(sr, odid);
956 if (od == NULL) {
957 smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
958 ERRDOS, ERROR_INVALID_HANDLE);
959 return (SDRC_ERROR);
960 }
961
962 smb_odir_close(od);
963 smb_odir_release(od);
|
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
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 *
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
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
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
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
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;
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
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,
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
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
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
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);
|