From: Andreas Schneider Date: Thu, 1 Aug 2024 10:45:14 +0000 (+0200) Subject: s4:torture: Add notify test for 'smb2 max async credits' X-Git-Tag: tdb-1.4.13~50 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=64168ebe8871e0d15d644966acb54af41f8f74af;p=thirdparty%2Fsamba.git s4:torture: Add notify test for 'smb2 max async credits' BUG: https://bugzilla.samba.org/show_bug.cgi?id=14430 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Stefan Metzmacher Signed-off-by: Andreas Schneider --- diff --git a/selftest/knownfail.d/smb2_credits b/selftest/knownfail.d/smb2_credits index 5b182521715..dd011726179 100644 --- a/selftest/knownfail.d/smb2_credits +++ b/selftest/knownfail.d/smb2_credits @@ -1,3 +1,4 @@ samba3.smb2.credits.*1conn_ipc_max_async_credits samba3.smb2.credits.*2conn_ipc_max_async_credits samba3.smb2.credits.*multichannel_ipc_max_async_credits +samba3.smb2.credits.*1conn_notify_max_async_credits diff --git a/source4/torture/smb2/credits.c b/source4/torture/smb2/credits.c index 3dc77e2dede..ebb0a3469a3 100644 --- a/source4/torture/smb2/credits.c +++ b/source4/torture/smb2/credits.c @@ -912,6 +912,433 @@ out: return ok; } + +#define TESTDIR "test_max_async_credits" + +#define SMBXCLI_NOTIFY_DIR_DESIRED_ACCESS \ + (SEC_STD_READ_CONTROL | SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | \ + SEC_FILE_APPEND_DATA | SEC_FILE_READ_EA | SEC_FILE_WRITE_EA | \ + SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_ATTRIBUTE | 0) + +struct test_notify_async_credit_loop; + +struct test_notify_async_credit_state { + struct torture_context *tctx; + struct smbXcli_conn *conn; + struct smbXcli_session *session; + struct smbXcli_tcon *tcon; + uint32_t timeout_msec; + uint16_t pid; + const char *fname; + + size_t num_loops; + struct test_notify_async_credit_loop *loops; + size_t num_status_received; + size_t num_started; + + size_t num_status_pending; + size_t num_status_insufficient; + + bool stop; +}; + +struct test_notify_async_credit_loop { + size_t idx; + struct test_notify_async_credit_state *state; + struct tevent_req *req; + + uint64_t fid_persistent; + uint64_t fid_volatile; + + struct tevent_immediate *im; + + NTSTATUS status; +}; + +static void test_async_notify_loop_do( + struct test_notify_async_credit_loop *loop); + +static void test_async_notify_loop_start(struct tevent_context *ctx, + struct tevent_immediate *im, + void *private_data) +{ + struct test_notify_async_credit_loop *loop = private_data; + + test_async_notify_loop_do(loop); +} + +static void test_async_notify_loop_done(struct tevent_req *req); + +static void test_async_notify_loop_do(struct test_notify_async_credit_loop *loop) +{ + struct test_notify_async_credit_state *state = loop->state; + bool ok; + + state->num_started += 1; + loop->req = smb2cli_notify_send(state->loops, + state->tctx->ev, + state->conn, + state->timeout_msec, + state->session, + state->tcon, + 10000, /* out_buffer_length */ + loop->fid_persistent, + loop->fid_volatile, + FILE_NOTIFY_CHANGE_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_CREATION, + false /* recursive */ + ); + torture_assert_not_null_goto(state->tctx, + loop->req, + ok, + asserted, + "smb2cli_notify_send failed"); + smb2cli_notify_set_notify_async(loop->req); + tevent_req_set_callback(loop->req, + test_async_notify_loop_done, + loop); + + + return; +asserted: + torture_comment(state->tctx, "Assserted for idx=%zu\n", loop->idx); + (void)ok; + state->stop = true; +} + +static void test_async_notify_loop_done(struct tevent_req *req) +{ + struct test_notify_async_credit_loop *loop = _tevent_req_callback_data( + req); + struct test_notify_async_credit_state *state = loop->state; + TALLOC_CTX *frame = talloc_stackframe(); + uint8_t *data = NULL; + uint32_t data_length = 0; + bool ok; + bool was_pending = false; + bool in_progress = false; + + torture_assert_goto(state->tctx, + loop->req == req, + ok, + asserted, + __location__); + + if (NT_STATUS_EQUAL(loop->status, NT_STATUS_PENDING)) { + was_pending = true; + } + + loop->status = smb2cli_notify_recv(req, frame, &data, &data_length); +#if 0 + torture_comment(state->tctx, + "loop->status: %s\n", + nt_errstr(loop->status)); +#endif + in_progress = tevent_req_is_in_progress(req); + state->num_status_received += 1; + + if (NT_STATUS_EQUAL(loop->status, NT_STATUS_PENDING)) { + state->num_status_pending += 1; + torture_assert_goto(state->tctx, + in_progress, + ok, + asserted, + __location__); + goto done; + } + loop->req = NULL; + TALLOC_FREE(req); + torture_assert_goto(state->tctx, + !in_progress, + ok, + asserted, + __location__); + if (was_pending && + NT_STATUS_EQUAL(loop->status, NT_STATUS_NOTIFY_CLEANUP)) + { + goto done; + } + torture_assert_ntstatus_equal_goto(state->tctx, + loop->status, + NT_STATUS_INSUFFICIENT_RESOURCES, + ok, + asserted, + __location__); + state->num_status_insufficient += 1; + +done: + TALLOC_FREE(frame); + return; +asserted: + (void)ok; + state->stop = true; + TALLOC_FREE(frame); +} + +static bool test_notify_max_async_credits(struct torture_context *tctx, + struct smb2_tree *trees[], + size_t num_trees, + size_t num_loops) +{ + struct test_notify_async_credit_state *states[num_trees]; + bool stop_loop = false; + NTSTATUS status; + size_t i, t; + bool ok; + + for (t = 0; t < num_trees; t++) { + uint16_t cur_credits; + + states[t] = NULL; + + cur_credits = smb2cli_conn_get_cur_credits( + trees[t]->session->transport->conn); + torture_assert_int_equal_goto( + tctx, + cur_credits, + num_loops, + ok, + out, + "Invalid number of granted credits"); + } + + torture_assert_int_not_equal_goto( + tctx, num_trees, 0, ok, out, "Invalid number of trees"); + + for (i = 0; i < num_trees; i++) { + struct smb2_tree *tree = trees[i]; + struct test_notify_async_credit_state *state = NULL; + + state = talloc_zero(tctx, + struct test_notify_async_credit_state); + torture_assert_not_null_goto( + tctx, state, ok, out, "tevent_zero failed"); + + state->tctx = tctx; + state->conn = tree->session->transport->conn; + state->session = tree->session->smbXcli; + state->tcon = tree->smbXcli; + state->timeout_msec = tree->session->transport->options + .request_timeout * 1000; + state->pid = 0; + state->fname = TESTDIR; + + state->num_loops = num_loops; + state->loops = talloc_zero_array( + state, + struct test_notify_async_credit_loop, + state->num_loops); + torture_assert_not_null_goto(tctx, + state->loops, + ok, + out, + "tevent_zero_array failed"); + + states[i] = state; + } + + for (i = 0; i < num_loops; i++) { + for (t = 0; t < num_trees; t++) { + struct test_notify_async_credit_state + *state = states[t]; + struct test_notify_async_credit_loop + *loop = &state->loops[i]; + + loop->idx = i; + loop->state = state; + loop->status = NT_STATUS_UNSUCCESSFUL; + + loop->im = tevent_create_immediate(state->loops); + torture_assert_not_null_goto( + tctx, + loop->im, + ok, + out, + "tevent_create_immediate failed"); + + /* Open directory */ + status = smb2cli_create( + state->conn, + state->timeout_msec, + state->session, + state->tcon, + state->fname, + SMB2_OPLOCK_LEVEL_NONE, + SMB2_IMPERSONATION_IMPERSONATION, + SMBXCLI_NOTIFY_DIR_DESIRED_ACCESS, + 0, /* file_attributes */ + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + FILE_DIRECTORY_FILE, + NULL, /* blobs */ + &loop->fid_persistent, + &loop->fid_volatile, + NULL, /* cr */ + state, /* mem_ctx */ + NULL, /* ret_blobs */ + NULL); /* psymlink */ + torture_assert_ntstatus_ok_goto( + tctx, status, ok, out, "smb2cli_create failed"); + + tevent_schedule_immediate(loop->im, + tctx->ev, + test_async_notify_loop_start, + loop); + } + } + + /* Loop to send and receive packets */ + while (!stop_loop) { + size_t loops_ready = 0; + int rc; + + rc = tevent_loop_once(tctx->ev); + torture_assert_int_equal_goto( + tctx, rc, 0, ok, out, "tevent_loop_once"); + + for (i = 0; i < num_trees; i++) { + struct test_notify_async_credit_state + *state = states[i]; + if (state->stop) { + stop_loop = true; + } + + if (state->num_status_received >= state->num_loops) { + loops_ready += 1; + } + } + + if (loops_ready >= num_trees) { + stop_loop = true; + } + } + + for (t = 0; t < num_trees; t++) { + struct test_notify_async_credit_state *state = states[t]; + size_t max_async_credits = 512; + size_t max_credits = max_async_credits + 2; + + torture_assert_goto(state->tctx, !state->stop, ok, out, ""); + torture_assert_int_equal_goto(state->tctx, + state->num_status_received, + state->num_loops, + ok, + out, + ""); + torture_assert_int_equal_goto(state->tctx, + state->num_status_pending, + max_async_credits - 1, + ok, + out, + ""); + torture_assert_int_equal_goto(state->tctx, + state->num_status_insufficient, + max_credits - max_async_credits + 1, + ok, + out, + ""); + } + + for (t = 0; t < num_trees; t++) { + struct test_notify_async_credit_state *state = states[t]; + + for (i = 0; i < num_loops; i++) { + struct test_notify_async_credit_loop + *loop = &state->loops[i]; + struct smb2_handle h = {{ + loop->fid_persistent, + loop->fid_volatile, + }}; + + /* the close cancels any pending notify request */ + status = smb2_util_close(trees[t], h); + torture_assert_ntstatus_ok_goto( + tctx, + status, + ok, + out, + "smb2_util_close failed"); + } + } + + for (t = 0; t < num_trees; t++) { + struct test_notify_async_credit_state *state = states[t]; + + for (i = 0; i < num_loops; i++) { + struct test_notify_async_credit_loop + *loop = &state->loops[i]; + + if (NT_STATUS_EQUAL(loop->status, + NT_STATUS_INSUFFICIENT_RESOURCES)) + { + continue; + } + + if (loop->req != NULL) { + ok = tevent_req_poll(loop->req, state->tctx->ev); + torture_assert_goto(state->tctx, + ok, + ok, + out, + "tevent_req_poll failed"); + } + torture_assert_ntstatus_equal_goto( + tctx, + loop->status, + NT_STATUS_NOTIFY_CLEANUP, + ok, + out, + __location__); + } + } + + ok = true; +out: + for (t = 0; t < num_trees; t++) { + TALLOC_FREE(states[t]); + } + + return ok; +} + +static bool test_1conn_notify_max_async_credits(struct torture_context *tctx, + struct smb2_tree *tree0) +{ + struct smb2_tree *trees[1] = { tree0 }; + struct smb2_handle dh = {{}}; + uint16_t max_async_credits = torture_setting_int( + tctx, + "maxasynccredits", + 512 /* lpcfg_smb2_max_async_credits(tctx->lp_ctx) */); + bool ok = false; + NTSTATUS status; + uint16_t max_credits = max_async_credits + 2; + + smb2_transport_credits_ask_num(tree0->session->transport, max_credits); + + /* Cleanup TESTDIR */ + smb2_deltree(tree0, TESTDIR); + + /* Create TESTDIR */ + status = torture_smb2_testdir(tree0, TESTDIR, &dh); + torture_assert_ntstatus_ok_goto( + tctx, status, ok, out, "smb2_create failed"); + status = smb2_util_close(tree0, dh); + torture_assert_ntstatus_ok_goto( + tctx, status, ok, out, "smb2_util_close failed"); + + ok = test_notify_max_async_credits(tctx, + trees, + ARRAY_SIZE(trees), + max_credits); +out: + /* Cleanup TESTDIR */ + smb2_deltree(tree0, TESTDIR); + + return ok; +} + struct torture_suite *torture_smb2_crediting_init(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "credits"); @@ -930,6 +1357,10 @@ struct torture_suite *torture_smb2_crediting_init(TALLOC_CTX *ctx) "multichannel_ipc_max_async_credits", test_multichannel_ipc_max_async_credits); + torture_suite_add_1smb2_test(suite, + "1conn_notify_max_async_credits", + test_1conn_notify_max_async_credits); + suite->description = talloc_strdup(suite, "SMB2-CREDITS tests"); return suite;