2 Unix SMB/CIFS implementation.
4 Copyright (C) Jeremy Allison 2022.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "torture/proto.h"
24 #include "../libcli/smb/smbXcli_base.h"
25 #include "libcli/security/security.h"
26 #include "libsmb/proto.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/gensec/gensec.h"
29 #include "auth_generic.h"
30 #include "../librpc/ndr/libndr.h"
31 #include "libsmb/clirap.h"
32 #include "async_smb.h"
33 #include "../lib/util/tevent_ntstatus.h"
35 extern fstring host
, workgroup
, share
, password
, username
, myname
;
36 extern struct cli_credentials
*torture_creds
;
39 * Open an SMB1 file readonly and return the inode number.
41 static NTSTATUS
get_smb1_inode(struct cli_state
*cli
,
53 status
= smb1cli_ntcreatex(cli
->conn
,
59 OPLOCK_NONE
, /* CreatFlags */
60 0, /* RootDirectoryFid */
63 SEC_FILE_READ_ATTRIBUTE
, /* DesiredAccess */
64 0, /* AllocationSize */
65 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
68 FILE_SHARE_DELETE
, /* ShareAccess */
69 FILE_OPEN
, /* CreateDisposition */
70 0, /* CreateOptions */
71 2, /* ImpersonationLevel */
72 0, /* SecurityFlags */
74 if (!NT_STATUS_IS_OK(status
)) {
79 * Get the inode. Note - we can use
80 * a higher-level cli_XXX function here
81 * for SMB1 as cli_qfileinfo_basic()
82 * doesn't use any pathnames, only fnums
83 * so it isn't affected by DFS pathnames.
85 status
= cli_qfileinfo_basic(cli
,
89 NULL
, /* create_time */
90 NULL
, /* access_time */
91 NULL
, /* write_time */
92 NULL
, /* change_time */
94 if (NT_STATUS_IS_OK(status
)) {
95 *ino_ret
= (uint64_t)ino
;
98 (void)smb1cli_close(cli
->conn
,
104 0); /* last_modified */
109 * Check an inode matches a given SMB1 path.
111 static bool smb1_inode_matches(struct cli_state
*cli
,
112 const char *match_pathname
,
113 uint64_t ino_tomatch
,
114 const char *test_pathname
)
116 uint64_t test_ino
= 0;
119 status
= get_smb1_inode(cli
,
122 if (!NT_STATUS_IS_OK(status
)) {
123 printf("%s: Failed to get ino "
124 "number for %s, (%s)\n",
130 if (test_ino
!= ino_tomatch
) {
131 printf("%s: Inode missmatch, ino_tomatch (%s) "
132 "ino=%"PRIu64
" test (%s) "
145 * Delete an SMB1 file on a DFS share.
147 static NTSTATUS
smb1_dfs_delete(struct cli_state
*cli
,
148 const char *pathname
)
157 status
= smb1cli_ntcreatex(cli
->conn
,
163 OPLOCK_NONE
, /* CreatFlags */
164 0, /* RootDirectoryFid */
166 SEC_STD_DELETE
, /* DesiredAccess */
167 0, /* AllocationSize */
168 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
171 FILE_SHARE_DELETE
, /* ShareAccess */
172 FILE_OPEN
, /* CreateDisposition */
173 0, /* CreateOptions */
174 2, /* ImpersonationLevel */
175 0, /* SecurityFlags */
177 if (!NT_STATUS_IS_OK(status
)) {
182 * Set delete on close. Note - we can use
183 * a higher-level cli_XXX function here
184 * for SMB1 as cli_nt_delete_on_close()
185 * doesn't use any pathnames, only fnums
186 * so it isn't affected by DFS pathnames.
190 status
= cli_nt_delete_on_close(cli
, fnum
, 1);
191 if (!NT_STATUS_IS_OK(status
)) {
194 return smb1cli_close(cli
->conn
,
200 0); /* last_modified */
203 static void smb1_mv_done(struct tevent_req
*subreq
);
205 struct smb1_mv_state
{
209 static struct tevent_req
*smb1_mv_send(TALLOC_CTX
*mem_ctx
,
210 struct tevent_context
*ev
,
211 struct cli_state
*cli
,
212 const char *src_dfs_name
,
213 const char *target_name
)
215 uint8_t *bytes
= NULL
;
216 struct tevent_req
*req
= NULL
;
217 struct tevent_req
*subreq
= NULL
;
218 struct smb1_mv_state
*state
= NULL
;
220 req
= tevent_req_create(mem_ctx
,
222 struct smb1_mv_state
);
227 PUSH_LE_U16(state
->vwv
,
229 FILE_ATTRIBUTE_SYSTEM
|
230 FILE_ATTRIBUTE_HIDDEN
|
231 FILE_ATTRIBUTE_DIRECTORY
);
233 bytes
= talloc_array(state
, uint8_t, 1);
234 if (tevent_req_nomem(bytes
, req
)) {
235 return tevent_req_post(req
, ev
);
238 bytes
= smb_bytes_push_str(bytes
,
239 smbXcli_conn_use_unicode(cli
->conn
),
241 strlen(src_dfs_name
)+1,
243 if (tevent_req_nomem(bytes
, req
)) {
244 return tevent_req_post(req
, ev
);
247 bytes
= talloc_realloc(state
,
250 talloc_get_size(bytes
)+1);
251 if (tevent_req_nomem(bytes
, req
)) {
252 return tevent_req_post(req
, ev
);
255 bytes
[talloc_get_size(bytes
)-1] = 4;
256 bytes
= smb_bytes_push_str(bytes
,
257 smbXcli_conn_use_unicode(cli
->conn
),
259 strlen(target_name
)+1,
261 if (tevent_req_nomem(bytes
, req
)) {
262 return tevent_req_post(req
, ev
);
265 subreq
= cli_smb_send(state
,
269 0, /* additional_flags */
270 0, /* additional_flags2 */
273 talloc_get_size(bytes
),
275 if (tevent_req_nomem(subreq
, req
)) {
276 return tevent_req_post(req
, ev
);
278 tevent_req_set_callback(subreq
, smb1_mv_done
, req
);
282 static void smb1_mv_done(struct tevent_req
*subreq
)
284 NTSTATUS status
= cli_smb_recv(subreq
,
292 tevent_req_simple_finish_ntstatus(subreq
,
296 static NTSTATUS
smb1_mv_recv(struct tevent_req
*req
)
298 return tevent_req_simple_recv_ntstatus(req
);
302 * Rename an SMB1 file on a DFS share. SMBmv version.
304 static NTSTATUS
smb1_mv(struct cli_state
*cli
,
305 const char *src_dfs_name
,
306 const char *target_name
)
308 TALLOC_CTX
*frame
= NULL
;
309 struct tevent_context
*ev
;
310 struct tevent_req
*req
;
313 frame
= talloc_stackframe();
315 ev
= samba_tevent_context_init(frame
);
317 status
= NT_STATUS_NO_MEMORY
;
321 req
= smb1_mv_send(frame
,
327 status
= NT_STATUS_NO_MEMORY
;
331 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
335 status
= smb1_mv_recv(req
);
343 static bool test_smb1_mv(struct cli_state
*cli
,
344 const char *src_dfs_name
)
346 uint64_t test_ino
= 0;
349 status
= smb1_mv(cli
,
351 "BAD\\BAD\\renamed_file");
352 if (!NT_STATUS_IS_OK(status
)) {
353 printf("%s:%d SMBmv of %s -> %s should succeed "
358 "BAD\\BAD\\renamed_file",
363 /* Ensure we did rename. */
364 status
= get_smb1_inode(cli
,
365 "BAD\\BAD\\renamed_file",
367 if (!NT_STATUS_IS_OK(status
)) {
368 printf("%s:%d Failed to get ino "
369 "number for %s, (%s)\n",
372 "BAD\\BAD\\renamed_file",
378 status
= smb1_mv(cli
,
379 "BAD\\BAD\\renamed_file",
381 if (!NT_STATUS_IS_OK(status
)) {
382 printf("%s:%d SMBmv of %s -> %s should succeed "
386 "BAD\\BAD\\renamed_file",
392 /* Ensure we did put it back. */
393 status
= get_smb1_inode(cli
,
396 if (!NT_STATUS_IS_OK(status
)) {
397 printf("%s:%d Failed to get ino "
398 "number for %s, (%s)\n",
406 /* Try with a non-DFS name. */
407 status
= smb1_mv(cli
,
410 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
)) {
411 /* Fails I think as target becomes "" on server. */
412 printf("%s:%d SMBmv of %s -> %s should get "
413 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
422 /* Try with a non-DFS name. */
423 status
= smb1_mv(cli
,
425 "BAD\\renamed_file");
426 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
)) {
427 /* Fails I think as target becomes "" on server. */
428 printf("%s:%d SMBmv of %s -> %s should get "
429 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
440 static void smb1_setpathinfo_done(struct tevent_req
*subreq
);
442 struct smb1_setpathinfo_state
{
448 static struct tevent_req
*smb1_setpathinfo_send(TALLOC_CTX
*mem_ctx
,
449 struct tevent_context
*ev
,
450 struct cli_state
*cli
,
451 const char *src_dfs_name
,
452 const char *target_name
,
455 struct tevent_req
*req
= NULL
;
456 struct tevent_req
*subreq
= NULL
;
457 struct smb1_setpathinfo_state
*state
= NULL
;
458 smb_ucs2_t
*converted_str
= NULL
;
459 size_t converted_size_bytes
= 0;
462 req
= tevent_req_create(mem_ctx
,
464 struct smb1_setpathinfo_state
);
469 PUSH_LE_U16(&state
->setup
, 0, TRANSACT2_SETPATHINFO
);
471 state
->param
= talloc_zero_array(state
, uint8_t, 6);
472 if (tevent_req_nomem(state
->param
, req
)) {
473 return tevent_req_post(req
, ev
);
475 PUSH_LE_U16(state
->param
, 0, info_level
);
477 state
->param
= trans2_bytes_push_str(state
->param
,
478 smbXcli_conn_use_unicode(cli
->conn
),
480 strlen(src_dfs_name
)+1,
482 if (tevent_req_nomem(state
->param
, req
)) {
483 return tevent_req_post(req
, ev
);
486 ok
= push_ucs2_talloc(state
,
489 &converted_size_bytes
);
491 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
492 return tevent_req_post(req
, ev
);
496 * W2K8 insists the dest name is not null
497 * terminated. Remove the last 2 zero bytes
498 * and reduce the name length.
501 if (converted_size_bytes
< 2) {
502 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
503 return tevent_req_post(req
, ev
);
505 converted_size_bytes
-= 2;
507 state
->data
= talloc_zero_array(state
,
509 12 + converted_size_bytes
);
510 if (tevent_req_nomem(state
->data
, req
)) {
511 return tevent_req_post(req
, ev
);
514 SIVAL(state
->data
, 8, converted_size_bytes
);
515 memcpy(state
->data
+ 12, converted_str
, converted_size_bytes
);
517 subreq
= cli_trans_send(state
, /* mem ctx. */
520 0,/* additional_flags2 */
521 SMBtrans2
, /* cmd. */
522 NULL
,/* pipe name. */
526 &state
->setup
,/* setup. */
527 1,/* num setup uint16_t words. */
528 0,/* max returned setup. */
529 state
->param
,/* param. */
530 talloc_get_size(state
->param
),/* num param. */
531 2,/* max returned param. */
532 state
->data
,/* data. */
533 talloc_get_size(state
->data
),/* num data. */
534 0);/* max returned data. */
536 if (tevent_req_nomem(subreq
, req
)) {
537 return tevent_req_post(req
, ev
);
539 tevent_req_set_callback(subreq
, smb1_setpathinfo_done
, req
);
543 static void smb1_setpathinfo_done(struct tevent_req
*subreq
)
545 NTSTATUS status
= cli_trans_recv(subreq
,
557 tevent_req_simple_finish_ntstatus(subreq
,
561 static NTSTATUS
smb1_setpathinfo_recv(struct tevent_req
*req
)
563 return tevent_req_simple_recv_ntstatus(req
);
567 * Rename or hardlink an SMB1 file on a DFS share. SMB1 setpathinfo
568 * (pathnames only) version.
570 static NTSTATUS
smb1_setpathinfo(struct cli_state
*cli
,
571 const char *src_dfs_name
,
572 const char *target_name
,
575 TALLOC_CTX
*frame
= NULL
;
576 struct tevent_context
*ev
;
577 struct tevent_req
*req
;
580 frame
= talloc_stackframe();
582 ev
= samba_tevent_context_init(frame
);
584 status
= NT_STATUS_NO_MEMORY
;
588 req
= smb1_setpathinfo_send(frame
,
595 status
= NT_STATUS_NO_MEMORY
;
599 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
603 status
= smb1_setpathinfo_recv(req
);
611 static NTSTATUS
smb1_setpathinfo_rename(struct cli_state
*cli
,
612 const char *src_dfs_name
,
613 const char *target_name
)
615 return smb1_setpathinfo(cli
,
618 SMB_FILE_RENAME_INFORMATION
);
621 static bool test_smb1_setpathinfo_rename(struct cli_state
*cli
,
622 const char *src_dfs_name
)
624 uint64_t test_ino
= 0;
626 const char *putback_path
= NULL
;
629 * On Windows, setpathinfo rename where the target contains
630 * any directory separator returns STATUS_NOT_SUPPORTED.
632 * MS-SMB behavior note: <133> Section 3.3.5.10.6:
634 * "If the file name pointed to by the FileName parameter of the
635 * FILE_RENAME_INFORMATION structure contains a separator character,
636 * then the request fails with STATUS_NOT_SUPPORTED."
638 status
= smb1_setpathinfo_rename(cli
,
640 "BAD\\BAD\\renamed_file");
641 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
642 printf("%s:%d SMB1 setpathinfo rename of %s -> %s should get "
643 "NT_STATUS_NOT_SUPPORTED got %s\n",
647 "BAD\\BAD\\renamed_file",
652 /* Try with a non-DFS name. */
653 status
= smb1_setpathinfo_rename(cli
,
656 if (!NT_STATUS_IS_OK(status
)) {
657 printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
658 "should succeed got %s\n",
667 /* Ensure we did rename. */
668 status
= get_smb1_inode(cli
,
669 "BAD\\BAD\\renamed_file",
671 if (!NT_STATUS_IS_OK(status
)) {
672 printf("%s:%d Failed to get ino "
673 "number for %s, (%s)\n",
676 "BAD\\BAD\\renamed_file",
682 * To put it back we need to reverse the DFS-ness of src
683 * and destination paths.
685 putback_path
= strrchr(src_dfs_name
, '\\');
686 if (putback_path
== NULL
) {
687 printf("%s:%d non DFS path %s passed. Internal error\n",
693 /* Walk past the last '\\' */
697 status
= smb1_setpathinfo_rename(cli
,
698 "BAD\\BAD\\renamed_file",
700 if (!NT_STATUS_IS_OK(status
)) {
701 printf("%s:%d SMB1 setpathinfo rename of %s -> %s "
702 "should succeed got %s\n",
705 "BAD\\BAD\\renamed_file",
711 /* Ensure we did rename. */
712 status
= get_smb1_inode(cli
,
715 if (!NT_STATUS_IS_OK(status
)) {
716 printf("%s:%d Failed to get ino "
717 "number for %s, (%s)\n",
728 static NTSTATUS
smb1_setpathinfo_hardlink(struct cli_state
*cli
,
729 const char *src_dfs_name
,
730 const char *target_name
)
732 return smb1_setpathinfo(cli
,
735 SMB_FILE_LINK_INFORMATION
);
738 static bool test_smb1_setpathinfo_hardlink(struct cli_state
*cli
,
739 const char *src_dfs_name
)
744 * On Windows, setpathinfo rename where the target contains
745 * any directory separator returns STATUS_NOT_SUPPORTED.
747 * MS-SMB behavior note: <133> Section 3.3.5.10.6:
749 * "If the file name pointed to by the FileName parameter of the
750 * FILE_RENAME_INFORMATION structure contains a separator character,
751 * then the request fails with STATUS_NOT_SUPPORTED."
753 * setpathinfo info level SMB_FILE_LINK_INFORMATION
754 * seems to do the same, but this could be an artifact
755 * of the Windows version tested (Win2K8). I will
756 * revisit this when I'm able to test against
757 * a later Windows version with a DFS server.
759 status
= smb1_setpathinfo_hardlink(cli
,
762 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
763 printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
764 "NT_STATUS_NOT_SUPPORTED got %s\n",
773 /* Try with a non-DFS name. */
775 * At least on Windows 2008 this also fails with
776 * NT_STATUS_NOT_SUPPORTED, leading me to believe
777 * setting hardlinks is only supported via NTrename
780 status
= smb1_setpathinfo_hardlink(cli
,
783 if (!NT_STATUS_EQUAL(status
, NT_STATUS_NOT_SUPPORTED
)) {
784 printf("%s:%d SMB1 setpathinfo hardlink of %s -> %s should get "
785 "NT_STATUS_NOT_SUPPORTED got %s\n",
796 static void smb1_ntrename_done(struct tevent_req
*subreq
);
798 struct smb1_ntrename_state
{
802 static struct tevent_req
*smb1_ntrename_send(TALLOC_CTX
*mem_ctx
,
803 struct tevent_context
*ev
,
804 struct cli_state
*cli
,
805 const char *src_dfs_name
,
806 const char *target_name
,
807 uint16_t rename_flag
)
809 struct tevent_req
*req
= NULL
;
810 struct tevent_req
*subreq
= NULL
;
811 struct smb1_ntrename_state
*state
= NULL
;
812 uint8_t *bytes
= NULL
;
814 req
= tevent_req_create(mem_ctx
,
816 struct smb1_ntrename_state
);
821 PUSH_LE_U16(state
->vwv
,
823 FILE_ATTRIBUTE_SYSTEM
|
824 FILE_ATTRIBUTE_HIDDEN
|
825 FILE_ATTRIBUTE_DIRECTORY
);
826 PUSH_LE_U16(state
->vwv
, 2, rename_flag
);
828 bytes
= talloc_array(state
, uint8_t, 1);
829 if (tevent_req_nomem(bytes
, req
)) {
830 return tevent_req_post(req
, ev
);
834 bytes
= smb_bytes_push_str(bytes
,
835 smbXcli_conn_use_unicode(cli
->conn
),
837 strlen(src_dfs_name
)+1,
839 if (tevent_req_nomem(bytes
, req
)) {
840 return tevent_req_post(req
, ev
);
842 bytes
= talloc_realloc(state
,
845 talloc_get_size(bytes
)+1);
846 if (tevent_req_nomem(bytes
, req
)) {
847 return tevent_req_post(req
, ev
);
850 bytes
[talloc_get_size(bytes
)-1] = 4;
851 bytes
= smb_bytes_push_str(bytes
,
852 smbXcli_conn_use_unicode(cli
->conn
),
854 strlen(target_name
)+1,
856 if (tevent_req_nomem(bytes
, req
)) {
857 return tevent_req_post(req
, ev
);
860 subreq
= cli_smb_send(state
,
864 0, /* additional_flags */
865 0, /* additional_flags2 */
868 talloc_get_size(bytes
),
870 if (tevent_req_nomem(subreq
, req
)) {
871 return tevent_req_post(req
, ev
);
873 tevent_req_set_callback(subreq
, smb1_ntrename_done
, req
);
877 static void smb1_ntrename_done(struct tevent_req
*subreq
)
879 NTSTATUS status
= cli_smb_recv(subreq
,
887 tevent_req_simple_finish_ntstatus(subreq
, status
);
890 static NTSTATUS
smb1_ntrename_recv(struct tevent_req
*req
)
892 return tevent_req_simple_recv_ntstatus(req
);
896 * Rename or hardlink an SMB1 file on a DFS share. SMB1 ntrename version.
899 static NTSTATUS
smb1_ntrename(struct cli_state
*cli
,
900 const char *src_dfs_name
,
901 const char *target_name
,
902 uint16_t rename_flag
)
904 TALLOC_CTX
*frame
= NULL
;
905 struct tevent_context
*ev
;
906 struct tevent_req
*req
;
909 frame
= talloc_stackframe();
911 ev
= samba_tevent_context_init(frame
);
913 status
= NT_STATUS_NO_MEMORY
;
917 req
= smb1_ntrename_send(frame
,
924 status
= NT_STATUS_NO_MEMORY
;
928 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
932 status
= smb1_ntrename_recv(req
);
940 * Rename an SMB1 file on a DFS share. SMB1 ntrename version.
942 static NTSTATUS
smb1_ntrename_rename(struct cli_state
*cli
,
943 const char *src_dfs_name
,
944 const char *target_name
)
946 return smb1_ntrename(cli
,
953 static bool test_smb1_ntrename_rename(struct cli_state
*cli
,
954 const char *src_dfs_name
)
956 uint64_t test_ino
= 0;
959 /* Try with a non-DFS name. */
960 status
= smb1_ntrename_rename(cli
,
963 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
)) {
964 /* Fails I think as target becomes "" on server. */
965 printf("%s:%d SMB1 ntrename rename of %s -> %s should get "
966 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
975 status
= smb1_ntrename_rename(cli
,
977 "BAD\\BAD\\renamed_file");
978 if (!NT_STATUS_IS_OK(status
)) {
979 printf("%s:%d SMB1 ntrename rename of %s -> %s should "
984 "BAD\\BAD\\renamed_file",
989 /* Ensure we did rename. */
990 status
= get_smb1_inode(cli
,
991 "BAD\\BAD\\renamed_file",
993 if (!NT_STATUS_IS_OK(status
)) {
994 printf("%s:%d Failed to get ino "
995 "number for %s, (%s)\n",
998 "BAD\\BAD\\renamed_file",
1004 status
= smb1_ntrename_rename(cli
,
1005 "BAD\\BAD\\renamed_file",
1007 if (!NT_STATUS_IS_OK(status
)) {
1008 printf("%s:%d SMB1 ntrename rename of %s -> %s "
1009 "should succeed got %s\n",
1012 "BAD\\BAD\\renamed_file",
1018 /* Ensure we did rename. */
1019 status
= get_smb1_inode(cli
,
1022 if (!NT_STATUS_IS_OK(status
)) {
1023 printf("%s:%d Failed to get ino "
1024 "number for %s, (%s)\n",
1036 * Hard link an SMB1 file on a DFS share. SMB1 ntrename version.
1038 static NTSTATUS
smb1_ntrename_hardlink(struct cli_state
*cli
,
1039 const char *src_dfs_name
,
1040 const char *target_name
)
1042 return smb1_ntrename(cli
,
1045 RENAME_FLAG_HARD_LINK
);
1048 static bool test_smb1_ntrename_hardlink(struct cli_state
*cli
,
1049 const char *src_dfs_name
)
1051 uint64_t test_ino
= 0;
1053 bool retval
= false;
1055 /* Try with a non-DFS name. */
1056 status
= smb1_ntrename_hardlink(cli
,
1059 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_SYNTAX_BAD
)) {
1060 /* Fails I think as target becomes "" on server. */
1061 printf("%s:%d SMB1 ntrename of %s -> %s should get "
1062 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD got %s\n",
1071 status
= smb1_ntrename_hardlink(cli
,
1074 if (!NT_STATUS_IS_OK(status
)) {
1075 printf("%s:%d SMB1 ntrename hardlink of %s -> %s "
1076 "should succeed got %s\n",
1085 /* Ensure we did hardlink. */
1086 status
= get_smb1_inode(cli
,
1089 if (!NT_STATUS_IS_OK(status
)) {
1090 printf("%s:%d Failed to get ino "
1091 "number for %s, (%s)\n",
1099 retval
= smb1_inode_matches(cli
,
1104 printf("%s:%d smb1_inode_matches failed for "
1115 /* Remove the hardlink to clean up. */
1116 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\hlink");
1120 static void smb1_setfileinfo_done(struct tevent_req
*subreq
);
1122 struct smb1_setfileinfo_state
{
1128 static struct tevent_req
*smb1_setfileinfo_send(TALLOC_CTX
*mem_ctx
,
1129 struct tevent_context
*ev
,
1130 struct cli_state
*cli
,
1132 const char *target_name
,
1133 uint16_t info_level
)
1135 struct tevent_req
*req
= NULL
;
1136 struct tevent_req
*subreq
= NULL
;
1137 struct smb1_setfileinfo_state
*state
= NULL
;
1138 smb_ucs2_t
*converted_str
= NULL
;
1139 size_t converted_size_bytes
= 0;
1142 req
= tevent_req_create(mem_ctx
,
1144 struct smb1_setfileinfo_state
);
1149 PUSH_LE_U16(&state
->setup
, 0, TRANSACT2_SETPATHINFO
);
1151 PUSH_LE_U16(state
->param
, 0, fnum
);
1152 PUSH_LE_U16(state
->param
, 2, info_level
);
1154 ok
= push_ucs2_talloc(state
,
1157 &converted_size_bytes
);
1159 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1160 return tevent_req_post(req
, ev
);
1164 * W2K8 insists the dest name is not null
1165 * terminated. Remove the last 2 zero bytes
1166 * and reduce the name length.
1169 if (converted_size_bytes
< 2) {
1170 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
1171 return tevent_req_post(req
, ev
);
1173 converted_size_bytes
-= 2;
1175 state
->data
= talloc_zero_array(state
,
1177 12 + converted_size_bytes
);
1178 if (tevent_req_nomem(state
->data
, req
)) {
1179 return tevent_req_post(req
, ev
);
1182 SIVAL(state
->data
, 8, converted_size_bytes
);
1183 memcpy(state
->data
+ 12, converted_str
, converted_size_bytes
);
1185 subreq
= cli_trans_send(state
, /* mem ctx. */
1187 cli
,/* cli_state. */
1188 0,/* additional_flags2 */
1189 SMBtrans2
, /* cmd. */
1190 NULL
,/* pipe name. */
1194 &state
->setup
,/* setup. */
1195 1,/* num setup uint16_t words. */
1196 0,/* max returned setup. */
1197 state
->param
,/* param. */
1199 2,/* max returned param. */
1200 state
->data
,/* data. */
1201 talloc_get_size(state
->data
),/* num data. */
1202 0);/* max returned data. */
1204 if (tevent_req_nomem(subreq
, req
)) {
1205 return tevent_req_post(req
, ev
);
1207 tevent_req_set_callback(subreq
, smb1_setfileinfo_done
, req
);
1211 static void smb1_setfileinfo_done(struct tevent_req
*subreq
)
1213 NTSTATUS status
= cli_trans_recv(subreq
,
1225 tevent_req_simple_finish_ntstatus(subreq
,
1229 static NTSTATUS
smb1_setfileinfo_recv(struct tevent_req
*req
)
1231 return tevent_req_simple_recv_ntstatus(req
);
1235 * Rename or hardlink an SMB1 file on a DFS share.
1236 * setfileinfo (file handle + target pathname) version.
1238 static NTSTATUS
smb1_setfileinfo(struct cli_state
*cli
,
1240 const char *target_name
,
1241 uint16_t info_level
)
1243 TALLOC_CTX
*frame
= NULL
;
1244 struct tevent_context
*ev
;
1245 struct tevent_req
*req
;
1248 frame
= talloc_stackframe();
1250 ev
= samba_tevent_context_init(frame
);
1252 status
= NT_STATUS_NO_MEMORY
;
1256 req
= smb1_setfileinfo_send(frame
,
1263 status
= NT_STATUS_NO_MEMORY
;
1267 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
1271 status
= smb1_setfileinfo_recv(req
);
1279 static NTSTATUS
smb1_setfileinfo_rename(struct cli_state
*cli
,
1281 const char *target_name
)
1283 return smb1_setfileinfo(cli
,
1286 SMB_FILE_RENAME_INFORMATION
);
1290 * On Windows, rename using a file handle as source
1294 static bool test_smb1_setfileinfo_rename(struct cli_state
*cli
,
1295 const char *src_dfs_name
)
1297 uint16_t fnum
= (uint16_t)-1;
1299 bool retval
= false;
1301 /* First open the source file. */
1302 status
= smb1cli_ntcreatex(cli
->conn
,
1308 OPLOCK_NONE
, /* CreatFlags */
1309 0, /* RootDirectoryFid */
1310 SEC_STD_SYNCHRONIZE
|
1311 SEC_STD_DELETE
, /* DesiredAccess */
1312 0, /* AllocationSize */
1313 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
1316 FILE_SHARE_DELETE
, /* ShareAccess */
1317 FILE_OPEN
, /* CreateDisposition */
1318 0, /* CreateOptions */
1319 2, /* ImpersonationLevel */
1320 0, /* SecurityFlags */
1322 if (!NT_STATUS_IS_OK(status
)) {
1323 printf("%s:%d failed to open %s, %s\n",
1332 * On Windows rename given a file handle returns
1333 * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
1336 status
= smb1_setfileinfo_rename(cli
,
1338 "BAD\\BAD\\renamed_file");
1339 if (!NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1340 printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
1341 "NT_STATUS_UNSUCCESSFUL got %s\n",
1350 /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
1351 status
= smb1_setfileinfo_rename(cli
,
1354 if (!NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1355 printf("%s:%d SMB1 setfileinfo rename of %s -> %s should get "
1356 "NT_STATUS_UNSUCCESSFUL got %s\n",
1369 if (fnum
!= (uint16_t)-1) {
1370 (void)smb1cli_close(cli
->conn
,
1376 0); /* last_modified */
1379 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\renamed_file");
1384 static NTSTATUS
smb1_setfileinfo_hardlink(struct cli_state
*cli
,
1386 const char *target_name
)
1388 return smb1_setfileinfo(cli
,
1391 SMB_FILE_LINK_INFORMATION
);
1395 * On Windows, hardlink using a file handle as source
1399 static bool test_smb1_setfileinfo_hardlink(struct cli_state
*cli
,
1400 const char *src_dfs_name
)
1402 uint16_t fnum
= (uint16_t)-1;
1404 bool retval
= false;
1406 /* First open the source file. */
1407 status
= smb1cli_ntcreatex(cli
->conn
,
1413 OPLOCK_NONE
, /* CreatFlags */
1414 0, /* RootDirectoryFid */
1415 SEC_STD_SYNCHRONIZE
|
1416 SEC_RIGHTS_FILE_READ
, /* DesiredAccess */
1417 0, /* AllocationSize */
1418 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
1421 FILE_SHARE_DELETE
, /* ShareAccess */
1422 FILE_OPEN
, /* CreateDisposition */
1423 0, /* CreateOptions */
1424 2, /* ImpersonationLevel */
1425 0, /* SecurityFlags */
1427 if (!NT_STATUS_IS_OK(status
)) {
1428 printf("%s:%d failed to open %s, %s\n",
1437 * On Windows hardlink given a file handle returns
1438 * NT_STATUS_UNSUCCESSFUL (not documented in MS-SMB).
1441 status
= smb1_setfileinfo_hardlink(cli
,
1444 if (!NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1445 printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
1446 "NT_STATUS_UNSUCCESSFUL got %s\n",
1455 /* Try with a non-DFS name - still gets NT_STATUS_UNSUCCESSFUL. */
1456 status
= smb1_setfileinfo_hardlink(cli
,
1459 if (!NT_STATUS_EQUAL(status
, NT_STATUS_UNSUCCESSFUL
)) {
1460 printf("%s:%d SMB1 setfileinfo hardlink of %s -> %s should get "
1461 "NT_STATUS_UNSUCCESSFUL got %s\n",
1474 if (fnum
!= (uint16_t)-1) {
1475 (void)smb1cli_close(cli
->conn
,
1481 0); /* last_modified */
1484 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\hlink");
1491 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/dc9978d7-6299-4c5a-a22d-a039cdc716ea
1493 * (Characters " \ / [ ] : | < > + = ; , * ?,
1494 * and control characters in range 0x00 through
1495 * 0x1F, inclusive, are illegal in a share name)
1497 * But Windows server only checks in DFS sharenames ':'. All other
1498 * share names are allowed.
1501 static bool test_smb1_dfs_sharenames(struct cli_state
*cli
,
1502 const char *dfs_root_share_name
,
1506 const char *test_str
= "/[]:|<>+=;,*?";
1509 bool ino_matched
= false;
1511 /* Setup template pathname. */
1512 memcpy(test_path
, "\\SERVER\\X", 10);
1514 /* Test invalid control characters. */
1515 for (i
= 1; i
< 0x20; i
++) {
1517 ino_matched
= smb1_inode_matches(cli
,
1518 dfs_root_share_name
,
1526 /* Test explicit invalid characters. */
1527 for (p
= test_str
; *p
!= '\0'; p
++) {
1531 * Only ':' is treated as an INVALID sharename
1532 * for a DFS SERVER\\SHARE path.
1534 uint64_t test_ino
= 0;
1535 NTSTATUS status
= get_smb1_inode(cli
,
1538 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_INVALID
)) {
1539 printf("%s:%d Open of %s should get "
1540 "NT_STATUS_OBJECT_NAME_INVALID, got %s\n",
1548 ino_matched
= smb1_inode_matches(cli
,
1549 dfs_root_share_name
,
1561 * "Raw" test of SMB1 paths to a DFS share.
1562 * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
1563 * not the cli_XXX() ones here as the ultimate goal is to fix our
1564 * cli_XXX() interfaces to work transparently over DFS.
1566 * So here, we're testing the server code, not the client code.
1568 * Passes cleanly against Windows.
1571 bool run_smb1_dfs_paths(int dummy
)
1573 struct cli_state
*cli
= NULL
;
1575 bool dfs_supported
= false;
1576 char *dfs_root_share_name
= NULL
;
1577 uint64_t root_ino
= 0;
1578 uint64_t test_ino
= 0;
1579 bool ino_matched
= false;
1580 bool retval
= false;
1583 uint16_t fnum
= (uint16_t)-1;
1585 printf("Starting SMB1-DFS-PATHS\n");
1587 if (!torture_init_connection(&cli
)) {
1591 if (!torture_open_connection(&cli
, 0)) {
1595 /* Ensure this is a DFS share. */
1596 dfs_supported
= smbXcli_conn_dfs_supported(cli
->conn
);
1597 if (!dfs_supported
) {
1598 printf("Server %s does not support DFS\n",
1599 smbXcli_conn_remote_name(cli
->conn
));
1602 dfs_supported
= smbXcli_tcon_is_dfs_share(cli
->smb1
.tcon
);
1603 if (!dfs_supported
) {
1604 printf("Share %s does not support DFS\n",
1609 /* Start with an empty share. */
1610 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\BAD");
1611 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\file");
1612 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\renamed_file");
1613 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\hlink");
1616 * Create the "official" DFS share root name.
1618 dfs_root_share_name
= talloc_asprintf(talloc_tos(),
1620 smbXcli_conn_remote_name(cli
->conn
),
1622 if (dfs_root_share_name
== NULL
) {
1623 printf("Out of memory\n");
1627 /* Get the share root inode number. */
1628 status
= get_smb1_inode(cli
,
1629 dfs_root_share_name
,
1631 if (!NT_STATUS_IS_OK(status
)) {
1632 printf("%s:%d Failed to get ino number for share root %s, (%s)\n",
1635 dfs_root_share_name
,
1641 * Test the Windows algorithm for parsing DFS names.
1644 * A single "SERVER" element should open and match the share root.
1646 ino_matched
= smb1_inode_matches(cli
,
1647 dfs_root_share_name
,
1649 smbXcli_conn_remote_name(cli
->conn
));
1651 printf("%s:%d Failed to match ino number for %s\n",
1654 smbXcli_conn_remote_name(cli
->conn
));
1658 /* An "" (empty) server name should open and match the share root. */
1659 ino_matched
= smb1_inode_matches(cli
,
1660 dfs_root_share_name
,
1664 printf("%s:%d Failed to match ino number for %s\n",
1672 * For SMB1 the server just strips off any number of leading '\\'
1673 * characters. Show this is the case.
1675 for (i
= 0; i
< 10; i
++) {
1676 char leading_backslash_name
[20];
1677 leading_backslash_name
[i
] = '\\';
1678 memcpy(&leading_backslash_name
[i
+1],
1680 strlen("SERVER")+1);
1682 ino_matched
= smb1_inode_matches(cli
,
1683 dfs_root_share_name
,
1685 leading_backslash_name
);
1687 printf("%s:%d Failed to match ino number for %s\n",
1690 leading_backslash_name
);
1695 /* A "BAD" server name should open and match the share root. */
1696 ino_matched
= smb1_inode_matches(cli
,
1697 dfs_root_share_name
,
1701 printf("%s:%d Failed to match ino number for %s\n",
1708 * A "BAD\\BAD" server and share name should open
1709 * and match the share root.
1711 ino_matched
= smb1_inode_matches(cli
,
1712 dfs_root_share_name
,
1716 printf("%s:%d Failed to match ino number for %s\n",
1723 * Trying to open "BAD\\BAD\\BAD" should get
1724 * NT_STATUS_OBJECT_NAME_NOT_FOUND.
1726 status
= get_smb1_inode(cli
,
1729 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
1730 printf("%s:%d Open of %s should get "
1731 "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
1739 * Trying to open "BAD\\BAD\\BAD\\BAD" should get
1740 * NT_STATUS_OBJECT_PATH_NOT_FOUND.
1742 status
= get_smb1_inode(cli
,
1743 "BAD\\BAD\\BAD\\BAD",
1745 if (!NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
1746 printf("%s:%d Open of %s should get "
1747 "STATUS_OBJECT_NAME_NOT_FOUND, got %s\n",
1750 "BAD\\BAD\\BAD\\BAD",
1755 * Test for invalid pathname characters in the servername.
1756 * They are ignored, and it still opens the share root.
1758 ino_matched
= smb1_inode_matches(cli
,
1759 dfs_root_share_name
,
1763 printf("%s:%d Failed to match ino number for %s\n",
1771 * Test for invalid pathname characters in the sharename.
1772 * Invalid sharename characters should still be flagged as
1773 * NT_STATUS_OBJECT_NAME_INVALID. It turns out only ':'
1774 * is considered an invalid sharename character.
1776 ok
= test_smb1_dfs_sharenames(cli
,
1777 dfs_root_share_name
,
1783 status
= smb1cli_ntcreatex(cli
->conn
,
1789 OPLOCK_NONE
, /* CreatFlags */
1790 0, /* RootDirectoryFid */
1791 SEC_STD_SYNCHRONIZE
|
1794 SEC_FILE_READ_ATTRIBUTE
, /* DesiredAccess */
1795 0, /* AllocationSize */
1796 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
1799 FILE_SHARE_DELETE
, /* ShareAccess */
1800 FILE_CREATE
, /* CreateDisposition */
1801 0, /* CreateOptions */
1802 2, /* ImpersonationLevel */
1803 0, /* SecurityFlags */
1805 if (!NT_STATUS_IS_OK(status
)) {
1806 printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
1814 /* Close "file" handle. */
1815 (void)smb1cli_close(cli
->conn
,
1821 0); /* last_modified */
1822 fnum
= (uint16_t)-1;
1825 * Trying to open "BAD\\BAD\\file" should now get
1828 status
= get_smb1_inode(cli
,
1831 if (!NT_STATUS_IS_OK(status
)) {
1832 printf("%s:%d Open of %s should succeed "
1842 * Test different SMB1 renames
1846 /* SMBmv only does rename. */
1847 ok
= test_smb1_mv(cli
,
1853 ok
= test_smb1_setpathinfo_rename(cli
,
1859 ok
= test_smb1_setpathinfo_hardlink(cli
,
1865 ok
= test_smb1_setfileinfo_rename(cli
,
1871 ok
= test_smb1_setfileinfo_hardlink(cli
,
1877 ok
= test_smb1_ntrename_rename(cli
,
1883 ok
= test_smb1_ntrename_hardlink(cli
,
1893 if (fnum
!= (uint16_t)-1) {
1894 (void)smb1cli_close(cli
->conn
,
1900 0); /* last_modified */
1903 /* Delete anything we made. */
1904 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\BAD");
1905 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\file");
1906 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\renamed_file");
1907 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\hlink");
1912 * SMB1 Findfirst. This is a minimal implementation
1913 * that expects all filename returns in one packet.
1914 * We're only using this to test the search DFS pathname
1918 /****************************************************************************
1919 Calculate a safe next_entry_offset.
1920 ****************************************************************************/
1922 static size_t calc_next_entry_offset(const uint8_t *base
,
1923 const uint8_t *pdata_end
)
1925 size_t next_entry_offset
= (size_t)PULL_LE_U32(base
,0);
1927 if (next_entry_offset
== 0 ||
1928 base
+ next_entry_offset
< base
||
1929 base
+ next_entry_offset
> pdata_end
) {
1930 next_entry_offset
= pdata_end
- base
;
1932 return next_entry_offset
;
1935 static size_t get_filename(TALLOC_CTX
*ctx
,
1936 struct cli_state
*cli
,
1937 const uint8_t *base_ptr
,
1938 uint16_t recv_flags2
,
1940 const uint8_t *pdata_end
,
1941 struct file_info
*finfo
)
1944 const uint8_t *base
= p
;
1948 ZERO_STRUCTP(finfo
);
1950 if (pdata_end
- base
< 94) {
1951 return pdata_end
- base
;
1953 p
+= 4; /* next entry offset */
1954 p
+= 4; /* fileindex */
1955 /* Offset zero is "create time", not "change time". */
1957 finfo
->atime_ts
= interpret_long_date((const char *)p
);
1959 finfo
->mtime_ts
= interpret_long_date((const char *)p
);
1961 finfo
->ctime_ts
= interpret_long_date((const char *)p
);
1963 finfo
->size
= PULL_LE_U64(p
, 0);
1965 p
+= 8; /* alloc size */
1966 finfo
->attr
= PULL_LE_U32(p
, 0);
1968 namelen
= PULL_LE_U32(p
, 0);
1970 p
+= 4; /* EA size */
1971 slen
= PULL_LE_U8(p
, 0);
1973 /* Bad short name length. */
1974 return pdata_end
- base
;
1977 ret
= pull_string_talloc(ctx
,
1984 if (ret
== (size_t)-1) {
1985 return pdata_end
- base
;
1987 p
+= 24; /* short name */
1988 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
1989 return pdata_end
- base
;
1991 ret
= pull_string_talloc(ctx
,
1998 if (ret
== (size_t)-1) {
1999 return pdata_end
- base
;
2001 return calc_next_entry_offset(base
, pdata_end
);
2004 /* Single shot SMB1 TRANS2 FindFirst. */
2006 static NTSTATUS
smb1_findfirst(TALLOC_CTX
*mem_ctx
,
2007 struct cli_state
*cli
,
2008 const char *search_name
,
2009 struct file_info
**names
,
2014 uint8_t *param
= NULL
;
2015 uint16_t recv_flags2
= 0;
2016 uint8_t *rparam
= NULL
;
2017 uint32_t num_rparam
= 0;
2018 uint8_t *rdata
= NULL
;
2019 uint32_t num_rdata
= 0;
2020 uint16_t num_names_returned
= 0;
2021 struct file_info
*finfo
= NULL
;
2023 uint8_t *data_end
= NULL
;
2026 PUSH_LE_U16(&setup
[0], 0, TRANSACT2_FINDFIRST
);
2028 param
= talloc_array(mem_ctx
, uint8_t, 12);
2029 if (param
== NULL
) {
2030 return NT_STATUS_NO_MEMORY
;
2033 PUSH_LE_U16(param
, 0, FILE_ATTRIBUTE_DIRECTORY
|
2034 FILE_ATTRIBUTE_SYSTEM
|
2035 FILE_ATTRIBUTE_HIDDEN
);
2036 PUSH_LE_U16(param
, 2, 1366); /* max_matches */
2037 PUSH_LE_U16(param
, 4, FLAG_TRANS2_FIND_CLOSE_IF_END
);
2038 PUSH_LE_U16(param
, 6, SMB_FIND_FILE_BOTH_DIRECTORY_INFO
); /* info_level */
2040 param
= trans2_bytes_push_str(param
,
2041 smbXcli_conn_use_unicode(cli
->conn
),
2043 strlen(search_name
)+1,
2045 if (param
== NULL
) {
2046 return NT_STATUS_NO_MEMORY
;
2050 * A one shot SMB1 findfirst will be enough to
2051 * return ".", "..", and "file".
2053 status
= cli_trans(mem_ctx
,
2055 SMBtrans2
, /* cmd */
2056 NULL
, /* pipe_name */
2061 1, /* num_setup uint16_t words */
2062 0, /* max returned setup */
2064 talloc_get_size(param
), /* num_param */
2065 10, /* max returned param */
2068 SMB_BUFFER_SIZE_MAX
, /* max retured data */
2069 /* Return values from here on.. */
2070 &recv_flags2
, /* recv_flags2 */
2072 0, /* min returned rsetup */
2073 NULL
, /* num_rsetup */
2075 6, /* min returned rparam */
2076 &num_rparam
, /* number of returned rparam */
2078 0, /* min returned rdata */
2080 if (!NT_STATUS_IS_OK(status
)) {
2084 num_names_returned
= PULL_LE_U16(rparam
, 2);
2086 finfo
= talloc_array(mem_ctx
, struct file_info
, num_names_returned
);
2087 if (param
== NULL
) {
2088 return NT_STATUS_NO_MEMORY
;
2092 data_end
= rdata
+ num_rdata
;
2094 for (i
= 0; i
< num_names_returned
; i
++) {
2095 if (p2
>= data_end
) {
2098 if (i
== num_names_returned
- 1) {
2099 /* Last entry - fixup the last offset length. */
2100 PUSH_LE_U32(p2
, 0, PTR_DIFF((rdata
+ num_rdata
), p2
));
2103 p2
+= get_filename(mem_ctx
,
2111 if (finfo
->name
== NULL
) {
2112 printf("%s:%d Unable to parse name from listing "
2113 "of %s, position %u\n",
2118 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
2123 return NT_STATUS_OK
;
2127 * Test a specific SMB1 findfirst path to see if it
2128 * matches a given file array.
2130 static bool test_smb1_findfirst_path(struct cli_state
*cli
,
2131 const char *search_path
,
2132 struct file_info
*root_finfo
,
2133 size_t num_root_finfo
)
2136 size_t num_finfo
= 0;
2137 struct file_info
*finfo
= NULL
;
2140 status
= smb1_findfirst(talloc_tos(),
2145 if (!NT_STATUS_IS_OK(status
)) {
2146 printf("%s:%d smb1findfirst on %s returned %s\n",
2154 if (num_finfo
!= num_root_finfo
) {
2155 printf("%s:%d On %s, num_finfo = %zu, num_root_finfo = %zu\n",
2163 for (i
= 0; i
< num_finfo
; i
++) {
2164 bool match
= strequal_m(finfo
[i
].name
,
2165 root_finfo
[i
].name
);
2167 printf("%s:%d Missmatch. For %s, at position %zu, "
2168 "finfo[i].name = %s, "
2169 "root_finfo[i].name = %s\n",
2175 root_finfo
[i
].name
);
2184 * "Raw" test of doing a SMB1 findfirst to a DFS share.
2185 * We must (mostly) use the lower level smb1cli_XXXX() interfaces,
2186 * not the cli_XXX() ones here as the ultimate goal is to fix our
2187 * cli_XXX() interfaces to work transparently over DFS.
2189 * So here, we're testing the server code, not the client code.
2191 * Passes cleanly against Windows.
2194 bool run_smb1_dfs_search_paths(int dummy
)
2196 struct cli_state
*cli
= NULL
;
2198 bool dfs_supported
= false;
2199 struct file_info
*root_finfo
= NULL
;
2200 size_t num_root_finfo
= 0;
2201 bool retval
= false;
2203 uint16_t fnum
= (uint16_t)-1;
2205 printf("Starting SMB1-DFS-SEARCH-PATHS\n");
2207 if (!torture_init_connection(&cli
)) {
2211 if (!torture_open_connection(&cli
, 0)) {
2215 /* Ensure this is a DFS share. */
2216 dfs_supported
= smbXcli_conn_dfs_supported(cli
->conn
);
2217 if (!dfs_supported
) {
2218 printf("Server %s does not support DFS\n",
2219 smbXcli_conn_remote_name(cli
->conn
));
2222 dfs_supported
= smbXcli_tcon_is_dfs_share(cli
->smb1
.tcon
);
2223 if (!dfs_supported
) {
2224 printf("Share %s does not support DFS\n",
2230 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\file");
2232 /* Create a test file to search for. */
2233 status
= smb1cli_ntcreatex(cli
->conn
,
2239 OPLOCK_NONE
, /* CreatFlags */
2240 0, /* RootDirectoryFid */
2241 SEC_STD_SYNCHRONIZE
|
2244 SEC_FILE_READ_ATTRIBUTE
, /* DesiredAccess */
2245 0, /* AllocationSize */
2246 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
2249 FILE_SHARE_DELETE
, /* ShareAccess */
2250 FILE_CREATE
, /* CreateDisposition */
2251 0, /* CreateOptions */
2252 2, /* ImpersonationLevel */
2253 0, /* SecurityFlags */
2255 if (!NT_STATUS_IS_OK(status
)) {
2256 printf("%s:%d smb1cli_ntcreatex on %s returned %s\n",
2264 /* Close "file" handle. */
2265 (void)smb1cli_close(cli
->conn
,
2271 0); /* last_modified */
2272 fnum
= (uint16_t)-1;
2274 /* Get the list of files in the share. */
2275 status
= smb1_findfirst(talloc_tos(),
2280 if (!NT_STATUS_IS_OK(status
)) {
2281 printf("%s:%d smb1findfirst on %s returned %s\n",
2290 * Try different search names. They should
2291 * all match the root directory list.
2293 ok
= test_smb1_findfirst_path(cli
,
2294 "\\SERVER\\SHARE\\*",
2301 ok
= test_smb1_findfirst_path(cli
,
2308 ok
= test_smb1_findfirst_path(cli
,
2315 ok
= test_smb1_findfirst_path(cli
,
2326 if (fnum
!= (uint16_t)-1) {
2327 (void)smb1cli_close(cli
->conn
,
2333 0); /* last_modified */
2336 /* Delete anything we made. */
2337 (void)smb1_dfs_delete(cli
, "BAD\\BAD\\file");