From e814586bddd081d85a216533bf47a1ac9d8b025f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 20:06:22 +1000 Subject: generate security descriptors in gentest_smb2 (This used to be commit 5ca7e9590e792dc409c6677321cd14362255964e) --- source4/torture/gentest_smb2.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c index a3a794d3ea..84178ed9a5 100644 --- a/source4/torture/gentest_smb2.c +++ b/source4/torture/gentest_smb2.c @@ -30,11 +30,13 @@ #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" #include "librpc/gen_ndr/security.h" +#include "librpc/gen_ndr/ndr_security.h" #include "auth/credentials/credentials.h" #include "libcli/resolve/resolve.h" #include "auth/gensec/gensec.h" #include "param/param.h" #include "dynconfig/dynconfig.h" +#include "libcli/security/security.h" #define NSERVERS 2 #define NINSTANCES 2 @@ -725,6 +727,26 @@ static struct smb_ea_list gen_ea_list(void) return eas; } +/* generate a security descriptor */ +static struct security_descriptor *gen_sec_desc(void) +{ + struct security_descriptor *sd; + if (gen_chance(90)) return NULL; + + sd = security_descriptor_dacl_create(current_op.mem_ctx, + 0, NULL, NULL, + NULL, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + return sd; +} + static void oplock_handler_close_recv(struct smb2_request *req) { NTSTATUS status; @@ -1066,6 +1088,13 @@ again: } \ } while(0) +#define CHECK_SECDESC(field) do { \ + if (!security_acl_equal(parm[0].field->dacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ +} while(0) + #define CHECK_ATTRIB(field) do { \ if (!options.mask_indexing) { \ CHECK_EQUAL(field); \ @@ -1134,6 +1163,7 @@ static bool handler_create(int instance) parm[0].in.query_maximal_access = gen_bool(); parm[0].in.timewarp = gen_timewarp(); parm[0].in.query_on_disk_id = gen_bool(); + parm[0].in.sec_desc = gen_sec_desc(); if (!options.use_oplocks) { /* mask out oplocks */ @@ -1340,7 +1370,7 @@ static void gen_fileinfo(int instance, union smb_fileinfo *info) LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), - LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), + LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), LVL(SEC_DESC), }; do { i = gen_int_range(0, ARRAY_SIZE(levels)-1); @@ -1490,9 +1520,11 @@ static bool cmp_fileinfo(int instance, } break; - /* Unhandled levels */ - case RAW_FILEINFO_SEC_DESC: + CHECK_SECDESC(query_secdesc.out.sd); + break; + + /* Unhandled levels */ case RAW_FILEINFO_EA_LIST: case RAW_FILEINFO_UNIX_BASIC: case RAW_FILEINFO_UNIX_LINK: -- cgit From f7bf79043e664b83e62dae6ea187b5c019968d28 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 20:06:38 +1000 Subject: added testing of some strange read semantics on windows (This used to be commit 46a0b65742bc0e4277da53df9df823abd4a0d150) --- source4/torture/smb2/read.c | 61 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c index b3c13ad667..1f306028f6 100644 --- a/source4/torture/smb2/read.c +++ b/source4/torture/smb2/read.c @@ -44,6 +44,9 @@ goto done; \ }} while (0) +#define FNAME "smb2_readtest.dat" +#define DNAME "smb2_readtest.dir" + static bool test_read_eof(struct torture_context *torture, struct smb2_tree *tree) { bool ret = true; @@ -55,7 +58,7 @@ static bool test_read_eof(struct torture_context *torture, struct smb2_tree *tre ZERO_STRUCT(buf); - status = torture_smb2_testfile(tree, "lock1.txt", &h); + status = torture_smb2_testfile(tree, FNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); @@ -139,7 +142,7 @@ static bool test_read_position(struct torture_context *torture, struct smb2_tree ZERO_STRUCT(buf); - status = torture_smb2_testfile(tree, "lock1.txt", &h); + status = torture_smb2_testfile(tree, FNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); @@ -172,6 +175,59 @@ done: return ret; } +static bool test_read_dir(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[100]; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + status = torture_smb2_testdir(tree, DNAME, &h); + if (!NT_STATUS_IS_OK(status)) { + printf(__location__ " Unable to create test directory '%s' - %s\n", DNAME, nt_errstr(status)); + return false; + } + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.min_count = 11; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.length = 0; + rd.in.min_count = 2592; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + + rd.in.length = 0; + rd.in.min_count = 0; + rd.in.channel = 0; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + +done: + talloc_free(tmp_ctx); + return ret; +} + + /* basic testing of SMB2 read */ @@ -181,6 +237,7 @@ struct torture_suite *torture_smb2_read_init(void) torture_suite_add_1smb2_test(suite, "EOF", test_read_eof); torture_suite_add_1smb2_test(suite, "POSITION", test_read_position); + torture_suite_add_1smb2_test(suite, "DIR", test_read_dir); suite->description = talloc_strdup(suite, "SMB2-READ tests"); -- cgit From 1eabf8155486d007a69667175ae7f6bebc440122 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 20:06:48 +1000 Subject: check that we can't change a file to a directory (This used to be commit e013ada415ebb25e925f715791757330ba003b43) --- source4/torture/smb2/setinfo.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source4/torture') diff --git a/source4/torture/smb2/setinfo.c b/source4/torture/smb2/setinfo.c index 685bb5a47b..7a912b4989 100644 --- a/source4/torture/smb2/setinfo.c +++ b/source4/torture/smb2/setinfo.c @@ -175,6 +175,10 @@ bool torture_smb2_setinfo(struct torture_context *torture) CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_HIDDEN); + printf("can't change a file to a directory\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_INVALID_PARAMETER); + printf("restore attribute\n"); sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); -- cgit From dfdfe06dc126822de8705d9308237864965c7d40 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 21:49:04 +1000 Subject: added --noacls options and checking for same mismatch in backtracking (This used to be commit c7d2e1bf65a28b6a2efd60585ae8ead2fb486e53) --- source4/torture/gentest_smb2.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'source4/torture') diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c index 84178ed9a5..e50c4969d5 100644 --- a/source4/torture/gentest_smb2.c +++ b/source4/torture/gentest_smb2.c @@ -57,6 +57,7 @@ static struct gentest_options { int fast_reconnect; int mask_indexing; int no_eas; + int no_acls; int skip_cleanup; int valid; } options; @@ -109,6 +110,7 @@ static struct { NTSTATUS status; uint_t opnum; TALLOC_CTX *mem_ctx; + const char *mismatch; } current_op; static struct smb2_handle bad_smb2_handle; @@ -731,7 +733,7 @@ static struct smb_ea_list gen_ea_list(void) static struct security_descriptor *gen_sec_desc(void) { struct security_descriptor *sd; - if (gen_chance(90)) return NULL; + if (options.no_acls || gen_chance(90)) return NULL; sd = security_descriptor_dacl_create(current_op.mem_ctx, 0, NULL, NULL, @@ -883,6 +885,7 @@ static bool compare_status(NTSTATUS status1, NTSTATUS status2) ignore_pattern(nt_errstr(status2))) { return true; } + current_op.mismatch = nt_errstr(status1); return false; } @@ -983,6 +986,7 @@ again: printf("Notify status mismatch - %s - %s\n", nt_errstr(notifies[0][j].status), nt_errstr(notifies[i][j].status)); + current_op.mismatch = "Notify status"; return false; } @@ -1082,6 +1086,7 @@ again: #define CHECK_EQUAL(field) do { \ if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%llx 0x%llx\n", #field, \ (unsigned long long)parm[0].field, (unsigned long long)parm[1].field); \ return false; \ @@ -1090,6 +1095,7 @@ again: #define CHECK_SECDESC(field) do { \ if (!security_acl_equal(parm[0].field->dacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s\n", #field); \ return false; \ } \ @@ -1099,6 +1105,7 @@ again: if (!options.mask_indexing) { \ CHECK_EQUAL(field); \ } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)parm[0].field, (int)parm[1].field); \ return false; \ @@ -1111,6 +1118,7 @@ again: return false; \ } \ if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - %s %s\n", #field, \ parm[0].field.s, parm[1].field.s); \ return false; \ @@ -1120,6 +1128,7 @@ again: #define CHECK_BLOB_EQUAL(field) do { \ if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s\n", #field); \ return false; \ } \ @@ -1130,6 +1139,7 @@ again: if (labs(nt_time_to_unix(parm[0].field) - \ nt_time_to_unix(parm[1].field)) > time_skew() && \ !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)nt_time_to_unix(parm[0].field), \ (int)nt_time_to_unix(parm[1].field)); \ @@ -1815,6 +1825,7 @@ static void backtrack_analyze(struct event_context *ev, struct loadparm_context *lp_ctx) { int chunk, ret; + const char *mismatch = current_op.mismatch; chunk = options.numops / 2; @@ -1841,6 +1852,9 @@ static void backtrack_analyze(struct event_context *ev, if (ret == options.numops) { /* this chunk is needed */ base += chunk; + } else if (strcmp(mismatch, current_op.mismatch)) { + base += chunk; + printf("Different error in backtracking\n"); } else if (ret < base) { printf("damn - inconsistent errors! found early error\n"); options.numops = ret+1; @@ -1989,6 +2003,7 @@ static bool split_unc_name(const char *unc, char **server, char **share) { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" }, {"maskindexing", 0, POPT_ARG_NONE, &options.mask_indexing, 0, "mask out the indexed file attrib", NULL}, {"noeas", 0, POPT_ARG_NONE, &options.no_eas, 0, "don't use extended attributes", NULL}, + {"noacls", 0, POPT_ARG_NONE, &options.no_acls, 0, "don't use ACLs", NULL}, {"skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, "don't delete files at start", NULL}, {"valid", 0, POPT_ARG_NONE, &options.valid, 0, "generate only valid fields", NULL}, POPT_COMMON_SAMBA -- cgit From 70eb8d54aec9cb97d8f6675c4dcac2510e1916bb Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 22:44:35 +1000 Subject: fixed current_op.mismatch for more cases (This used to be commit 8a2a31d050a31308036545414f8d180ec8148f1d) --- source4/torture/gentest_smb2.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c index e50c4969d5..5a209180e3 100644 --- a/source4/torture/gentest_smb2.c +++ b/source4/torture/gentest_smb2.c @@ -878,7 +878,10 @@ static bool compare_status(NTSTATUS status1, NTSTATUS status2) if (NT_STATUS_EQUAL(status1, status2)) return true; /* one code being an error and the other OK is always an error */ - if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return false; + if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) { + current_op.mismatch = nt_errstr(status1); + return false; + } /* if we are ignoring one of the status codes then consider this a match */ if (ignore_pattern(nt_errstr(status1)) || @@ -932,6 +935,7 @@ again: oplocks[i][j].got_break, oplocks[i][j].handle, oplocks[i][j].level); + current_op.mismatch = "oplock break"; return false; } } @@ -976,6 +980,7 @@ again: printf("Notify count inconsistent %d %d\n", notifies[0][j].notify_count, notifies[i][j].notify_count); + current_op.mismatch = "notify count"; return false; } @@ -1003,6 +1008,7 @@ again: printf("Notify action %d inconsistent %d %d\n", n, not1.nttrans.out.changes[n].action, not2.nttrans.out.changes[n].action); + current_op.mismatch = "notify action"; return false; } if (strcmp(not1.nttrans.out.changes[n].name.s, @@ -1010,6 +1016,7 @@ again: printf("Notify name %d inconsistent %s %s\n", n, not1.nttrans.out.changes[n].name.s, not2.nttrans.out.changes[n].name.s); + current_op.mismatch = "notify name"; return false; } if (not1.nttrans.out.changes[n].name.private_length != @@ -1017,6 +1024,7 @@ again: printf("Notify name length %d inconsistent %d %d\n", n, not1.nttrans.out.changes[n].name.private_length, not2.nttrans.out.changes[n].name.private_length); + current_op.mismatch = "notify name length"; return false; } } @@ -1049,6 +1057,7 @@ again: if (!compare_status(status[i], status[0])) { \ printf("status different in %s - %s %s\n", #call, \ nt_errstr(status[0]), nt_errstr(status[i])); \ + current_op.mismatch = nt_errstr(status[0]); \ return false; \ } \ } \ @@ -1114,6 +1123,7 @@ again: #define CHECK_WSTR_EQUAL(field) do { \ if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ + current_op.mismatch = #field; \ printf("%s is NULL!\n", #field); \ return false; \ } \ @@ -1852,7 +1862,8 @@ static void backtrack_analyze(struct event_context *ev, if (ret == options.numops) { /* this chunk is needed */ base += chunk; - } else if (strcmp(mismatch, current_op.mismatch)) { + } else if (mismatch != current_op.mismatch && + strcmp(mismatch, current_op.mismatch)) { base += chunk; printf("Different error in backtracking\n"); } else if (ret < base) { -- cgit From c793faf9986a754d4b68b9feb080878d130601e3 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 22:44:54 +1000 Subject: check the set of file attributes which are ignored (This used to be commit c818f56d8ea3ddc6f4cc61e9d5ed6fd195280a5d) --- source4/torture/smb2/create.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 733b2c2e8a..58d38a2cb3 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -52,7 +52,7 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre struct smb2_create io; NTSTATUS status; TALLOC_CTX *tmp_ctx = talloc_new(tree); - uint32_t access_mask, file_attributes, denied_mask; + uint32_t access_mask, file_attributes, file_attributes_set, denied_mask; ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; @@ -115,7 +115,8 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre for (i=0;i<32;i++) { io.in.desired_access = 1< Date: Thu, 29 May 2008 19:16:44 +1000 Subject: test the maximal access return (This used to be commit 23ffec5d140463c8307fd7e444ae25781ea3d792) --- source4/torture/smb2/create.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'source4/torture') diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 58d38a2cb3..eb6b333ef5 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -41,7 +41,7 @@ if (v != correct) { \ printf("(%s) Incorrect value for %s 0x%08llx - should be 0x%08llx\n", \ __location__, #v, (unsigned long long)v, (unsigned long long)correct); \ - return false; \ + return false; \ }} while (0) /* @@ -53,6 +53,7 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre NTSTATUS status; TALLOC_CTX *tmp_ctx = talloc_new(tree); uint32_t access_mask, file_attributes, file_attributes_set, denied_mask; + union smb_fileinfo q; ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; @@ -181,6 +182,20 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre status = smb2_create(tree, tmp_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + io.in.fname = FNAME; + io.in.file_attributes = 0; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA; + io.in.query_maximal_access = true; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); + + q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + q.access_information.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tmp_ctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(q.access_information.out.access_flags, io.in.desired_access); + talloc_free(tmp_ctx); smb2_deltree(tree, FNAME); -- cgit From 523a0fccf374d56e9e73642a95564f5cb246d13d Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 29 May 2008 19:30:11 +1000 Subject: check maximal_access here too (This used to be commit 8159b1598e21daee730e185d025694e27df18d1b) --- source4/torture/smb2/create.c | 1 + 1 file changed, 1 insertion(+) (limited to 'source4/torture') diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index eb6b333ef5..c23ff8b8ce 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -256,6 +256,7 @@ static bool test_create_blob(struct torture_context *torture, struct smb2_tree * io.in.query_maximal_access = true; status = smb2_create(tree, tmp_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); -- cgit From 8eef2e34128d713b741307a8c7b1650217698e06 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 29 May 2008 21:32:08 +1000 Subject: merged gentest.c and gentest_smb2.c The one gentest tool now covers both SMB and SMB2, using the command line switch --smb2 for SMB2 (This used to be commit d1125a303a31fbe08a9bd0064ec132b4d7cbb131) --- source4/torture/config.mk | 17 - source4/torture/gentest.c | 2225 ++++++++++++++++++++++++++++------------ source4/torture/gentest_smb2.c | 2122 -------------------------------------- 3 files changed, 1575 insertions(+), 2789 deletions(-) delete mode 100644 source4/torture/gentest_smb2.c (limited to 'source4/torture') diff --git a/source4/torture/config.mk b/source4/torture/config.mk index 2857b99582..5a1746c215 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -262,23 +262,6 @@ gentest_OBJ_FILES = $(torturesrcdir)/gentest.o MANPAGES += $(torturesrcdir)/man/gentest.1 -################################# -# Start BINARY gentest_smb2 -[BINARY::gentest_smb2] -INSTALLDIR = BINDIR -PRIVATE_DEPENDENCIES = \ - LIBSAMBA-HOSTCONFIG \ - LIBSAMBA-UTIL \ - LIBPOPT \ - POPT_SAMBA \ - POPT_CREDENTIALS \ - LIBCLI_SMB \ - LIBCLI_RAW -# End BINARY gentest_smb2 -################################# - -gentest_smb2_OBJ_FILES = $(torturesrcdir)/gentest_smb2.o - ################################# # Start BINARY masktest [BINARY::masktest] diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index 068b6bdc2f..fd6bd5cc0a 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. - generic testing tool - Copyright (C) Andrew Tridgell 2003 + + generic testing tool - version with both SMB and SMB2 support + + Copyright (C) Andrew Tridgell 2003-2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,12 +27,17 @@ #include "libcli/raw/request.h" #include "libcli/libcli.h" #include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" #include "librpc/gen_ndr/security.h" +#include "librpc/gen_ndr/ndr_security.h" #include "auth/credentials/credentials.h" #include "libcli/resolve/resolve.h" #include "auth/gensec/gensec.h" #include "param/param.h" #include "dynconfig/dynconfig.h" +#include "libcli/security/security.h" +#include "libcli/raw/raw_proto.h" #define NSERVERS 2 #define NINSTANCES 2 @@ -49,14 +56,20 @@ static struct gentest_options { const char *seeds_file; int use_preset_seeds; int fast_reconnect; + int mask_indexing; + int no_eas; + int no_acls; int skip_cleanup; + int valid; + int smb2; } options; /* mapping between open handles on the server and local handles */ static struct { bool active; uint_t instance; - uint_t server_fnum[NSERVERS]; + struct smb2_handle smb2_handle[NSERVERS]; /* SMB2 */ + uint16_t smb_handle[NSERVERS]; /* SMB */ const char *name; } *open_handles; static uint_t num_open_handles; @@ -64,7 +77,8 @@ static uint_t num_open_handles; /* state information for the servers. We open NINSTANCES connections to each server */ static struct { - struct smbcli_state *cli[NINSTANCES]; + struct smb2_tree *smb2_tree[NINSTANCES]; + struct smbcli_tree *smb_tree[NINSTANCES]; char *server_name; char *share_name; struct cli_credentials *credentials; @@ -80,7 +94,8 @@ static struct { /* oplock break info */ static struct { bool got_break; - uint16_t fnum; + struct smb2_handle smb2_handle; + uint16_t smb_handle; uint16_t handle; uint8_t level; bool do_close; @@ -100,14 +115,19 @@ static struct { NTSTATUS status; uint_t opnum; TALLOC_CTX *mem_ctx; + const char *mismatch; } current_op; +static struct smb2_handle bad_smb2_handle; #define BAD_HANDLE 0xFFFE -static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private); -static void idle_func(struct smbcli_transport *transport, void *private); +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data); +static void idle_func_smb2(struct smb2_transport *transport, void *private); +static bool oplock_handler_smb(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private); +static void idle_func_smb(struct smbcli_transport *transport, void *private); /* check if a string should be ignored. This is used as the basis @@ -139,8 +159,15 @@ static bool connect_servers_fast(void) for (h=0;htree, - open_handles[h].server_fnum[i])))) { + NTSTATUS status; + if (options.smb2) { + status = smb2_util_close(servers[i].smb2_tree[open_handles[h].instance], + open_handles[h].smb2_handle[i]); + } else { + status = smbcli_close(servers[i].smb_tree[open_handles[h].instance], + open_handles[h].smb_handle[i]); + } + if (NT_STATUS_IS_ERR(status)) { return false; } open_handles[h].active = false; @@ -161,7 +188,7 @@ static bool connect_servers(struct event_context *ev, { int i, j; - if (options.fast_reconnect && servers[0].cli[0]) { + if (options.fast_reconnect && servers[0].smb2_tree[0]) { if (connect_servers_fast()) { return true; } @@ -170,17 +197,21 @@ static bool connect_servers(struct event_context *ev, /* close any existing connections */ for (i=0;itransport, oplock_handler, NULL); - smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL); + if (options.smb2) { + servers[i].smb2_tree[j]->session->transport->oplock.handler = oplock_handler_smb2; + servers[i].smb2_tree[j]->session->transport->oplock.private_data = (void *)(uintptr_t)((i<<8)|j); + smb2_transport_idle_handler(servers[i].smb2_tree[j]->session->transport, + idle_func_smb2, 50000, NULL); + } else { + smbcli_oplock_handler(servers[i].smb_tree[j]->session->transport, oplock_handler_smb, + (void *)(uintptr_t)((i<<8)|j)); + smbcli_transport_idle_handler(servers[i].smb_tree[j]->session->transport, idle_func_smb, + 50000, (void *)(uintptr_t)((i<<8)|j)); + } } } @@ -218,33 +269,98 @@ static bool connect_servers(struct event_context *ev, static uint_t time_skew(void) { uint_t ret; - ret = labs(servers[0].cli[0]->transport->negotiate.server_time - - servers[1].cli[0]->transport->negotiate.server_time); + if (options.smb2) { + ret = labs(servers[0].smb2_tree[0]->session->transport->negotiate.system_time - + servers[1].smb2_tree[0]->session->transport->negotiate.system_time); + } else { + ret = labs(servers[0].smb_tree[0]->session->transport->negotiate.server_time - + servers[1].smb_tree[0]->session->transport->negotiate.server_time); + } return ret + 300; } + +static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) +{ + return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; +} + /* - turn an fnum for an instance into a handle + turn a server handle into a local handle */ -static uint_t fnum_to_handle(int server, int instance, uint16_t fnum) +static uint_t fnum_to_handle_smb2(int server, int instance, struct smb2_handle server_handle) { uint_t i; for (i=0;itree, - open_handles[h].server_fnum[i])))) { + NTSTATUS status; + status = smbcli_close(servers[i].smb_tree[open_handles[h].instance], + open_handles[h].smb_handle[i]); + if (NT_STATUS_IS_ERR(status)) { printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n", - smbcli_errstr(servers[i].cli[open_handles[h].instance]->tree)); + nt_errstr(status)); } } printf("Recovered handle %d\n", h); num_open_handles--; } for (i=0;itransport; - - tid = SVAL(req->in.hdr, HDR_TID); - - notify.nttrans.level = RAW_NOTIFY_NTTRANS; - status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); - if (NT_STATUS_IS_OK(status)) { - printf("notify tid=%d num_changes=%d action=%d name=%s\n", - tid, - notify.nttrans.out.num_changes, - notify.nttrans.out.changes[0].action, - notify.nttrans.out.changes[0].name.s); + struct smb_ea_list eas; + int i; + if (options.no_eas) { + ZERO_STRUCT(eas); + return eas; } - - for (i=0;itransport && - tid == servers[i].cli[j]->tree->tid) { - notifies[i][j].notify_count++; - notifies[i][j].status = status; - notifies[i][j].notify = notify; - } - } + eas.num_eas = gen_int_range(0, 3); + eas.eas = talloc_array(current_op.mem_ctx, struct ea_struct, eas.num_eas); + for (i=0;itransport && - tid == servers[i].cli[j]->tree->tid) { + if (transport == servers[i].smb_tree[j]->session->transport && + tid == servers[i].smb_tree[j]->tid) { oplocks[i][j].got_break = true; - oplocks[i][j].fnum = fnum; - oplocks[i][j].handle = fnum_to_handle(i, j, fnum); + oplocks[i][j].smb_handle = fnum; + oplocks[i][j].handle = fnum_to_handle_smb(i, j, fnum); oplocks[i][j].level = level; oplocks[i][j].do_close = do_close; - tree = servers[i].cli[j]->tree; + tree = servers[i].smb_tree[j]; } } } @@ -784,7 +1012,7 @@ static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin return false; } - req->async.fn = oplock_handler_close_recv; + req->async.fn = oplock_handler_close_recv_smb; req->async.private = NULL; return true; @@ -796,83 +1024,212 @@ static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin an operation on another connection blocking until that break is acked we check for operations on all transports in the idle function */ -static void idle_func(struct smbcli_transport *transport, void *private) +static void idle_func_smb(struct smbcli_transport *transport, void *private) { int i, j; for (i=0;itransport) { - smbcli_transport_process(servers[i].cli[j]->transport); + if (servers[i].smb_tree[j] && + transport != servers[i].smb_tree[j]->session->transport) { + smbcli_transport_process(servers[i].smb_tree[j]->session->transport); } } } } - -/* - compare NTSTATUS, using checking ignored patterns -*/ -static bool compare_status(NTSTATUS status1, NTSTATUS status2) +static void oplock_handler_close_recv_smb2(struct smb2_request *req) { - if (NT_STATUS_EQUAL(status1, status2)) return true; + NTSTATUS status; + struct smb2_close io; + status = smb2_close_recv(req, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler\n"); + smb_panic("close failed in oplock_handler"); + } +} - /* one code being an error and the other OK is always an error */ - if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return false; +static void oplock_handler_ack_callback_smb2(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; - /* if we are ignoring one of the status codes then consider this a match */ - if (ignore_pattern(nt_errstr(status1)) || - ignore_pattern(nt_errstr(status2))) { - return true; + status = smb2_break_recv(req, &br); + if (!NT_STATUS_IS_OK(status)) { + printf("oplock break ack failed in oplock_handler\n"); + smb_panic("oplock break ack failed in oplock_handler"); } - return false; } - -/* - check for pending packets on all connections -*/ -static void check_pending(void) +static bool send_oplock_ack_smb2(struct smb2_tree *tree, struct smb2_handle handle, + uint8_t level) { - int i, j; + struct smb2_break br; + struct smb2_request *req; - msleep(20); + ZERO_STRUCT(br); + br.in.file.handle = handle; + br.in.oplock_level = level; + br.in.reserved = gen_reserved8(); + br.in.reserved2 = gen_reserved32(); - for (j=0;jtransport); - } - } + req = smb2_break_send(tree, &br); + if (req == NULL) return false; + req->async.fn = oplock_handler_ack_callback_smb2; + req->async.private_data = NULL; + return true; } /* - check that the same oplock breaks have been received by all instances + the oplock handler will either ack the break or close the file */ -static bool check_oplocks(const char *call) +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data) { - int i, j; - int tries = 0; + struct smb2_close io; + unsigned i, j; + bool do_close; + struct smb2_tree *tree = NULL; + struct smb2_request *req; -again: - check_pending(); + srandom(current_op.seed); + do_close = gen_chance(50); - for (j=0;j> 8; + j = ((uintptr_t)private_data) & 0xFF; + + if (i >= NSERVERS || j >= NINSTANCES) { + printf("Bad private_data in oplock_handler\n"); + return false; + } + + oplocks[i][j].got_break = true; + oplocks[i][j].smb2_handle = *handle; + oplocks[i][j].handle = fnum_to_handle_smb2(i, j, *handle); + oplocks[i][j].level = level; + oplocks[i][j].do_close = do_close; + tree = talloc_get_type(servers[i].smb2_tree[j], struct smb2_tree); + + if (!tree) { + printf("Oplock break not for one of our trees!?\n"); + return false; + } + + if (!do_close) { + printf("oplock ack handle=%d\n", oplocks[i][j].handle); + return send_oplock_ack_smb2(tree, *handle, level); + } + + printf("oplock close fnum=%d\n", oplocks[i][j].handle); + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = 0; + req = smb2_close_send(tree, &io); + + if (req == NULL) { + printf("WARNING: close failed in oplock_handler_close\n"); + return false; + } + + req->async.fn = oplock_handler_close_recv_smb2; + req->async.private_data = NULL; + + return true; +} + + +/* + the idle function tries to cope with getting an oplock break on a connection, and + an operation on another connection blocking until that break is acked + we check for operations on all transports in the idle function +*/ +static void idle_func_smb2(struct smb2_transport *transport, void *private) +{ + int i, j; + for (i=0;isession->transport) { + // smb2_transport_process(servers[i].smb2_tree[j]->session->transport); + } + } + } + +} + + +/* + compare NTSTATUS, using checking ignored patterns +*/ +static bool compare_status(NTSTATUS status1, NTSTATUS status2) +{ + if (NT_STATUS_EQUAL(status1, status2)) return true; + + /* one code being an error and the other OK is always an error */ + if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) { + current_op.mismatch = nt_errstr(status1); + return false; + } + + /* if we are ignoring one of the status codes then consider this a match */ + if (ignore_pattern(nt_errstr(status1)) || + ignore_pattern(nt_errstr(status2))) { + return true; + } + current_op.mismatch = nt_errstr(status1); + return false; +} + +/* + check for pending packets on all connections +*/ +static void check_pending(void) +{ + int i, j; + + msleep(20); + + for (j=0;jsession->transport); + } + } +} + +/* + check that the same oplock breaks have been received by all instances +*/ +static bool check_oplocks(const char *call) +{ + int i, j; + int tries = 0; + + if (!options.use_oplocks || options.smb2) { + /* no smb2 oplocks in gentest yet */ + return true; + } + +again: + check_pending(); + + for (j=0;jtree; \ + struct treetype *tree = servers[i].treefield[instance]; \ status[i] = call; \ } \ current_op.status = status[0]; \ @@ -984,43 +1350,92 @@ again: if (!compare_status(status[i], status[0])) { \ printf("status different in %s - %s %s\n", #call, \ nt_errstr(status[0]), nt_errstr(status[i])); \ + current_op.mismatch = nt_errstr(status[0]); \ return false; \ } \ } \ - if (!check_oplocks(#call)) return false; \ - if (!check_notifies(#call)) return false; \ + if (!check_oplocks(#call)) return false; \ + if (!check_notifies(#call)) return false; \ if (!NT_STATUS_IS_OK(status[0])) { \ return true; \ } \ } while(0) -#define ADD_HANDLE(name, field) do { \ - uint16_t fnums[NSERVERS]; \ +#define GEN_CALL_SMB(call) GEN_CALL(call, smbcli_tree, smb_tree) +#define GEN_CALL_SMB2(call) GEN_CALL(call, smb2_tree, smb2_tree) + +#define ADD_HANDLE_SMB2(name, field) do { \ + struct smb2_handle handles[NSERVERS]; \ + int i; \ + for (i=0;idacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ +} while(0) + +#define CHECK_ATTRIB(field) do { \ + if (!options.mask_indexing) { \ + CHECK_EQUAL(field); \ + } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)parm[0].field, (int)parm[1].field); \ return false; \ @@ -1029,10 +1444,12 @@ again: #define CHECK_WSTR_EQUAL(field) do { \ if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ + current_op.mismatch = #field; \ printf("%s is NULL!\n", #field); \ return false; \ } \ if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - %s %s\n", #field, \ parm[0].field.s, parm[1].field.s); \ return false; \ @@ -1042,6 +1459,7 @@ again: #define CHECK_BLOB_EQUAL(field) do { \ if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s\n", #field); \ return false; \ } \ @@ -1051,6 +1469,7 @@ again: #define CHECK_TIMES_EQUAL(field) do { \ if (labs(parm[0].field - parm[1].field) > time_skew() && \ !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)parm[0].field, (int)parm[1].field); \ return false; \ @@ -1061,6 +1480,7 @@ again: if (labs(nt_time_to_unix(parm[0].field) - \ nt_time_to_unix(parm[1].field)) > time_skew() && \ !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)nt_time_to_unix(parm[0].field), \ (int)nt_time_to_unix(parm[1].field)); \ @@ -1068,89 +1488,305 @@ again: } \ } while(0) + /* - generate openx operations + compare returned fileinfo structures */ -static bool handler_openx(int instance) +static bool cmp_fileinfo(int instance, + union smb_fileinfo parm[NSERVERS], + NTSTATUS status[NSERVERS]) { - union smb_open parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].openx.level = RAW_OPEN_OPENX; - parm[0].openx.in.flags = gen_openx_flags(); - parm[0].openx.in.open_mode = gen_openx_mode(); - parm[0].openx.in.search_attrs = gen_attrib(); - parm[0].openx.in.file_attrs = gen_attrib(); - parm[0].openx.in.write_time = gen_timet(); - parm[0].openx.in.open_func = gen_openx_func(); - parm[0].openx.in.size = gen_io_count(); - parm[0].openx.in.timeout = gen_timeout(); - parm[0].openx.in.fname = gen_fname_open(instance); + int i; + enum smb_fileinfo_level level = parm[0].generic.level; - if (!options.use_oplocks) { - /* mask out oplocks */ - parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK| - OPENX_FLAGS_REQUEST_BATCH_OPLOCK); + if (level == RAW_FILEINFO_ALL_INFORMATION && + options.smb2) { + level = RAW_FILEINFO_SMB2_ALL_INFORMATION; } - - GEN_COPY_PARM; - GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); - - CHECK_EQUAL(openx.out.attrib); - CHECK_EQUAL(openx.out.size); - CHECK_EQUAL(openx.out.access); - CHECK_EQUAL(openx.out.ftype); - CHECK_EQUAL(openx.out.devstate); - CHECK_EQUAL(openx.out.action); - CHECK_EQUAL(openx.out.access_mask); - CHECK_EQUAL(openx.out.unknown); - CHECK_TIMES_EQUAL(openx.out.write_time); - /* open creates a new file handle */ - ADD_HANDLE(parm[0].openx.in.fname, openx.out.file.fnum); + switch (level) { + case RAW_FILEINFO_GENERIC: + return false; - return true; -} + case RAW_FILEINFO_GETATTR: + CHECK_ATTRIB(getattr.out.attrib); + CHECK_EQUAL(getattr.out.size); + CHECK_TIMES_EQUAL(getattr.out.write_time); + break; + case RAW_FILEINFO_GETATTRE: + CHECK_TIMES_EQUAL(getattre.out.create_time); + CHECK_TIMES_EQUAL(getattre.out.access_time); + CHECK_TIMES_EQUAL(getattre.out.write_time); + CHECK_EQUAL(getattre.out.size); + CHECK_EQUAL(getattre.out.alloc_size); + CHECK_ATTRIB(getattre.out.attrib); + break; -/* - generate open operations -*/ -static bool handler_open(int instance) -{ - union smb_open parm[NSERVERS]; - NTSTATUS status[NSERVERS]; + case RAW_FILEINFO_STANDARD: + CHECK_TIMES_EQUAL(standard.out.create_time); + CHECK_TIMES_EQUAL(standard.out.access_time); + CHECK_TIMES_EQUAL(standard.out.write_time); + CHECK_EQUAL(standard.out.size); + CHECK_EQUAL(standard.out.alloc_size); + CHECK_ATTRIB(standard.out.attrib); + break; - parm[0].openold.level = RAW_OPEN_OPEN; - parm[0].openold.in.open_mode = gen_bits_mask2(0xF, 0xFFFF); - parm[0].openold.in.search_attrs = gen_attrib(); - parm[0].openold.in.fname = gen_fname_open(instance); + case RAW_FILEINFO_EA_SIZE: + CHECK_TIMES_EQUAL(ea_size.out.create_time); + CHECK_TIMES_EQUAL(ea_size.out.access_time); + CHECK_TIMES_EQUAL(ea_size.out.write_time); + CHECK_EQUAL(ea_size.out.size); + CHECK_EQUAL(ea_size.out.alloc_size); + CHECK_ATTRIB(ea_size.out.attrib); + CHECK_EQUAL(ea_size.out.ea_size); + break; - if (!options.use_oplocks) { - /* mask out oplocks */ - parm[0].openold.in.open_mode &= ~(OPENX_FLAGS_REQUEST_OPLOCK| - OPENX_FLAGS_REQUEST_BATCH_OPLOCK); - } - - GEN_COPY_PARM; - GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + case RAW_FILEINFO_ALL_EAS: + CHECK_EQUAL(all_eas.out.num_eas); + for (i=0;igeneric.level = levels[i].level; -} - -/* - compare returned fileinfo structures -*/ -static bool cmp_fileinfo(int instance, - union smb_fileinfo parm[NSERVERS], - NTSTATUS status[NSERVERS]) -{ - int i; - - switch (parm[0].generic.level) { - case RAW_FILEINFO_GENERIC: - return false; - case RAW_FILEINFO_GETATTR: - CHECK_EQUAL(getattr.out.attrib); - CHECK_EQUAL(getattr.out.size); - CHECK_TIMES_EQUAL(getattr.out.write_time); + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_GETATTRE: - CHECK_TIMES_EQUAL(getattre.out.create_time); - CHECK_TIMES_EQUAL(getattre.out.access_time); - CHECK_TIMES_EQUAL(getattre.out.write_time); - CHECK_EQUAL(getattre.out.size); - CHECK_EQUAL(getattre.out.alloc_size); - CHECK_EQUAL(getattre.out.attrib); + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_STANDARD: - CHECK_TIMES_EQUAL(standard.out.create_time); - CHECK_TIMES_EQUAL(standard.out.access_time); - CHECK_TIMES_EQUAL(standard.out.write_time); - CHECK_EQUAL(standard.out.size); - CHECK_EQUAL(standard.out.alloc_size); - CHECK_EQUAL(standard.out.attrib); + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_EA_SIZE: - CHECK_TIMES_EQUAL(ea_size.out.create_time); - CHECK_TIMES_EQUAL(ea_size.out.access_time); - CHECK_TIMES_EQUAL(ea_size.out.write_time); - CHECK_EQUAL(ea_size.out.size); - CHECK_EQUAL(ea_size.out.alloc_size); - CHECK_EQUAL(ea_size.out.attrib); - CHECK_EQUAL(ea_size.out.ea_size); + case RAW_SFILEINFO_EA_SET: { + static struct ea_struct ea; + info->ea_set.in.num_eas = 1; + info->ea_set.in.eas = &ea; + info->ea_set.in.eas[0] = gen_ea_struct(); + } break; - - case RAW_FILEINFO_ALL_EAS: - CHECK_EQUAL(all_eas.out.num_eas); - for (i=0;ibasic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); break; - - case RAW_FILEINFO_IS_NAME_VALID: + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); break; - - case RAW_FILEINFO_BASIC_INFO: - case RAW_FILEINFO_BASIC_INFORMATION: - CHECK_NTTIMES_EQUAL(basic_info.out.create_time); - CHECK_NTTIMES_EQUAL(basic_info.out.access_time); - CHECK_NTTIMES_EQUAL(basic_info.out.write_time); - CHECK_NTTIMES_EQUAL(basic_info.out.change_time); - CHECK_EQUAL(basic_info.out.attrib); + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); + break; + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); + break; + case RAW_SFILEINFO_RENAME_INFORMATION: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); + break; + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); + break; + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); + break; + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SEC_DESC: + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_1023: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1039: + case RAW_SFILEINFO_1040: + case RAW_SFILEINFO_UNIX_INFO2: + /* Untested */ break; + } +} +#endif - case RAW_FILEINFO_STANDARD_INFO: - case RAW_FILEINFO_STANDARD_INFORMATION: - CHECK_EQUAL(standard_info.out.alloc_size); - CHECK_EQUAL(standard_info.out.size); - CHECK_EQUAL(standard_info.out.nlink); - CHECK_EQUAL(standard_info.out.delete_pending); - CHECK_EQUAL(standard_info.out.directory); +/* + generate a fileinfo query structure +*/ +static void gen_setfileinfo(int instance, union smb_setfileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} + struct levels { + enum smb_setfileinfo_level level; + const char *name; + }; + struct levels smb_levels[] = { + LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), + LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), + LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040), + }; + struct levels smb2_levels[] = { + LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) + }; + struct levels *levels = options.smb2?smb2_levels:smb_levels; + uint32_t num_levels = options.smb2?ARRAY_SIZE(smb2_levels):ARRAY_SIZE(smb_levels); + + do { + i = gen_int_range(0, num_levels-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; + + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); + break; + case RAW_SFILEINFO_EA_SET: { + static struct ea_struct ea; + info->ea_set.in.num_eas = 1; + info->ea_set.in.eas = &ea; + info->ea_set.in.eas[0] = gen_ea_struct(); + break; + } + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + info->basic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); + break; + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); + break; + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); + break; + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); + break; + case RAW_SFILEINFO_RENAME_INFORMATION: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); + break; + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); + break; + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); break; - case RAW_FILEINFO_EA_INFO: - case RAW_FILEINFO_EA_INFORMATION: - CHECK_EQUAL(ea_info.out.ea_size); + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SEC_DESC: + case RAW_SFILEINFO_1023: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1039: + case RAW_SFILEINFO_1040: + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_INFO2: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + /* Untested */ break; + } +} + + + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo_smb(int instance, union smb_fileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD), + LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID), + LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO), + LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO), + LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION) + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; +} + +/* + generate qpathinfo operations +*/ +static bool handler_smb_qpathinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.path = gen_fname_open(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_CALL_SMB(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + +/* + generate qfileinfo operations +*/ +static bool handler_smb_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + +/* + generate setpathinfo operations +*/ +static bool handler_smb_spathinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.path = gen_fname_open(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + + /* a special case for the fid in a RENAME */ + if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION && + parm[0].rename_information.in.root_fid != 0) { + GEN_SET_FNUM_SMB(rename_information.in.root_fid); + } + + GEN_CALL_SMB(smb_raw_setpathinfo(tree, &parm[i])); + + return true; +} + + +/* + generate setfileinfo operations +*/ +static bool handler_smb_sfileinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_setfileinfo(tree, &parm[i])); + + return true; +} + + +/* + this is called when a change notify reply comes in +*/ +static void async_notify_smb(struct smbcli_request *req) +{ + union smb_notify notify; + NTSTATUS status; + int i, j; + uint16_t tid; + struct smbcli_transport *transport = req->transport; + + tid = SVAL(req->in.hdr, HDR_TID); + + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); + if (NT_STATUS_IS_OK(status) && notify.nttrans.out.num_changes > 0) { + printf("notify tid=%d num_changes=%d action=%d name=%s\n", + tid, + notify.nttrans.out.num_changes, + notify.nttrans.out.changes[0].action, + notify.nttrans.out.changes[0].name.s); + } + + for (i=0;isession->transport && + tid == servers[i].smb_tree[j]->tid) { + notifies[i][j].notify_count++; + notifies[i][j].status = status; + notifies[i][j].notify = notify; + } + } + } +} + +/* + generate change notify operations +*/ +static bool handler_smb_notify(int instance) +{ + union smb_notify parm[NSERVERS]; + int n; + + ZERO_STRUCT(parm[0]); + parm[0].nttrans.level = RAW_NOTIFY_NTTRANS; + parm[0].nttrans.in.buffer_size = gen_io_count(); + parm[0].nttrans.in.completion_filter = gen_bits_mask(0xFF); + parm[0].nttrans.in.file.fnum = gen_fnum(instance); + parm[0].nttrans.in.recursive = gen_bool(); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(nttrans.in.file.fnum); + + for (n=0;nasync.fn = async_notify_smb; + } + + return true; +} + + +/* + generate ntcreatex operations +*/ +static bool handler_smb2_create(int instance) +{ + struct smb2_create parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.security_flags = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFF); + parm[0].in.oplock_level = gen_bits_levels(3, 90, 0x0, 70, 0x9, 100, 0xFF); + parm[0].in.impersonation_level = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFFFFFFFF); + parm[0].in.create_flags = gen_reserved64(); + parm[0].in.reserved = gen_reserved64(); + parm[0].in.desired_access = gen_access_mask(); + parm[0].in.file_attributes = gen_attrib(); + parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); + parm[0].in.create_disposition = gen_open_disp(); + parm[0].in.create_options = gen_create_options(); + parm[0].in.fname = gen_fname_open(instance); + parm[0].in.eas = gen_ea_list(); + parm[0].in.alloc_size = gen_alloc_size(); + parm[0].in.durable_open = gen_bool(); + parm[0].in.query_maximal_access = gen_bool(); + parm[0].in.timewarp = gen_timewarp(); + parm[0].in.query_on_disk_id = gen_bool(); + parm[0].in.sec_desc = gen_sec_desc(); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].in.oplock_level = 0; + } + + if (options.valid) { + parm[0].in.security_flags &= 3; + parm[0].in.oplock_level &= 9; + parm[0].in.impersonation_level &= 3; + } + + GEN_COPY_PARM; + GEN_CALL_SMB2(smb2_create(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.oplock_level); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.create_action); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_ATTRIB(out.file_attr); + CHECK_EQUAL(out.reserved2); + CHECK_EQUAL(out.maximal_access); + + /* ntcreatex creates a new file handle */ + ADD_HANDLE_SMB2(parm[0].in.fname, out.file.handle); + + return true; +} + +/* + generate close operations +*/ +static bool handler_smb2_close(int instance) +{ + struct smb2_close parm[NSERVERS]; + NTSTATUS status[NSERVERS]; - case RAW_FILEINFO_NAME_INFO: - case RAW_FILEINFO_NAME_INFORMATION: - CHECK_WSTR_EQUAL(name_info.out.fname); - break; + ZERO_STRUCT(parm[0]); + parm[0].in.file.handle.data[0] = gen_fnum_close(instance); + parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); - case RAW_FILEINFO_ALL_INFO: - case RAW_FILEINFO_ALL_INFORMATION: - CHECK_NTTIMES_EQUAL(all_info.out.create_time); - CHECK_NTTIMES_EQUAL(all_info.out.access_time); - CHECK_NTTIMES_EQUAL(all_info.out.write_time); - CHECK_NTTIMES_EQUAL(all_info.out.change_time); - CHECK_EQUAL(all_info.out.attrib); - CHECK_EQUAL(all_info.out.alloc_size); - CHECK_EQUAL(all_info.out.size); - CHECK_EQUAL(all_info.out.nlink); - CHECK_EQUAL(all_info.out.delete_pending); - CHECK_EQUAL(all_info.out.directory); - CHECK_EQUAL(all_info.out.ea_size); - CHECK_WSTR_EQUAL(all_info.out.fname); - break; + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_close(tree, &parm[i])); - case RAW_FILEINFO_ALT_NAME_INFO: - case RAW_FILEINFO_ALT_NAME_INFORMATION: - CHECK_WSTR_EQUAL(alt_name_info.out.fname); - break; + CHECK_EQUAL(out.flags); + CHECK_EQUAL(out._pad); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_ATTRIB(out.file_attr); - case RAW_FILEINFO_STREAM_INFO: - case RAW_FILEINFO_STREAM_INFORMATION: - CHECK_EQUAL(stream_info.out.num_streams); - for (i=0;isession->transport)); + + return true; +} + + /* generate a fileinfo query structure */ -static void gen_setfileinfo(int instance, union smb_setfileinfo *info) +static void gen_fileinfo_smb2(int instance, union smb_fileinfo *info) { int i; - #undef LVL - #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} struct { - enum smb_setfileinfo_level level; + enum smb_fileinfo_level level; const char *name; } levels[] = { -#if 0 - /* disabled until win2003 can handle them ... */ - LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), - LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), -#endif - LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), - LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), - LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), - LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), - LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) + LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), + LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), LVL(SEC_DESC), }; do { i = gen_int_range(0, ARRAY_SIZE(levels)-1); } while (ignore_pattern(levels[i].name)); info->generic.level = levels[i].level; - - switch (info->generic.level) { - case RAW_SFILEINFO_SETATTR: - info->setattr.in.attrib = gen_attrib(); - info->setattr.in.write_time = gen_timet(); - break; - case RAW_SFILEINFO_SETATTRE: - info->setattre.in.create_time = gen_timet(); - info->setattre.in.access_time = gen_timet(); - info->setattre.in.write_time = gen_timet(); - break; - case RAW_SFILEINFO_STANDARD: - info->standard.in.create_time = gen_timet(); - info->standard.in.access_time = gen_timet(); - info->standard.in.write_time = gen_timet(); - break; - case RAW_SFILEINFO_EA_SET: { - static struct ea_struct ea; - info->ea_set.in.num_eas = 1; - info->ea_set.in.eas = &ea; - info->ea_set.in.eas[0] = gen_ea_struct(); - } - break; - case RAW_SFILEINFO_BASIC_INFO: - case RAW_SFILEINFO_BASIC_INFORMATION: - info->basic_info.in.create_time = gen_nttime(); - info->basic_info.in.access_time = gen_nttime(); - info->basic_info.in.write_time = gen_nttime(); - info->basic_info.in.change_time = gen_nttime(); - info->basic_info.in.attrib = gen_attrib(); - break; - case RAW_SFILEINFO_DISPOSITION_INFO: - case RAW_SFILEINFO_DISPOSITION_INFORMATION: - info->disposition_info.in.delete_on_close = gen_bool(); - break; - case RAW_SFILEINFO_ALLOCATION_INFO: - case RAW_SFILEINFO_ALLOCATION_INFORMATION: - info->allocation_info.in.alloc_size = gen_alloc_size(); - break; - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - info->end_of_file_info.in.size = gen_offset(); - break; - case RAW_SFILEINFO_RENAME_INFORMATION: - case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: - info->rename_information.in.overwrite = gen_bool(); - info->rename_information.in.root_fid = gen_root_fid(instance); - info->rename_information.in.new_name = gen_fname_open(instance); - break; - case RAW_SFILEINFO_POSITION_INFORMATION: - info->position_information.in.position = gen_offset(); - break; - case RAW_SFILEINFO_MODE_INFORMATION: - info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); - break; - case RAW_SFILEINFO_GENERIC: - case RAW_SFILEINFO_SEC_DESC: - case RAW_SFILEINFO_UNIX_BASIC: - case RAW_SFILEINFO_UNIX_LINK: - case RAW_SFILEINFO_UNIX_HLINK: - case RAW_SFILEINFO_1023: - case RAW_SFILEINFO_1025: - case RAW_SFILEINFO_1029: - case RAW_SFILEINFO_1032: - case RAW_SFILEINFO_1039: - case RAW_SFILEINFO_1040: - case RAW_SFILEINFO_UNIX_INFO2: - /* Untested */ - break; - } } /* - generate setpathinfo operations + generate qfileinfo operations */ -static bool handler_spathinfo(int instance) +static bool handler_smb2_qfileinfo(int instance) { - union smb_setfileinfo parm[NSERVERS]; + union smb_fileinfo parm[NSERVERS]; NTSTATUS status[NSERVERS]; - parm[0].generic.in.file.path = gen_fname_open(instance); + parm[0].generic.in.file.handle.data[0] = gen_fnum(instance); - gen_setfileinfo(instance, &parm[0]); + gen_fileinfo_smb2(instance, &parm[0]); GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_getinfo_file(tree, current_op.mem_ctx, &parm[i])); - /* a special case for the fid in a RENAME */ - if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION && - parm[0].rename_information.in.root_fid != 0) { - GEN_SET_FNUM(rename_information.in.root_fid); - } - - GEN_CALL(smb_raw_setpathinfo(tree, &parm[i])); - - return true; + return cmp_fileinfo(instance, parm, status); } /* generate setfileinfo operations */ -static bool handler_sfileinfo(int instance) +static bool handler_smb2_sfileinfo(int instance) { union smb_setfileinfo parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1826,36 +2744,8 @@ static bool handler_sfileinfo(int instance) gen_setfileinfo(instance, &parm[0]); GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.fnum); - GEN_CALL(smb_raw_setfileinfo(tree, &parm[i])); - - return true; -} - - -/* - generate change notify operations -*/ -static bool handler_notify(int instance) -{ - union smb_notify parm[NSERVERS]; - int n; - - ZERO_STRUCT(parm[0]); - parm[0].nttrans.level = RAW_NOTIFY_NTTRANS; - parm[0].nttrans.in.buffer_size = gen_io_count(); - parm[0].nttrans.in.completion_filter = gen_bits_mask(0xFF); - parm[0].nttrans.in.file.fnum = gen_fnum(instance); - parm[0].nttrans.in.recursive = gen_bool(); - - GEN_COPY_PARM; - GEN_SET_FNUM(nttrans.in.file.fnum); - - for (n=0;ntree, &parm[n]); - req->async.fn = async_notify; - } + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_setinfo_file(tree, &parm[i])); return true; } @@ -1866,20 +2756,30 @@ static bool handler_notify(int instance) static void wipe_files(void) { int i; + NTSTATUS status; if (options.skip_cleanup) { return; } for (i=0;itree, "\\gentest"); + int n; + if (options.smb2) { + n = smb2_deltree(servers[i].smb2_tree[0], "gentest"); + } else { + n = smbcli_deltree(servers[i].smb_tree[0], "gentest"); + } if (n == -1) { printf("Failed to wipe tree on server %d\n", i); exit(1); } - if (NT_STATUS_IS_ERR(smbcli_mkdir(servers[i].cli[0]->tree, "\\gentest"))) { - printf("Failed to create \\gentest - %s\n", - smbcli_errstr(servers[i].cli[0]->tree)); + if (options.smb2) { + status = smb2_util_mkdir(servers[i].smb2_tree[0], "gentest"); + } else { + status = smbcli_mkdir(servers[i].smb_tree[0], "gentest"); + } + if (NT_STATUS_IS_ERR(status)) { + printf("Failed to create gentest on server %d - %s\n", i, nt_errstr(status)); exit(1); } if (n > 0) { @@ -1917,27 +2817,39 @@ static void dump_seeds(void) static struct { const char *name; bool (*handler)(int instance); + bool smb2; int count, success_count; } gen_ops[] = { - {"OPEN", handler_open}, - {"OPENX", handler_openx}, - {"NTCREATEX", handler_ntcreatex}, - {"CLOSE", handler_close}, - {"UNLINK", handler_unlink}, - {"MKDIR", handler_mkdir}, - {"RMDIR", handler_rmdir}, - {"RENAME", handler_rename}, - {"NTRENAME", handler_ntrename}, - {"READX", handler_readx}, - {"WRITEX", handler_writex}, - {"CHKPATH", handler_chkpath}, - {"LOCKINGX", handler_lockingx}, - {"QPATHINFO", handler_qpathinfo}, - {"QFILEINFO", handler_qfileinfo}, - {"SPATHINFO", handler_spathinfo}, - {"SFILEINFO", handler_sfileinfo}, - {"NOTIFY", handler_notify}, - {"SEEK", handler_seek}, + {"CREATE", handler_smb2_create, true}, + {"CLOSE", handler_smb2_close, true}, + {"READ", handler_smb2_read, true}, + {"WRITE", handler_smb2_write, true}, + {"LOCK", handler_smb2_lock, true}, + {"FLUSH", handler_smb2_flush, true}, + {"ECHO", handler_smb2_echo, true}, + {"QFILEINFO", handler_smb2_qfileinfo, true}, + {"SFILEINFO", handler_smb2_sfileinfo, true}, + + {"OPEN", handler_smb_open, false}, + {"OPENX", handler_smb_openx, false}, + {"NTCREATEX", handler_smb_ntcreatex, false}, + {"CLOSE", handler_smb_close, false}, + {"UNLINK", handler_smb_unlink, false}, + {"MKDIR", handler_smb_mkdir, false}, + {"RMDIR", handler_smb_rmdir, false}, + {"RENAME", handler_smb_rename, false}, + {"NTRENAME", handler_smb_ntrename, false}, + {"READX", handler_smb_readx, false}, + {"WRITEX", handler_smb_writex, false}, + {"CHKPATH", handler_smb_chkpath, false}, + {"SEEK", handler_smb_seek, false}, + {"LOCKINGX", handler_smb_lockingx, false}, + {"QPATHINFO", handler_smb_qpathinfo, false}, + {"QFILEINFO", handler_smb_qfileinfo, false}, + {"SPATHINFO", handler_smb_spathinfo, false}, + {"SFILEINFO", handler_smb_sfileinfo, false}, + {"NOTIFY", handler_smb_notify, false}, + {"SEEK", handler_smb_seek, false}, }; @@ -1981,7 +2893,8 @@ static int run_test(struct event_context *ev, struct loadparm_context *lp_ctx) /* generate a non-ignored operation */ do { which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1); - } while (ignore_pattern(gen_ops[which_op].name)); + } while (ignore_pattern(gen_ops[which_op].name) || + gen_ops[which_op].smb2 != options.smb2); DEBUG(3,("Generating op %s on instance %d\n", gen_ops[which_op].name, instance)); @@ -2030,6 +2943,7 @@ static void backtrack_analyze(struct event_context *ev, struct loadparm_context *lp_ctx) { int chunk, ret; + const char *mismatch = current_op.mismatch; chunk = options.numops / 2; @@ -2056,6 +2970,10 @@ static void backtrack_analyze(struct event_context *ev, if (ret == options.numops) { /* this chunk is needed */ base += chunk; + } else if (mismatch != current_op.mismatch && + strcmp(mismatch, current_op.mismatch)) { + base += chunk; + printf("Different error in backtracking\n"); } else if (ret < base) { printf("damn - inconsistent errors! found early error\n"); options.numops = ret+1; @@ -2189,6 +3107,7 @@ static bool split_unc_name(const char *unc, char **server, char **share) enum {OPT_UNCLIST=1000}; struct poptOption long_options[] = { POPT_AUTOHELP + {"smb2", 0, POPT_ARG_NONE, &options.smb2, 0, "use SMB2 protocol", NULL}, {"seed", 0, POPT_ARG_INT, &options.seed, 0, "Seed to use for randomizer", NULL}, {"num-ops", 0, POPT_ARG_INT, &options.numops, 0, "num ops", NULL}, {"oplocks", 0, POPT_ARG_NONE, &options.use_oplocks,0, "use oplocks", NULL}, @@ -2202,7 +3121,11 @@ static bool split_unc_name(const char *unc, char **server, char **share) {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, {"seedsfile", 0, POPT_ARG_STRING, &options.seeds_file, 0, "seed file", NULL}, { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" }, + {"maskindexing", 0, POPT_ARG_NONE, &options.mask_indexing, 0, "mask out the indexed file attrib", NULL}, + {"noeas", 0, POPT_ARG_NONE, &options.no_eas, 0, "don't use extended attributes", NULL}, + {"noacls", 0, POPT_ARG_NONE, &options.no_acls, 0, "don't use ACLs", NULL}, {"skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, "don't delete files at start", NULL}, + {"valid", 0, POPT_ARG_NONE, &options.valid, 0, "generate only valid fields", NULL}, POPT_COMMON_SAMBA POPT_COMMON_CONNECTION POPT_COMMON_CREDENTIALS @@ -2210,6 +3133,8 @@ static bool split_unc_name(const char *unc, char **server, char **share) { NULL } }; + memset(&bad_smb2_handle, 0xFF, sizeof(bad_smb2_handle)); + setlinebuf(stdout); options.seed = time(NULL); options.numops = 1000; diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c deleted file mode 100644 index 5a209180e3..0000000000 --- a/source4/torture/gentest_smb2.c +++ /dev/null @@ -1,2122 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - generic testing tool - version with SMB2 support - - Copyright (C) Andrew Tridgell 2003-2008 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include "includes.h" -#include "lib/cmdline/popt_common.h" -#include "lib/events/events.h" -#include "system/time.h" -#include "system/filesys.h" -#include "libcli/raw/request.h" -#include "libcli/libcli.h" -#include "libcli/raw/libcliraw.h" -#include "libcli/smb2/smb2.h" -#include "libcli/smb2/smb2_calls.h" -#include "librpc/gen_ndr/security.h" -#include "librpc/gen_ndr/ndr_security.h" -#include "auth/credentials/credentials.h" -#include "libcli/resolve/resolve.h" -#include "auth/gensec/gensec.h" -#include "param/param.h" -#include "dynconfig/dynconfig.h" -#include "libcli/security/security.h" - -#define NSERVERS 2 -#define NINSTANCES 2 - -/* global options */ -static struct gentest_options { - int showall; - int analyze; - int analyze_always; - int analyze_continuous; - uint_t max_open_handles; - uint_t seed; - uint_t numops; - int use_oplocks; - char **ignore_patterns; - const char *seeds_file; - int use_preset_seeds; - int fast_reconnect; - int mask_indexing; - int no_eas; - int no_acls; - int skip_cleanup; - int valid; -} options; - -/* mapping between open handles on the server and local handles */ -static struct { - bool active; - uint_t instance; - struct smb2_handle server_handle[NSERVERS]; - const char *name; -} *open_handles; -static uint_t num_open_handles; - -/* state information for the servers. We open NINSTANCES connections to - each server */ -static struct { - struct smb2_tree *tree[NINSTANCES]; - char *server_name; - char *share_name; - struct cli_credentials *credentials; -} servers[NSERVERS]; - -/* the seeds and flags for each operation */ -static struct { - uint_t seed; - bool disabled; -} *op_parms; - - -/* oplock break info */ -static struct { - bool got_break; - struct smb2_handle server_handle; - uint16_t handle; - uint8_t level; - bool do_close; -} oplocks[NSERVERS][NINSTANCES]; - -/* change notify reply info */ -static struct { - int notify_count; - NTSTATUS status; - union smb_notify notify; -} notifies[NSERVERS][NINSTANCES]; - -/* info relevant to the current operation */ -static struct { - const char *name; - uint_t seed; - NTSTATUS status; - uint_t opnum; - TALLOC_CTX *mem_ctx; - const char *mismatch; -} current_op; - -static struct smb2_handle bad_smb2_handle; - - -#define BAD_HANDLE 0xFFFE - -static bool oplock_handler(struct smb2_transport *transport, const struct smb2_handle *handle, - uint8_t level, void *private_data); -static void idle_func(struct smb2_transport *transport, void *private); - -/* - check if a string should be ignored. This is used as the basis - for all error ignore settings -*/ -static bool ignore_pattern(const char *str) -{ - int i; - if (!options.ignore_patterns) return false; - - for (i=0;options.ignore_patterns[i];i++) { - if (strcmp(options.ignore_patterns[i], str) == 0 || - gen_fnmatch(options.ignore_patterns[i], str) == 0) { - DEBUG(2,("Ignoring '%s'\n", str)); - return true; - } - } - return false; -} - -/***************************************************** -connect to the servers -*******************************************************/ -static bool connect_servers_fast(void) -{ - int h, i; - - /* close all open files */ - for (h=0;husername, j); - - cli_credentials_set_workstation(servers[i].credentials, - "gentest", CRED_SPECIFIED); - - status = smb2_connect(NULL, servers[i].server_name, - servers[i].share_name, - lp_resolve_context(lp_ctx), - servers[i].credentials, - &servers[i].tree[j], - ev); - if (!NT_STATUS_IS_OK(status)) { - printf("Failed to connect to \\\\%s\\%s - %s\n", - servers[i].server_name, servers[i].share_name, - nt_errstr(status)); - return false; - } - - servers[i].tree[j]->session->transport->oplock.handler = oplock_handler; - servers[i].tree[j]->session->transport->oplock.private_data = (void *)(uintptr_t)((i<<8)|j); - smb2_transport_idle_handler(servers[i].tree[j]->session->transport, idle_func, 50000, NULL); - } - } - - return true; -} - -/* - work out the time skew between the servers - be conservative -*/ -static uint_t time_skew(void) -{ - uint_t ret; - ret = labs(servers[0].tree[0]->session->transport->negotiate.system_time - - servers[1].tree[0]->session->transport->negotiate.system_time); - return ret + 300; -} - - -static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) -{ - return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; -} - -/* - turn a server handle into a local handle -*/ -static uint_t fnum_to_handle(int server, int instance, struct smb2_handle server_handle) -{ - uint_t i; - for (i=0;i 0 && count++ < 10*options.max_open_handles) { - h = random() % options.max_open_handles; - if (open_handles[h].active && - open_handles[h].instance == instance) { - return h; - } - } - return BAD_HANDLE; -} - -/* - return a file handle, but skewed so we don't close the last - couple of handles too readily -*/ -static uint16_t gen_fnum_close(int instance) -{ - if (num_open_handles < 5) { - if (gen_chance(90)) return BAD_HANDLE; - } - - return gen_fnum(instance); -} - -/* - generate an integer in a specified range -*/ -static int gen_int_range(uint64_t min, uint64_t max) -{ - uint_t r = random(); - return min + (r % (1+max-min)); -} - -/* - return a fnum for use as a root fid - be careful to call GEN_SET_FNUM() when you use this! -*/ -static uint16_t gen_root_fid(int instance) -{ - if (gen_chance(5)) return gen_fnum(instance); - return 0; -} - -/* - generate a file offset -*/ -static int gen_offset(void) -{ - if (gen_chance(20)) return 0; -// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); - return gen_int_range(0, 1024*1024); -} - -/* - generate a io count -*/ -static int gen_io_count(void) -{ - if (gen_chance(20)) return 0; -// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); - return gen_int_range(0, 4096); -} - -/* - generate a filename -*/ -static const char *gen_fname(void) -{ - const char *names[] = {"gentest\\gentest.dat", - "gentest\\foo", - "gentest\\foo2.sym", - "gentest\\foo3.dll", - "gentest\\foo4", - "gentest\\foo4:teststream1", - "gentest\\foo4:teststream2", - "gentest\\foo5.exe", - "gentest\\foo5.exe:teststream3", - "gentest\\foo5.exe:teststream4", - "gentest\\foo6.com", - "gentest\\blah", - "gentest\\blah\\blergh.txt", - "gentest\\blah\\blergh2", - "gentest\\blah\\blergh3.txt", - "gentest\\blah\\blergh4", - "gentest\\blah\\blergh5.txt", - "gentest\\blah\\blergh5", - "gentest\\blah\\.", -#if 0 - /* this causes problem with w2k3 */ - "gentest\\blah\\..", -#endif - "gentest\\a_very_long_name.bin", - "gentest\\x.y", - "gentest\\blah"}; - int i; - - do { - i = gen_int_range(0, ARRAY_SIZE(names)-1); - } while (ignore_pattern(names[i])); - - return names[i]; -} - -/* - generate a filename with a higher chance of choosing an already - open file -*/ -static const char *gen_fname_open(int instance) -{ - uint16_t h; - h = gen_fnum(instance); - if (h == BAD_HANDLE) { - return gen_fname(); - } - return open_handles[h].name; -} - -/* - generate a wildcard pattern -*/ -static const char *gen_pattern(void) -{ - int i; - const char *names[] = {"gentest\\*.dat", - "gentest\\*", - "gentest\\*.*", - "gentest\\blah\\*.*", - "gentest\\blah\\*", - "gentest\\?"}; - - if (gen_chance(50)) return gen_fname(); - - do { - i = gen_int_range(0, ARRAY_SIZE(names)-1); - } while (ignore_pattern(names[i])); - - return names[i]; -} - -static uint32_t gen_bits_levels(int nlevels, ...) -{ - va_list ap; - uint32_t pct; - uint32_t mask; - int i; - va_start(ap, nlevels); - for (i=0;iasync.fn = oplock_handler_ack_callback; - req->async.private_data = NULL; - return true; -} - -/* - the oplock handler will either ack the break or close the file -*/ -static bool oplock_handler(struct smb2_transport *transport, const struct smb2_handle *handle, - uint8_t level, void *private_data) -{ - struct smb2_close io; - unsigned i, j; - bool do_close; - struct smb2_tree *tree = NULL; - struct smb2_request *req; - - srandom(current_op.seed); - do_close = gen_chance(50); - - i = ((uintptr_t)private_data) >> 8; - j = ((uintptr_t)private_data) & 0xFF; - - if (i >= NSERVERS || j >= NINSTANCES) { - printf("Bad private_data in oplock_handler\n"); - return false; - } - - oplocks[i][j].got_break = true; - oplocks[i][j].server_handle = *handle; - oplocks[i][j].handle = fnum_to_handle(i, j, *handle); - oplocks[i][j].level = level; - oplocks[i][j].do_close = do_close; - tree = talloc_get_type(servers[i].tree[j], struct smb2_tree); - - if (!tree) { - printf("Oplock break not for one of our trees!?\n"); - return false; - } - - if (!do_close) { - printf("oplock ack handle=%d\n", oplocks[i][j].handle); - return send_oplock_ack(tree, *handle, level); - } - - printf("oplock close fnum=%d\n", oplocks[i][j].handle); - - ZERO_STRUCT(io); - io.in.file.handle = *handle; - io.in.flags = 0; - req = smb2_close_send(tree, &io); - - if (req == NULL) { - printf("WARNING: close failed in oplock_handler_close\n"); - return false; - } - - req->async.fn = oplock_handler_close_recv; - req->async.private_data = NULL; - - return true; -} - - -/* - the idle function tries to cope with getting an oplock break on a connection, and - an operation on another connection blocking until that break is acked - we check for operations on all transports in the idle function -*/ -static void idle_func(struct smb2_transport *transport, void *private) -{ - int i, j; - for (i=0;isession->transport) { - // smb2_transport_process(servers[i].tree[j]->session->transport); - } - } - } - -} - - -/* - compare NTSTATUS, using checking ignored patterns -*/ -static bool compare_status(NTSTATUS status1, NTSTATUS status2) -{ - if (NT_STATUS_EQUAL(status1, status2)) return true; - - /* one code being an error and the other OK is always an error */ - if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) { - current_op.mismatch = nt_errstr(status1); - return false; - } - - /* if we are ignoring one of the status codes then consider this a match */ - if (ignore_pattern(nt_errstr(status1)) || - ignore_pattern(nt_errstr(status2))) { - return true; - } - current_op.mismatch = nt_errstr(status1); - return false; -} - -#if 0 -/* - check for pending packets on all connections -*/ -static void check_pending(void) -{ - int i, j; - - msleep(20); - - for (j=0;jsession->transport); - } - } -} -#endif - -/* - check that the same oplock breaks have been received by all instances -*/ -static bool check_oplocks(const char *call) -{ -#if 0 - int i, j; - int tries = 0; - -again: - check_pending(); - - for (j=0;jdacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ - current_op.mismatch = #field; \ - printf("Mismatch in %s\n", #field); \ - return false; \ - } \ -} while(0) - -#define CHECK_ATTRIB(field) do { \ - if (!options.mask_indexing) { \ - CHECK_EQUAL(field); \ - } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ - current_op.mismatch = #field; \ - printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ - (int)parm[0].field, (int)parm[1].field); \ - return false; \ - } \ -} while(0) - -#define CHECK_WSTR_EQUAL(field) do { \ - if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ - current_op.mismatch = #field; \ - printf("%s is NULL!\n", #field); \ - return false; \ - } \ - if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ - current_op.mismatch = #field; \ - printf("Mismatch in %s - %s %s\n", #field, \ - parm[0].field.s, parm[1].field.s); \ - return false; \ - } \ - CHECK_EQUAL(field.private_length); \ -} while(0) - -#define CHECK_BLOB_EQUAL(field) do { \ - if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ - current_op.mismatch = #field; \ - printf("Mismatch in %s\n", #field); \ - return false; \ - } \ - CHECK_EQUAL(field.length); \ -} while(0) - -#define CHECK_NTTIMES_EQUAL(field) do { \ - if (labs(nt_time_to_unix(parm[0].field) - \ - nt_time_to_unix(parm[1].field)) > time_skew() && \ - !ignore_pattern(#field)) { \ - current_op.mismatch = #field; \ - printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ - (int)nt_time_to_unix(parm[0].field), \ - (int)nt_time_to_unix(parm[1].field)); \ - return false; \ - } \ -} while(0) - -/* - generate ntcreatex operations -*/ -static bool handler_create(int instance) -{ - struct smb2_create parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - ZERO_STRUCT(parm[0]); - parm[0].in.security_flags = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFF); - parm[0].in.oplock_level = gen_bits_levels(3, 90, 0x0, 70, 0x9, 100, 0xFF); - parm[0].in.impersonation_level = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFFFFFFFF); - parm[0].in.create_flags = gen_reserved64(); - parm[0].in.reserved = gen_reserved64(); - parm[0].in.desired_access = gen_access_mask(); - parm[0].in.file_attributes = gen_attrib(); - parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); - parm[0].in.create_disposition = gen_open_disp(); - parm[0].in.create_options = gen_create_options(); - parm[0].in.fname = gen_fname_open(instance); - parm[0].in.eas = gen_ea_list(); - parm[0].in.alloc_size = gen_alloc_size(); - parm[0].in.durable_open = gen_bool(); - parm[0].in.query_maximal_access = gen_bool(); - parm[0].in.timewarp = gen_timewarp(); - parm[0].in.query_on_disk_id = gen_bool(); - parm[0].in.sec_desc = gen_sec_desc(); - - if (!options.use_oplocks) { - /* mask out oplocks */ - parm[0].in.oplock_level = 0; - } - - if (options.valid) { - parm[0].in.security_flags &= 3; - parm[0].in.oplock_level &= 9; - parm[0].in.impersonation_level &= 3; - } - - GEN_COPY_PARM; - GEN_CALL(smb2_create(tree, current_op.mem_ctx, &parm[i])); - - CHECK_EQUAL(out.oplock_level); - CHECK_EQUAL(out.reserved); - CHECK_EQUAL(out.create_action); - CHECK_NTTIMES_EQUAL(out.create_time); - CHECK_NTTIMES_EQUAL(out.access_time); - CHECK_NTTIMES_EQUAL(out.write_time); - CHECK_NTTIMES_EQUAL(out.change_time); - CHECK_EQUAL(out.alloc_size); - CHECK_EQUAL(out.size); - CHECK_ATTRIB(out.file_attr); - CHECK_EQUAL(out.reserved2); - CHECK_EQUAL(out.maximal_access); - - /* ntcreatex creates a new file handle */ - ADD_HANDLE(parm[0].in.fname, out.file.handle); - - return true; -} - -/* - generate close operations -*/ -static bool handler_close(int instance) -{ - struct smb2_close parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - ZERO_STRUCT(parm[0]); - parm[0].in.file.handle.data[0] = gen_fnum_close(instance); - parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_close(tree, &parm[i])); - - CHECK_EQUAL(out.flags); - CHECK_EQUAL(out._pad); - CHECK_NTTIMES_EQUAL(out.create_time); - CHECK_NTTIMES_EQUAL(out.access_time); - CHECK_NTTIMES_EQUAL(out.write_time); - CHECK_NTTIMES_EQUAL(out.change_time); - CHECK_EQUAL(out.alloc_size); - CHECK_EQUAL(out.size); - CHECK_ATTRIB(out.file_attr); - - REMOVE_HANDLE(in.file.handle); - - return true; -} - -/* - generate read operations -*/ -static bool handler_read(int instance) -{ - struct smb2_read parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.reserved = gen_reserved8(); - parm[0].in.length = gen_io_count(); - parm[0].in.offset = gen_offset(); - parm[0].in.min_count = gen_io_count(); - parm[0].in.channel = gen_bits_mask2(0x0, 0xFFFFFFFF); - parm[0].in.remaining = gen_bits_mask2(0x0, 0xFFFFFFFF); - parm[0].in.channel_offset = gen_bits_mask2(0x0, 0xFFFF); - parm[0].in.channel_length = gen_bits_mask2(0x0, 0xFFFF); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_read(tree, current_op.mem_ctx, &parm[i])); - - CHECK_EQUAL(out.remaining); - CHECK_EQUAL(out.reserved); - CHECK_EQUAL(out.data.length); - - return true; -} - -/* - generate write operations -*/ -static bool handler_write(int instance) -{ - struct smb2_write parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.offset = gen_offset(); - parm[0].in.unknown1 = gen_bits_mask2(0, 0xFFFFFFFF); - parm[0].in.unknown2 = gen_bits_mask2(0, 0xFFFFFFFF); - parm[0].in.data = data_blob_talloc(current_op.mem_ctx, NULL, - gen_io_count()); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_write(tree, &parm[i])); - - CHECK_EQUAL(out._pad); - CHECK_EQUAL(out.nwritten); - CHECK_EQUAL(out.unknown1); - - return true; -} - -/* - generate lockingx operations -*/ -static bool handler_lock(int instance) -{ - struct smb2_lock parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - int n; - - parm[0].level = RAW_LOCK_LOCKX; - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.lock_count = gen_lock_count(); - parm[0].in.reserved = gen_reserved32(); - - parm[0].in.locks = talloc_array(current_op.mem_ctx, - struct smb2_lock_element, - parm[0].in.lock_count); - for (n=0;nsession->transport)); - - return true; -} - - - -/* - generate a fileinfo query structure -*/ -static void gen_fileinfo(int instance, union smb_fileinfo *info) -{ - int i; - #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} - struct { - enum smb_fileinfo_level level; - const char *name; - } levels[] = { - LVL(BASIC_INFORMATION), - LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), - LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), - LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), - LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), - LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), - LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), LVL(SEC_DESC), - }; - do { - i = gen_int_range(0, ARRAY_SIZE(levels)-1); - } while (ignore_pattern(levels[i].name)); - - info->generic.level = levels[i].level; -} - -/* - compare returned fileinfo structures -*/ -static bool cmp_fileinfo(int instance, - union smb_fileinfo parm[NSERVERS], - NTSTATUS status[NSERVERS]) -{ - int i; - - switch (parm[0].generic.level) { - case RAW_FILEINFO_GENERIC: - return false; - - /* SMB1 specific values */ - case RAW_FILEINFO_GETATTR: - case RAW_FILEINFO_GETATTRE: - case RAW_FILEINFO_STANDARD: - case RAW_FILEINFO_EA_SIZE: - case RAW_FILEINFO_ALL_EAS: - case RAW_FILEINFO_IS_NAME_VALID: - case RAW_FILEINFO_BASIC_INFO: - case RAW_FILEINFO_STANDARD_INFO: - case RAW_FILEINFO_EA_INFO: - case RAW_FILEINFO_NAME_INFO: - case RAW_FILEINFO_ALL_INFO: - case RAW_FILEINFO_ALT_NAME_INFO: - case RAW_FILEINFO_STREAM_INFO: - case RAW_FILEINFO_COMPRESSION_INFO: - return false; - - case RAW_FILEINFO_BASIC_INFORMATION: - CHECK_NTTIMES_EQUAL(basic_info.out.create_time); - CHECK_NTTIMES_EQUAL(basic_info.out.access_time); - CHECK_NTTIMES_EQUAL(basic_info.out.write_time); - CHECK_NTTIMES_EQUAL(basic_info.out.change_time); - CHECK_ATTRIB(basic_info.out.attrib); - break; - - case RAW_FILEINFO_STANDARD_INFORMATION: - CHECK_EQUAL(standard_info.out.alloc_size); - CHECK_EQUAL(standard_info.out.size); - CHECK_EQUAL(standard_info.out.nlink); - CHECK_EQUAL(standard_info.out.delete_pending); - CHECK_EQUAL(standard_info.out.directory); - break; - - case RAW_FILEINFO_EA_INFORMATION: - CHECK_EQUAL(ea_info.out.ea_size); - break; - - case RAW_FILEINFO_NAME_INFORMATION: - CHECK_WSTR_EQUAL(name_info.out.fname); - break; - - case RAW_FILEINFO_ALT_NAME_INFORMATION: - CHECK_WSTR_EQUAL(alt_name_info.out.fname); - break; - - case RAW_FILEINFO_STREAM_INFORMATION: - CHECK_EQUAL(stream_info.out.num_streams); - for (i=0;igeneric.level = levels[i].level; - - switch (info->generic.level) { - case RAW_SFILEINFO_SETATTR: - case RAW_SFILEINFO_SETATTRE: - case RAW_SFILEINFO_STANDARD: - case RAW_SFILEINFO_EA_SET: - case RAW_SFILEINFO_BASIC_INFO: - case RAW_SFILEINFO_DISPOSITION_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_ALLOCATION_INFO: - break; - - case RAW_SFILEINFO_BASIC_INFORMATION: - info->basic_info.in.create_time = gen_nttime(); - info->basic_info.in.access_time = gen_nttime(); - info->basic_info.in.write_time = gen_nttime(); - info->basic_info.in.change_time = gen_nttime(); - info->basic_info.in.attrib = gen_attrib(); - break; - case RAW_SFILEINFO_DISPOSITION_INFORMATION: - info->disposition_info.in.delete_on_close = gen_bool(); - break; - case RAW_SFILEINFO_ALLOCATION_INFORMATION: - info->allocation_info.in.alloc_size = gen_alloc_size(); - break; - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - info->end_of_file_info.in.size = gen_offset(); - break; - case RAW_SFILEINFO_RENAME_INFORMATION: - case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: - info->rename_information.in.overwrite = gen_bool(); - info->rename_information.in.root_fid = gen_root_fid(instance); - info->rename_information.in.new_name = gen_fname_open(instance); - break; - case RAW_SFILEINFO_POSITION_INFORMATION: - info->position_information.in.position = gen_offset(); - break; - case RAW_SFILEINFO_MODE_INFORMATION: - info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); - break; - case RAW_SFILEINFO_GENERIC: - case RAW_SFILEINFO_SEC_DESC: - case RAW_SFILEINFO_1023: - case RAW_SFILEINFO_1025: - case RAW_SFILEINFO_1029: - case RAW_SFILEINFO_1032: - case RAW_SFILEINFO_1039: - case RAW_SFILEINFO_1040: - case RAW_SFILEINFO_UNIX_BASIC: - case RAW_SFILEINFO_UNIX_INFO2: - case RAW_SFILEINFO_UNIX_LINK: - case RAW_SFILEINFO_UNIX_HLINK: - /* Untested */ - break; - } -} - -/* - generate setfileinfo operations -*/ -static bool handler_sfileinfo(int instance) -{ - union smb_setfileinfo parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].generic.in.file.fnum = gen_fnum(instance); - - gen_setfileinfo(instance, &parm[0]); - - GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.handle); - GEN_CALL(smb2_setinfo_file(tree, &parm[i])); - - return true; -} - -/* - wipe any relevant files -*/ -static void wipe_files(void) -{ - int i; - NTSTATUS status; - - if (options.skip_cleanup) { - return; - } - - for (i=0;i 0) { - printf("Deleted %d files on server %d\n", n, i); - } - } -} - -/* - dump the current seeds - useful for continuing a backtrack -*/ -static void dump_seeds(void) -{ - int i; - FILE *f; - - if (!options.seeds_file) { - return; - } - f = fopen("seeds.tmp", "w"); - if (!f) return; - - for (i=0;i 0 && base+chunk < options.numops && options.numops > 1; ) { - int i, max; - - chunk = MIN(chunk, options.numops / 2); - - /* mark this range as disabled */ - max = MIN(options.numops, base+chunk); - for (i=base;i 0); - - printf("Reduced to %d ops\n", options.numops); - ret = run_test(ev, lp_ctx); - if (ret != options.numops - 1) { - printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops); - } -} - -/* - start the main gentest process -*/ -static bool start_gentest(struct event_context *ev, - struct loadparm_context *lp_ctx) -{ - int op; - int ret; - - /* allocate the open_handles array */ - open_handles = calloc(options.max_open_handles, sizeof(open_handles[0])); - - srandom(options.seed); - op_parms = calloc(options.numops, sizeof(op_parms[0])); - - /* generate the seeds - after this everything is deterministic */ - if (options.use_preset_seeds) { - int numops; - char **preset = file_lines_load(options.seeds_file, &numops, NULL); - if (!preset) { - printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno)); - exit(1); - } - if (numops < options.numops) { - options.numops = numops; - } - for (op=0;op "); - - lp_ctx = cmdline_lp_ctx; - servers[0].credentials = cli_credentials_init(talloc_autofree_context()); - servers[1].credentials = cli_credentials_init(talloc_autofree_context()); - cli_credentials_guess(servers[0].credentials, lp_ctx); - cli_credentials_guess(servers[1].credentials, lp_ctx); - - while((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_UNCLIST: - lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc)); - break; - case 'U': - if (username_count == 2) { - usage(pc); - exit(1); - } - cli_credentials_parse_string(servers[username_count].credentials, poptGetOptArg(pc), CRED_SPECIFIED); - username_count++; - break; - } - } - - if (ignore_file) { - options.ignore_patterns = file_lines_load(ignore_file, NULL, NULL); - } - - argv_new = discard_const_p(char *, poptGetArgs(pc)); - argc_new = argc; - for (i=0; i= 3)) { - usage(pc); - exit(1); - } - - setlinebuf(stdout); - - setup_logging("gentest", DEBUG_STDOUT); - - if (argc < 3 || argv[1][0] == '-') { - usage(pc); - exit(1); - } - - setup_logging(argv[0], DEBUG_STDOUT); - - for (i=0;i Date: Fri, 30 May 2008 07:28:29 +1000 Subject: don't emulate broken SMB2 locking behaviour from windows (This used to be commit c50e7a15f9a7f2c5821b5ee468f9ade6eaa0ed55) --- source4/torture/smb2/lock.c | 70 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c index 5a36ac3eae..35ad839610 100644 --- a/source4/torture/smb2/lock.c +++ b/source4/torture/smb2/lock.c @@ -106,7 +106,11 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree lck.in.reserved = 0x123ab3; status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); lck.in.reserved = 0x123ab4; @@ -115,7 +119,11 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree lck.in.reserved = 0x123ab5; status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); lck.in.lock_count = 0x0001; @@ -133,14 +141,22 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); el[0].flags = 0x00000000; @@ -473,6 +489,51 @@ static bool test_lock_rw_exclusiv(struct torture_context *torture, struct smb2_t return test_lock_read_write(torture, tree, &s); } + +static bool test_lock_auto_unlock(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + ZERO_STRUCT(buf); + + status = torture_smb2_testfile(tree, "autounlock.txt", &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(lck); + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smb2_lock(tree, &lck); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } + + + +done: + return ret; +} + + /* basic testing of SMB2 locking */ struct torture_suite *torture_smb2_lock_init(void) @@ -483,6 +544,7 @@ struct torture_suite *torture_smb2_lock_init(void) torture_suite_add_1smb2_test(suite, "RW-NONE", test_lock_rw_none); torture_suite_add_1smb2_test(suite, "RW-SHARED", test_lock_rw_shared); torture_suite_add_1smb2_test(suite, "RW-EXCLUSIV", test_lock_rw_exclusiv); + torture_suite_add_1smb2_test(suite, "AUTO-UNLOCK", test_lock_auto_unlock); suite->description = talloc_strdup(suite, "SMB2-LOCK tests"); -- cgit From beaa01e403dda7557a6acdf0181d79d58a33bbbe Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Fri, 30 May 2008 17:03:54 +1000 Subject: implemented client side SMB2 signing This doessn't work against Windows yet, and I've submitted a WSPP request for clarification of the docs to try and find out why. Meanwhile this is no worse than what we had, as it only gets used when the server demands signing, and we didn't work then anyway. (This used to be commit b788096add3586d7277efcd3bf5ca7f3a604cb7a) --- source4/torture/gentest.c | 7 ++++--- source4/torture/smb2/read.c | 1 - source4/torture/smb2/scan.c | 11 +++++++---- source4/torture/smb2/util.c | 5 ++++- 4 files changed, 15 insertions(+), 9 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index fd6bd5cc0a..07d394fad6 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -213,6 +213,9 @@ static bool connect_servers(struct event_context *ev, for (i=0;iusername, j); @@ -226,10 +229,8 @@ static bool connect_servers(struct event_context *ev, lp_resolve_context(lp_ctx), servers[i].credentials, &servers[i].smb2_tree[j], - ev); + ev, &smb_options); } else { - struct smbcli_options smb_options; - lp_smbcli_options(lp_ctx, &smb_options); status = smbcli_tree_full_connection(NULL, &servers[i].smb_tree[j], servers[i].server_name, diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c index 1f306028f6..548bd1ce61 100644 --- a/source4/torture/smb2/read.c +++ b/source4/torture/smb2/read.c @@ -180,7 +180,6 @@ static bool test_read_dir(struct torture_context *torture, struct smb2_tree *tre bool ret = true; NTSTATUS status; struct smb2_handle h; - uint8_t buf[100]; struct smb2_read rd; TALLOC_CTX *tmp_ctx = talloc_new(tree); diff --git a/source4/torture/smb2/scan.c b/source4/torture/smb2/scan.c index 889d343a49..1ce796be4d 100644 --- a/source4/torture/smb2/scan.c +++ b/source4/torture/smb2/scan.c @@ -203,17 +203,20 @@ bool torture_smb2_scan(struct torture_context *torture) NTSTATUS status; int opcode; struct smb2_request *req; + struct smbcli_options options; + + lp_smbcli_options(torture->lp_ctx, &options); status = smb2_connect(mem_ctx, host, share, lp_resolve_context(torture->lp_ctx), credentials, &tree, - torture->ev); + torture->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Connection failed - %s\n", nt_errstr(status)); return false; } - tree->session->transport->options.timeout = 3; + tree->session->transport->options.request_timeout = 3; for (opcode=0;opcode<1000;opcode++) { req = smb2_request_init_tree(tree, opcode, 2, false, 0); @@ -224,12 +227,12 @@ bool torture_smb2_scan(struct torture_context *torture) status = smb2_connect(mem_ctx, host, share, lp_resolve_context(torture->lp_ctx), credentials, &tree, - torture->ev); + torture->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Connection failed - %s\n", nt_errstr(status)); return false; } - tree->session->transport->options.timeout = 3; + tree->session->transport->options.request_timeout = 3; } else { status = smb2_request_destroy(req); printf("active opcode %4d gave status %s\n", opcode, nt_errstr(status)); diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c index af4f345104..3a437acbab 100644 --- a/source4/torture/smb2/util.c +++ b/source4/torture/smb2/util.c @@ -270,11 +270,14 @@ bool torture_smb2_connection(struct torture_context *tctx, struct smb2_tree **tr const char *host = torture_setting_string(tctx, "host", NULL); const char *share = torture_setting_string(tctx, "share", NULL); struct cli_credentials *credentials = cmdline_credentials; + struct smbcli_options options; + + lp_smbcli_options(tctx->lp_ctx, &options); status = smb2_connect(tctx, host, share, lp_resolve_context(tctx->lp_ctx), credentials, tree, - tctx->ev); + tctx->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect to SMB2 share \\\\%s\\%s - %s\n", host, share, nt_errstr(status)); -- cgit