From b4af7abf91c4aa652e40d34f6b57127e204b1b52 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 21 Sep 2022 13:58:41 -0700 Subject: [PATCH 1/2] WIP1 compound_async1 Signed-off-by: Jeremy Allison --- source4/torture/smb2/compound.c | 140 ++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c index cf19361130f..559eb8fb02f 100644 --- a/source4/torture/smb2/compound.c +++ b/source4/torture/smb2/compound.c @@ -27,6 +27,7 @@ #include "libcli/security/security.h" #include "librpc/gen_ndr/ndr_security.h" #include "../libcli/smb/smbXcli_base.h" +#include "tevent.h" #define CHECK_STATUS(status, correct) do { \ if (!NT_STATUS_EQUAL(status, correct)) { \ @@ -44,6 +45,22 @@ ret = false; \ }} while (0) +#define CHECK_WIRE_STR(field, value) do { \ + if (!field.s || strcmp(field.s, value)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) %s [%s] != %s\n", __location__, #field, \ + field.s, value); \ + ret = false; \ + goto done; \ + }} while (0) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(tctx->ev) != 0) { \ + break; \ + } \ + } + static struct { struct smb2_handle handle; uint8_t level; @@ -1371,6 +1388,128 @@ done: return ret; } +/* + * Part1 of compound async tests. + * + * Opendir foo + * notify foo <-- goes async + * create foo/bar + * notify recv <-- async reply + * delete foo/bar + * C -------------- + * notify foo-handle + * close (compound handle). + * -------------- + */ + +static bool test_compound_async1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle dhandle = { .data = { 0, 0 } }; + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_notify n; + struct smb2_close cl; + const char *dname = "compound_async1_dir"; + const char *fname = "compound_async1_dir\\file"; + const char *notify_fname = "file"; + struct smb2_request *notify_req = NULL; + struct smb2_request *req[2]; + NTSTATUS status; + bool ret = false; + + /* Start clean. */ + smb2_deltree(tree, dname); + + /* Create the directory, get a handle to it. */ + status = torture_smb2_testdir_access(tree, + dname, + &dhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Set a notify on it. */ + ZERO_STRUCT(n); + n.in.buffer_size = 1000; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.file.handle = dhandle; + n.in.recursive = false; + notify_req = smb2_notify_send(tree, &n); + torture_assert_not_null_goto(tctx, notify_req, ret, done, + "smb2_notify_send failed\n"); + + WAIT_FOR_ASYNC_RESPONSE(notify_req); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, fhandle); + CHECK_STATUS(status, NT_STATUS_OK); + + /* and get the notify return. */ + status = smb2_notify_recv(notify_req, tctx, &n); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(n.out.num_changes, 1); + CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(n.out.changes[0].name, notify_fname); + + /* Now, keeping dhandle open, delete fname. */ + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now do a compound notify start + close dhandle. */ + smb2_transport_compound_start(tree->session->transport, 2); + + ZERO_STRUCT(n); + n.in.buffer_size = 1000; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.file.handle = dhandle; + n.in.recursive = false; + req[0] = smb2_notify_send(tree, &n); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_notify_send failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + ZERO_STRUCT(cl); + cl.in.file.handle = relhandle; + req[1] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_close_send failed\n"); + + /* + * The notify should immediately return + * as there is a pending change to reap. + * + * But against Windows 2022 it returns + * NT_STATUS_INTERNAL_ERROR. + */ + status = smb2_notify_recv(req[0], tree, &n); + torture_assert_ntstatus_equal_goto(tctx, + status, + NT_STATUS_INTERNAL_ERROR, + ret, + done, + "smb2_notify_recv didn't return " + "NT_STATUS_INTERNAL_ERROR.\n"); + status = smb2_close_recv(req[1], &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close_recv failed."); + + ZERO_STRUCT(dhandle); + ret = true; + +done: + + if (dhandle.data[0] != 0) { + smb2_util_close(tree, dhandle); + } + smb2_deltree(tree, dname); + return ret; +} + static bool test_compound_unrelated1(struct torture_context *tctx, struct smb2_tree *tree) { @@ -2089,6 +2228,7 @@ struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding); torture_suite_add_1smb2_test(suite, "create-write-close", test_compound_create_write_close); + torture_suite_add_1smb2_test(suite, "async1", test_compound_async1); suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests"); -- 2.34.1 From 7ffed2f9046a248047e0140f7b0dd031285a205f Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 22 Sep 2022 15:29:19 -0700 Subject: [PATCH 2/2] WIP. compound_async2 Signed-off-by: Jeremy Allison --- source4/torture/smb2/compound.c | 140 ++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c index 559eb8fb02f..891ba3a06c0 100644 --- a/source4/torture/smb2/compound.c +++ b/source4/torture/smb2/compound.c @@ -1510,6 +1510,145 @@ done: return ret; } +/* + * Part2 of compound async tests. + * + * Opendir foo + * notify foo <-- goes async + * create foo/bar + * notify recv <-- async reply + * delete foo/bar + * C -------------- + * getinfo foo-handle + * notify (compound handle) + * close (compound handle). + * -------------- + */ + +static bool test_compound_async2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_handle dhandle = { .data = { 0, 0 } }; + struct smb2_handle fhandle = { .data = { 0, 0 } }; + struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } }; + struct smb2_notify n; + struct smb2_close cl; + struct smb2_getinfo gi; + const char *dname = "compound_async2_dir"; + const char *fname = "compound_async2_dir\\file"; + const char *notify_fname = "file"; + struct smb2_request *notify_req = NULL; + struct smb2_request *req[3]; + NTSTATUS status; + bool ret = false; + + /* Start clean. */ + smb2_deltree(tree, dname); + + /* Create the directory, get a handle to it. */ + status = torture_smb2_testdir_access(tree, + dname, + &dhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Set a notify on it. */ + ZERO_STRUCT(n); + n.in.buffer_size = 1000; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.file.handle = dhandle; + n.in.recursive = false; + notify_req = smb2_notify_send(tree, &n); + torture_assert_not_null_goto(tctx, notify_req, ret, done, + "smb2_notify_send failed\n"); + + WAIT_FOR_ASYNC_RESPONSE(notify_req); + + /* Create a file. */ + status = torture_smb2_testfile_access(tree, + fname, + &fhandle, + SEC_RIGHTS_FILE_ALL); + CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, fhandle); + CHECK_STATUS(status, NT_STATUS_OK); + + /* and get the notify return. */ + status = smb2_notify_recv(notify_req, tctx, &n); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(n.out.num_changes, 1); + CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_ADDED); + CHECK_WIRE_STR(n.out.changes[0].name, notify_fname); + + /* Now, keeping dhandle open, delete fname. */ + status = smb2_util_unlink(tree, fname); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Now do a compound getingo + notify start + close dhandle. */ + smb2_transport_compound_start(tree->session->transport, 3); + + ZERO_STRUCT(gi); + gi.in.info_type = SMB2_0_INFO_FILE; + gi.in.info_class = 18; /* FSCC_FILE_ALL_INFORMATION */ + gi.in.output_buffer_length = 0x10000; + gi.in.file.handle = dhandle; + req[0] = smb2_getinfo_send(tree, &gi); + torture_assert_not_null_goto(tctx, req[0], ret, done, + "smb2_getinfo_send failed\n"); + + smb2_transport_compound_set_related(tree->session->transport, true); + + ZERO_STRUCT(n); + n.level = RAW_NOTIFY_SMB2; + n.in.buffer_size = 1000; + n.in.completion_filter = FILE_NOTIFY_CHANGE_ALL; + n.in.file.handle = relhandle; + n.in.recursive = false; + req[1] = smb2_notify_send(tree, &n); + torture_assert_not_null_goto(tctx, req[1], ret, done, + "smb2_notify_send failed\n"); + + ZERO_STRUCT(cl); + cl.in.file.handle = relhandle; + req[2] = smb2_close_send(tree, &cl); + torture_assert_not_null_goto(tctx, req[2], ret, done, + "smb2_close_send failed\n"); + + status = smb2_getinfo_recv(req[0], tree, &gi); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_recv failed."); + + /* + * The notify should immediately return + * as there is a pending change to reap. + * + * But against Windows 2022 it returns + * NT_STATUS_INTERNAL_ERROR. + */ + status = smb2_notify_recv(req[1], tree, &n); + torture_assert_ntstatus_equal_goto(tctx, + status, + NT_STATUS_INTERNAL_ERROR, + ret, + done, + "smb2_notify_recv didn't return " + "NT_STATUS_INTERNAL_ERROR.\n"); + status = smb2_close_recv(req[2], &cl); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close_recv failed."); + + ZERO_STRUCT(dhandle); + ret = true; + +done: + + if (dhandle.data[0] != 0) { + smb2_util_close(tree, dhandle); + } + smb2_deltree(tree, dname); + return ret; +} + static bool test_compound_unrelated1(struct torture_context *tctx, struct smb2_tree *tree) { @@ -2229,6 +2368,7 @@ struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "create-write-close", test_compound_create_write_close); torture_suite_add_1smb2_test(suite, "async1", test_compound_async1); + torture_suite_add_1smb2_test(suite, "async2", test_compound_async2); suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests"); -- 2.34.1