From 1160d680620d71a7d5632b73c76d516f89696b65 Mon Sep 17 00:00:00 2001 From: Steven Danneman Date: Thu, 30 Jul 2009 15:10:50 -0700 Subject: s4/torture: Ported SMB oplock torture tests to SMB2 I've ported all applicable SMB oplock torture tests to SMB2, giving us a good base for SMB2 oplock testing. There are several differences between oplocks in SMB and SMB2, mostly because of differences in W2K3 and W2K8. The existing SMB oplock tests all pass against W2K3, but several fail against W2K8. These same tests were failing in SMB2, util I reworked them. BATCH19, BATCH20: In W2K3/SMB a setfileinfo - rename command wouldn't cause a sharing violation or break an existing oplock. It appears that in W2K8/SMB2 a sharing violation is raised. BATCH22: In W2K3/SMB when a second opener was waiting the full timeout of an oplock break, it would receive NT_STATUS_SHARING_VIOLATION after about 35 seconds. This bug has been fixed in W2K8/SMB2 and instead the second opener succeeds. LEVELII500: Added 1 new test checking that the server returns a proper error code when a client improperly replies to a levelII to none break notification. STREAM1: W2K8 now grants oplocks on alternate data streams. --- source4/torture/smb2/oplocks.c | 3580 +++++++++++++++++++++++++++++++++++++++- source4/torture/smb2/smb2.c | 4 +- 2 files changed, 3513 insertions(+), 71 deletions(-) (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/oplocks.c b/source4/torture/smb2/oplocks.c index 3fee0b4ab6..f686de6a2f 100644 --- a/source4/torture/smb2/oplocks.c +++ b/source4/torture/smb2/oplocks.c @@ -3,6 +3,7 @@ test suite for SMB2 oplocks + Copyright (C) Andrew Tridgell 2003 Copyright (C) Stefan Metzmacher 2008 This program is free software; you can redistribute it and/or modify @@ -20,26 +21,46 @@ */ #include "includes.h" -#include "librpc/gen_ndr/security.h" + #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" + +#include "lib/cmdline/popt_common.h" +#include "lib/events/events.h" + +#include "param/param.h" +#include "system/filesys.h" + #include "torture/torture.h" #include "torture/smb2/proto.h" +#define CHECK_RANGE(v, min, max) do { \ + if ((v) < (min) || (v) > (max)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got %d - should be between %d and %d\n", \ + __location__, #v, (int)v, (int)min, (int)max); \ + ret = false; \ + }} while (0) + +#define CHECK_STRMATCH(v, correct) do { \ + if (!v || strstr((v),(correct)) == NULL) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s "\ + "got '%s' - should be '%s'\n", \ + __location__, #v, v?v:"NULL", correct); \ + ret = false; \ + }} while (0) + #define CHECK_VAL(v, correct) do { \ if ((v) != (correct)) { \ - torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got 0x%x - should be 0x%x\n", \ __location__, #v, (int)v, (int)correct); \ ret = false; \ }} while (0) -#define CHECK_STATUS(status, correct) do { \ - if (!NT_STATUS_EQUAL(status, correct)) { \ - torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ - nt_errstr(status), nt_errstr(correct)); \ - ret = false; \ - goto done; \ - }} while (0) +#define BASEDIR "oplock_test" static struct { struct smb2_handle handle; @@ -47,6 +68,7 @@ static struct { struct smb2_break br; int count; int failures; + NTSTATUS failure_status; } break_info; static void torture_oplock_break_callback(struct smb2_request *req) @@ -58,19 +80,23 @@ static void torture_oplock_break_callback(struct smb2_request *req) status = smb2_break_recv(req, &break_info.br); if (!NT_STATUS_IS_OK(status)) { break_info.failures++; + break_info.failure_status = status; } return; } -/* a oplock break request handler */ +/* A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. */ static bool torture_oplock_handler(struct smb2_transport *transport, const struct smb2_handle *handle, - uint8_t level, void *private_data) + 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; @@ -87,10 +113,105 @@ static bool torture_oplock_handler(struct smb2_transport *transport, name = "unknown"; break_info.failures++; } - printf("Acking to %s [0x%02X] in oplock handler\n", - name, level); + printf("Acking to %s [0x%02X] in oplock handler\n", name, level); + + 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; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} + +/* + A handler function for oplock break notifications. Send a break to none + request. +*/ +static bool torture_oplock_handler_ack_to_none(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + printf("Acking to none in oplock handler\n"); + + ZERO_STRUCT(break_info.br); + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + + return true; +} + +/* + A handler function for oplock break notifications. Break from level II to + none. SMB2 requires that the client does not send an oplock break request to + the server in this case. +*/ +static bool torture_oplock_handler_level2_to_none( + struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Break from level II to none in oplock handler\n"); + + return true; +} + +/* A handler function for oplock break notifications. This should be used when + * test expects two break notifications, first to level II, then to none. */ +static bool torture_oplock_handler_two_notifications( + 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++; + } + printf("Breaking to %s [0x%02X] in oplock handler\n", name, level); + + if (level == SMB2_OPLOCK_LEVEL_NONE) + return true; + break_info.br.in.file.handle = *handle; break_info.br.in.oplock_level = level; break_info.br.in.reserved = 0; @@ -99,79 +220,3398 @@ static bool torture_oplock_handler(struct smb2_transport *transport, req = smb2_break_send(tree, &break_info.br); req->async.fn = torture_oplock_break_callback; req->async.private_data = NULL; + return true; +} +static void torture_oplock_handler_close_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req)) { + printf("close failed in oplock_handler_close\n"); + break_info.failures++; + } +} + +/* + a handler function for oplock break requests - close the file +*/ +static bool torture_oplock_handler_close(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_close io; + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = RAW_CLOSE_SMB2; + req = smb2_close_send(tree, &io); + if (req == NULL) { + printf("failed to send close in oplock_handler_close\n"); + return false; + } + + req->async.fn = torture_oplock_handler_close_recv; + req->async.private_data = NULL; + + return true; +} + +/* + a handler function for oplock break requests. Let it timeout +*/ +static bool torture_oplock_handler_timeout(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + printf("Let oplock break timeout\n"); return true; } -bool torture_smb2_oplock_batch1(struct torture_context *tctx, - struct smb2_tree *tree) +static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx, + struct smb2_tree **tree) { - TALLOC_CTX *mem_ctx = talloc_new(tctx); - struct smb2_handle h1, h2; - struct smb2_create io; NTSTATUS status; - const char *fname = "oplock.dat"; + 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); + options.use_level2_oplocks = false; + + status = smb2_connect(tctx, host, + lp_smb_ports(tctx->lp_ctx), share, + lp_resolve_context(tctx->lp_ctx), + credentials, tree, tctx->ev, &options, + lp_socket_options(tctx->lp_ctx), + lp_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to connect to SMB2 share " + "\\\\%s\\%s - %s\n", host, share, + nt_errstr(status)); + return false; + } + 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; + return; +} + +/* + Wait a short period of time to receive a single oplock break request +*/ +static 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 seconds for an oplock break */ + ne = tevent_timeval_current_ofs(0, 100000); + + if ((te = event_add_timed(tctx->ev, tmp_ctx, ne, timeout_cb, ×up)) + == NULL) + { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate."); + goto done; + } + + while (!timesup && break_info.count < old_count + 1) { + if (event_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate."); + goto done; + } + } + +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); + + return; +} + +static bool test_smb2_oplock_exclusive1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive1.dat"; + NTSTATUS status; bool ret = true; + union smb_open io; + union smb_unlink unl; + struct smb2_handle h1; + struct smb2_handle h; - tree->session->transport->oplock.handler = torture_oplock_handler; - tree->session->transport->oplock.private_data = tree; + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - smb2_util_unlink(tree, fname); + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive " + "oplock (share mode: none)\n"); ZERO_STRUCT(break_info); + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - ZERO_STRUCT(io); - io.in.security_flags = 0x00; - io.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; - io.in.create_flags = 0x00000000; - io.in.reserved = 0x00000000; - io.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE | - NTCREATEX_SHARE_ACCESS_DELETE; - io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | - NTCREATEX_OPTIONS_ASYNC_ALERT | - NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | - 0x00200000; - io.in.fname = fname; - - status = smb2_create(tree, mem_ctx, &io); - CHECK_STATUS(status, NT_STATUS_OK); - CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - /*CHECK_VAL(io.out.reserved, 0);*/ - CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_CREATED); - CHECK_VAL(io.out.alloc_size, 0); - CHECK_VAL(io.out.size, 0); - CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); - CHECK_VAL(io.out.reserved2, 0); - CHECK_VAL(break_info.count, 0); - - h1 = io.out.file.handle; - - ZERO_STRUCT(io.in.blobs); - status = smb2_create(tree, mem_ctx, &io); - CHECK_VAL(break_info.count, 1); + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should not cause a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "unlink it - should also be no break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive " + "oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should cause a break to level 2\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_STATUS(status, NT_STATUS_OK); - CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - /*CHECK_VAL(io.out.reserved, 0);*/ - CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); - CHECK_VAL(io.out.alloc_size, 0); - CHECK_VAL(io.out.size, 0); - CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); - CHECK_VAL(io.out.reserved2, 0); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); - h2 = io.out.file.handle; + /* now we have 2 level II oplocks... */ + torture_comment(tctx, "try to unlink it - should cause a break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Error unlinking the file"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); -done: - talloc_free(mem_ctx); + torture_comment(tctx, "close both handles\n"); + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); - smb2_util_close(tree, h1); - smb2_util_close(tree, h2); - smb2_util_unlink(tree, fname); + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive " + "oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "setpathinfo EOF should trigger a break to " + "none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_exclusive6_1.dat"; + const char *fname2 = BASEDIR "\\test_exclusive6_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sinfo; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree2, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "rename should not generate a break but get " + "a sharing violation\n"); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname2; + status = smb2_setinfo_file(tree1, &sinfo); + + torture_comment(tctx, "trying rename while first file open\n"); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); return ret; } + +static bool test_smb2_oplock_batch1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH1: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break\n"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should generate a self break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + char c = 0; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH2: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break, which we ack " + "as break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_ack_to_none; + tree1->session->transport->oplock.private_data = tree1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should not generate a break\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH3: if we close on break then the unlink " + "can succeed\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_read r; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH4: a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(r); + r.in.file.handle = h1; + r.in.offset = 0; + + status = smb2_read(tree1, tree1, &r); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH5: a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch6.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH6: a 2nd open should give a break to " + "level II if the first open allowed shared read\n"); + ZERO_STRUCT(break_info); + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch7(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch7.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when " + "we close instead of ack\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree2, h1); + smb2_util_close(tree2, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch8(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch8.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH8: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch9(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create " + "file\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Subsequent normal open should break oplock on " + "attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, h2); + + torture_comment(tctx, "third oplocked open should grant level2 without " + "break\n"); + ZERO_STRUCT(break_info); + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree2, h2, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch10(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch10.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock " + "open should grant level2\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, 0); + + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "write should trigger a break to none\n"); + { + struct smb2_write wr; + DATA_BLOB data; + data = data_blob_talloc(tree1, NULL, UINT16_MAX); + data.data[0] = (const uint8_t)'x'; + ZERO_STRUCT(wr); + wr.in.file.handle = h1; + wr.in.offset = 0; + wr.in.data = data; + status = smb2_write(tree1, &wr); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + } + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch11(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch11.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-eof on pathname breaks an exclusive oplock. */ + torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks " + "oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch12(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch12.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-allocation size on pathname breaks an exclusive + * oplock. */ + torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size " + "breaks oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_ALLOCATION_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.allocation_info.in.alloc_size = 65536 * 8; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch13(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch13.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH13: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + + return ret; +} + +static bool test_smb2_oplock_batch14(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch14.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH14: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_SUPERSEDE dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch15(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch15.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a qpathinfo all info on pathname breaks a batch oplock. */ + torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks " + "a batch oplock (should not).\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree2, tctx, &qfi); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch16(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch16.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH16: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH17 test. Since + * SMB2 doesn't have a RENAME command this test isn't applicable. However, + * it's much less confusing, when comparing test, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch17(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH18 test. Since + * SMB2 doesn't have an NTRENAME command this test isn't applicable. However, + * it's much less confusing, when comparing tests, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch18(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +static bool test_smb2_oplock_batch19(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname1 = BASEDIR "\\test_batch19_1.dat"; + const char *fname2 = BASEDIR "\\test_batch19_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH19: open a file with an batch oplock " + "(share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + smb2_deltree(tree1, fname2); + return ret; +} + +static bool test_smb2_oplock_batch20(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_batch20_1.dat"; + const char *fname2 = BASEDIR "\\test_batch20_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH20: open a file with an batch oplock " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + torture_comment(tctx, "open the file a second time requesting batch " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.fname = fname1; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(break_info); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h2; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree2, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h2; + + status = smb2_getinfo_file(tree2, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + return ret; +} + +static bool test_smb2_oplock_batch21(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch21.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH21: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "writing should not generate a break\n"); + status = smb2_util_write(tree1, h1, &c, 0, 1); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch22(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch22.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct timeval tv; + int timeout = torture_setting_int(tctx, "oplocktimeout", 30); + int te; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "BATCH22 disabled against samba3\n"); + } + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH22: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "a 2nd open should succeed after the oplock " + "break timeout\n"); + tv = timeval_current(); + tree1->session->transport->oplock.handler = + torture_oplock_handler_timeout; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + CHECK_RANGE(te, timeout - 1, timeout + 15); + torture_comment(tctx, "waited %d seconds for oplock timeout\n", te); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch23(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch23.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH23: an open and ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open without level2 oplock support " + "should generate a break to level2\n"); + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h3 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 3rd open with level2 oplock support should " + "not generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree3, h3); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch24(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch24.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH24: a open without level support and " + "ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open with level2 oplock support should " + "generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree3, h2); + smb2_util_close(tree2, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch25(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch25.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH25: open a file with an batch oplock " + "(share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "changing the file attribute info should trigger " + "a break and a violation\n"); + + status = smb2_util_setatr(tree1, fname, FILE_ATTRIBUTE_HIDDEN); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname); + return ret; +} + +/* Test how oplocks work on streams. */ +static bool test_raw_oplock_stream1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + union smb_open io; + const char *fname_base = BASEDIR "\\test_stream1.txt"; + const char *fname_stream, *fname_default_stream; + const char *default_stream = "::$DATA"; + const char *stream = "Stream One:$DATA"; + bool ret = true; + struct smb2_handle h, h_base, h_stream; + int i; + +#define NSTREAM_OPLOCK_RESULTS 8 + struct { + const char **fname; + bool open_base_file; + uint32_t oplock_req; + uint32_t oplock_granted; + } stream_oplock_results[NSTREAM_OPLOCK_RESULTS] = { + /* Request oplock on stream without the base file open. */ + {&fname_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + + /* Request oplock on stream with the base file open. */ + {&fname_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_II}, + {&fname_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_II}, + }; + + /* Only passes against windows at the moment. */ + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "STREAM1 disabled against samba3+4\n"); + } + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); + fname_default_stream = talloc_asprintf(tctx, "%s%s", fname_base, + default_stream); + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* Initialize handles to "closed". Using -1 in the first 64-bytes + * as the sentry for this */ + h_stream.data[0] = -1; + + /* cleanup */ + smb2_util_unlink(tree1, fname_base); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* Setup generic open parameters. */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = (SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL); + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + + /* Create the file with a stream */ + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error creating file"); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Change the disposition to open now that the file has been created. */ + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + + /* Try some permutations of taking oplocks on streams. */ + for (i = 0; i < NSTREAM_OPLOCK_RESULTS; i++) { + const char *fname = *stream_oplock_results[i].fname; + bool open_base_file = stream_oplock_results[i].open_base_file; + uint32_t oplock_req = stream_oplock_results[i].oplock_req; + uint32_t oplock_granted = + stream_oplock_results[i].oplock_granted; + + if (open_base_file) { + torture_comment(tctx, "Opening base file: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, + "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, + SMB2_OPLOCK_LEVEL_BATCH); + h_base = io.smb2.out.file.handle; + } + + torture_comment(tctx, "%d: Opening stream: %s with %d\n", i, + fname, oplock_req); + io.smb2.in.fname = fname; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = oplock_req; + + /* Do the open with the desired oplock on the stream. */ + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, oplock_granted); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Cleanup the base file if it was opened. */ + if (open_base_file) + smb2_util_close(tree2, h_base); + } + + /* Open the stream with an exclusive oplock. */ + torture_comment(tctx, "Opening stream: %s with %d\n", + fname_stream, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + h_stream = io.smb2.out.file.handle; + + /* Open the base file and see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening base file: %s with %d\n", + fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* Open the stream again to see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening stream again: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + /* Close the stream. */ + if (h_stream.data[0] != -1) { + smb2_util_close(tree1, h_stream); + } + + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_doc(struct torture_context *tctx, struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_oplock_doc.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree, fname); + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "open a delete-on-close file with a batch " + "oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + smb2_util_close(tree, h1); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock, then open it again from a second client + * requesting no oplock. Having two open file handles should break our own + * oplock during BRL acquisition. + */ +static bool test_smb2_oplock_brl1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1,buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree2, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Open a file with a batch oplock on one tree and then acquire a brl. + * We should not contend our own oplock. + */ +static bool test_smb2_oplock_brl2(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should not break to " + "none\n"); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + /* With one file handle open a BRL should not contend our oplock. + * Thus, no oplock break will be received and the entire break_info + * struct will be 0 */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock twice from one tree and then acquire a + * brl. BRL acquisition should break our own oplock. + */ +static bool test_smb2_oplock_brl3(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1, h2; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree1, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Starting the SMB2 specific oplock tests at 500 so we can keep the SMB1 + * tests in sync with an identically numbered SMB2 test */ + +/* Test whether the server correctly returns an error when we send + * a response to a levelII to none oplock notification. */ +static bool test_smb2_oplock_levelII500(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_levelII500.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "LEVELII500: acknowledging a break from II to " + "none should return an error\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none and when " + "we reply, an oplock break failure\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + /* Wait several times to receive both the break notification, and the + * NT_STATUS_INVALID_OPLOCK_PROTOCOL error in the break response */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + /* There appears to be a race condition in W2K8 and W2K8R2 where + * sometimes the server will happily reply to our break response with + * NT_STATUS_OK, and sometimes it will return the OPLOCK_PROTOCOL + * error. As the MS-SMB2 doc states that a client should not reply to + * a level2 to none break notification, I'm leaving the protocol error + * as the expected behavior. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 1); + torture_assert_ntstatus_equal(tctx, break_info.failure_status, + NT_STATUS_INVALID_OPLOCK_PROTOCOL, + "Incorrect status"); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +struct torture_suite *torture_smb2_oplocks_init(void) +{ + struct torture_suite *suite = + torture_suite_create(talloc_autofree_context(), "OPLOCK"); + + torture_suite_add_2smb2_test(suite, "EXCLUSIVE1", test_smb2_oplock_exclusive1); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE2", test_smb2_oplock_exclusive2); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE3", test_smb2_oplock_exclusive3); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE4", test_smb2_oplock_exclusive4); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE5", test_smb2_oplock_exclusive5); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE6", test_smb2_oplock_exclusive6); + torture_suite_add_2smb2_test(suite, "BATCH1", test_smb2_oplock_batch1); + torture_suite_add_2smb2_test(suite, "BATCH2", test_smb2_oplock_batch2); + torture_suite_add_2smb2_test(suite, "BATCH3", test_smb2_oplock_batch3); + torture_suite_add_2smb2_test(suite, "BATCH4", test_smb2_oplock_batch4); + torture_suite_add_2smb2_test(suite, "BATCH5", test_smb2_oplock_batch5); + torture_suite_add_2smb2_test(suite, "BATCH6", test_smb2_oplock_batch6); + torture_suite_add_2smb2_test(suite, "BATCH7", test_smb2_oplock_batch7); + torture_suite_add_2smb2_test(suite, "BATCH8", test_smb2_oplock_batch8); + torture_suite_add_2smb2_test(suite, "BATCH9", test_smb2_oplock_batch9); + torture_suite_add_2smb2_test(suite, "BATCH10", test_smb2_oplock_batch10); + torture_suite_add_2smb2_test(suite, "BATCH11", test_smb2_oplock_batch11); + torture_suite_add_2smb2_test(suite, "BATCH12", test_smb2_oplock_batch12); + torture_suite_add_2smb2_test(suite, "BATCH13", test_smb2_oplock_batch13); + torture_suite_add_2smb2_test(suite, "BATCH14", test_smb2_oplock_batch14); + torture_suite_add_2smb2_test(suite, "BATCH15", test_smb2_oplock_batch15); + torture_suite_add_2smb2_test(suite, "BATCH16", test_smb2_oplock_batch16); + torture_suite_add_1smb2_test(suite, "BATCH19", test_smb2_oplock_batch19); + torture_suite_add_2smb2_test(suite, "BATCH20", test_smb2_oplock_batch20); + torture_suite_add_1smb2_test(suite, "BATCH21", test_smb2_oplock_batch21); + torture_suite_add_1smb2_test(suite, "BATCH22", test_smb2_oplock_batch22); + torture_suite_add_2smb2_test(suite, "BATCH23", test_smb2_oplock_batch23); + torture_suite_add_2smb2_test(suite, "BATCH24", test_smb2_oplock_batch24); + torture_suite_add_1smb2_test(suite, "BATCH25", test_smb2_oplock_batch25); + torture_suite_add_2smb2_test(suite, "STREAM1", test_raw_oplock_stream1); + torture_suite_add_1smb2_test(suite, "DOC", test_smb2_oplock_doc); + torture_suite_add_2smb2_test(suite, "BRL1", test_smb2_oplock_brl1); + torture_suite_add_1smb2_test(suite, "BRL2", test_smb2_oplock_brl2); + torture_suite_add_1smb2_test(suite, "BRL3", test_smb2_oplock_brl3); + torture_suite_add_1smb2_test(suite, "LEVELII500", test_smb2_oplock_levelII500); + + suite->description = talloc_strdup(suite, "SMB2-OPLOCK tests"); + + return suite; +} + +/* + stress testing of oplocks +*/ +bool test_smb2_bench_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree **trees; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int i, count=0; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + union smb_open io; + struct timeval tv; + struct smb2_handle h; + + trees = talloc_array(mem_ctx, struct smb2_tree *, torture_nprocs); + + torture_comment(tctx, "Opening %d connections\n", torture_nprocs); + for (i=0;isession->transport->oplock.handler = + torture_oplock_handler_close; + trees[i]->session->transport->oplock.private_data = trees[i]; + } + + status = torture_smb2_testdir(trees[0], BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + ZERO_STRUCT(io.smb2); + io.smb2.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\test.dat"; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + tv = timeval_current(); + + /* + we open the same file with SHARE_ACCESS_NONE from all the + connections in a round robin fashion. Each open causes an + oplock break on the previous connection, which is answered + by the oplock_handler_close() to close the file. + + This measures how fast we can pass on oplocks, and stresses + the oplock handling code + */ + torture_comment(tctx, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + for (i=0;idata[0], handle->data[1]); + return false; + } + + info = &hold_info[i]; + + if (info->close_on_break) { + printf("oplock break on %s - closing\n", info->fname); + torture_oplock_handler_close(transport, handle, + level, private_data); + return true; + } + + printf("oplock break on %s - acking break\n", info->fname); + printf("Acking to none in oplock handler\n"); + + torture_oplock_handler_ack_to_none(transport, handle, + level, private_data); + return true; +} + +/* + used for manual testing of oplocks - especially interaction with + other filesystems (such as NFS and local access) +*/ +bool test_smb2_hold_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct torture_context *mem_ctx = talloc_new(tctx); + struct tevent_context *ev = + (struct tevent_context *)tree->session->transport->socket->event.ctx; + int i; + struct smb2_handle h; + NTSTATUS status; + + torture_comment(tctx, "Setting up open files with oplocks in %s\n", + BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + tree->session->transport->oplock.handler = torture_oplock_handler_hold; + tree->session->transport->oplock.private_data = tree; + + /* setup the files */ + for (i=0;idescription = talloc_strdup(suite, "SMB2-specific tests"); -- cgit From b2bcfaaeed44af3a60667894ce0d7647f0382a26 Mon Sep 17 00:00:00 2001 From: Steven Danneman Date: Thu, 1 Oct 2009 13:47:28 -0700 Subject: s4/torture: rename oplocks.c to oplock.c to match SMB1 file layout --- source4/torture/smb2/config.mk | 2 +- source4/torture/smb2/oplocks.c | 3617 ---------------------------------------- 2 files changed, 1 insertion(+), 3618 deletions(-) delete mode 100644 source4/torture/smb2/oplocks.c (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index 2aba86a1ab..f617f70ad2 100644 --- a/source4/torture/smb2/config.mk +++ b/source4/torture/smb2/config.mk @@ -20,7 +20,7 @@ TORTURE_SMB2_OBJ_FILES = $(addprefix $(torturesrcdir)/smb2/, \ notify.o \ smb2.o \ durable_open.o \ - oplocks.o \ + oplock.o \ dir.o \ lease.o \ create.o \ diff --git a/source4/torture/smb2/oplocks.c b/source4/torture/smb2/oplocks.c deleted file mode 100644 index f686de6a2f..0000000000 --- a/source4/torture/smb2/oplocks.c +++ /dev/null @@ -1,3617 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - test suite for SMB2 oplocks - - Copyright (C) Andrew Tridgell 2003 - Copyright (C) Stefan Metzmacher 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 "libcli/smb2/smb2.h" -#include "libcli/smb2/smb2_calls.h" -#include "libcli/smb_composite/smb_composite.h" -#include "libcli/resolve/resolve.h" - -#include "lib/cmdline/popt_common.h" -#include "lib/events/events.h" - -#include "param/param.h" -#include "system/filesys.h" - -#include "torture/torture.h" -#include "torture/smb2/proto.h" - -#define CHECK_RANGE(v, min, max) do { \ - if ((v) < (min) || (v) > (max)) { \ - torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ - "got %d - should be between %d and %d\n", \ - __location__, #v, (int)v, (int)min, (int)max); \ - ret = false; \ - }} while (0) - -#define CHECK_STRMATCH(v, correct) do { \ - if (!v || strstr((v),(correct)) == NULL) { \ - torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s "\ - "got '%s' - should be '%s'\n", \ - __location__, #v, v?v:"NULL", correct); \ - ret = false; \ - }} while (0) - -#define CHECK_VAL(v, correct) do { \ - if ((v) != (correct)) { \ - torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ - "got 0x%x - should be 0x%x\n", \ - __location__, #v, (int)v, (int)correct); \ - ret = false; \ - }} while (0) - -#define BASEDIR "oplock_test" - -static struct { - struct smb2_handle handle; - uint8_t level; - struct smb2_break br; - int count; - int failures; - NTSTATUS failure_status; -} break_info; - -static void torture_oplock_break_callback(struct smb2_request *req) -{ - NTSTATUS status; - struct smb2_break br; - - ZERO_STRUCT(br); - status = smb2_break_recv(req, &break_info.br); - if (!NT_STATUS_IS_OK(status)) { - break_info.failures++; - break_info.failure_status = status; - } - - return; -} - -/* A general oplock break notification handler. This should be used when a - * test expects to break from batch or exclusive to a lower level. */ -static bool torture_oplock_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++; - } - printf("Acking to %s [0x%02X] in oplock handler\n", name, level); - - 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; - - req = smb2_break_send(tree, &break_info.br); - req->async.fn = torture_oplock_break_callback; - req->async.private_data = NULL; - return true; -} - -/* - A handler function for oplock break notifications. Send a break to none - request. -*/ -static bool torture_oplock_handler_ack_to_none(struct smb2_transport *transport, - const struct smb2_handle *handle, - uint8_t level, - void *private_data) -{ - struct smb2_tree *tree = private_data; - struct smb2_request *req; - - break_info.handle = *handle; - break_info.level = level; - break_info.count++; - - printf("Acking to none in oplock handler\n"); - - ZERO_STRUCT(break_info.br); - break_info.br.in.file.handle = *handle; - break_info.br.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; - break_info.br.in.reserved = 0; - break_info.br.in.reserved2 = 0; - - req = smb2_break_send(tree, &break_info.br); - req->async.fn = torture_oplock_break_callback; - req->async.private_data = NULL; - - return true; -} - -/* - A handler function for oplock break notifications. Break from level II to - none. SMB2 requires that the client does not send an oplock break request to - the server in this case. -*/ -static bool torture_oplock_handler_level2_to_none( - struct smb2_transport *transport, - const struct smb2_handle *handle, - uint8_t level, - void *private_data) -{ - struct smb2_tree *tree = private_data; - - break_info.handle = *handle; - break_info.level = level; - break_info.count++; - - printf("Break from level II to none in oplock handler\n"); - - return true; -} - -/* A handler function for oplock break notifications. This should be used when - * test expects two break notifications, first to level II, then to none. */ -static bool torture_oplock_handler_two_notifications( - 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++; - } - printf("Breaking to %s [0x%02X] in oplock handler\n", name, level); - - if (level == SMB2_OPLOCK_LEVEL_NONE) - return true; - - 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; - - req = smb2_break_send(tree, &break_info.br); - req->async.fn = torture_oplock_break_callback; - req->async.private_data = NULL; - return true; -} -static void torture_oplock_handler_close_recv(struct smb2_request *req) -{ - if (!smb2_request_receive(req)) { - printf("close failed in oplock_handler_close\n"); - break_info.failures++; - } -} - -/* - a handler function for oplock break requests - close the file -*/ -static bool torture_oplock_handler_close(struct smb2_transport *transport, - const struct smb2_handle *handle, - uint8_t level, - void *private_data) -{ - struct smb2_close io; - struct smb2_tree *tree = private_data; - struct smb2_request *req; - - break_info.handle = *handle; - break_info.level = level; - break_info.count++; - - ZERO_STRUCT(io); - io.in.file.handle = *handle; - io.in.flags = RAW_CLOSE_SMB2; - req = smb2_close_send(tree, &io); - if (req == NULL) { - printf("failed to send close in oplock_handler_close\n"); - return false; - } - - req->async.fn = torture_oplock_handler_close_recv; - req->async.private_data = NULL; - - return true; -} - -/* - a handler function for oplock break requests. Let it timeout -*/ -static bool torture_oplock_handler_timeout(struct smb2_transport *transport, - const struct smb2_handle *handle, - uint8_t level, - void *private_data) -{ - break_info.handle = *handle; - break_info.level = level; - break_info.count++; - - printf("Let oplock break timeout\n"); - return true; -} - -static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx, - struct smb2_tree **tree) -{ - NTSTATUS status; - 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); - options.use_level2_oplocks = false; - - status = smb2_connect(tctx, host, - lp_smb_ports(tctx->lp_ctx), share, - lp_resolve_context(tctx->lp_ctx), - credentials, tree, tctx->ev, &options, - lp_socket_options(tctx->lp_ctx), - lp_gensec_settings(tctx, tctx->lp_ctx)); - if (!NT_STATUS_IS_OK(status)) { - torture_comment(tctx, "Failed to connect to SMB2 share " - "\\\\%s\\%s - %s\n", host, share, - nt_errstr(status)); - return false; - } - 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; - return; -} - -/* - Wait a short period of time to receive a single oplock break request -*/ -static 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 seconds for an oplock break */ - ne = tevent_timeval_current_ofs(0, 100000); - - if ((te = event_add_timed(tctx->ev, tmp_ctx, ne, timeout_cb, ×up)) - == NULL) - { - torture_comment(tctx, "Failed to wait for an oplock break. " - "test results may not be accurate."); - goto done; - } - - while (!timesup && break_info.count < old_count + 1) { - if (event_loop_once(tctx->ev) != 0) { - torture_comment(tctx, "Failed to wait for an oplock " - "break. test results may not be " - "accurate."); - goto done; - } - } - -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); - - return; -} - -static bool test_smb2_oplock_exclusive1(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_exclusive1.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_unlink unl; - struct smb2_handle h1; - struct smb2_handle h; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive " - "oplock (share mode: none)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - torture_comment(tctx, "a 2nd open should not cause a break\n"); - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - torture_comment(tctx, "unlink it - should also be no break\n"); - unl.unlink.in.pattern = fname; - unl.unlink.in.attrib = 0; - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_exclusive2(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_exclusive2.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_unlink unl; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive " - "oplock (share mode: all)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - torture_comment(tctx, "a 2nd open should cause a break to level 2\n"); - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h2 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - ZERO_STRUCT(break_info); - - /* now we have 2 level II oplocks... */ - torture_comment(tctx, "try to unlink it - should cause a break\n"); - unl.unlink.in.pattern = fname; - unl.unlink.in.attrib = 0; - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_ok(tctx, status, "Error unlinking the file"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - torture_comment(tctx, "close both handles\n"); - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_exclusive3(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_exclusive3.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_setfileinfo sfi; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive " - "oplock (share mode: none)\n"); - - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - torture_comment(tctx, "setpathinfo EOF should trigger a break to " - "none\n"); - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; - sfi.generic.in.file.path = fname; - sfi.end_of_file_info.in.size = 100; - - status = smb2_composite_setpathinfo(tree2, &sfi); - - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_exclusive4(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_exclusive4.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - ZERO_STRUCT(break_info); - torture_comment(tctx, "second open with attributes only shouldn't " - "cause oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, NO_OPLOCK_RETURN); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_exclusive5(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_exclusive5.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "second open with attributes only and " - "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " - "oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_exclusive6(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname1 = BASEDIR "\\test_exclusive6_1.dat"; - const char *fname2 = BASEDIR "\\test_exclusive6_2.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_setfileinfo sinfo; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname1); - smb2_util_unlink(tree2, fname2); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname1; - - torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive " - "oplock (share mode: none)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - - torture_comment(tctx, "rename should not generate a break but get " - "a sharing violation\n"); - ZERO_STRUCT(sinfo); - sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; - sinfo.rename_information.in.file.handle = h1; - sinfo.rename_information.in.overwrite = true; - sinfo.rename_information.in.new_name = fname2; - status = smb2_setinfo_file(tree1, &sinfo); - - torture_comment(tctx, "trying rename while first file open\n"); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch1(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch1.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - char c = 0; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "BATCH1: open with batch oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "unlink should generate a break\n"); - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - torture_comment(tctx, "2nd unlink should not generate a break\n"); - ZERO_STRUCT(break_info); - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - torture_comment(tctx, "writing should generate a self break to none\n"); - tree1->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - smb2_util_write(tree1, h1, &c, 0, 1); - - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch2(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch2.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - char c = 0; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH2: open with batch oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "unlink should generate a break, which we ack " - "as break to none\n"); - tree1->session->transport->oplock.handler = - torture_oplock_handler_ack_to_none; - tree1->session->transport->oplock.private_data = tree1; - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - torture_comment(tctx, "2nd unlink should not generate a break\n"); - ZERO_STRUCT(break_info); - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - torture_comment(tctx, "writing should not generate a break\n"); - smb2_util_write(tree1, h1, &c, 0, 1); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch3(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch3.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH3: if we close on break then the unlink " - "can succeed\n"); - ZERO_STRUCT(break_info); - tree1->session->transport->oplock.handler = - torture_oplock_handler_close; - tree1->session->transport->oplock.private_data = tree1; - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - status = smb2_util_unlink(tree2, fname); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch4(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch4.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_read r; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH4: a self read should not cause a break\n"); - ZERO_STRUCT(break_info); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(r); - r.in.file.handle = h1; - r.in.offset = 0; - - status = smb2_read(tree1, tree1, &r); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch5(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch5.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH5: a 2nd open should give a break\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch6(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch6.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - char c = 0; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH6: a 2nd open should give a break to " - "level II if the first open allowed shared read\n"); - ZERO_STRUCT(break_info); - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, 1); - CHECK_VAL(break_info.failures, 0); - ZERO_STRUCT(break_info); - - torture_comment(tctx, "write should trigger a break to none on both\n"); - tree1->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - tree2->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - smb2_util_write(tree1, h1, &c, 0, 1); - - /* We expect two breaks */ - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 2); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch7(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch7.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when " - "we close instead of ack\n"); - ZERO_STRUCT(break_info); - tree1->session->transport->oplock.handler = - torture_oplock_handler_close; - tree1->session->transport->oplock.private_data = tree1; - - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h2.data[0]); - CHECK_VAL(break_info.level, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree2, h1); - smb2_util_close(tree2, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch8(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch8.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH8: open with batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - torture_comment(tctx, "second open with attributes only shouldn't " - "cause oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch9(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch9.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - char c = 0; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH9: open with attributes only can create " - "file\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "Subsequent normal open should break oplock on " - "attribute only open to level II\n"); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - smb2_util_close(tree2, h2); - - torture_comment(tctx, "third oplocked open should grant level2 without " - "break\n"); - ZERO_STRUCT(break_info); - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "write should trigger a break to none on both\n"); - tree1->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - tree2->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - smb2_util_write(tree2, h2, &c, 0, 1); - - /* We expect two breaks */ - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 2); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch10(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch10.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock " - "open should grant level2\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, 0); - - tree2->session->transport->oplock.handler = - torture_oplock_handler_level2_to_none; - tree2->session->transport->oplock.private_data = tree2; - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_comment(tctx, "write should trigger a break to none\n"); - { - struct smb2_write wr; - DATA_BLOB data; - data = data_blob_talloc(tree1, NULL, UINT16_MAX); - data.data[0] = (const uint8_t)'x'; - ZERO_STRUCT(wr); - wr.in.file.handle = h1; - wr.in.offset = 0; - wr.in.data = data; - status = smb2_write(tree1, &wr); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - } - - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h2.data[0]); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch11(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch11.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_setfileinfo sfi; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = - torture_oplock_handler_two_notifications; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* Test if a set-eof on pathname breaks an exclusive oplock. */ - torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks " - "oplocks.\n"); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; - sfi.generic.in.file.path = fname; - sfi.end_of_file_info.in.size = 100; - - status = smb2_composite_setpathinfo(tree2, &sfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - /* We expect two breaks */ - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 2); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.level, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch12(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch12.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_setfileinfo sfi; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = - torture_oplock_handler_two_notifications; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* Test if a set-allocation size on pathname breaks an exclusive - * oplock. */ - torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size " - "breaks oplocks.\n"); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_ALLOCATION_INFORMATION; - sfi.generic.in.file.path = fname; - sfi.allocation_info.in.alloc_size = 65536 * 8; - - status = smb2_composite_setpathinfo(tree2, &sfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - /* We expect two breaks */ - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - - CHECK_VAL(break_info.count, 2); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.level, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch13(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch13.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH13: open with batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "second open with attributes only and " - "NTCREATEX_DISP_OVERWRITE dispostion causes " - "oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - - return ret; -} - -static bool test_smb2_oplock_batch14(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch14.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH14: open with batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "second open with attributes only and " - "NTCREATEX_DISP_SUPERSEDE dispostion causes " - "oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch15(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch15.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_fileinfo qfi; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* Test if a qpathinfo all info on pathname breaks a batch oplock. */ - torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks " - "a batch oplock (should not).\n"); - - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(qfi); - qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; - qfi.generic.in.file.handle = h1; - status = smb2_getinfo_file(tree2, tctx, &qfi); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch16(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch16.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH16: open with batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "second open with attributes only and " - "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " - "oplock break\n"); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | - SEC_FILE_WRITE_ATTRIBUTE | - SEC_STD_SYNCHRONIZE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH17 test. Since - * SMB2 doesn't have a RENAME command this test isn't applicable. However, - * it's much less confusing, when comparing test, to keep the SMB1 and SMB2 - * test numbers in sync. */ -#if 0 -static bool test_raw_oplock_batch17(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - return true; -} -#endif - -/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH18 test. Since - * SMB2 doesn't have an NTRENAME command this test isn't applicable. However, - * it's much less confusing, when comparing tests, to keep the SMB1 and SMB2 - * test numbers in sync. */ -#if 0 -static bool test_raw_oplock_batch18(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - return true; -} -#endif - -static bool test_smb2_oplock_batch19(struct torture_context *tctx, - struct smb2_tree *tree1) -{ - const char *fname1 = BASEDIR "\\test_batch19_1.dat"; - const char *fname2 = BASEDIR "\\test_batch19_2.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_fileinfo qfi; - union smb_setfileinfo sfi; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname1); - smb2_util_unlink(tree1, fname2); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname1; - - torture_comment(tctx, "BATCH19: open a file with an batch oplock " - "(share mode: none)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "setfileinfo rename info should not trigger " - "a break but should cause a sharing violation\n"); - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; - sfi.generic.in.file.path = fname1; - sfi.rename_information.in.file.handle = h1; - sfi.rename_information.in.overwrite = 0; - sfi.rename_information.in.root_fid = 0; - sfi.rename_information.in.new_name = fname2; - - status = smb2_setinfo_file(tree1, &sfi); - - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - ZERO_STRUCT(qfi); - qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; - qfi.generic.in.file.handle = h1; - - status = smb2_getinfo_file(tree1, tctx, &qfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, fname1); - smb2_deltree(tree1, fname2); - return ret; -} - -static bool test_smb2_oplock_batch20(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname1 = BASEDIR "\\test_batch20_1.dat"; - const char *fname2 = BASEDIR "\\test_batch20_2.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - union smb_fileinfo qfi; - union smb_setfileinfo sfi; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname1); - smb2_util_unlink(tree1, fname2); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname1; - - torture_comment(tctx, "BATCH20: open a file with an batch oplock " - "(share mode: all)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "setfileinfo rename info should not trigger " - "a break but should cause a sharing violation\n"); - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; - sfi.rename_information.in.file.handle = h1; - sfi.rename_information.in.overwrite = 0; - sfi.rename_information.in.new_name = fname2; - - status = smb2_setinfo_file(tree1, &sfi); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - ZERO_STRUCT(qfi); - qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; - qfi.generic.in.file.handle = h1; - - status = smb2_getinfo_file(tree1, tctx, &qfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); - - torture_comment(tctx, "open the file a second time requesting batch " - "(share mode: all)\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - io.smb2.in.fname = fname1; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - - torture_comment(tctx, "setfileinfo rename info should not trigger " - "a break but should cause a sharing violation\n"); - ZERO_STRUCT(break_info); - ZERO_STRUCT(sfi); - sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; - sfi.rename_information.in.file.handle = h2; - sfi.rename_information.in.overwrite = 0; - sfi.rename_information.in.new_name = fname2; - - status = smb2_setinfo_file(tree2, &sfi); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - ZERO_STRUCT(qfi); - qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; - qfi.generic.in.file.handle = h1; - - status = smb2_getinfo_file(tree1, tctx, &qfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); - - ZERO_STRUCT(qfi); - qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; - qfi.generic.in.file.handle = h2; - - status = smb2_getinfo_file(tree2, tctx, &qfi); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, fname1); - return ret; -} - -static bool test_smb2_oplock_batch21(struct torture_context *tctx, - struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_batch21.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - char c = 0; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "BATCH21: open with batch oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "writing should not generate a break\n"); - status = smb2_util_write(tree1, h1, &c, 0, 1); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch22(struct torture_context *tctx, - struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_batch22.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - struct timeval tv; - int timeout = torture_setting_int(tctx, "oplocktimeout", 30); - int te; - - if (torture_setting_bool(tctx, "samba3", false)) { - torture_skip(tctx, "BATCH22 disabled against samba3\n"); - } - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "BATCH22: open with batch oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| - NTCREATEX_SHARE_ACCESS_WRITE| - NTCREATEX_SHARE_ACCESS_DELETE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "a 2nd open should succeed after the oplock " - "break timeout\n"); - tv = timeval_current(); - tree1->session->transport->oplock.handler = - torture_oplock_handler_timeout; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - te = (int)timeval_elapsed(&tv); - CHECK_RANGE(te, timeout - 1, timeout + 15); - torture_comment(tctx, "waited %d seconds for oplock timeout\n", te); - - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h2); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch23(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch23.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2, h3; - struct smb2_tree *tree3 = NULL; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); - CHECK_VAL(ret, true); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - tree3->session->transport->oplock.handler = torture_oplock_handler; - tree3->session->transport->oplock.private_data = tree3; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH23: an open and ask for a batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a 2nd open without level2 oplock support " - "should generate a break to level2\n"); - status = smb2_create(tree3, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h3 = io.smb2.out.file.handle; - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a 3rd open with level2 oplock support should " - "not generate a break\n"); - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree3, h3); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch24(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch24.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1, h2; - struct smb2_tree *tree3 = NULL; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); - CHECK_VAL(ret, true); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - tree3->session->transport->oplock.handler = torture_oplock_handler; - tree3->session->transport->oplock.private_data = tree3; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH24: a open without level support and " - "ask for a batch oplock\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree3, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h2 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a 2nd open with level2 oplock support should " - "generate a break\n"); - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.handle.data[0], h2.data[0]); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree3, h2); - smb2_util_close(tree2, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_batch25(struct torture_context *tctx, - struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_batch25.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "BATCH25: open a file with an batch oplock " - "(share mode: none)\n"); - - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - torture_comment(tctx, "changing the file attribute info should trigger " - "a break and a violation\n"); - - status = smb2_util_setatr(tree1, fname, FILE_ATTRIBUTE_HIDDEN); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, fname); - return ret; -} - -/* Test how oplocks work on streams. */ -static bool test_raw_oplock_stream1(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - NTSTATUS status; - union smb_open io; - const char *fname_base = BASEDIR "\\test_stream1.txt"; - const char *fname_stream, *fname_default_stream; - const char *default_stream = "::$DATA"; - const char *stream = "Stream One:$DATA"; - bool ret = true; - struct smb2_handle h, h_base, h_stream; - int i; - -#define NSTREAM_OPLOCK_RESULTS 8 - struct { - const char **fname; - bool open_base_file; - uint32_t oplock_req; - uint32_t oplock_granted; - } stream_oplock_results[NSTREAM_OPLOCK_RESULTS] = { - /* Request oplock on stream without the base file open. */ - {&fname_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, - {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, - {&fname_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, - {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, - - /* Request oplock on stream with the base file open. */ - {&fname_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, - {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_II}, - {&fname_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, - {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_II}, - }; - - /* Only passes against windows at the moment. */ - if (torture_setting_bool(tctx, "samba3", false) || - torture_setting_bool(tctx, "samba4", false)) { - torture_skip(tctx, "STREAM1 disabled against samba3+4\n"); - } - - fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); - fname_default_stream = talloc_asprintf(tctx, "%s%s", fname_base, - default_stream); - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* Initialize handles to "closed". Using -1 in the first 64-bytes - * as the sentry for this */ - h_stream.data[0] = -1; - - /* cleanup */ - smb2_util_unlink(tree1, fname_base); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - tree2->session->transport->oplock.handler = torture_oplock_handler; - tree2->session->transport->oplock.private_data = tree2; - - /* Setup generic open parameters. */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = (SEC_FILE_READ_DATA | - SEC_FILE_WRITE_DATA | - SEC_FILE_APPEND_DATA | - SEC_STD_READ_CONTROL); - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - - /* Create the file with a stream */ - io.smb2.in.fname = fname_stream; - io.smb2.in.create_flags = 0; - io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error creating file"); - smb2_util_close(tree1, io.smb2.out.file.handle); - - /* Change the disposition to open now that the file has been created. */ - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; - - /* Try some permutations of taking oplocks on streams. */ - for (i = 0; i < NSTREAM_OPLOCK_RESULTS; i++) { - const char *fname = *stream_oplock_results[i].fname; - bool open_base_file = stream_oplock_results[i].open_base_file; - uint32_t oplock_req = stream_oplock_results[i].oplock_req; - uint32_t oplock_granted = - stream_oplock_results[i].oplock_granted; - - if (open_base_file) { - torture_comment(tctx, "Opening base file: %s with " - "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); - io.smb2.in.fname = fname_base; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, - "Error opening file"); - CHECK_VAL(io.smb2.out.oplock_level, - SMB2_OPLOCK_LEVEL_BATCH); - h_base = io.smb2.out.file.handle; - } - - torture_comment(tctx, "%d: Opening stream: %s with %d\n", i, - fname, oplock_req); - io.smb2.in.fname = fname; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = oplock_req; - - /* Do the open with the desired oplock on the stream. */ - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening file"); - CHECK_VAL(io.smb2.out.oplock_level, oplock_granted); - smb2_util_close(tree1, io.smb2.out.file.handle); - - /* Cleanup the base file if it was opened. */ - if (open_base_file) - smb2_util_close(tree2, h_base); - } - - /* Open the stream with an exclusive oplock. */ - torture_comment(tctx, "Opening stream: %s with %d\n", - fname_stream, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - io.smb2.in.fname = fname_stream; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening file"); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); - h_stream = io.smb2.out.file.handle; - - /* Open the base file and see if it contends. */ - ZERO_STRUCT(break_info); - torture_comment(tctx, "Opening base file: %s with %d\n", - fname_base, SMB2_OPLOCK_LEVEL_BATCH); - io.smb2.in.fname = fname_base; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening file"); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - smb2_util_close(tree2, io.smb2.out.file.handle); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - - /* Open the stream again to see if it contends. */ - ZERO_STRUCT(break_info); - torture_comment(tctx, "Opening stream again: %s with " - "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); - io.smb2.in.fname = fname_stream; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - status = smb2_create(tree2, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening file"); - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - smb2_util_close(tree2, io.smb2.out.file.handle); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - - /* Close the stream. */ - if (h_stream.data[0] != -1) { - smb2_util_close(tree1, h_stream); - } - - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -static bool test_smb2_oplock_doc(struct torture_context *tctx, struct smb2_tree *tree) -{ - const char *fname = BASEDIR "\\test_oplock_doc.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - - status = torture_smb2_testdir(tree, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree, fname); - tree->session->transport->oplock.handler = torture_oplock_handler; - tree->session->transport->oplock.private_data = tree; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "open a delete-on-close file with a batch " - "oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - smb2_util_close(tree, h1); - - smb2_util_unlink(tree, fname); - smb2_deltree(tree, BASEDIR); - return ret; -} - -/* Open a file with a batch oplock, then open it again from a second client - * requesting no oplock. Having two open file handles should break our own - * oplock during BRL acquisition. - */ -static bool test_smb2_oplock_brl1(struct torture_context *tctx, - struct smb2_tree *tree1, - struct smb2_tree *tree2) -{ - const char *fname = BASEDIR "\\test_batch_brl.dat"; - /*int fname, f;*/ - bool ret = true; - uint8_t buf[1000]; - bool correct = true; - union smb_open io; - NTSTATUS status; - struct smb2_lock lck; - struct smb2_lock_element lock[1]; - struct smb2_handle h, h1, h2; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = - torture_oplock_handler_two_notifications; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "open with batch oplock\n"); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - /* create a file with bogus data */ - memset(buf, 0, sizeof(buf)); - - status = smb2_util_write(tree1, h1,buf, 0, sizeof(buf)); - if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { - torture_comment(tctx, "Failed to create file\n"); - correct = false; - goto done; - } - - torture_comment(tctx, "a 2nd open should give a break\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = 0; - status = smb2_create(tree2, tctx, &(io.smb2)); - h2 = io.smb2.out.file.handle; - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a self BRL acquisition should break to none\n"); - lock[0].offset = 0; - lock[0].length = 4; - lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | - SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - - ZERO_STRUCT(lck); - lck.in.file.handle = h1; - lck.in.locks = &lock[0]; - lck.in.lock_count = 1; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.failures, 0); - - /* expect no oplock break */ - ZERO_STRUCT(break_info); - lock[0].offset = 2; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree2, h2); - smb2_util_close(tree1, h); - -done: - smb2_deltree(tree1, BASEDIR); - return ret; - -} - -/* Open a file with a batch oplock on one tree and then acquire a brl. - * We should not contend our own oplock. - */ -static bool test_smb2_oplock_brl2(struct torture_context *tctx, struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_batch_brl.dat"; - /*int fname, f;*/ - bool ret = true; - uint8_t buf[1000]; - bool correct = true; - union smb_open io; - NTSTATUS status; - struct smb2_handle h, h1; - struct smb2_lock lck; - struct smb2_lock_element lock[1]; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "open with batch oplock\n"); - ZERO_STRUCT(break_info); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - /* create a file with bogus data */ - memset(buf, 0, sizeof(buf)); - - status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); - if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { - torture_comment(tctx, "Failed to create file\n"); - correct = false; - goto done; - } - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a self BRL acquisition should not break to " - "none\n"); - - lock[0].offset = 0; - lock[0].length = 4; - lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | - SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - - ZERO_STRUCT(lck); - lck.in.file.handle = h1; - lck.in.locks = &lock[0]; - lck.in.lock_count = 1; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - lock[0].offset = 2; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, - "Incorrect status"); - - /* With one file handle open a BRL should not contend our oplock. - * Thus, no oplock break will be received and the entire break_info - * struct will be 0 */ - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - -done: - smb2_deltree(tree1, BASEDIR); - return ret; -} - -/* Open a file with a batch oplock twice from one tree and then acquire a - * brl. BRL acquisition should break our own oplock. - */ -static bool test_smb2_oplock_brl3(struct torture_context *tctx, struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_batch_brl.dat"; - bool ret = true; - uint8_t buf[1000]; - bool correct = true; - union smb_open io; - NTSTATUS status; - struct smb2_handle h, h1, h2; - struct smb2_lock lck; - struct smb2_lock_element lock[1]; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - tree1->session->transport->oplock.handler = - torture_oplock_handler_two_notifications; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - /* - with a batch oplock we get a break - */ - torture_comment(tctx, "open with batch oplock\n"); - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); - - /* create a file with bogus data */ - memset(buf, 0, sizeof(buf)); - status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); - - if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { - torture_comment(tctx, "Failed to create file\n"); - correct = false; - goto done; - } - - torture_comment(tctx, "a 2nd open should give a break\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = 0; - status = smb2_create(tree1, tctx, &(io.smb2)); - h2 = io.smb2.out.file.handle; - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); - CHECK_VAL(break_info.failures, 0); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "a self BRL acquisition should break to none\n"); - - lock[0].offset = 0; - lock[0].length = 4; - lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | - SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - - ZERO_STRUCT(lck); - lck.in.file.handle = h1; - lck.in.locks = &lock[0]; - lck.in.lock_count = 1; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); - CHECK_VAL(break_info.handle.data[0], h1.data[0]); - CHECK_VAL(break_info.failures, 0); - - /* expect no oplock break */ - ZERO_STRUCT(break_info); - lock[0].offset = 2; - status = smb2_lock(tree1, &lck); - torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, - "Incorrect status"); - - torture_wait_for_oplock_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 0); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h2); - smb2_util_close(tree1, h); - -done: - smb2_deltree(tree1, BASEDIR); - return ret; - -} - -/* Starting the SMB2 specific oplock tests at 500 so we can keep the SMB1 - * tests in sync with an identically numbered SMB2 test */ - -/* Test whether the server correctly returns an error when we send - * a response to a levelII to none oplock notification. */ -static bool test_smb2_oplock_levelII500(struct torture_context *tctx, - struct smb2_tree *tree1) -{ - const char *fname = BASEDIR "\\test_levelII500.dat"; - NTSTATUS status; - bool ret = true; - union smb_open io; - struct smb2_handle h, h1; - char c = 0; - - status = torture_smb2_testdir(tree1, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - /* cleanup */ - smb2_util_unlink(tree1, fname); - - tree1->session->transport->oplock.handler = torture_oplock_handler; - tree1->session->transport->oplock.private_data = tree1; - - /* - base ntcreatex parms - */ - ZERO_STRUCT(io.smb2); - io.generic.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = fname; - - torture_comment(tctx, "LEVELII500: acknowledging a break from II to " - "none should return an error\n"); - ZERO_STRUCT(break_info); - - io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | - SEC_RIGHTS_FILE_WRITE; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; - status = smb2_create(tree1, tctx, &(io.smb2)); - torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); - h1 = io.smb2.out.file.handle; - CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); - - ZERO_STRUCT(break_info); - - torture_comment(tctx, "write should trigger a break to none and when " - "we reply, an oplock break failure\n"); - smb2_util_write(tree1, h1, &c, 0, 1); - - /* Wait several times to receive both the break notification, and the - * NT_STATUS_INVALID_OPLOCK_PROTOCOL error in the break response */ - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - torture_wait_for_oplock_break(tctx); - - /* There appears to be a race condition in W2K8 and W2K8R2 where - * sometimes the server will happily reply to our break response with - * NT_STATUS_OK, and sometimes it will return the OPLOCK_PROTOCOL - * error. As the MS-SMB2 doc states that a client should not reply to - * a level2 to none break notification, I'm leaving the protocol error - * as the expected behavior. */ - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.level, 0); - CHECK_VAL(break_info.failures, 1); - torture_assert_ntstatus_equal(tctx, break_info.failure_status, - NT_STATUS_INVALID_OPLOCK_PROTOCOL, - "Incorrect status"); - - smb2_util_close(tree1, h1); - smb2_util_close(tree1, h); - - smb2_deltree(tree1, BASEDIR); - return ret; -} - -struct torture_suite *torture_smb2_oplocks_init(void) -{ - struct torture_suite *suite = - torture_suite_create(talloc_autofree_context(), "OPLOCK"); - - torture_suite_add_2smb2_test(suite, "EXCLUSIVE1", test_smb2_oplock_exclusive1); - torture_suite_add_2smb2_test(suite, "EXCLUSIVE2", test_smb2_oplock_exclusive2); - torture_suite_add_2smb2_test(suite, "EXCLUSIVE3", test_smb2_oplock_exclusive3); - torture_suite_add_2smb2_test(suite, "EXCLUSIVE4", test_smb2_oplock_exclusive4); - torture_suite_add_2smb2_test(suite, "EXCLUSIVE5", test_smb2_oplock_exclusive5); - torture_suite_add_2smb2_test(suite, "EXCLUSIVE6", test_smb2_oplock_exclusive6); - torture_suite_add_2smb2_test(suite, "BATCH1", test_smb2_oplock_batch1); - torture_suite_add_2smb2_test(suite, "BATCH2", test_smb2_oplock_batch2); - torture_suite_add_2smb2_test(suite, "BATCH3", test_smb2_oplock_batch3); - torture_suite_add_2smb2_test(suite, "BATCH4", test_smb2_oplock_batch4); - torture_suite_add_2smb2_test(suite, "BATCH5", test_smb2_oplock_batch5); - torture_suite_add_2smb2_test(suite, "BATCH6", test_smb2_oplock_batch6); - torture_suite_add_2smb2_test(suite, "BATCH7", test_smb2_oplock_batch7); - torture_suite_add_2smb2_test(suite, "BATCH8", test_smb2_oplock_batch8); - torture_suite_add_2smb2_test(suite, "BATCH9", test_smb2_oplock_batch9); - torture_suite_add_2smb2_test(suite, "BATCH10", test_smb2_oplock_batch10); - torture_suite_add_2smb2_test(suite, "BATCH11", test_smb2_oplock_batch11); - torture_suite_add_2smb2_test(suite, "BATCH12", test_smb2_oplock_batch12); - torture_suite_add_2smb2_test(suite, "BATCH13", test_smb2_oplock_batch13); - torture_suite_add_2smb2_test(suite, "BATCH14", test_smb2_oplock_batch14); - torture_suite_add_2smb2_test(suite, "BATCH15", test_smb2_oplock_batch15); - torture_suite_add_2smb2_test(suite, "BATCH16", test_smb2_oplock_batch16); - torture_suite_add_1smb2_test(suite, "BATCH19", test_smb2_oplock_batch19); - torture_suite_add_2smb2_test(suite, "BATCH20", test_smb2_oplock_batch20); - torture_suite_add_1smb2_test(suite, "BATCH21", test_smb2_oplock_batch21); - torture_suite_add_1smb2_test(suite, "BATCH22", test_smb2_oplock_batch22); - torture_suite_add_2smb2_test(suite, "BATCH23", test_smb2_oplock_batch23); - torture_suite_add_2smb2_test(suite, "BATCH24", test_smb2_oplock_batch24); - torture_suite_add_1smb2_test(suite, "BATCH25", test_smb2_oplock_batch25); - torture_suite_add_2smb2_test(suite, "STREAM1", test_raw_oplock_stream1); - torture_suite_add_1smb2_test(suite, "DOC", test_smb2_oplock_doc); - torture_suite_add_2smb2_test(suite, "BRL1", test_smb2_oplock_brl1); - torture_suite_add_1smb2_test(suite, "BRL2", test_smb2_oplock_brl2); - torture_suite_add_1smb2_test(suite, "BRL3", test_smb2_oplock_brl3); - torture_suite_add_1smb2_test(suite, "LEVELII500", test_smb2_oplock_levelII500); - - suite->description = talloc_strdup(suite, "SMB2-OPLOCK tests"); - - return suite; -} - -/* - stress testing of oplocks -*/ -bool test_smb2_bench_oplock(struct torture_context *tctx, - struct smb2_tree *tree) -{ - struct smb2_tree **trees; - bool ret = true; - NTSTATUS status; - TALLOC_CTX *mem_ctx = talloc_new(tctx); - int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); - int i, count=0; - int timelimit = torture_setting_int(tctx, "timelimit", 10); - union smb_open io; - struct timeval tv; - struct smb2_handle h; - - trees = talloc_array(mem_ctx, struct smb2_tree *, torture_nprocs); - - torture_comment(tctx, "Opening %d connections\n", torture_nprocs); - for (i=0;isession->transport->oplock.handler = - torture_oplock_handler_close; - trees[i]->session->transport->oplock.private_data = trees[i]; - } - - status = torture_smb2_testdir(trees[0], BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - ZERO_STRUCT(io.smb2); - io.smb2.level = RAW_OPEN_SMB2; - io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.smb2.in.alloc_size = 0; - io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; - io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; - io.smb2.in.create_options = 0; - io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; - io.smb2.in.security_flags = 0; - io.smb2.in.fname = BASEDIR "\\test.dat"; - io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; - io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; - - tv = timeval_current(); - - /* - we open the same file with SHARE_ACCESS_NONE from all the - connections in a round robin fashion. Each open causes an - oplock break on the previous connection, which is answered - by the oplock_handler_close() to close the file. - - This measures how fast we can pass on oplocks, and stresses - the oplock handling code - */ - torture_comment(tctx, "Running for %d seconds\n", timelimit); - while (timeval_elapsed(&tv) < timelimit) { - for (i=0;idata[0], handle->data[1]); - return false; - } - - info = &hold_info[i]; - - if (info->close_on_break) { - printf("oplock break on %s - closing\n", info->fname); - torture_oplock_handler_close(transport, handle, - level, private_data); - return true; - } - - printf("oplock break on %s - acking break\n", info->fname); - printf("Acking to none in oplock handler\n"); - - torture_oplock_handler_ack_to_none(transport, handle, - level, private_data); - return true; -} - -/* - used for manual testing of oplocks - especially interaction with - other filesystems (such as NFS and local access) -*/ -bool test_smb2_hold_oplock(struct torture_context *tctx, - struct smb2_tree *tree) -{ - struct torture_context *mem_ctx = talloc_new(tctx); - struct tevent_context *ev = - (struct tevent_context *)tree->session->transport->socket->event.ctx; - int i; - struct smb2_handle h; - NTSTATUS status; - - torture_comment(tctx, "Setting up open files with oplocks in %s\n", - BASEDIR); - - status = torture_smb2_testdir(tree, BASEDIR, &h); - torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); - - tree->session->transport->oplock.handler = torture_oplock_handler_hold; - tree->session->transport->oplock.private_data = tree; - - /* setup the files */ - for (i=0;i Date: Thu, 1 Oct 2009 16:38:40 -0700 Subject: s4/torture: second try on renaming oplocks.c to oplock.c Forgot to "git add" the new file in commit b2bcfaae --- source4/torture/smb2/oplock.c | 3617 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3617 insertions(+) create mode 100644 source4/torture/smb2/oplock.c (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c new file mode 100644 index 0000000000..f686de6a2f --- /dev/null +++ b/source4/torture/smb2/oplock.c @@ -0,0 +1,3617 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 oplocks + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) Stefan Metzmacher 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 "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/resolve/resolve.h" + +#include "lib/cmdline/popt_common.h" +#include "lib/events/events.h" + +#include "param/param.h" +#include "system/filesys.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" + +#define CHECK_RANGE(v, min, max) do { \ + if ((v) < (min) || (v) > (max)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got %d - should be between %d and %d\n", \ + __location__, #v, (int)v, (int)min, (int)max); \ + ret = false; \ + }} while (0) + +#define CHECK_STRMATCH(v, correct) do { \ + if (!v || strstr((v),(correct)) == NULL) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s "\ + "got '%s' - should be '%s'\n", \ + __location__, #v, v?v:"NULL", correct); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \ + "got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define BASEDIR "oplock_test" + +static struct { + struct smb2_handle handle; + uint8_t level; + struct smb2_break br; + int count; + int failures; + NTSTATUS failure_status; +} break_info; + +static void torture_oplock_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + ZERO_STRUCT(br); + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + break_info.failure_status = status; + } + + return; +} + +/* A general oplock break notification handler. This should be used when a + * test expects to break from batch or exclusive to a lower level. */ +static bool torture_oplock_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++; + } + printf("Acking to %s [0x%02X] in oplock handler\n", name, level); + + 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; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} + +/* + A handler function for oplock break notifications. Send a break to none + request. +*/ +static bool torture_oplock_handler_ack_to_none(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Acking to none in oplock handler\n"); + + ZERO_STRUCT(break_info.br); + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + + return true; +} + +/* + A handler function for oplock break notifications. Break from level II to + none. SMB2 requires that the client does not send an oplock break request to + the server in this case. +*/ +static bool torture_oplock_handler_level2_to_none( + struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_tree *tree = private_data; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Break from level II to none in oplock handler\n"); + + return true; +} + +/* A handler function for oplock break notifications. This should be used when + * test expects two break notifications, first to level II, then to none. */ +static bool torture_oplock_handler_two_notifications( + 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++; + } + printf("Breaking to %s [0x%02X] in oplock handler\n", name, level); + + if (level == SMB2_OPLOCK_LEVEL_NONE) + return true; + + 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; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private_data = NULL; + return true; +} +static void torture_oplock_handler_close_recv(struct smb2_request *req) +{ + if (!smb2_request_receive(req)) { + printf("close failed in oplock_handler_close\n"); + break_info.failures++; + } +} + +/* + a handler function for oplock break requests - close the file +*/ +static bool torture_oplock_handler_close(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + struct smb2_close io; + struct smb2_tree *tree = private_data; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = RAW_CLOSE_SMB2; + req = smb2_close_send(tree, &io); + if (req == NULL) { + printf("failed to send close in oplock_handler_close\n"); + return false; + } + + req->async.fn = torture_oplock_handler_close_recv; + req->async.private_data = NULL; + + return true; +} + +/* + a handler function for oplock break requests. Let it timeout +*/ +static bool torture_oplock_handler_timeout(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, + void *private_data) +{ + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + printf("Let oplock break timeout\n"); + return true; +} + +static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx, + struct smb2_tree **tree) +{ + NTSTATUS status; + 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); + options.use_level2_oplocks = false; + + status = smb2_connect(tctx, host, + lp_smb_ports(tctx->lp_ctx), share, + lp_resolve_context(tctx->lp_ctx), + credentials, tree, tctx->ev, &options, + lp_socket_options(tctx->lp_ctx), + lp_gensec_settings(tctx, tctx->lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "Failed to connect to SMB2 share " + "\\\\%s\\%s - %s\n", host, share, + nt_errstr(status)); + return false; + } + 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; + return; +} + +/* + Wait a short period of time to receive a single oplock break request +*/ +static 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 seconds for an oplock break */ + ne = tevent_timeval_current_ofs(0, 100000); + + if ((te = event_add_timed(tctx->ev, tmp_ctx, ne, timeout_cb, ×up)) + == NULL) + { + torture_comment(tctx, "Failed to wait for an oplock break. " + "test results may not be accurate."); + goto done; + } + + while (!timesup && break_info.count < old_count + 1) { + if (event_loop_once(tctx->ev) != 0) { + torture_comment(tctx, "Failed to wait for an oplock " + "break. test results may not be " + "accurate."); + goto done; + } + } + +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); + + return; +} + +static bool test_smb2_oplock_exclusive1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + struct smb2_handle h1; + struct smb2_handle h; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE1: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should not cause a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "unlink it - should also be no break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_unlink unl; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE2: open a file with an exclusive " + "oplock (share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "a 2nd open should cause a break to level 2\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + /* now we have 2 level II oplocks... */ + torture_comment(tctx, "try to unlink it - should cause a break\n"); + unl.unlink.in.pattern = fname; + unl.unlink.in.attrib = 0; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Error unlinking the file"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "close both handles\n"); + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE3: open a file with an exclusive " + "oplock (share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "setpathinfo EOF should trigger a break to " + "none\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE4: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, NO_OPLOCK_RETURN); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_exclusive5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "EXCLUSIVE5: open with exclusive oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_exclusive6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_exclusive6_1.dat"; + const char *fname2 = BASEDIR "\\test_exclusive6_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sinfo; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree2, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "EXCLUSIVE6: open a file with an exclusive " + "oplock (share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + + torture_comment(tctx, "rename should not generate a break but get " + "a sharing violation\n"); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = true; + sinfo.rename_information.in.new_name = fname2; + status = smb2_setinfo_file(tree1, &sinfo); + + torture_comment(tctx, "trying rename while first file open\n"); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch1.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH1: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break\n"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should generate a self break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch2(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + char c = 0; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH2: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "unlink should generate a break, which we ack " + "as break to none\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_ack_to_none; + tree1->session->transport->oplock.private_data = tree1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + torture_comment(tctx, "2nd unlink should not generate a break\n"); + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + torture_comment(tctx, "writing should not generate a break\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch3(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch3.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH3: if we close on break then the unlink " + "can succeed\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch4(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch4.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_read r; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH4: a self read should not cause a break\n"); + ZERO_STRUCT(break_info); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(r); + r.in.file.handle = h1; + r.in.offset = 0; + + status = smb2_read(tree1, tree1, &r); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch5(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch5.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH5: a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch6(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch6.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH6: a 2nd open should give a break to " + "level II if the first open allowed shared read\n"); + ZERO_STRUCT(break_info); + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree1, h1, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch7(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch7.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH7: a 2nd open should get an oplock when " + "we close instead of ack\n"); + ZERO_STRUCT(break_info); + tree1->session->transport->oplock.handler = + torture_oplock_handler_close; + tree1->session->transport->oplock.private_data = tree1; + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree2, h1); + smb2_util_close(tree2, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch8(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch8.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH8: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + torture_comment(tctx, "second open with attributes only shouldn't " + "cause oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch9(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch9.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create " + "file\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Subsequent normal open should break oplock on " + "attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, h2); + + torture_comment(tctx, "third oplocked open should grant level2 without " + "break\n"); + ZERO_STRUCT(break_info); + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + tree1->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + smb2_util_write(tree2, h2, &c, 0, 1); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch10(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch10.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH10: Open with oplock after a non-oplock " + "open should grant level2\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, 0); + + tree2->session->transport->oplock.handler = + torture_oplock_handler_level2_to_none; + tree2->session->transport->oplock.private_data = tree2; + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "write should trigger a break to none\n"); + { + struct smb2_write wr; + DATA_BLOB data; + data = data_blob_talloc(tree1, NULL, UINT16_MAX); + data.data[0] = (const uint8_t)'x'; + ZERO_STRUCT(wr); + wr.in.file.handle = h1; + wr.in.offset = 0; + wr.in.data = data; + status = smb2_write(tree1, &wr); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + } + + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch11(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch11.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-eof on pathname breaks an exclusive oplock. */ + torture_comment(tctx, "BATCH11: Test if setpathinfo set EOF breaks " + "oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.end_of_file_info.in.size = 100; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch12(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch12.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a set-allocation size on pathname breaks an exclusive + * oplock. */ + torture_comment(tctx, "BATCH12: Test if setpathinfo allocation size " + "breaks oplocks.\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_ALLOCATION_INFORMATION; + sfi.generic.in.file.path = fname; + sfi.allocation_info.in.alloc_size = 65536 * 8; + + status = smb2_composite_setpathinfo(tree2, &sfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + /* We expect two breaks */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + CHECK_VAL(break_info.count, 2); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch13(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch13.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH13: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + + return ret; +} + +static bool test_smb2_oplock_batch14(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch14.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH14: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_SUPERSEDE dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch15(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch15.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Test if a qpathinfo all info on pathname breaks a batch oplock. */ + torture_comment(tctx, "BATCH15: Test if qpathinfo all info breaks " + "a batch oplock (should not).\n"); + + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree2, tctx, &qfi); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch16(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch16.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH16: open with batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "second open with attributes only and " + "NTCREATEX_DISP_OVERWRITE_IF dispostion causes " + "oplock break\n"); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_STD_SYNCHRONIZE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH17 test. Since + * SMB2 doesn't have a RENAME command this test isn't applicable. However, + * it's much less confusing, when comparing test, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch17(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +/* This function is a placeholder for the SMB1 RAW-OPLOCK-BATCH18 test. Since + * SMB2 doesn't have an NTRENAME command this test isn't applicable. However, + * it's much less confusing, when comparing tests, to keep the SMB1 and SMB2 + * test numbers in sync. */ +#if 0 +static bool test_raw_oplock_batch18(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + return true; +} +#endif + +static bool test_smb2_oplock_batch19(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname1 = BASEDIR "\\test_batch19_1.dat"; + const char *fname2 = BASEDIR "\\test_batch19_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH19: open a file with an batch oplock " + "(share mode: none)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.generic.in.file.path = fname1; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.root_fid = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + smb2_deltree(tree1, fname2); + return ret; +} + +static bool test_smb2_oplock_batch20(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname1 = BASEDIR "\\test_batch20_1.dat"; + const char *fname2 = BASEDIR "\\test_batch20_2.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + union smb_fileinfo qfi; + union smb_setfileinfo sfi; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname1; + + torture_comment(tctx, "BATCH20: open a file with an batch oplock " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h1; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree1, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + torture_comment(tctx, "open the file a second time requesting batch " + "(share mode: all)\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.fname = fname1; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + + torture_comment(tctx, "setfileinfo rename info should not trigger " + "a break but should cause a sharing violation\n"); + ZERO_STRUCT(break_info); + ZERO_STRUCT(sfi); + sfi.generic.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfi.rename_information.in.file.handle = h2; + sfi.rename_information.in.overwrite = 0; + sfi.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree2, &sfi); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h1; + + status = smb2_getinfo_file(tree1, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + ZERO_STRUCT(qfi); + qfi.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + qfi.generic.in.file.handle = h2; + + status = smb2_getinfo_file(tree2, tctx, &qfi); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_STRMATCH(qfi.all_info2.out.fname.s, fname1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname1); + return ret; +} + +static bool test_smb2_oplock_batch21(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch21.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH21: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "writing should not generate a break\n"); + status = smb2_util_write(tree1, h1, &c, 0, 1); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch22(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch22.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct timeval tv; + int timeout = torture_setting_int(tctx, "oplocktimeout", 30); + int te; + + if (torture_setting_bool(tctx, "samba3", false)) { + torture_skip(tctx, "BATCH22 disabled against samba3\n"); + } + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "BATCH22: open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "a 2nd open should succeed after the oplock " + "break timeout\n"); + tv = timeval_current(); + tree1->session->transport->oplock.handler = + torture_oplock_handler_timeout; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + te = (int)timeval_elapsed(&tv); + CHECK_RANGE(te, timeout - 1, timeout + 15); + torture_comment(tctx, "waited %d seconds for oplock timeout\n", te); + + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch23(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch23.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH23: an open and ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open without level2 oplock support " + "should generate a break to level2\n"); + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h3 = io.smb2.out.file.handle; + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 3rd open with level2 oplock support should " + "not generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree3, h3); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch24(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch24.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2; + struct smb2_tree *tree3 = NULL; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + ret = open_smb2_connection_no_level2_oplocks(tctx, &tree3); + CHECK_VAL(ret, true); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree3->session->transport->oplock.handler = torture_oplock_handler; + tree3->session->transport->oplock.private_data = tree3; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH24: a open without level support and " + "ask for a batch oplock\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree3, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h2 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a 2nd open with level2 oplock support should " + "generate a break\n"); + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.handle.data[0], h2.data[0]); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree3, h2); + smb2_util_close(tree2, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_batch25(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch25.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "BATCH25: open a file with an batch oplock " + "(share mode: none)\n"); + + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "changing the file attribute info should trigger " + "a break and a violation\n"); + + status = smb2_util_setatr(tree1, fname, FILE_ATTRIBUTE_HIDDEN); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_SHARING_VIOLATION, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, fname); + return ret; +} + +/* Test how oplocks work on streams. */ +static bool test_raw_oplock_stream1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + NTSTATUS status; + union smb_open io; + const char *fname_base = BASEDIR "\\test_stream1.txt"; + const char *fname_stream, *fname_default_stream; + const char *default_stream = "::$DATA"; + const char *stream = "Stream One:$DATA"; + bool ret = true; + struct smb2_handle h, h_base, h_stream; + int i; + +#define NSTREAM_OPLOCK_RESULTS 8 + struct { + const char **fname; + bool open_base_file; + uint32_t oplock_req; + uint32_t oplock_granted; + } stream_oplock_results[NSTREAM_OPLOCK_RESULTS] = { + /* Request oplock on stream without the base file open. */ + {&fname_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, false, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + + /* Request oplock on stream with the base file open. */ + {&fname_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_BATCH}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_BATCH, SMB2_OPLOCK_LEVEL_II}, + {&fname_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_EXCLUSIVE}, + {&fname_default_stream, true, SMB2_OPLOCK_LEVEL_EXCLUSIVE, SMB2_OPLOCK_LEVEL_II}, + }; + + /* Only passes against windows at the moment. */ + if (torture_setting_bool(tctx, "samba3", false) || + torture_setting_bool(tctx, "samba4", false)) { + torture_skip(tctx, "STREAM1 disabled against samba3+4\n"); + } + + fname_stream = talloc_asprintf(tctx, "%s:%s", fname_base, stream); + fname_default_stream = talloc_asprintf(tctx, "%s%s", fname_base, + default_stream); + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* Initialize handles to "closed". Using -1 in the first 64-bytes + * as the sentry for this */ + h_stream.data[0] = -1; + + /* cleanup */ + smb2_util_unlink(tree1, fname_base); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + /* Setup generic open parameters. */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = (SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL); + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + + /* Create the file with a stream */ + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error creating file"); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Change the disposition to open now that the file has been created. */ + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + + /* Try some permutations of taking oplocks on streams. */ + for (i = 0; i < NSTREAM_OPLOCK_RESULTS; i++) { + const char *fname = *stream_oplock_results[i].fname; + bool open_base_file = stream_oplock_results[i].open_base_file; + uint32_t oplock_req = stream_oplock_results[i].oplock_req; + uint32_t oplock_granted = + stream_oplock_results[i].oplock_granted; + + if (open_base_file) { + torture_comment(tctx, "Opening base file: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, + "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, + SMB2_OPLOCK_LEVEL_BATCH); + h_base = io.smb2.out.file.handle; + } + + torture_comment(tctx, "%d: Opening stream: %s with %d\n", i, + fname, oplock_req); + io.smb2.in.fname = fname; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = oplock_req; + + /* Do the open with the desired oplock on the stream. */ + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, oplock_granted); + smb2_util_close(tree1, io.smb2.out.file.handle); + + /* Cleanup the base file if it was opened. */ + if (open_base_file) + smb2_util_close(tree2, h_base); + } + + /* Open the stream with an exclusive oplock. */ + torture_comment(tctx, "Opening stream: %s with %d\n", + fname_stream, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_EXCLUSIVE); + h_stream = io.smb2.out.file.handle; + + /* Open the base file and see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening base file: %s with %d\n", + fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_base; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + + /* Open the stream again to see if it contends. */ + ZERO_STRUCT(break_info); + torture_comment(tctx, "Opening stream again: %s with " + "%d\n", fname_base, SMB2_OPLOCK_LEVEL_BATCH); + io.smb2.in.fname = fname_stream; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + status = smb2_create(tree2, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening file"); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + smb2_util_close(tree2, io.smb2.out.file.handle); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + + /* Close the stream. */ + if (h_stream.data[0] != -1) { + smb2_util_close(tree1, h_stream); + } + + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +static bool test_smb2_oplock_doc(struct torture_context *tctx, struct smb2_tree *tree) +{ + const char *fname = BASEDIR "\\test_oplock_doc.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree, fname); + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "open a delete-on-close file with a batch " + "oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + smb2_util_close(tree, h1); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock, then open it again from a second client + * requesting no oplock. Having two open file handles should break our own + * oplock during BRL acquisition. + */ +static bool test_smb2_oplock_brl1(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + struct smb2_handle h, h1, h2; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1,buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree2, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree2, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Open a file with a batch oplock on one tree and then acquire a brl. + * We should not contend our own oplock. + */ +static bool test_smb2_oplock_brl2(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + /*int fname, f;*/ + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + ZERO_STRUCT(break_info); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should not break to " + "none\n"); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + /* With one file handle open a BRL should not contend our oplock. + * Thus, no oplock break will be received and the entire break_info + * struct will be 0 */ + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; +} + +/* Open a file with a batch oplock twice from one tree and then acquire a + * brl. BRL acquisition should break our own oplock. + */ +static bool test_smb2_oplock_brl3(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_batch_brl.dat"; + bool ret = true; + uint8_t buf[1000]; + bool correct = true; + union smb_open io; + NTSTATUS status; + struct smb2_handle h, h1, h2; + struct smb2_lock lck; + struct smb2_lock_element lock[1]; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + tree1->session->transport->oplock.handler = + torture_oplock_handler_two_notifications; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + with a batch oplock we get a break + */ + torture_comment(tctx, "open with batch oplock\n"); + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + /* create a file with bogus data */ + memset(buf, 0, sizeof(buf)); + status = smb2_util_write(tree1, h1, buf, 0, sizeof(buf)); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "Failed to create file\n"); + correct = false; + goto done; + } + + torture_comment(tctx, "a 2nd open should give a break\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = 0; + status = smb2_create(tree1, tctx, &(io.smb2)); + h2 = io.smb2.out.file.handle; + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "a self BRL acquisition should break to none\n"); + + lock[0].offset = 0; + lock[0].length = 4; + lock[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + + ZERO_STRUCT(lck); + lck.in.file.handle = h1; + lck.in.locks = &lock[0]; + lck.in.lock_count = 1; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_ok(tctx, status, "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_NONE); + CHECK_VAL(break_info.handle.data[0], h1.data[0]); + CHECK_VAL(break_info.failures, 0); + + /* expect no oplock break */ + ZERO_STRUCT(break_info); + lock[0].offset = 2; + status = smb2_lock(tree1, &lck); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOCK_NOT_GRANTED, + "Incorrect status"); + + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 0); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h2); + smb2_util_close(tree1, h); + +done: + smb2_deltree(tree1, BASEDIR); + return ret; + +} + +/* Starting the SMB2 specific oplock tests at 500 so we can keep the SMB1 + * tests in sync with an identically numbered SMB2 test */ + +/* Test whether the server correctly returns an error when we send + * a response to a levelII to none oplock notification. */ +static bool test_smb2_oplock_levelII500(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + const char *fname = BASEDIR "\\test_levelII500.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1; + char c = 0; + + status = torture_smb2_testdir(tree1, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + /* cleanup */ + smb2_util_unlink(tree1, fname); + + tree1->session->transport->oplock.handler = torture_oplock_handler; + tree1->session->transport->oplock.private_data = tree1; + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + torture_comment(tctx, "LEVELII500: acknowledging a break from II to " + "none should return an error\n"); + ZERO_STRUCT(break_info); + + io.smb2.in.desired_access = SEC_RIGHTS_FILE_READ | + SEC_RIGHTS_FILE_WRITE; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_II; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none and when " + "we reply, an oplock break failure\n"); + smb2_util_write(tree1, h1, &c, 0, 1); + + /* Wait several times to receive both the break notification, and the + * NT_STATUS_INVALID_OPLOCK_PROTOCOL error in the break response */ + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + torture_wait_for_oplock_break(tctx); + + /* There appears to be a race condition in W2K8 and W2K8R2 where + * sometimes the server will happily reply to our break response with + * NT_STATUS_OK, and sometimes it will return the OPLOCK_PROTOCOL + * error. As the MS-SMB2 doc states that a client should not reply to + * a level2 to none break notification, I'm leaving the protocol error + * as the expected behavior. */ + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.level, 0); + CHECK_VAL(break_info.failures, 1); + torture_assert_ntstatus_equal(tctx, break_info.failure_status, + NT_STATUS_INVALID_OPLOCK_PROTOCOL, + "Incorrect status"); + + smb2_util_close(tree1, h1); + smb2_util_close(tree1, h); + + smb2_deltree(tree1, BASEDIR); + return ret; +} + +struct torture_suite *torture_smb2_oplocks_init(void) +{ + struct torture_suite *suite = + torture_suite_create(talloc_autofree_context(), "OPLOCK"); + + torture_suite_add_2smb2_test(suite, "EXCLUSIVE1", test_smb2_oplock_exclusive1); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE2", test_smb2_oplock_exclusive2); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE3", test_smb2_oplock_exclusive3); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE4", test_smb2_oplock_exclusive4); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE5", test_smb2_oplock_exclusive5); + torture_suite_add_2smb2_test(suite, "EXCLUSIVE6", test_smb2_oplock_exclusive6); + torture_suite_add_2smb2_test(suite, "BATCH1", test_smb2_oplock_batch1); + torture_suite_add_2smb2_test(suite, "BATCH2", test_smb2_oplock_batch2); + torture_suite_add_2smb2_test(suite, "BATCH3", test_smb2_oplock_batch3); + torture_suite_add_2smb2_test(suite, "BATCH4", test_smb2_oplock_batch4); + torture_suite_add_2smb2_test(suite, "BATCH5", test_smb2_oplock_batch5); + torture_suite_add_2smb2_test(suite, "BATCH6", test_smb2_oplock_batch6); + torture_suite_add_2smb2_test(suite, "BATCH7", test_smb2_oplock_batch7); + torture_suite_add_2smb2_test(suite, "BATCH8", test_smb2_oplock_batch8); + torture_suite_add_2smb2_test(suite, "BATCH9", test_smb2_oplock_batch9); + torture_suite_add_2smb2_test(suite, "BATCH10", test_smb2_oplock_batch10); + torture_suite_add_2smb2_test(suite, "BATCH11", test_smb2_oplock_batch11); + torture_suite_add_2smb2_test(suite, "BATCH12", test_smb2_oplock_batch12); + torture_suite_add_2smb2_test(suite, "BATCH13", test_smb2_oplock_batch13); + torture_suite_add_2smb2_test(suite, "BATCH14", test_smb2_oplock_batch14); + torture_suite_add_2smb2_test(suite, "BATCH15", test_smb2_oplock_batch15); + torture_suite_add_2smb2_test(suite, "BATCH16", test_smb2_oplock_batch16); + torture_suite_add_1smb2_test(suite, "BATCH19", test_smb2_oplock_batch19); + torture_suite_add_2smb2_test(suite, "BATCH20", test_smb2_oplock_batch20); + torture_suite_add_1smb2_test(suite, "BATCH21", test_smb2_oplock_batch21); + torture_suite_add_1smb2_test(suite, "BATCH22", test_smb2_oplock_batch22); + torture_suite_add_2smb2_test(suite, "BATCH23", test_smb2_oplock_batch23); + torture_suite_add_2smb2_test(suite, "BATCH24", test_smb2_oplock_batch24); + torture_suite_add_1smb2_test(suite, "BATCH25", test_smb2_oplock_batch25); + torture_suite_add_2smb2_test(suite, "STREAM1", test_raw_oplock_stream1); + torture_suite_add_1smb2_test(suite, "DOC", test_smb2_oplock_doc); + torture_suite_add_2smb2_test(suite, "BRL1", test_smb2_oplock_brl1); + torture_suite_add_1smb2_test(suite, "BRL2", test_smb2_oplock_brl2); + torture_suite_add_1smb2_test(suite, "BRL3", test_smb2_oplock_brl3); + torture_suite_add_1smb2_test(suite, "LEVELII500", test_smb2_oplock_levelII500); + + suite->description = talloc_strdup(suite, "SMB2-OPLOCK tests"); + + return suite; +} + +/* + stress testing of oplocks +*/ +bool test_smb2_bench_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct smb2_tree **trees; + bool ret = true; + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); + int i, count=0; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + union smb_open io; + struct timeval tv; + struct smb2_handle h; + + trees = talloc_array(mem_ctx, struct smb2_tree *, torture_nprocs); + + torture_comment(tctx, "Opening %d connections\n", torture_nprocs); + for (i=0;isession->transport->oplock.handler = + torture_oplock_handler_close; + trees[i]->session->transport->oplock.private_data = trees[i]; + } + + status = torture_smb2_testdir(trees[0], BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + ZERO_STRUCT(io.smb2); + io.smb2.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = BASEDIR "\\test.dat"; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + + tv = timeval_current(); + + /* + we open the same file with SHARE_ACCESS_NONE from all the + connections in a round robin fashion. Each open causes an + oplock break on the previous connection, which is answered + by the oplock_handler_close() to close the file. + + This measures how fast we can pass on oplocks, and stresses + the oplock handling code + */ + torture_comment(tctx, "Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + for (i=0;idata[0], handle->data[1]); + return false; + } + + info = &hold_info[i]; + + if (info->close_on_break) { + printf("oplock break on %s - closing\n", info->fname); + torture_oplock_handler_close(transport, handle, + level, private_data); + return true; + } + + printf("oplock break on %s - acking break\n", info->fname); + printf("Acking to none in oplock handler\n"); + + torture_oplock_handler_ack_to_none(transport, handle, + level, private_data); + return true; +} + +/* + used for manual testing of oplocks - especially interaction with + other filesystems (such as NFS and local access) +*/ +bool test_smb2_hold_oplock(struct torture_context *tctx, + struct smb2_tree *tree) +{ + struct torture_context *mem_ctx = talloc_new(tctx); + struct tevent_context *ev = + (struct tevent_context *)tree->session->transport->socket->event.ctx; + int i; + struct smb2_handle h; + NTSTATUS status; + + torture_comment(tctx, "Setting up open files with oplocks in %s\n", + BASEDIR); + + status = torture_smb2_testdir(tree, BASEDIR, &h); + torture_assert_ntstatus_ok(tctx, status, "Error creating directory"); + + tree->session->transport->oplock.handler = torture_oplock_handler_hold; + tree->session->transport->oplock.private_data = tree; + + /* setup the files */ + for (i=0;i Date: Tue, 29 Sep 2009 11:49:50 +0200 Subject: s4: fix various warnings (not "const" related ones) --- source4/torture/smb2/compound.c | 4 ---- source4/torture/smb2/dir.c | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c index 00f6f3340f..e9b5ee9408 100644 --- a/source4/torture/smb2/compound.c +++ b/source4/torture/smb2/compound.c @@ -44,7 +44,6 @@ static bool test_compound_related1(struct torture_context *tctx, struct smb2_close cl; bool ret = true; struct smb2_request *req[2]; - DATA_BLOB data; smb2_transport_credits_ask_num(tree->session->transport, 2); @@ -170,8 +169,6 @@ static bool test_compound_unrelated1(struct torture_context *tctx, struct smb2_close cl; bool ret = true; struct smb2_request *req[5]; - uint64_t uid; - uint32_t tid; smb2_transport_credits_ask_num(tree->session->transport, 5); @@ -237,7 +234,6 @@ static bool test_compound_invalid1(struct torture_context *tctx, struct smb2_close cl; bool ret = true; struct smb2_request *req[2]; - DATA_BLOB data; smb2_transport_credits_ask_num(tree->session->transport, 2); diff --git a/source4/torture/smb2/dir.c b/source4/torture/smb2/dir.c index 3551f9718f..4af6900a81 100644 --- a/source4/torture/smb2/dir.c +++ b/source4/torture/smb2/dir.c @@ -1105,7 +1105,7 @@ static bool test_file_index(struct torture_context *tctx, struct smb2_find f; struct smb2_handle h; union smb_search_data *d; - int count; + unsigned count; smb2_deltree(tree, DNAME); @@ -1223,9 +1223,9 @@ static bool test_large_files(struct torture_context *tctx, struct smb2_find f; struct smb2_handle h; union smb_search_data *d; - int count, file_count = 0; + int i, j, file_count = 0; char **strs = NULL; - int i, j; + unsigned count; torture_comment(tctx, "Testing directory enumeration in a directory with >1000 files\n"); -- cgit From 0ec8fe420f65fabce9d6b0a998ef892201f3e395 Mon Sep 17 00:00:00 2001 From: Aravind Srinivasan Date: Tue, 6 Oct 2009 20:25:15 -0700 Subject: s4/torture: Ported SMBv1 RAW-STREAMS tests to SMB2-STREAMS --- source4/torture/smb2/config.mk | 3 +- source4/torture/smb2/smb2.c | 1 + source4/torture/smb2/streams.c | 1767 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1770 insertions(+), 1 deletion(-) create mode 100644 source4/torture/smb2/streams.c (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/config.mk b/source4/torture/smb2/config.mk index f617f70ad2..e26ad26e7f 100644 --- a/source4/torture/smb2/config.mk +++ b/source4/torture/smb2/config.mk @@ -25,7 +25,8 @@ TORTURE_SMB2_OBJ_FILES = $(addprefix $(torturesrcdir)/smb2/, \ lease.o \ create.o \ read.o \ - compound.o) + compound.o \ + streams.o) $(eval $(call proto_header_template,$(torturesrcdir)/smb2/proto.h,$(TORTURE_SMB2_OBJ_FILES:.o=.c))) diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index 2430b095b9..f1d8fbb814 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -143,6 +143,7 @@ NTSTATUS torture_smb2_init(void) torture_suite_add_suite(suite, torture_smb2_lease_init()); torture_suite_add_suite(suite, torture_smb2_compound_init()); torture_suite_add_suite(suite, torture_smb2_oplocks_init()); + torture_suite_add_suite(suite, torture_smb2_streams_init()); torture_suite_add_1smb2_test(suite, "BENCH-OPLOCK", test_smb2_bench_oplock); torture_suite_add_1smb2_test(suite, "HOLD-OPLOCK", test_smb2_hold_oplock); diff --git a/source4/torture/smb2/streams.c b/source4/torture/smb2/streams.c new file mode 100644 index 0000000000..50f27a835b --- /dev/null +++ b/source4/torture/smb2/streams.c @@ -0,0 +1,1767 @@ +/* + Unix SMB/CIFS implementation. + + test alternate data streams + + Copyright (C) Andrew Tridgell 2004 + + 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 "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "libcli/smb_composite/smb_composite.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "libcli/libcli.h" + +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "torture/util.h" + +#include "system/filesys.h" + +#define DNAME "teststreams" + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +#define CHECK_VALUE(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%d - should be %d\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, \ + "(%s) Incorrect value %s=%llu - should be %llu\n", \ + __location__, #v, (unsigned long long)v, \ + (unsigned long long)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STR(v, correct) do { \ + bool ok; \ + if ((v) && !(correct)) { \ + ok = false; \ + } else if (!(v) && (correct)) { \ + ok = false; \ + } else if (!(v) && !(correct)) { \ + ok = true; \ + } else if (strcmp((v), (correct)) == 0) { \ + ok = true; \ + } else { \ + ok = false; \ + } \ + if (!ok) { \ + torture_comment(tctx,"(%s) Incorrect value %s='%s' - " \ + "should be '%s'\n", \ + __location__, #v, (v)?(v):"NULL", \ + (correct)?(correct):"NULL"); \ + ret = false; \ + }} while (0) + + +static int qsort_string(const void *v1, + const void *v2) +{ + char * const *s1 = v1; + char * const *s2 = v2; + return strcmp(*s1, *s2); +} + +static int qsort_stream(const void *v1, + const void *v2) +{ + const struct stream_struct * s1 = v1; + const struct stream_struct * s2 = v2; + return strcmp(s1->stream_name.s, s2->stream_name.s); +} + +static bool check_stream(struct smb2_tree *tree, + const char *location, + TALLOC_CTX *mem_ctx, + const char *fname, + const char *sname, + const char *value) +{ + struct smb2_handle handle; + struct smb2_create create; + struct smb2_read r; + NTSTATUS status; + const char *full_name; + + full_name = talloc_asprintf(mem_ctx, "%s:%s", fname, sname); + + ZERO_STRUCT(create); + create.in.desired_access = SEC_RIGHTS_FILE_ALL; + create.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + create.in.create_disposition = NTCREATEX_DISP_OPEN; + create.in.fname = full_name; + + status = smb2_create(tree, mem_ctx, &create); + if (!NT_STATUS_IS_OK(status)) { + if (value == NULL) { + return true; + } else { + torture_comment(mem_ctx, "Unable to open stream %s\n", + full_name); + return false; + } + } + + handle = create.out.file.handle; + if (value == NULL) { + return true; + } + + + ZERO_STRUCT(r); + r.in.file.handle = handle; + r.in.length = strlen(value)+11; + r.in.offset = 0; + + status = smb2_read(tree, tree, &r); + + if (!NT_STATUS_IS_OK(status)) { + torture_comment(mem_ctx, "(%s) Failed to read %lu bytes from " + "stream '%s'\n", location, (long)strlen(value), full_name); + return false; + } + + if (memcmp(r.out.data.data, value, strlen(value)) != 0) { + torture_comment(mem_ctx, "(%s) Bad data in stream\n", location); + return false; + } + + smb2_util_close(tree, handle); + return true; +} + +static bool check_stream_list(struct smb2_tree *tree, + struct torture_context *tctx, + const char *fname, + int num_exp, + const char **exp, + struct smb2_handle h) +{ + union smb_fileinfo finfo; + NTSTATUS status; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(tctx); + char **exp_sort; + struct stream_struct *stream_sort; + bool ret = false; + + finfo.generic.level = RAW_FILEINFO_STREAM_INFORMATION; + finfo.generic.in.file.handle = h; + + status = smb2_getinfo_file(tree, tctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) smb_raw_pathinfo failed: %s\n", + __location__, nt_errstr(status)); + goto fail; + } + + if (finfo.stream_info.out.num_streams != num_exp) { + torture_comment(tctx, "(%s) expected %d streams, got %d\n", + __location__, num_exp, finfo.stream_info.out.num_streams); + goto fail; + } + + if (num_exp == 0) { + ret = true; + goto fail; + } + + exp_sort = talloc_memdup(tmp_ctx, exp, num_exp * sizeof(*exp)); + + if (exp_sort == NULL) { + goto fail; + } + + qsort(exp_sort, num_exp, sizeof(*exp_sort), qsort_string); + + stream_sort = talloc_memdup(tmp_ctx, finfo.stream_info.out.streams, + finfo.stream_info.out.num_streams * + sizeof(*stream_sort)); + + if (stream_sort == NULL) { + goto fail; + } + + qsort(stream_sort, finfo.stream_info.out.num_streams, + sizeof(*stream_sort), qsort_stream); + + for (i=0; i expected[%s]\n", + __location__, fname, isprint(i)?(char)i:' ', i, + isprint(i)?"":" (not printable)", + nt_errstr(expected)); + } + CHECK_STATUS(status, expected); + + talloc_free(path); + } + +done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +#define CHECK_CALL_HANDLE(call, rightstatus) do { \ + check_handle = true; \ + call_name = #call; \ + sfinfo.generic.level = RAW_SFILEINFO_ ## call; \ + sfinfo.generic.in.file.handle = h1; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_EQUAL(status, rightstatus)) { \ + torture_comment(tctx,"(%s) %s - %s (should be %s)\n", \ + __location__, #call, \ + nt_errstr(status), nt_errstr(rightstatus)); \ + ret = false; \ + } \ + finfo1.generic.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo1.generic.in.file.handle = h1; \ + status2 = smb2_getinfo_file(tree, tctx, &finfo1); \ + if (!NT_STATUS_IS_OK(status2)) { \ + torture_comment(tctx,"(%s) %s pathinfo - %s\n", \ + __location__, #call, nt_errstr(status)); \ + ret = false; \ + }} while (0) + +/* + test stream renames +*/ +static bool test_stream_rename(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status, status2; + union smb_open io; + const char *fname = DNAME "\\stream_rename.txt"; + const char *sname1, *sname2; + union smb_fileinfo finfo1; + union smb_setfileinfo sfinfo; + bool ret = true; + struct smb2_handle h, h1; + bool check_handle; + const char *call_name; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s:$DaTa", fname, + "Second Stream"); + + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "(%s) testing stream renames\n", __location__); + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_RIGHTS_FILE_ALL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + + /* Create two streams. */ + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + smb2_util_close(tree, h1); + + io.smb2.in.fname = sname2; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + smb2_util_close(tree, h1); + + /* + * Open the second stream. + */ + + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Now rename the second stream onto the first. + */ + + ZERO_STRUCT(sfinfo); + + sfinfo.rename_information.in.overwrite = 1; + sfinfo.rename_information.in.root_fid = 0; + sfinfo.rename_information.in.new_name = ":Stream One"; + CHECK_CALL_HANDLE(RENAME_INFORMATION, NT_STATUS_OK); +done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool test_stream_rename2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname1 = DNAME "\\stream_rename2.txt"; + const char *fname2 = DNAME "\\stream2_rename2.txt"; + const char *stream_name1 = ":Stream One:$DATA"; + const char *stream_name2 = ":Stream Two:$DATA"; + const char *stream_name_default = "::$DATA"; + const char *sname1; + const char *sname2; + bool ret = true; + struct smb2_handle h, h1; + union smb_setfileinfo sinfo; + + sname1 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream One"); + sname2 = talloc_asprintf(mem_ctx, "%s:%s", fname1, "Stream Two"); + + smb2_util_unlink(tree, fname1); + smb2_util_unlink(tree, fname2); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_STD_DELETE | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = sname1; + + /* Open/create new stream. */ + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * Reopen the stream for SMB2 renames. + */ + io.smb2.in.fname = sname1; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Check SMB2 rename of a stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using " + ":\n", __location__); + ZERO_STRUCT(sinfo); + sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION_SMB2; + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.overwrite = 1; + sinfo.rename_information.in.root_fid = 0; + sinfo.rename_information.in.new_name = stream_name1; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + /* + * Check SMB2 rename of an overwriting stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of an overwriting " + "stream using :\n", __location__); + + /* Create second stream. */ + io.smb2.in.fname = sname2; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + smb2_util_close(tree, io.smb2.out.file.handle); + + /* Rename the first stream onto the second. */ + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = stream_name2; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h1); + + /* + * Reopen the stream with the new name. + */ + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.fname = sname2; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + /* + * Check SMB2 rename of a stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename of a stream using " + ":\n", __location__); + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = sname1; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); + + /* + * Check SMB2 rename to the default stream using :. + */ + torture_comment(tctx, "(%s) Checking SMB2 rename to defaualt stream " + "using :\n", __location__); + sinfo.rename_information.in.file.handle = h1; + sinfo.rename_information.in.new_name = stream_name_default; + status = smb2_setinfo_file(tree, &sinfo); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_close(tree, h1); + + done: + smb2_util_close(tree, h1); + status = smb2_util_unlink(tree, fname1); + status = smb2_util_unlink(tree, fname2); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool create_file_with_stream(struct torture_context *tctx, + struct smb2_tree *tree, + TALLOC_CTX *mem_ctx, + const char *base_fname, + const char *stream) +{ + NTSTATUS status; + bool ret = true; + union smb_open io; + + /* Create a file with a stream */ + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = stream; + + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + done: + smb2_util_close(tree, io.smb2.out.file.handle); + return ret; +} + + +/* Test how streams interact with create dispositions */ +static bool test_stream_create_disposition(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream_create_disp.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + const char *default_stream_name = "::$DATA"; + const char *stream_list[2]; + bool ret = true; + struct smb2_handle h, h1; + + /* clean slate .. */ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream); + + stream_list[0] = talloc_asprintf(mem_ctx, ":%s", stream); + stream_list[1] = default_stream_name; + + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + /* Open the base file with OPEN */ + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* + * check create open: sanity check + */ + torture_comment(tctx, "(%s) Checking create disp: open\n", + __location__); + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 2, stream_list, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite\n", + __location__); + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite_if + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite_if\n", + __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, fname_stream)) + goto done; + + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create supersede + */ + torture_comment(tctx, "(%s) Checking create disp: supersede\n", + __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + io.smb2.in.create_disposition = NTCREATEX_DISP_SUPERSEDE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 1, &default_stream_name, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + + /* + * check create overwrite_if on a stream. + */ + torture_comment(tctx, "(%s) Checking create disp: overwrite_if on " + "stream\n", __location__); + smb2_util_unlink(tree, fname); + if (!create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream)) { + goto done; + } + + io.smb2.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.smb2.in.fname = fname_stream; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + if (!check_stream_list(tree, tctx, fname, 2, stream_list, + io.smb2.out.file.handle)) { + goto done; + } + smb2_util_close(tree, io.smb2.out.file.handle); + done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + +static bool open_stream(struct smb2_tree *tree, + struct torture_context *mem_ctx, + const char *fname, + struct smb2_handle *h_out) +{ + NTSTATUS status; + union smb_open io; + + ZERO_STRUCT(io.smb2); + io.smb2.in.create_flags = 0; + io.smb2.in.desired_access = SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_ATTRIBUTE; + io.smb2.in.create_options = 0; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = 0; + io.smb2.in.alloc_size = 0; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &(io.smb2)); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + *h_out = io.smb2.out.file.handle; + return true; +} + + +/* Test the effect of setting attributes on a stream. */ +static bool test_stream_attributes(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + bool ret = true; + NTSTATUS status; + union smb_open io; + const char *fname = DNAME "\\stream_attr.txt"; + const char *stream = "Stream One:$DATA"; + const char *fname_stream; + struct smb2_handle h, h1; + union smb_fileinfo finfo; + union smb_setfileinfo sfinfo; + time_t basetime = (time(NULL) - 86400) & ~1; + + torture_comment(tctx, "(%s) testing attribute setting on stream\n", + __location__); + + /* clean slate .. */ + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + smb2_deltree(tree, DNAME); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + fname_stream = talloc_asprintf(mem_ctx, "%s:%s", fname, stream); + + /* Create a file with a stream with attribute FILE_ATTRIBUTE_ARCHIVE. */ + ret = create_file_with_stream(tctx, tree, mem_ctx, fname, + fname_stream); + if (!ret) { + goto done; + } + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = io.smb2.out.file.handle; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_OK); + + if (finfo.basic_info.out.attrib != FILE_ATTRIBUTE_ARCHIVE) { + torture_comment(tctx, "(%s) Incorrect attrib %x - should be " + "%x\n", __location__, + (unsigned int)finfo.basic_info.out.attrib, + (unsigned int)FILE_ATTRIBUTE_ARCHIVE); + ret = false; + goto done; + } + + smb2_util_close(tree, io.smb2.out.file.handle); + /* Now open the stream name. */ + + if (!open_stream(tree, tctx, fname_stream, &h1)) { + goto done; + } + + /* Change the time on the stream. */ + ZERO_STRUCT(sfinfo); + unix_to_nt_time(&sfinfo.basic_info.in.write_time, basetime); + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "(%s) %s - %s (should be %s)\n", + __location__, "SETATTR", + nt_errstr(status), nt_errstr(NT_STATUS_OK)); + ret = false; + goto done; + } + + smb2_util_close(tree, h1); + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + if (!NT_STATUS_IS_OK(status)) { + torture_comment(tctx, "(%s) %s pathinfo - %s\n", + __location__, "SETATTRE", nt_errstr(status)); + ret = false; + goto done; + } + + if (nt_time_to_unix(finfo.basic_info.out.write_time) != basetime) { + torture_comment(tctx, "(%s) time incorrect.\n", __location__); + ret = false; + goto done; + } + smb2_util_close(tree, h1); + + if (!open_stream(tree, tctx, fname_stream, &h1)) { + goto done; + } + + /* Changing attributes on stream */ + ZERO_STRUCT(sfinfo); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_READONLY; + + sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION; + sfinfo.generic.in.file.handle = h1; + status = smb2_setinfo_file(tree, &sfinfo); + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { + torture_comment(tctx, "(%s) %s - %s (should be %s)\n", + __location__, "SETATTR", + nt_errstr(status), nt_errstr(NT_STATUS_OK)); + ret = false; + goto done; + } + + smb2_util_close(tree, h1); + + ZERO_STRUCT(io.smb2); + io.smb2.in.fname = fname; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + io.smb2.in.desired_access = SEC_FILE_READ_DATA; + status = smb2_create(tree, mem_ctx, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + ZERO_STRUCT(finfo); + finfo.generic.level = RAW_FILEINFO_BASIC_INFORMATION; + finfo.generic.in.file.handle = h1; + status = smb2_getinfo_file(tree, mem_ctx, &finfo); + CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED); + +done: + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + talloc_free(mem_ctx); + + return ret; +} + + +/* + basic testing of streams calls SMB2 +*/ +struct torture_suite *torture_smb2_streams_init(void) +{ + struct torture_suite *suite = + torture_suite_create(talloc_autofree_context(), "STREAMS"); + + torture_suite_add_1smb2_test(suite, "DIR", test_stream_dir); + torture_suite_add_1smb2_test(suite, "IO", test_stream_io); + torture_suite_add_1smb2_test(suite, "SHAREMODES", test_stream_sharemodes); + torture_suite_add_1smb2_test(suite, "NAMES", test_stream_names); + torture_suite_add_1smb2_test(suite, "NAMES2", test_stream_names2); + torture_suite_add_1smb2_test(suite, "RENAME", test_stream_rename); + torture_suite_add_1smb2_test(suite, "RENAME2", test_stream_rename2); + torture_suite_add_1smb2_test(suite, "CREATE-DISPOSITION", test_stream_create_disposition); + torture_suite_add_1smb2_test(suite, "ATTRIBUTES", test_stream_attributes); + torture_suite_add_1smb2_test(suite, "DELETE", test_stream_delete); + + suite->description = talloc_strdup(suite, "SMB2-STREAM tests"); + + return suite; +} -- cgit From 0bff2ced790fb9ff352b848246338999099f7f5f Mon Sep 17 00:00:00 2001 From: Aravind Srinivasan Date: Tue, 6 Oct 2009 20:12:09 -0700 Subject: s4/torture: convert all printf to torture_comments --- source4/torture/smb2/create.c | 52 ++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index febfbe03ec..3f9e29ad70 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -31,15 +31,20 @@ #define CHECK_STATUS(status, correct) do { \ if (!NT_STATUS_EQUAL(status, correct)) { \ - printf("(%s) Incorrect status %s - should be %s\n", \ - __location__, nt_errstr(status), nt_errstr(correct)); \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) Incorrect status %s - should be %s\n", \ + __location__, nt_errstr(status), nt_errstr(correct)); \ return false; \ }} while (0) #define CHECK_EQUAL(v, correct) do { \ 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); \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) Incorrect value for %s 0x%08llx - " \ + "should be 0x%08llx\n", \ + __location__, #v, \ + (unsigned long long)v, \ + (unsigned long long)correct); \ return false; \ }} while (0) @@ -131,7 +136,9 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre CHECK_STATUS(status, NT_STATUS_OK); } else { unexpected_mask |= 1<dacl, sd2->dacl)) { - printf("%s: security descriptors don't match!\n", __location__); - printf("got:\n"); + torture_comment(torture, + "%s: security descriptors don't match!\n", __location__); + torture_comment(torture, "got:\n"); NDR_PRINT_DEBUG(security_descriptor, sd2); - printf("expected:\n"); + torture_comment(torture, "expected:\n"); NDR_PRINT_DEBUG(security_descriptor, sd); return false; } -- cgit From 2f379d70ffa1dc8d2899489e35e715efe96a4b51 Mon Sep 17 00:00:00 2001 From: Aravind Srinivasan Date: Tue, 6 Oct 2009 20:16:38 -0700 Subject: s4/torture: Ported SMBv1 RAW-OPEN tests to SMB2-CREATE Four tests were ported from raw/open.c One new tests added LEADING-SLASH, which tests that a server provides the proper error when a relative path is given to a CREATE PDU with a leading "/". --- source4/torture/smb2/create.c | 574 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 573 insertions(+), 1 deletion(-) (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 3f9e29ad70..be825b24a7 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -23,11 +23,19 @@ #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" #include "torture/torture.h" +#include "torture/util.h" #include "torture/smb2/proto.h" #include "librpc/gen_ndr/ndr_security.h" #include "libcli/security/security.h" +#include "system/filesys.h" +#include "auth/credentials/credentials.h" +#include "lib/cmdline/popt_common.h" +#include "librpc/gen_ndr/security.h" +#include "lib/events/events.h" + #define FNAME "test_create.dat" +#define DNAME "smb2_open" #define CHECK_STATUS(status, correct) do { \ if (!NT_STATUS_EQUAL(status, correct)) { \ @@ -48,6 +56,76 @@ return false; \ }} while (0) +#define CHECK_TIME(t, field) do { \ + time_t t1, t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, torture, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t1 = t & ~1; \ + t2 = nt_time_to_unix(finfo.all_info.out.field) & ~1; \ + if (abs(t1-t2) > 2) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + timestring(torture, t1), \ + timestring(torture, t2)); \ + dump_all_info(torture, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_NTTIME(t, field) do { \ + NTTIME t2; \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, torture, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + t2 = finfo.all_info.out.field; \ + if (t != t2) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) wrong time for field %s %s - %s\n", \ + __location__, #field, \ + nt_time_string(torture, t), \ + nt_time_string(torture, t2)); \ + dump_all_info(torture, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_ALL_INFO(v, field) do { \ + finfo.all_info.level = RAW_FILEINFO_ALL_INFORMATION; \ + finfo.all_info.in.file.handle = h1; \ + status = smb2_getinfo_file(tree, torture, &finfo); \ + CHECK_STATUS(status, NT_STATUS_OK); \ + if ((v) != (finfo.all_info.out.field)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) wrong value for field %s 0x%x - 0x%x\n", \ + __location__, #field, (int)v,\ + (int)(finfo.all_info.out.field)); \ + dump_all_info(torture, &finfo); \ + ret = false; \ + }} while (0) + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(torture, TORTURE_FAIL, \ + "(%s) wrong value for %s 0x%x - should be 0x%x\n", \ + __location__, #v, (int)(v), (int)correct); \ + ret = false; \ + }} while (0) + +#define SET_ATTRIB(sattrib) do { \ + union smb_setfileinfo sfinfo; \ + ZERO_STRUCT(sfinfo.basic_info.in); \ + sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; \ + sfinfo.basic_info.in.file.handle = h1; \ + sfinfo.basic_info.in.attrib = sattrib; \ + status = smb2_setinfo_file(tree, &sfinfo); \ + if (!NT_STATUS_IS_OK(status)) { \ + torture_comment(torture, \ + "(%s) Failed to set attrib 0x%x on %s\n", \ + __location__, sattrib, fname); \ + }} while (0) + #define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false)) /* @@ -469,7 +547,496 @@ static bool test_create_acl(struct torture_context *torture, struct smb2_tree *t return true; } -/* +/* + test SMB2 open +*/ +static bool test_smb2_open(struct torture_context *torture, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(torture); + union smb_open io; + union smb_fileinfo finfo; + const char *fname = DNAME "\\torture_ntcreatex.txt"; + const char *dname = DNAME "\\torture_ntcreatex.dir"; + NTSTATUS status; + struct smb2_handle h, h1; + bool ret = true; + int i; + struct { + uint32_t create_disp; + bool with_file; + NTSTATUS correct_status; + } open_funcs[] = { + { NTCREATEX_DISP_SUPERSEDE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_SUPERSEDE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_CREATE, true, NT_STATUS_OBJECT_NAME_COLLISION }, + { NTCREATEX_DISP_CREATE, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OPEN_IF, false, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE, false, NT_STATUS_OBJECT_NAME_NOT_FOUND }, + { NTCREATEX_DISP_OVERWRITE_IF, true, NT_STATUS_OK }, + { NTCREATEX_DISP_OVERWRITE_IF, false, NT_STATUS_OK }, + { 6, true, NT_STATUS_INVALID_PARAMETER }, + { 6, false, NT_STATUS_INVALID_PARAMETER }, + }; + + torture_comment(torture, "Checking SMB2 Open\n"); + + smb2_util_unlink(tree, fname); + smb2_util_rmdir(tree, dname); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(io.smb2); + /* reasonable default parameters */ + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 1024*1024; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* test the create disposition */ + for (i=0; iev == NULL) || (trees == NULL) || (requests == NULL) || + (ios == NULL)) { + torture_comment(torture, ("talloc failed\n")); + ret = false; + goto done; + } + + tree->session->transport->options.request_timeout = 60; + + for (i=0; isession->transport->options.request_timeout = 60; + } + + /* cleanup */ + smb2_util_unlink(tree, fname); + + /* + base ntcreatex parms + */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.alloc_size = 0; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE| + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + io.smb2.in.create_flags = 0; + + for (i=0; istate < SMB2_REQUEST_DONE) { + unreplied = true; + break; + } + status = smb2_create_recv(requests[i], mem_ctx, + &(ios[i].smb2)); + + torture_comment(torture, + "File %d returned status %s\n", i, + nt_errstr(status)); + + if (NT_STATUS_IS_OK(status)) { + num_ok += 1; + } + + if (NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { + num_collision += 1; + } + + requests[i] = NULL; + } + if (!unreplied) { + break; + } + + if (event_loop_once(torture->ev) != 0) { + torture_comment(torture, "event_loop_once failed\n"); + ret = false; + goto done; + } + } + + if ((num_ok != 1) || (num_ok + num_collision != num_files)) { + ret = false; + } +done: + talloc_free(mem_ctx); + smb2_deltree(tree, fname); + + return ret; +} + +/* + test opening for delete on a read-only attribute file. +*/ + +static bool test_smb2_open_for_delete(struct torture_context *torture, + struct smb2_tree *tree) +{ + union smb_open io; + union smb_fileinfo finfo; + const char *fname = DNAME "\\torture_open_for_delete.txt"; + NTSTATUS status; + struct smb2_handle h, h1; + bool ret = true; + + torture_comment(torture, + "Checking SMB2_OPEN for delete on a readonly file.\n"); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, fname); + + status = torture_smb2_testdir(tree, DNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + /* reasonable default parameters */ + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED; + io.smb2.in.alloc_size = 0; + io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_READONLY; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; + io.smb2.in.create_options = 0; + io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; + io.smb2.in.security_flags = 0; + io.smb2.in.fname = fname; + + /* Create the readonly file. */ + + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.smb2.out.file.handle; + + CHECK_VAL(io.smb2.out.oplock_level, 0); + io.smb2.in.create_options = 0; + CHECK_VAL(io.smb2.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_ALL_INFO(io.smb2.out.file_attr, attrib); + smb2_util_close(tree, h1); + + /* Now try and open for delete only - should succeed. */ + io.smb2.in.desired_access = SEC_STD_DELETE; + io.smb2.in.file_attributes = 0; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; + status = smb2_create(tree, torture, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_unlink(tree, fname); + + smb2_util_close(tree, h1); + smb2_util_unlink(tree, fname); + smb2_deltree(tree, DNAME); + + return ret; +} + +/* + test SMB2 open with a leading slash on the path. + Trying to create a directory with a leading slash + should give NT_STATUS_INVALID_PARAMETER error +*/ +static bool test_smb2_leading_slash(struct torture_context *torture, + struct smb2_tree *tree) +{ + union smb_open io; + const char *dnameslash = "\\"DNAME; + NTSTATUS status; + bool ret = true; + + torture_comment(torture, + "Trying to create a directory with leading slash on path\n"); + smb2_deltree(tree, dnameslash); + + ZERO_STRUCT(io.smb2); + io.generic.level = RAW_OPEN_SMB2; + io.smb2.in.oplock_level = 0; + io.smb2.in.desired_access = SEC_RIGHTS_DIR_ALL; + io.smb2.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY; + io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; + io.smb2.in.fname = dnameslash; + + status = smb2_create(tree, tree, &(io.smb2)); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + smb2_deltree(tree, dnameslash); + return ret; +} + + +/* basic testing of SMB2 read */ struct torture_suite *torture_smb2_create_init(void) @@ -479,6 +1046,11 @@ struct torture_suite *torture_smb2_create_init(void) torture_suite_add_1smb2_test(suite, "GENTEST", test_create_gentest); torture_suite_add_1smb2_test(suite, "BLOB", test_create_blob); torture_suite_add_1smb2_test(suite, "ACL", test_create_acl); + torture_suite_add_1smb2_test(suite, "OPEN", test_smb2_open); + torture_suite_add_1smb2_test(suite, "BRLOCKED", test_smb2_open_brlocked); + torture_suite_add_1smb2_test(suite, "MULTI", test_smb2_open_multi); + torture_suite_add_1smb2_test(suite, "DELETE", test_smb2_open_for_delete); + torture_suite_add_1smb2_test(suite, "LEADING-SLASH", test_smb2_leading_slash); suite->description = talloc_strdup(suite, "SMB2-CREATE tests"); -- cgit From 622bffce4723353964bf0dc7bbf60235e417caa6 Mon Sep 17 00:00:00 2001 From: Steven Danneman Date: Fri, 9 Oct 2009 14:01:33 -0700 Subject: s4/torture: fix build break "implicit declaration of function 'isprint'" --- source4/torture/smb2/streams.c | 1 + 1 file changed, 1 insertion(+) (limited to 'source4/torture/smb2') diff --git a/source4/torture/smb2/streams.c b/source4/torture/smb2/streams.c index 50f27a835b..f186a54896 100644 --- a/source4/torture/smb2/streams.c +++ b/source4/torture/smb2/streams.c @@ -32,6 +32,7 @@ #include "torture/util.h" #include "system/filesys.h" +#include "system/locale.h" #define DNAME "teststreams" -- cgit