diff options
Diffstat (limited to 'source4/torture/smb2/oplock_break_handler.c')
-rw-r--r-- | source4/torture/smb2/oplock_break_handler.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c new file mode 100644 index 0000000..c17d32a --- /dev/null +++ b/source4/torture/smb2/oplock_break_handler.c @@ -0,0 +1,169 @@ +/* + * Unix SMB/CIFS implementation. + * + * test suite for SMB2 replay + * + * Copyright (C) Anubhav Rakshit 2014 + * Copyright (C) Stefan Metzmacher 2014 + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "oplock_break_handler.h" +#include "lib/events/events.h" + +struct break_info break_info; + +static void torture_oplock_ack_callback(struct smb2_request *req) +{ + NTSTATUS status; + + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + break_info.failure_status = status; + } +} + +/** + * A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. + */ + +bool torture_oplock_ack_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + + ZERO_STRUCT(break_info.br); + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + break_info.received_transport = tree->session->transport; + SMB_ASSERT(tree->session->transport == transport); + + if (break_info.oplock_skip_ack) { + torture_comment(break_info.tctx, + "transport[%p] skip acking to %s [0x%02X] in oplock handler\n", + transport, name, level); + return true; + } + + torture_comment(break_info.tctx, + "transport[%p] Acking to %s [0x%02X] in oplock handler\n", + transport, name, level); + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_ack_callback; + req->async.private_data = NULL; + + return true; +} + +/** + * A oplock break handler designed to ignore incoming break requests. + * This is used when incoming oplock break requests need to be ignored + */ +bool torture_oplock_ignore_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + return true; +} + +/* + * Timer handler function notifies the registering function that time is up + */ +static void timeout_cb(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + bool *timesup = (bool *)private_data; + *timesup = true; +} + +/* + * Wait a short period of time to receive a single oplock break request + */ +void torture_wait_for_oplock_break(struct torture_context *tctx) +{ + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + struct tevent_timer *te = NULL; + struct timeval ne; + bool timesup = false; + int old_count = break_info.count; + + /* Wait 1 second for an oplock break */ + ne = tevent_timeval_current_ofs(0, 1000000); + + te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up); + if (te == NULL) { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate.\n"); + goto done; + } + + torture_comment(tctx, "Waiting for a potential oplock break...\n"); + while (!timesup && break_info.count < old_count + 1) { + if (tevent_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate\n."); + goto done; + } + } + if (timesup) { + torture_comment(tctx, "... waiting for an oplock break timed out\n"); + } else { + torture_comment(tctx, "Got %u oplock breaks\n", + break_info.count - old_count); + } + +done: + /* We don't know if the timed event fired and was freed, we received + * our oplock break, or some other event triggered the loop. Thus, + * we create a tmp_ctx to be able to safely free/remove the timed + * event in all 3 cases. + */ + talloc_free(tmp_ctx); +} |