From 72a454704762fd33fba5f93700f11765809ca76e Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 23 Sep 2014 14:08:10 -0700 Subject: [PATCH 01/38] s3-winbindd: Allow winbindd to connect over SMB2 to servers This allows SMB signing to work against many more DCs, and so improves network security. The default for "client max protocol" remains NT1 in the rest of the code. Andrew Bartlett Signed-off-by: Andrew Bartlett Reviewed-by: Stefan Metzmacher (cherry picked from commit 14f6256c515ff4af4f478f947ad89b7edc8743cf) --- docs-xml/smbdotconf/protocol/clientmaxprotocol.xml | 9 +++++++-- lib/param/loadparm.c | 11 ++++++++++- lib/param/param_table.c | 3 ++- libcli/smb/smb_constants.h | 3 ++- source3/include/proto.h | 2 ++ source3/param/loadparm.c | 20 +++++++++++++++++++- source3/winbindd/winbindd_cm.c | 2 +- 7 files changed, 43 insertions(+), 7 deletions(-) diff --git a/docs-xml/smbdotconf/protocol/clientmaxprotocol.xml b/docs-xml/smbdotconf/protocol/clientmaxprotocol.xml index 6693cd3..121eeb8 100644 --- a/docs-xml/smbdotconf/protocol/clientmaxprotocol.xml +++ b/docs-xml/smbdotconf/protocol/clientmaxprotocol.xml @@ -2,6 +2,7 @@ context="G" type="enum" developer="1" + function="_client_max_protocol" xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> The value of the parameter (a string) is the highest @@ -71,11 +72,15 @@ Normally this option should not be set as the automatic negotiation phase in the SMB protocol takes care of choosing the appropriate protocol. + + The value default refers to the default protocol in each + part of the code, currently NT1 in the client tools and + SMB3_02 in winbindd. server max protocol -client mn protocol +client min protocol -NT1 +default LANMAN1 diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c index 9953053..25dbdf6 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -2474,7 +2474,7 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx) lpcfg_do_global_parameter(lp_ctx, "server min protocol", "LANMAN1"); lpcfg_do_global_parameter(lp_ctx, "server max protocol", "SMB3"); lpcfg_do_global_parameter(lp_ctx, "client min protocol", "CORE"); - lpcfg_do_global_parameter(lp_ctx, "client max protocol", "NT1"); + lpcfg_do_global_parameter(lp_ctx, "client max protocol", "default"); lpcfg_do_global_parameter(lp_ctx, "security", "AUTO"); lpcfg_do_global_parameter(lp_ctx, "EncryptPasswords", "True"); lpcfg_do_global_parameter(lp_ctx, "ReadRaw", "True"); @@ -3148,6 +3148,15 @@ int lpcfg_security(struct loadparm_context *lp_ctx) lpcfg__security(lp_ctx)); } +int lpcfg_client_max_protocol(struct loadparm_context *lp_ctx) +{ + int client_max_protocol = lpcfg__client_max_protocol(lp_ctx); + if (client_max_protocol == PROTOCOL_DEFAULT) { + return PROTOCOL_NT1; + } + return client_max_protocol; +} + bool lpcfg_server_signing_allowed(struct loadparm_context *lp_ctx, bool *mandatory) { bool allowed = true; diff --git a/lib/param/param_table.c b/lib/param/param_table.c index 4d0e6a9..53c299c 100644 --- a/lib/param/param_table.c +++ b/lib/param/param_table.c @@ -38,6 +38,7 @@ #endif static const struct enum_list enum_protocol[] = { + {PROTOCOL_DEFAULT, "default"}, /* the caller decides what this means */ {PROTOCOL_SMB2_10, "SMB2"}, /* for now keep PROTOCOL_SMB2_10 */ {PROTOCOL_SMB3_00, "SMB3"}, /* for now keep PROTOCOL_SMB3_00 */ {PROTOCOL_SMB3_02, "SMB3_02"}, @@ -1386,7 +1387,7 @@ struct parm_struct parm_table[] = { .label = "client max protocol", .type = P_ENUM, .p_class = P_GLOBAL, - .offset = GLOBAL_VAR(client_max_protocol), + .offset = GLOBAL_VAR(_client_max_protocol), .special = NULL, .enum_list = enum_protocol, .flags = FLAG_ADVANCED, diff --git a/libcli/smb/smb_constants.h b/libcli/smb/smb_constants.h index 763f4fa..f841ca9 100644 --- a/libcli/smb/smb_constants.h +++ b/libcli/smb/smb_constants.h @@ -76,7 +76,8 @@ /* protocol types. It assumes that higher protocols include lower protocols as subsets. */ enum protocol_types { - PROTOCOL_NONE, + PROTOCOL_DEFAULT=-1, + PROTOCOL_NONE=0, PROTOCOL_CORE, PROTOCOL_COREPLUS, PROTOCOL_LANMAN1, diff --git a/source3/include/proto.h b/source3/include/proto.h index eed57ff..ce23289 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -987,6 +987,8 @@ bool lp_idmap_default_range(uint32_t *low, uint32_t *high); const char *lp_idmap_backend(const char *domain_name); const char *lp_idmap_default_backend (void); int lp_security(void); +int lp_client_max_protocol(void); +int lp_winbindd_max_protocol(void); int lp_smb2_max_credits(void); int lp_cups_encrypt(void); bool lp_widelinks(int ); diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 884cc45..d2afac7 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -643,7 +643,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) Globals.max_open_files = max_open_files(); Globals.server_max_protocol = PROTOCOL_SMB3_00; Globals.server_min_protocol = PROTOCOL_LANMAN1; - Globals.client_max_protocol = PROTOCOL_NT1; + Globals._client_max_protocol = PROTOCOL_DEFAULT; Globals.client_min_protocol = PROTOCOL_CORE; Globals._security = SEC_AUTO; Globals.encrypt_passwords = true; @@ -4336,6 +4336,24 @@ int lp_security(void) lp__security()); } +int lp_client_max_protocol(void) +{ + int client_max_protocol = lp__client_max_protocol(); + if (client_max_protocol == PROTOCOL_DEFAULT) { + return PROTOCOL_NT1; + } + return client_max_protocol; +} + +int lp_winbindd_max_protocol(void) +{ + int client_max_protocol = lp__client_max_protocol(); + if (client_max_protocol == PROTOCOL_DEFAULT) { + return PROTOCOL_LATEST; + } + return client_max_protocol; +} + struct loadparm_global * get_globals(void) { return &Globals; diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index ccbe4f5..cbce0c4 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -936,7 +936,7 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, result = smbXcli_negprot((*cli)->conn, (*cli)->timeout, lp_client_min_protocol(), - lp_client_max_protocol()); + lp_winbindd_max_protocol()); if (!NT_STATUS_IS_OK(result)) { DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result))); -- 1.9.1 From f0797c8b9994832d45090e9571f205f34adfd9ce Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 5 Nov 2014 10:12:20 -0800 Subject: [PATCH 02/38] s4:torture: Add smb2.oplock test batch9a and raw.oplock test batch9a Shows attribute(stat) access open can create a file, and subsequent attribute(stat) opens don't break oplocks. Can be extended to explore more varients. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 8db5150143c4770bf2ffc40a5234f7b090ec8208) --- selftest/knownfail | 1 + source4/torture/raw/oplock.c | 121 +++++++++++++++++++++++++++++++++++++++ source4/torture/smb2/oplock.c | 128 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) diff --git a/selftest/knownfail b/selftest/knownfail index 6cca3dd..1c4f446 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -167,6 +167,7 @@ ^samba4.smb2.oplock.batch1\(.*\)$ # samba 4 oplocks are a mess ^samba4.smb2.oplock.batch6\(.*\)$ # samba 4 oplocks are a mess ^samba4.smb2.oplock.batch9\(.*\)$ # samba 4 oplocks are a mess +^samba4.smb2.oplock.batch9a\(.*\)$ # samba 4 oplocks are a mess ^samba4.smb2.oplock.batch10\(.*\)$ # samba 4 oplocks are a mess ^samba4.smb2.oplock.batch20\(.*\)$ # samba 4 oplocks are a mess ^samba4.smb2.oplock.batch26\(.*\)$ diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c index a4f6a05..1d7522f 100644 --- a/source4/torture/raw/oplock.c +++ b/source4/torture/raw/oplock.c @@ -1873,6 +1873,126 @@ done: return ret; } +static bool test_raw_oplock_batch9a(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) +{ + const char *fname = BASEDIR "\\test_batch9a.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + uint16_t fnum=0, fnum2=0; + char c = 0; + + if (!torture_setup_dir(cli1, BASEDIR)) { + return false; + } + + /* cleanup */ + smbcli_unlink(cli1->tree, fname); + + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + /* + base ntcreatex parms + */ + io.generic.level = RAW_OPEN_NTCREATEX; + io.ntcreatex.in.root_fid.fnum = 0; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.alloc_size = 0; + io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; + io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; + io.ntcreatex.in.create_options = 0; + io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; + io.ntcreatex.in.security_flags = 0; + io.ntcreatex.in.fname = fname; + + torture_comment(tctx, "BATCH9: open with attributes only can create file\n"); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_FILE_READ_ATTRIBUTE|SEC_FILE_WRITE_ATTRIBUTE|SEC_STD_SYNCHRONIZE; + status = smb_raw_open(cli1->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum = io.ntcreatex.out.file.fnum; + CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_CREATED); + CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN); + + torture_comment(tctx, "Subsequent attributes open should not break\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(io.ntcreatex.out.create_action, FILE_WAS_OPENED); + CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "Subsequent normal open should break oplock on attribute only open to level II\n"); + + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.fnum, fnum); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + smbcli_close(cli2->tree, fnum2); + + torture_comment(tctx, "third oplocked open should grant level2 without break\n"); + ZERO_STRUCT(break_info); + smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given, cli1->tree); + smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given, cli2->tree); + io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED | + NTCREATEX_FLAGS_REQUEST_OPLOCK | + NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; + io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + status = smb_raw_open(cli2->tree, tctx, &io); + CHECK_STATUS(tctx, status, NT_STATUS_OK); + fnum2 = io.ntcreatex.out.file.fnum; + torture_wait_for_oplock_break(tctx); + CHECK_VAL(break_info.count, 0); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN); + + ZERO_STRUCT(break_info); + + torture_comment(tctx, "write should trigger a break to none on both\n"); + smbcli_write(cli2->tree, fnum2, 0, &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); + + smbcli_close(cli1->tree, fnum); + smbcli_close(cli2->tree, fnum2); + +done: + smb_raw_exit(cli1->session); + smb_raw_exit(cli2->session); + smbcli_deltree(cli1->tree, BASEDIR); + return ret; +} + static bool test_raw_oplock_batch10(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2) { const char *fname = BASEDIR "\\test_batch10.dat"; @@ -4311,6 +4431,7 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx) torture_suite_add_2smb_test(suite, "batch7", test_raw_oplock_batch7); torture_suite_add_2smb_test(suite, "batch8", test_raw_oplock_batch8); torture_suite_add_2smb_test(suite, "batch9", test_raw_oplock_batch9); + torture_suite_add_2smb_test(suite, "batch9a", test_raw_oplock_batch9a); torture_suite_add_2smb_test(suite, "batch10", test_raw_oplock_batch10); torture_suite_add_2smb_test(suite, "batch11", test_raw_oplock_batch11); torture_suite_add_2smb_test(suite, "batch12", test_raw_oplock_batch12); diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c index d2a2832..be1c5eb 100644 --- a/source4/torture/smb2/oplock.c +++ b/source4/torture/smb2/oplock.c @@ -1611,6 +1611,133 @@ static bool test_smb2_oplock_batch9(struct torture_context *tctx, return ret; } +static bool test_smb2_oplock_batch9a(struct torture_context *tctx, + struct smb2_tree *tree1, + struct smb2_tree *tree2) +{ + const char *fname = BASEDIR "\\test_batch9a.dat"; + NTSTATUS status; + bool ret = true; + union smb_open io; + struct smb2_handle h, h1, h2, h3; + 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; + status = smb2_create(tree1, tctx, &(io.smb2)); + torture_assert_ntstatus_ok(tctx, status, "Error creating the file"); + h1 = io.smb2.out.file.handle; + CHECK_VAL(io.smb2.out.create_action, FILE_WAS_CREATED); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + + torture_comment(tctx, "Subsequent attributes open should not break\n"); + + ZERO_STRUCT(break_info); + + status = smb2_create(tree2, 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, 0); + CHECK_VAL(io.smb2.out.create_action, FILE_WAS_OPENED); + CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree2, h3); + + 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) @@ -3836,6 +3963,7 @@ struct torture_suite *torture_smb2_oplocks_init(void) 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, "batch9a", test_smb2_oplock_batch9a); 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); -- 1.9.1 From 5054085357a259dd194a1d06fb3a6855afe47e82 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 22:56:41 +0200 Subject: [PATCH 03/38] libcli/smb: remember the lease_version in struct smb2_lease Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 171cefe48fe1d312c60426c09876700a07d45547) --- libcli/smb/smb2_lease.c | 1 + librpc/idl/smb2_lease_struct.idl | 1 + 2 files changed, 2 insertions(+) diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c index f97f096..5e9a34d 100644 --- a/libcli/smb/smb2_lease.c +++ b/libcli/smb/smb2_lease.c @@ -43,6 +43,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, lease->lease_state = IVAL(buf, 16); lease->lease_flags = IVAL(buf, 20); lease->lease_duration = BVAL(buf, 24); + lease->lease_version = version; switch (version) { case 1: diff --git a/librpc/idl/smb2_lease_struct.idl b/librpc/idl/smb2_lease_struct.idl index be80d14..5ccd8a3 100644 --- a/librpc/idl/smb2_lease_struct.idl +++ b/librpc/idl/smb2_lease_struct.idl @@ -28,6 +28,7 @@ interface smb2_lease_struct uint32 lease_flags; hyper lease_duration; /* should be 0 */ smb2_lease_key parent_lease_key; + uint16 lease_version; uint16 lease_epoch; } smb2_lease; }; \ No newline at end of file -- 1.9.1 From 2bfe38487c7611f2ec792f6ce91a83a074920b34 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 22 Sep 2014 21:21:36 +0200 Subject: [PATCH 04/38] libcli/smb: mask off SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET for version 1 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit a6affb7bb3ff595165e708c56ede2181f0bb570f) --- libcli/smb/smb2_lease.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c index 5e9a34d..e817c34 100644 --- a/libcli/smb/smb2_lease.c +++ b/libcli/smb/smb2_lease.c @@ -48,6 +48,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, switch (version) { case 1: ZERO_STRUCT(lease->parent_lease_key); + lease->lease_flags &= ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET; lease->lease_epoch = 0; break; case 2: -- 1.9.1 From 2c64060a06cc35c3e7bcd578737093e625f39e6d Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 29 Oct 2014 13:55:16 +0100 Subject: [PATCH 05/38] libcli/smb: add smb2_lease_key_equal() helper function Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 2fc8f761c188b1abc90df68003b05b7a098aeabe) --- libcli/smb/smb2_lease.c | 6 ++++++ libcli/smb/smb2_lease.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c index e817c34..41eafc9 100644 --- a/libcli/smb/smb2_lease.c +++ b/libcli/smb/smb2_lease.c @@ -87,3 +87,9 @@ bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len) return true; } + +bool smb2_lease_key_equal(const struct smb2_lease_key *k1, + const struct smb2_lease_key *k2) +{ + return ((k1->data[0] == k2->data[0]) && (k1->data[1] == k2->data[1])); +} diff --git a/libcli/smb/smb2_lease.h b/libcli/smb/smb2_lease.h index ba8178d..9db239d 100644 --- a/libcli/smb/smb2_lease.h +++ b/libcli/smb/smb2_lease.h @@ -32,5 +32,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, struct smb2_lease *lease); bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len); +bool smb2_lease_key_equal(const struct smb2_lease_key *k1, + const struct smb2_lease_key *k2); #endif /* _LIBCLI_SMB_SMB2_LEASE_H_ */ -- 1.9.1 From c3dc8e256fe9f32ff8e6e623c90b28ad7cdb17db Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 4 Nov 2014 21:44:45 -0800 Subject: [PATCH 06/38] libcli/smb: Add smb2_lease_equal() which compares client_guids and keys. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Nov 7 22:41:47 CET 2014 on sn-devel-104 (cherry picked from commit dbb191f35bb093ad7fc8839b3d47e508be8f6069) --- libcli/smb/smb2_lease.c | 8 ++++++++ libcli/smb/smb2_lease.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c index 41eafc9..7705256 100644 --- a/libcli/smb/smb2_lease.c +++ b/libcli/smb/smb2_lease.c @@ -93,3 +93,11 @@ bool smb2_lease_key_equal(const struct smb2_lease_key *k1, { return ((k1->data[0] == k2->data[0]) && (k1->data[1] == k2->data[1])); } + +bool smb2_lease_equal(const struct GUID *g1, + const struct smb2_lease_key *k1, + const struct GUID *g2, + const struct smb2_lease_key *k2) +{ + return GUID_equal(g1, g2) && smb2_lease_key_equal(k1, k2); +} diff --git a/libcli/smb/smb2_lease.h b/libcli/smb/smb2_lease.h index 9db239d..2e6faf7 100644 --- a/libcli/smb/smb2_lease.h +++ b/libcli/smb/smb2_lease.h @@ -23,6 +23,7 @@ #ifndef _LIBCLI_SMB_SMB2_LEASE_H_ #define _LIBCLI_SMB_SMB2_LEASE_H_ +#include "librpc/gen_ndr/ndr_misc.h" #include "librpc/gen_ndr/smb2_lease_struct.h" /* @@ -34,5 +35,9 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len); bool smb2_lease_key_equal(const struct smb2_lease_key *k1, const struct smb2_lease_key *k2); +bool smb2_lease_equal(const struct GUID *g1, + const struct smb2_lease_key *k1, + const struct GUID *g2, + const struct smb2_lease_key *k2); #endif /* _LIBCLI_SMB_SMB2_LEASE_H_ */ -- 1.9.1 From 6a2af1a05da982eaaa5f100fc3d57a5ad01068c4 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 23:34:14 +0200 Subject: [PATCH 07/38] s3:smbd: break oplocks to none with FILE_OVERWRITE Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 87a102189bf2d87e39dd1762fff92465aa7be5ec) --- source3/smbd/open.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index ccea1e9..26244f6 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1431,6 +1431,7 @@ static bool delay_for_oplock(files_struct *fsp, switch (create_disposition) { case FILE_SUPERSEDE: + case FILE_OVERWRITE: case FILE_OVERWRITE_IF: break_to = NO_OPLOCK; break; -- 1.9.1 From fdf22fd44ee5d513a63136d719c53622b99cd56e Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 28 Oct 2014 15:27:09 -0700 Subject: [PATCH 08/38] s3:smbd: move all oplock granting code to grant_fsp_oplock_type() Pair-Programmed-With: Stefan Metzmacher Pair-Programmed-With: Jeremy Allison Signed-off-by: Volker Lendecke Signed-off-by: Stefan Metzmacher Signed-off-by: Jeremy Allison (cherry picked from commit a08b0e78220f84f87b2af1535d645a994a5c93ab) --- source3/smbd/open.c | 73 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 26244f6..945850c 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1490,38 +1490,37 @@ static bool file_has_brlocks(files_struct *fsp) return (brl_num_locks(br_lck) > 0); } -static void grant_fsp_oplock_type(files_struct *fsp, - struct share_mode_lock *lck, - int oplock_request) +static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, + struct files_struct *fsp, + struct share_mode_lock *lck, + int oplock_request) { bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) && lp_level2_oplocks(SNUM(fsp->conn)); bool got_level2_oplock, got_a_none_oplock; uint32_t i; + bool ok; + NTSTATUS status; /* Start by granting what the client asked for, but ensure no SAMBA_PRIVATE bits can be set. */ fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK); + if (fsp->oplock_type == NO_OPLOCK) { + goto type_selected; + } + if (oplock_request & INTERNAL_OPEN_ONLY) { /* No oplocks on internal open. */ fsp->oplock_type = NO_OPLOCK; - DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", - fsp->oplock_type, fsp_str_dbg(fsp))); - return; + goto type_selected; } if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) { DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n", fsp_str_dbg(fsp))); fsp->oplock_type = NO_OPLOCK; - } - - if (is_stat_open(fsp->access_mask)) { - /* Leave the value already set. */ - DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", - fsp->oplock_type, fsp_str_dbg(fsp))); - return; + goto type_selected; } got_level2_oplock = false; @@ -1555,6 +1554,23 @@ static void grant_fsp_oplock_type(files_struct *fsp, */ if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) { fsp->oplock_type = NO_OPLOCK; + goto type_selected; + } + + type_selected: + status = set_file_oplock(fsp); + if (!NT_STATUS_IS_OK(status)) { + /* + * Could not get the kernel oplock + */ + fsp->oplock_type = NO_OPLOCK; + } + + ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn), + req ? req->mid : 0, + fsp->oplock_type); + if (!ok) { + return NT_STATUS_NO_MEMORY; } if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) { @@ -1572,6 +1588,8 @@ static void grant_fsp_oplock_type(files_struct *fsp, DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", fsp->oplock_type, fsp_str_dbg(fsp))); + + return NT_STATUS_OK; } static bool request_timed_out(struct timeval request_time, @@ -2739,8 +2757,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } } - grant_fsp_oplock_type(fsp, lck, oplock_request); - /* * We have the share entry *locked*..... */ @@ -2800,9 +2816,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, } if (file_existed) { - /* stat opens on existing files don't get oplocks. */ + /* + * stat opens on existing files don't get oplocks. + * + * Note that we check for stat open on the *open_access_mask*, + * i.e. the access mask we actually used to do the open, + * not the one the client asked for (which is in + * fsp->access_mask). This is due to the fact that + * FILE_OVERWRITE and FILE_OVERWRITE_IF add in O_TRUNC, + * which adds FILE_WRITE_DATA to open_access_mask. + */ if (is_stat_open(open_access_mask)) { - fsp->oplock_type = NO_OPLOCK; + oplock_request = NO_OPLOCK; } } @@ -2824,21 +2849,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, * Setup the oplock info in both the shared memory and * file structs. */ - - status = set_file_oplock(fsp); + status = grant_fsp_oplock_type(req, fsp, lck, oplock_request); if (!NT_STATUS_IS_OK(status)) { - /* - * Could not get the kernel oplock - */ - fsp->oplock_type = NO_OPLOCK; - } - - if (!set_share_mode(lck, fsp, get_current_uid(conn), - req ? req->mid : 0, - fsp->oplock_type)) { TALLOC_FREE(lck); fd_close(fsp); - return NT_STATUS_NO_MEMORY; + return status; } /* Handle strange delete on close create semantics. */ -- 1.9.1 From e93bf26c65697650bdcf6b1b696f43e6e0635e5e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 22 Oct 2014 17:53:01 -0700 Subject: [PATCH 09/38] s3:smbd: Don't set fsp->oplock_type before we've granted any oplocks. It's not needed, and may lead to unexpected side effects. grant_fsp_oplock_type() is the only place to touch this. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 1020c5942a996e3e900e2b81f9964dd61fc9c71d) --- source3/smbd/open.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 945850c..28ab434 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -2423,9 +2423,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, * the open is done. */ fsp->posix_open = posix_open; - /* Ensure no SAMBA_PRIVATE bits can be set. */ - fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK); - if (timeval_is_zero(&request_time)) { request_time = fsp->open_time; } -- 1.9.1 From 6707a0f20df6b07c77927c43c067713caad2bf01 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 28 Oct 2014 15:27:09 -0700 Subject: [PATCH 10/38] s3:locking: convert brl_have_read field to brl_num_read. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 837e29035c911f3509135252c3f423d0f56b606d) --- source3/locking/brlock.c | 123 +++++++++++++++++++++-------------------------- source3/locking/proto.h | 6 +-- source3/smbd/open.c | 15 ++---- source3/smbd/oplock.c | 115 +++++++++++++++++++++++++++----------------- source3/smbd/proto.h | 2 + 5 files changed, 135 insertions(+), 126 deletions(-) diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 1c4c4d0..b7dcb41 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -47,7 +47,7 @@ struct byte_range_lock { struct files_struct *fsp; unsigned int num_locks; bool modified; - bool have_read_oplocks; + uint32_t num_read_oplocks; struct lock_struct *lock_data; struct db_record *record; }; @@ -82,18 +82,18 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl) return brl->fsp; } -bool brl_have_read_oplocks(const struct byte_range_lock *brl) +uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl) { - return brl->have_read_oplocks; + return brl->num_read_oplocks; } -void brl_set_have_read_oplocks(struct byte_range_lock *brl, - bool have_read_oplocks) +void brl_set_num_read_oplocks(struct byte_range_lock *brl, + uint32_t num_read_oplocks) { - DEBUG(10, ("Setting have_read_oplocks to %s\n", - have_read_oplocks ? "true" : "false")); + DEBUG(10, ("Setting num_read_oplocks to %"PRIu32"\n", + num_read_oplocks)); SMB_ASSERT(brl->record != NULL); /* otherwise we're readonly */ - brl->have_read_oplocks = have_read_oplocks; + brl->num_read_oplocks = num_read_oplocks; brl->modified = true; } @@ -1841,7 +1841,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid, static void byte_range_lock_flush(struct byte_range_lock *br_lck) { - size_t data_len; unsigned i; struct lock_struct *locks = br_lck->lock_data; @@ -1865,15 +1864,7 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck) } } - data_len = br_lck->num_locks * sizeof(struct lock_struct); - - if (br_lck->have_read_oplocks) { - data_len += 1; - } - - DEBUG(10, ("data_len=%d\n", (int)data_len)); - - if (data_len == 0) { + if ((br_lck->num_locks == 0) && (br_lck->num_read_oplocks == 0)) { /* No locks - delete this entry. */ NTSTATUS status = dbwrap_record_delete(br_lck->record); if (!NT_STATUS_IS_OK(status)) { @@ -1882,19 +1873,20 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck) smb_panic("Could not delete byte range lock entry"); } } else { + size_t lock_len, data_len; TDB_DATA data; NTSTATUS status; + lock_len = br_lck->num_locks * sizeof(struct lock_struct); + data_len = lock_len + sizeof(br_lck->num_read_oplocks); + data.dsize = data_len; data.dptr = talloc_array(talloc_tos(), uint8_t, data_len); SMB_ASSERT(data.dptr != NULL); - memcpy(data.dptr, br_lck->lock_data, - br_lck->num_locks * sizeof(struct lock_struct)); - - if (br_lck->have_read_oplocks) { - data.dptr[data_len-1] = 1; - } + memcpy(data.dptr, br_lck->lock_data, lock_len); + memcpy(data.dptr + lock_len, &br_lck->num_read_oplocks, + sizeof(br_lck->num_read_oplocks)); status = dbwrap_record_store(br_lck->record, data, TDB_REPLACE); TALLOC_FREE(data.dptr); @@ -1917,6 +1909,32 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck) return 0; } +static bool brl_parse_data(struct byte_range_lock *br_lck, TDB_DATA data) +{ + size_t data_len; + + if (data.dsize == 0) { + return true; + } + if (data.dsize % sizeof(struct lock_struct) != + sizeof(br_lck->num_read_oplocks)) { + DEBUG(1, ("Invalid data size: %u\n", (unsigned)data.dsize)); + return false; + } + + br_lck->num_locks = data.dsize / sizeof(struct lock_struct); + data_len = br_lck->num_locks * sizeof(struct lock_struct); + + br_lck->lock_data = talloc_memdup(br_lck, data.dptr, data_len); + if (br_lck->lock_data == NULL) { + DEBUG(1, ("talloc_memdup failed\n")); + return false; + } + memcpy(&br_lck->num_read_oplocks, data.dptr + data_len, + sizeof(br_lck->num_read_oplocks)); + return true; +} + /******************************************************************* Fetch a set of byte range lock data from the database. Leave the record locked. @@ -1926,16 +1944,14 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck) struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp) { TDB_DATA key, data; - struct byte_range_lock *br_lck = talloc(mem_ctx, struct byte_range_lock); + struct byte_range_lock *br_lck; + br_lck = talloc_zero(mem_ctx, struct byte_range_lock); if (br_lck == NULL) { return NULL; } br_lck->fsp = fsp; - br_lck->num_locks = 0; - br_lck->have_read_oplocks = false; - br_lck->modified = False; key.dptr = (uint8 *)&fsp->file_id; key.dsize = sizeof(struct file_id); @@ -1950,30 +1966,12 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp) data = dbwrap_record_get_value(br_lck->record); - br_lck->lock_data = NULL; - - talloc_set_destructor(br_lck, byte_range_lock_destructor); - - br_lck->num_locks = data.dsize / sizeof(struct lock_struct); - - if (br_lck->num_locks != 0) { - br_lck->lock_data = talloc_array( - br_lck, struct lock_struct, br_lck->num_locks); - if (br_lck->lock_data == NULL) { - DEBUG(0, ("malloc failed\n")); - TALLOC_FREE(br_lck); - return NULL; - } - - memcpy(br_lck->lock_data, data.dptr, - talloc_get_size(br_lck->lock_data)); + if (!brl_parse_data(br_lck, data)) { + TALLOC_FREE(br_lck); + return NULL; } - DEBUG(10, ("data.dsize=%d\n", (int)data.dsize)); - - if ((data.dsize % sizeof(struct lock_struct)) == 1) { - br_lck->have_read_oplocks = (data.dptr[data.dsize-1] == 1); - } + talloc_set_destructor(br_lck, byte_range_lock_destructor); if (DEBUGLEVEL >= 10) { unsigned int i; @@ -1999,28 +1997,19 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data, { struct brl_get_locks_readonly_state *state = (struct brl_get_locks_readonly_state *)private_data; - struct byte_range_lock *br_lock; + struct byte_range_lock *br_lck; - br_lock = talloc_pooled_object( + br_lck = talloc_pooled_object( state->mem_ctx, struct byte_range_lock, 1, data.dsize); - if (br_lock == NULL) { + if (br_lck == NULL) { *state->br_lock = NULL; return; } - br_lock->lock_data = (struct lock_struct *)talloc_memdup( - br_lock, data.dptr, data.dsize); - br_lock->num_locks = data.dsize / sizeof(struct lock_struct); - - if ((data.dsize % sizeof(struct lock_struct)) == 1) { - br_lock->have_read_oplocks = (data.dptr[data.dsize-1] == 1); - } else { - br_lock->have_read_oplocks = false; + if (!brl_parse_data(br_lck, data)) { + *state->br_lock = NULL; + return; } - - DEBUG(10, ("Got %d bytes, have_read_oplocks: %s\n", (int)data.dsize, - br_lock->have_read_oplocks ? "true" : "false")); - - *state->br_lock = br_lock; + *state->br_lock = br_lck; } struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) @@ -2063,7 +2052,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) return NULL; } - br_lock->have_read_oplocks = false; + br_lock->num_read_oplocks = 0; br_lock->num_locks = 0; br_lock->lock_data = NULL; diff --git a/source3/locking/proto.h b/source3/locking/proto.h index 44f3ba1..8eccff8 100644 --- a/source3/locking/proto.h +++ b/source3/locking/proto.h @@ -30,9 +30,9 @@ void brl_shutdown(void); unsigned int brl_num_locks(const struct byte_range_lock *brl); struct files_struct *brl_fsp(struct byte_range_lock *brl); -bool brl_have_read_oplocks(const struct byte_range_lock *brl); -void brl_set_have_read_oplocks(struct byte_range_lock *brl, - bool have_read_oplocks); +uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl); +void brl_set_num_read_oplocks(struct byte_range_lock *brl, + uint32_t num_read_oplocks); NTSTATUS brl_lock_windows_default(struct byte_range_lock *br_lck, struct lock_struct *plock, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 28ab434..1952823 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1573,17 +1573,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, return NT_STATUS_NO_MEMORY; } - if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) { - /* - * We're the first level2 oplock. Indicate that in brlock.tdb. - */ - struct byte_range_lock *brl; - - brl = brl_get_locks(talloc_tos(), fsp); - if (brl != NULL) { - brl_set_have_read_oplocks(brl, true); - TALLOC_FREE(brl); - } + ok = update_num_read_oplocks(fsp, lck); + if (!ok) { + del_share_mode(lck, fsp); + return NT_STATUS_INTERNAL_ERROR; } DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 7935092..aa99f68 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -148,6 +148,53 @@ static void downgrade_file_oplock(files_struct *fsp) TALLOC_FREE(fsp->oplock_timeout); } +bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck) +{ + struct share_mode_data *d = lck->data; + struct byte_range_lock *br_lck; + uint32_t num_read_oplocks = 0; + uint32_t i; + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* + * If we're the only one, we don't need a brlock entry + */ + SMB_ASSERT(d->num_share_modes == 1); + SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type)); + return true; + } + + for (i=0; inum_share_modes; i++) { + struct share_mode_entry *e = &d->share_modes[i]; + + if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) { + num_read_oplocks += 1; + continue; + } + + if (LEVEL_II_OPLOCK_TYPE(e->op_type)) { + num_read_oplocks += 1; + continue; + } + } + + br_lck = brl_get_locks_readonly(fsp); + if (br_lck == NULL) { + return false; + } + if (brl_num_read_oplocks(br_lck) == num_read_oplocks) { + return true; + } + + br_lck = brl_get_locks(talloc_tos(), fsp); + if (br_lck == NULL) { + return false; + } + brl_set_num_read_oplocks(br_lck, num_read_oplocks); + TALLOC_FREE(br_lck); + return true; +} + /**************************************************************************** Remove a file oplock. Copes with level II and exclusive. Locks then unlocks the share mode lock. Client can decide to go directly @@ -170,44 +217,6 @@ bool remove_oplock(files_struct *fsp) return False; } - if (fsp->oplock_type == LEVEL_II_OPLOCK) { - - /* - * If we're the only LEVEL_II holder, we have to remove the - * have_read_oplocks from the brlock entry - */ - - struct share_mode_data *data = lck->data; - uint32_t i, num_level2; - - num_level2 = 0; - for (i=0; inum_share_modes; i++) { - if (data->share_modes[i].op_type == LEVEL_II_OPLOCK) { - num_level2 += 1; - } - if (num_level2 > 1) { - /* - * No need to count them all... - */ - break; - } - } - - if (num_level2 == 1) { - /* - * That's only us. We are dropping that level2 oplock, - * so remove the brlock flag. - */ - struct byte_range_lock *brl; - - brl = brl_get_locks(talloc_tos(), fsp); - if (brl) { - brl_set_have_read_oplocks(brl, false); - TALLOC_FREE(brl); - } - } - } - ret = remove_share_oplock(lck, fsp); if (!ret) { DEBUG(0,("remove_oplock: failed to remove share oplock for " @@ -216,6 +225,15 @@ bool remove_oplock(files_struct *fsp) file_id_string_tos(&fsp->file_id))); } release_file_oplock(fsp); + + ret = update_num_read_oplocks(fsp, lck); + if (!ret) { + DEBUG(0, ("%s: update_num_read_oplocks failed for " + "file %s, %s, %s\n", + __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), + file_id_string_tos(&fsp->file_id))); + } + TALLOC_FREE(lck); return ret; } @@ -227,7 +245,6 @@ bool downgrade_oplock(files_struct *fsp) { bool ret; struct share_mode_lock *lck; - struct byte_range_lock *brl; DEBUG(10, ("downgrade_oplock called for %s\n", fsp_str_dbg(fsp))); @@ -245,13 +262,14 @@ bool downgrade_oplock(files_struct *fsp) fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), file_id_string_tos(&fsp->file_id))); } - downgrade_file_oplock(fsp); - brl = brl_get_locks(talloc_tos(), fsp); - if (brl != NULL) { - brl_set_have_read_oplocks(brl, true); - TALLOC_FREE(brl); + ret = update_num_read_oplocks(fsp, lck); + if (!ret) { + DEBUG(0, ("%s: update_num_read_oplocks failed for " + "file %s, %s, %s\n", + __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp), + file_id_string_tos(&fsp->file_id))); } TALLOC_FREE(lck); @@ -596,6 +614,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, struct tevent_immediate *im; struct break_to_none_state *state; struct byte_range_lock *brl; + uint32_t num_read_oplocks = 0; /* * If this file is level II oplocked then we need @@ -613,7 +632,13 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, } brl = brl_get_locks_readonly(fsp); - if ((brl != NULL) && !brl_have_read_oplocks(brl)) { + if (brl != NULL) { + num_read_oplocks = brl_num_read_oplocks(brl); + } + + DEBUG(10, ("num_read_oplocks = %"PRIu32"\n", num_read_oplocks)); + + if (num_read_oplocks == 0) { DEBUG(10, ("No read oplocks around\n")); return; } diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 68c2da2..1080895 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -647,6 +647,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn, /* The following definitions come from smbd/oplock.c */ +bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck); + void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp); NTSTATUS set_file_oplock(files_struct *fsp); bool remove_oplock(files_struct *fsp); -- 1.9.1 From 296ba618940d4d85217419853920c4648ca993ef Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 18:49:46 +0200 Subject: [PATCH 11/38] s3:smb2_break: First test for NT_STATUS_INVALID_OPLOCK_PROTOCOL, then for in_oplock_level being reasonable Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Nov 12 00:03:34 CET 2014 on sn-devel-104 (cherry picked from commit 2d44498740d98edd9d09f12d35dc91d8d17e0c62) --- source3/smbd/smb2_break.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c index 5c079ec..4492456 100644 --- a/source3/smbd/smb2_break.c +++ b/source3/smbd/smb2_break.c @@ -52,11 +52,6 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req) in_oplock_level = CVAL(inbody, 0x02); - if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE && - in_oplock_level != SMB2_OPLOCK_LEVEL_II) { - return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); - } - /* 0x03 1 bytes reserved */ /* 0x04 4 bytes reserved */ in_file_id_persistent = BVAL(inbody, 0x08); @@ -67,6 +62,17 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req) return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); } + /* Are we awaiting a break message ? */ + if (in_fsp->oplock_timeout == NULL) { + return smbd_smb2_request_error( + req, NT_STATUS_INVALID_OPLOCK_PROTOCOL); + } + + if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE && + in_oplock_level != SMB2_OPLOCK_LEVEL_II) { + return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); + } + subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx, req, in_fsp, in_oplock_level); if (subreq == NULL) { @@ -177,12 +183,6 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp))); - /* Are we awaiting a break message ? */ - if (fsp->oplock_timeout == NULL) { - tevent_req_nterror(req, NT_STATUS_INVALID_OPLOCK_PROTOCOL); - return tevent_req_post(req, ev); - } - if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) || (break_to_none)) { result = remove_oplock(fsp); -- 1.9.1 From 5d5918fd9fa40bb4af1d6f24f46a14912f990f26 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 11:06:16 +0100 Subject: [PATCH 12/38] Revert "libcli/smb: mask off SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET for version 1" This reverts commit a6affb7bb3ff595165e708c56ede2181f0bb570f. This is not really needed. The caller should ignore this flag. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 334089c1015ca35ee37d4c9bf5da455e72c3e86e) --- libcli/smb/smb2_lease.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c index 7705256..fc641ff 100644 --- a/libcli/smb/smb2_lease.c +++ b/libcli/smb/smb2_lease.c @@ -48,7 +48,6 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len, switch (version) { case 1: ZERO_STRUCT(lease->parent_lease_key); - lease->lease_flags &= ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET; lease->lease_epoch = 0; break; case 2: -- 1.9.1 From 4101ab280391e6d70be1bf87fdcb9fc820b5e5db Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 13 Nov 2014 12:10:46 +0100 Subject: [PATCH 13/38] s3:smb2_create: send interim responses after 0.5 milliseconds We don't have to care about delayed NT_STATUS_SHARING_VIOLATION anymore after the following commit: commit 4111fcfd4f570d39d46a0d414546ca62c7b609be Author: Jeremy Allison Date: Thu May 2 11:12:47 2013 -0700 Only do the 1 second delay for sharing violations for SMB1, not SMB2. Match Windows behavior. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit fe0ad5c66884da5e7cba5c7b0d40a8c41cf6a63b) --- source3/smbd/smb2_create.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index 48bc486..1e54801 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -241,13 +241,7 @@ NTSTATUS smbd_smb2_request_process_create(struct smbd_smb2_request *smb2req) } tevent_req_set_callback(tsubreq, smbd_smb2_request_create_done, smb2req); - /* - * For now we keep the logic that we do not send STATUS_PENDING - * for sharing violations, so we just wait 2 seconds. - * - * TODO: we need more tests for this. - */ - return smbd_smb2_request_pending_queue(smb2req, tsubreq, 2000000); + return smbd_smb2_request_pending_queue(smb2req, tsubreq, 500); } static uint64_t get_mid_from_smb2req(struct smbd_smb2_request *smb2req) -- 1.9.1 From a562d22474320041035836c73e2b5d948631aebd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:30:31 +0100 Subject: [PATCH 14/38] s4:libcli/smb_composite: use the options on the transport These are the options which really belong to the connection and might not be the the same as the hints given from the caller. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 55750f010423b0548e9d6b8f5745d7ad5fc4b773) --- source4/libcli/smb_composite/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/libcli/smb_composite/connect.c b/source4/libcli/smb_composite/connect.c index 14b53e5..d87d5ec 100644 --- a/source4/libcli/smb_composite/connect.c +++ b/source4/libcli/smb_composite/connect.c @@ -297,7 +297,7 @@ static NTSTATUS connect_send_negprot(struct composite_context *c, state->subreq = smb_raw_negotiate_send(state, state->transport->ev, state->transport, - io->in.options.max_protocol); + state->transport->options.max_protocol); NT_STATUS_HAVE_NO_MEMORY(state->subreq); tevent_req_set_callback(state->subreq, subreq_handler, c); state->stage = CONNECT_NEGPROT; -- 1.9.1 From c9db7736b836ed3d2d15d81c2a2a8b04df40fac6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:32:40 +0100 Subject: [PATCH 15/38] s4:libcli/raw: fix up the max_protocol value for the current transport connection This allows the caller to pass PROTOCOL_DEFAULT, which results in PROTOCOL_NT1. As smbcli_transport_init() is about SMB1 only we downgrade to PROTOCOL_NT1 if a higher value (for SMB2 or SMB3) was given. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit bc83e45a19deccfb0106c9ad79b0624b1c5d1fa7) --- source4/libcli/raw/clitransport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source4/libcli/raw/clitransport.c b/source4/libcli/raw/clitransport.c index f9d96b5..d0dd1f9 100644 --- a/source4/libcli/raw/clitransport.c +++ b/source4/libcli/raw/clitransport.c @@ -58,6 +58,14 @@ struct smbcli_transport *smbcli_transport_init(struct smbcli_socket *sock, transport->ev = sock->event.ctx; transport->options = *options; + if (transport->options.max_protocol == PROTOCOL_DEFAULT) { + transport->options.max_protocol = PROTOCOL_NT1; + } + + if (transport->options.max_protocol > PROTOCOL_NT1) { + transport->options.max_protocol = PROTOCOL_NT1; + } + TALLOC_FREE(sock->event.fde); TALLOC_FREE(sock->event.te); -- 1.9.1 From b37f194d7dae02ffaf48f5657aee95527629a872 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:35:38 +0100 Subject: [PATCH 16/38] s4:libcli/smb2: allow the caller to specify a specific value for max_protocol. The default is still PROTOCOL_LATEST. As smb2_connect*() is about SMB2/3 only we upgrade to PROTOCOL_LATEST if PROTOCOL_NT1 or lower is given. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit d0a1995b934895ae9f0bbeed52772a4ef5d4b0dd) --- source4/libcli/smb2/connect.c | 3 ++- source4/libcli/smb2/transport.c | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c index c81bd67..9535380 100644 --- a/source4/libcli/smb2/connect.c +++ b/source4/libcli/smb2/connect.c @@ -149,7 +149,8 @@ static void smb2_connect_socket_done(struct composite_context *creq) subreq = smbXcli_negprot_send(state, state->ev, state->transport->conn, timeout_msec, - PROTOCOL_SMB2_02, PROTOCOL_LATEST); + PROTOCOL_SMB2_02, + state->transport->options.max_protocol); if (tevent_req_nomem(subreq, req)) { return; } diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index 9b0c146..e3b2954 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -55,6 +55,14 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, transport->ev = sock->event.ctx; transport->options = *options; + if (transport->options.max_protocol == PROTOCOL_DEFAULT) { + transport->options.max_protocol = PROTOCOL_LATEST; + } + + if (transport->options.max_protocol < PROTOCOL_SMB2_02) { + transport->options.max_protocol = PROTOCOL_LATEST; + } + TALLOC_FREE(sock->event.fde); TALLOC_FREE(sock->event.te); -- 1.9.1 From efab43633c3c544bc736ce50184b7abab675bdd5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:38:09 +0100 Subject: [PATCH 17/38] s4:param: don't expand PROTOCOL_DEFAULT in lpcfg_smbcli_options() We let the low-level smb1 or smb2 code assign what PROTOCOL_DEFAULT means. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit d7669ea451fa445b6ec77fa08d951fa59b08ab5f) --- source4/param/loadparm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/param/loadparm.c b/source4/param/loadparm.c index 529c828..af3313f 100644 --- a/source4/param/loadparm.c +++ b/source4/param/loadparm.c @@ -40,7 +40,7 @@ void lpcfg_smbcli_options(struct loadparm_context *lp_ctx, options->signing = lpcfg_client_signing(lp_ctx); options->request_timeout = SMB_REQUEST_TIMEOUT; options->ntstatus_support = lpcfg_nt_status_support(lp_ctx); - options->max_protocol = lpcfg_client_max_protocol(lp_ctx); + options->max_protocol = lpcfg__client_max_protocol(lp_ctx); options->unicode = lpcfg_unicode(lp_ctx); options->use_oplocks = true; options->use_level2_oplocks = true; -- 1.9.1 From fe7f82509d3b6b3141546719de8d8f8f1e12109d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:33:13 +0100 Subject: [PATCH 18/38] s4:libcli/smb2: add new_epoch to struct smb2_lease_break Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 3327615c23fbfd26b572b01acfe7e1402f018a16) --- source4/libcli/raw/interfaces.h | 1 + source4/libcli/smb2/transport.c | 1 + 2 files changed, 2 insertions(+) diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index 9003c12..05f5da0 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -57,6 +57,7 @@ struct smb2_handle { struct smb2_lease_break { struct smb2_lease current_lease; + uint16_t new_epoch; /* only for v2 leases */ uint32_t break_flags; uint32_t new_lease_state; uint32_t break_reason; /* should be 0 */ diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index e3b2954..166f34b 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -398,6 +398,7 @@ static void smb2_transport_break_handler(struct tevent_req *subreq) struct smb2_lease_break lb; ZERO_STRUCT(lb); + lb.new_epoch = SVAL(body, 0x2); lb.break_flags = SVAL(body, 0x4); memcpy(&lb.current_lease.lease_key, body+0x8, sizeof(struct smb2_lease_key)); -- 1.9.1 From 3c58632dc7f52d68ab691b6fdfe158462908220e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 11:13:15 +0100 Subject: [PATCH 19/38] s4:libcli/smb2: initialize ls->lease_version Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 38b0fded58f9e066ad7190f7797cdfb92d3d23ff) --- source4/libcli/smb2/create.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index 10fe4f7..550069a 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -385,9 +385,11 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct switch (io->out.blobs.blobs[i].data.length) { case 32: ls = &io->out.lease_response; + ls->lease_version = 1; break; case 52: ls = &io->out.lease_response_v2; + ls->lease_version = 2; break; default: smb2_request_destroy(req); -- 1.9.1 From 5bae844aecc2d3d1f03ed00db7601c3fd824492d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:19:49 +0100 Subject: [PATCH 20/38] s4:torture/smb2: skip lease tests if the server doesn't support them Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 683b9569487bd30ecd0ba677367dbe9e0eff16c2) --- source4/torture/smb2/lease.c | 62 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 4326958..e06faea 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -142,12 +142,14 @@ static bool test_lease_request(struct torture_context *tctx, CHECK_LEASE(&io, "RHW", true, LEASE1); /* But will reject leases on directories. */ - smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW")); - status = smb2_create(tree, mem_ctx, &io); - CHECK_STATUS(status, NT_STATUS_OK); - CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); - CHECK_LEASE(&io, "", false, 0); - smb2_util_close(tree, io.out.file.handle); + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); + CHECK_LEASE(&io, "", false, 0); + smb2_util_close(tree, io.out.file.handle); + } /* Also rejects multiple files leased under the same key. */ smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW")); @@ -1191,6 +1193,21 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx, NTSTATUS status; const char *fname = "lease.dat"; bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + torture_skip(tctx, "directory leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } smb2_util_unlink(tree, fname); @@ -1230,6 +1247,7 @@ static bool test_lease_break_twice(struct torture_context *tctx, const char *fname = "lease.dat"; bool ret = true; uint32_t caps; + enum protocol_types protocol; caps = smb2cli_conn_server_capabilities( tree->session->transport->conn); @@ -1237,6 +1255,11 @@ static bool test_lease_break_twice(struct torture_context *tctx, torture_skip(tctx, "leases are not supported"); } + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + smb2_util_unlink(tree, fname); ZERO_STRUCT(break_info); @@ -1297,6 +1320,21 @@ static bool test_lease_v2_request(struct torture_context *tctx, const char *dnamefname = "lease.dir\\lease.dat"; const char *dnamefname2 = "lease.dir\\lease2.dat"; bool ret = true; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { + torture_skip(tctx, "directory leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } smb2_util_unlink(tree, fname); smb2_deltree(tree, dname); @@ -1427,6 +1465,18 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx, const char *fname = "lease.dat"; bool ret = true; NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } smb2_util_unlink(tree, fname); -- 1.9.1 From a75d6f92d9eda9991806c3e9209062495ac7935f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:23:31 +0100 Subject: [PATCH 21/38] s4:torture/smb2: make lease tests more reliable by calling torture_wait_for_lease_break() They now work against w2k8r2, w2012, w2012r2. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 34926bd4bd13ea6518273e94473fbb47b7cf565e) --- source4/torture/smb2/lease.c | 77 +++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index e06faea..5aade0c 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -406,14 +406,35 @@ static struct { int oplock_failures; } break_info; +#define CHECK_NO_BREAK(tctx) \ + do { \ + torture_wait_for_lease_break(tctx); \ + CHECK_VAL(break_info.failures, 0); \ + CHECK_VAL(break_info.count, 0); \ + CHECK_VAL(break_info.oplock_failures, 0); \ + CHECK_VAL(break_info.oplock_count, 0); \ + } while(0) + +#define CHECK_OPLOCK_BREAK(__brokento) \ + do { \ + torture_wait_for_lease_break(tctx); \ + CHECK_VAL(break_info.oplock_count, 1); \ + CHECK_VAL(break_info.oplock_failures, 0); \ + CHECK_VAL(break_info.oplock_level, \ + smb2_util_oplock_level(__brokento)); \ + break_info.held_oplock_level = break_info.oplock_level; \ + } while(0) + #define CHECK_BREAK_INFO(__oldstate, __state, __key) \ do { \ + torture_wait_for_lease_break(tctx); \ CHECK_VAL(break_info.failures, 0); \ CHECK_VAL(break_info.count, 1); \ CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \ (__state), (__key)); \ if (break_info.lease_break.break_flags & \ SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \ + torture_wait_for_lease_break(tctx); \ CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \ (__state), (__key)); \ } \ @@ -819,9 +840,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx, /* Make sure we don't break ourselves on write */ status = smb2_util_write(tree, h1, &c, 0, 1); - torture_wait_for_lease_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); + CHECK_STATUS(status, NT_STATUS_OK); CHECK_BREAK_INFO("R", "", LEASE2); /* Try the other way round. First, upgrade LEASE2 to R again */ @@ -837,18 +856,14 @@ static bool test_lease_nobreakself(struct torture_context *tctx, ZERO_STRUCT(break_info); status = smb2_util_write(tree, h2, &c, 0, 1); - torture_wait_for_lease_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); + CHECK_STATUS(status, NT_STATUS_OK); CHECK_BREAK_INFO("R", "", LEASE1); /* .. and break LEASE2 via h1 */ ZERO_STRUCT(break_info); status = smb2_util_write(tree, h1, &c, 0, 1); - torture_wait_for_lease_break(tctx); - CHECK_VAL(break_info.count, 1); - CHECK_VAL(break_info.failures, 0); + CHECK_STATUS(status, NT_STATUS_OK); CHECK_BREAK_INFO("R", "", LEASE2); done: @@ -992,8 +1007,7 @@ static bool test_lease_oplock(struct torture_context *tctx, if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { CHECK_BREAK_INFO(held, brokento, LEASE1); } else { - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); + CHECK_NO_BREAK(tctx); } smb2_util_close(tree, h); @@ -1033,13 +1047,9 @@ static bool test_lease_oplock(struct torture_context *tctx, CHECK_LEASE(&io, granted, true, LEASE1); if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) { - CHECK_VAL(break_info.oplock_count, 1); - CHECK_VAL(break_info.oplock_failures, 0); - CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento)); - break_info.held_oplock_level = break_info.oplock_level; + CHECK_OPLOCK_BREAK(brokento); } else { - CHECK_VAL(break_info.oplock_count, 0); - CHECK_VAL(break_info.oplock_failures, 0); + CHECK_NO_BREAK(tctx); } smb2_util_close(tree, h); @@ -1141,8 +1151,7 @@ static bool test_lease_multibreak(struct torture_context *tctx, break_info.held_oplock_level = io.out.oplock_level; /* Verify no breaks. */ - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); + CHECK_NO_BREAK(tctx); /* Open for truncate, force a break. */ smb2_generic_create(&io, NULL, false, fname, @@ -1165,9 +1174,7 @@ static bool test_lease_multibreak(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); /* Verify one oplock break, one lease break. */ - CHECK_VAL(break_info.oplock_count, 1); - CHECK_VAL(break_info.oplock_failures, 0); - CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level("")); + CHECK_OPLOCK_BREAK(""); CHECK_BREAK_INFO("R", "", LEASE1); done: @@ -1384,9 +1391,7 @@ static bool test_lease_v2_request(struct torture_context *tctx, CHECK_LEASE_V2(&io, "RHW", true, LEASE3, SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2); - torture_wait_for_lease_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); + CHECK_NO_BREAK(tctx); ZERO_STRUCT(io); smb2_lease_v2_create_share(&io, &ls, false, dnamefname2, @@ -1400,10 +1405,7 @@ static bool test_lease_v2_request(struct torture_context *tctx, CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0); - torture_wait_for_lease_break(tctx); - torture_wait_for_lease_break(tctx); CHECK_BREAK_INFO("RH", "", LEASE2); - torture_wait_for_lease_break(tctx); ZERO_STRUCT(break_info); @@ -1429,16 +1431,19 @@ static bool test_lease_v2_request(struct torture_context *tctx, status = smb2_write(tree, &w); CHECK_STATUS(status, NT_STATUS_OK); - smb_msleep(2000); - torture_wait_for_lease_break(tctx); - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); - + /* + * Wait 4 seconds in order to check if the write time + * was updated (after 2 seconds). + */ + smb_msleep(4000); + CHECK_NO_BREAK(tctx); + + /* + * only the close on the modified file break the + * directory lease. + */ smb2_util_close(tree, h4); - torture_wait_for_lease_break(tctx); - torture_wait_for_lease_break(tctx); CHECK_BREAK_INFO("RH", "", LEASE2); - torture_wait_for_lease_break(tctx); done: smb2_util_close(tree, h1); -- 1.9.1 From 1cbc22d7434ded0164201021b4cf51aba9f7996e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:24:16 +0100 Subject: [PATCH 22/38] s4:torture/smb2: lease per test fnames Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 2742257be693d57c075523e2691137692aa9e34a) --- source4/torture/smb2/lease.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 5aade0c..ef88473 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -116,10 +116,10 @@ static bool test_lease_request(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h1, h2; NTSTATUS status; - const char *fname = "lease.dat"; - const char *fname2 = "lease2.dat"; - const char *sname = "lease.dat:stream"; - const char *dname = "lease.dir"; + const char *fname = "lease_request.dat"; + const char *fname2 = "lease_request.2.dat"; + const char *sname = "lease_request.dat:stream"; + const char *dname = "lease_request.dir"; bool ret = true; int i; uint32_t caps; @@ -204,7 +204,7 @@ static bool test_lease_upgrade(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h, hnew; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_upgrade.dat"; bool ret = true; uint32_t caps; @@ -328,7 +328,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx, NTSTATUS status; struct smb2_create io; struct smb2_lease ls; - const char *fname = "lease.dat"; + const char *fname = "lease_upgrade2.dat"; bool ret = true; int i; uint32_t caps; @@ -545,7 +545,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx, NTSTATUS status; struct smb2_create io; struct smb2_lease ls; - const char *fname = "upgrade3.dat"; + const char *fname = "lease_upgrade3.dat"; bool ret = true; int i; uint32_t caps; @@ -711,7 +711,7 @@ static bool test_lease_break(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h, h2, h3; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_break.dat"; bool ret = true; int i; uint32_t caps; @@ -803,7 +803,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h1, h2; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_nobreakself.dat"; bool ret = true; uint32_t caps; char c = 0; @@ -958,7 +958,7 @@ static bool test_lease_oplock(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h, h2; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_oplock.dat"; bool ret = true; int i; uint32_t caps; @@ -1079,7 +1079,7 @@ static bool test_lease_multibreak(struct torture_context *tctx, struct smb2_handle h, h2, h3; struct smb2_write w; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_multibreak.dat"; bool ret = true; uint32_t caps; @@ -1198,7 +1198,7 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx, struct smb2_handle h1; uint64_t parent = LEASE2; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_v2_request_parent.dat"; bool ret = true; uint32_t caps; enum protocol_types protocol; @@ -1251,7 +1251,7 @@ static bool test_lease_break_twice(struct torture_context *tctx, struct smb2_lease ls; struct smb2_handle h1; NTSTATUS status; - const char *fname = "lease.dat"; + const char *fname = "lease_break_twice.dat"; bool ret = true; uint32_t caps; enum protocol_types protocol; @@ -1322,10 +1322,10 @@ static bool test_lease_v2_request(struct torture_context *tctx, struct smb2_handle h1, h2, h3, h4, h5; struct smb2_write w; NTSTATUS status; - const char *fname = "lease.dat"; - const char *dname = "lease.dir"; - const char *dnamefname = "lease.dir\\lease.dat"; - const char *dnamefname2 = "lease.dir\\lease2.dat"; + const char *fname = "lease_v2_request.dat"; + const char *dname = "lease_v2_request.dir"; + const char *dnamefname = "lease_v2_request.dir\\lease.dat"; + const char *dnamefname2 = "lease_v2_request.dir\\lease2.dat"; bool ret = true; uint32_t caps; enum protocol_types protocol; @@ -1467,7 +1467,7 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx, struct smb2_create io; struct smb2_lease ls; struct smb2_handle h; - const char *fname = "lease.dat"; + const char *fname = "lease_v2_epoch1.dat"; bool ret = true; NTSTATUS status; uint32_t caps; -- 1.9.1 From 54b95dae083d73223c79ca77873be92084397c7d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 17:00:44 +0100 Subject: [PATCH 23/38] s4:torture/smb2: verify lease_flags in CHECK_LEASE_BREAK() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 8fa2fb7dde10640ded645d8180d441012dbad155) --- source4/torture/smb2/lease.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index ef88473..999c9d4 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -377,10 +377,18 @@ static bool test_lease_upgrade2(struct torture_context *tctx, #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \ do { \ - CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state)); \ - CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \ + uint16_t __new = smb2_util_lease_state(__state); \ + uint16_t __old = smb2_util_lease_state(__oldstate); \ + CHECK_VAL((__lb)->new_lease_state, __new); \ + CHECK_VAL((__lb)->current_lease.lease_state, __old); \ CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \ CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \ + if (__old & (SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE)) { \ + CHECK_VAL((__lb)->break_flags, \ + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); \ + } else { \ + CHECK_VAL((__lb)->break_flags, 0); \ + } \ } while(0) #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \ -- 1.9.1 From 9765ac5f8bb788fdace6637f231ea73ea4d4c5a8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:35:07 +0100 Subject: [PATCH 24/38] s4:torture/smb2: always verify the v2 lease epoch. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit c0f2b46d69dc7e729bac766785b26a1911a80825) --- source4/torture/smb2/lease.c | 115 ++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 999c9d4..d10dcc5 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -53,6 +53,7 @@ #define CHECK_LEASE(__io, __state, __oplevel, __key) \ do { \ + CHECK_VAL((__io)->out.lease_response.lease_version, 1); \ if (__oplevel) { \ CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \ @@ -67,10 +68,12 @@ \ CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \ CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \ } while(0) -#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent) \ +#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \ do { \ + CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \ if (__oplevel) { \ CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \ CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \ @@ -89,6 +92,7 @@ CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \ } \ CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \ + CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \ } while(0) static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull; @@ -147,7 +151,7 @@ static bool test_lease_request(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); - CHECK_LEASE(&io, "", false, 0); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); smb2_util_close(tree, io.out.file.handle); } @@ -403,6 +407,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx, static struct { struct smb2_lease_break lease_break; + struct smb2_transport *lease_transport; struct smb2_lease_break_ack lease_break_ack; int count; int failures; @@ -433,7 +438,7 @@ static struct { break_info.held_oplock_level = break_info.oplock_level; \ } while(0) -#define CHECK_BREAK_INFO(__oldstate, __state, __key) \ +#define _CHECK_BREAK_INFO(__oldstate, __state, __key) \ do { \ torture_wait_for_lease_break(tctx); \ CHECK_VAL(break_info.failures, 0); \ @@ -448,6 +453,20 @@ static struct { } \ } while(0) +#define CHECK_BREAK_INFO(__oldstate, __state, __key) \ + do { \ + _CHECK_BREAK_INFO(__oldstate, __state, __key); \ + CHECK_VAL(break_info.lease_break.new_epoch, 0); \ + } while(0) + +#define CHECK_BREAK_INFO_V2(__transport, __oldstate, __state, __key, __epoch) \ + do { \ + _CHECK_BREAK_INFO(__oldstate, __state, __key); \ + CHECK_VAL(break_info.lease_break.new_epoch, __epoch); \ + CHECK_VAL((uintptr_t)break_info.lease_transport, \ + (uintptr_t)__transport); \ + } while(0) + static void torture_lease_break_callback(struct smb2_request *req) { NTSTATUS status; @@ -468,6 +487,7 @@ static bool torture_lease_handler(struct smb2_transport *transport, struct smb2_lease_break_ack io; struct smb2_request *req; + break_info.lease_transport = transport; break_info.lease_break = *lb; break_info.count++; @@ -765,8 +785,7 @@ static bool test_lease_break(struct torture_context *tctx, if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { CHECK_BREAK_INFO(held, brokento, LEASE1); } else { - CHECK_VAL(break_info.count, 0); - CHECK_VAL(break_info.failures, 0); + CHECK_NO_BREAK(tctx); } ZERO_STRUCT(break_info); @@ -1233,14 +1252,15 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx, smb2_util_share_access("RWD"), LEASE1, &parent, smb2_util_lease_state("RHW"), - 0); + 0x11); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE1, - SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2); + SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2, + ls.lease_epoch + 1); done: smb2_util_close(tree, h1); @@ -1256,7 +1276,8 @@ static bool test_lease_break_twice(struct torture_context *tctx, { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; - struct smb2_lease ls; + struct smb2_lease ls1; + struct smb2_lease ls2; struct smb2_handle h1; NTSTATUS status; const char *fname = "lease_break_twice.dat"; @@ -1279,17 +1300,16 @@ static bool test_lease_break_twice(struct torture_context *tctx, ZERO_STRUCT(break_info); ZERO_STRUCT(io); - ZERO_STRUCT(ls); smb2_lease_v2_create_share( - &io, &ls, false, fname, smb2_util_share_access("RWD"), - LEASE1, NULL, smb2_util_lease_state("RWH"), 0); + &io, &ls1, false, fname, smb2_util_share_access("RWD"), + LEASE1, NULL, smb2_util_lease_state("RWH"), 0x11); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1); tree->session->transport->lease.handler = torture_lease_handler; tree->session->transport->lease.private_data = tree; @@ -1297,22 +1317,25 @@ static bool test_lease_break_twice(struct torture_context *tctx, ZERO_STRUCT(break_info); smb2_lease_v2_create_share( - &io, &ls, false, fname, smb2_util_share_access("R"), - LEASE2, NULL, smb2_util_lease_state("RWH"), 0); + &io, &ls2, false, fname, smb2_util_share_access("R"), + LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION); - CHECK_BREAK_INFO("RWH", "RW", LEASE1); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RW", LEASE1, ls1.lease_epoch + 2); smb2_lease_v2_create_share( - &io, &ls, false, fname, smb2_util_share_access("RWD"), - LEASE2, NULL, smb2_util_lease_state("RWH"), 0); + &io, &ls2, false, fname, smb2_util_share_access("RWD"), + LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22); ZERO_STRUCT(break_info); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); - CHECK_BREAK_INFO("RW", "R", LEASE1); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RW", "R", LEASE1, ls1.lease_epoch + 3); done: smb2_util_close(tree, h1); @@ -1326,7 +1349,7 @@ static bool test_lease_v2_request(struct torture_context *tctx, { TALLOC_CTX *mem_ctx = talloc_new(tctx); struct smb2_create io; - struct smb2_lease ls; + struct smb2_lease ls1, ls2, ls2t, ls3, ls4; struct smb2_handle h1, h2, h3, h4, h5; struct smb2_write w; NTSTATUS status; @@ -1362,73 +1385,75 @@ static bool test_lease_v2_request(struct torture_context *tctx, ZERO_STRUCT(break_info); ZERO_STRUCT(io); - smb2_lease_v2_create_share(&io, &ls, false, fname, + smb2_lease_v2_create_share(&io, &ls1, false, fname, smb2_util_share_access("RWD"), LEASE1, NULL, smb2_util_lease_state("RHW"), - 0); + 0x11); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1); ZERO_STRUCT(io); - smb2_lease_v2_create_share(&io, &ls, true, dname, + smb2_lease_v2_create_share(&io, &ls2, true, dname, smb2_util_share_access("RWD"), LEASE2, NULL, smb2_util_lease_state("RHW"), - 0); + 0x22); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY); - CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1); ZERO_STRUCT(io); - smb2_lease_v2_create_share(&io, &ls, false, dnamefname, + smb2_lease_v2_create_share(&io, &ls3, false, dnamefname, smb2_util_share_access("RWD"), LEASE3, &LEASE2, smb2_util_lease_state("RHW"), - 0); + 0x33); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); CHECK_LEASE_V2(&io, "RHW", true, LEASE3, - SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2); + SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2, + ls3.lease_epoch + 1); CHECK_NO_BREAK(tctx); ZERO_STRUCT(io); - smb2_lease_v2_create_share(&io, &ls, false, dnamefname2, + smb2_lease_v2_create_share(&io, &ls4, false, dnamefname2, smb2_util_share_access("RWD"), LEASE4, NULL, smb2_util_lease_state("RHW"), - 0); + 0x44); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h4 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0); + CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0, ls4.lease_epoch + 1); - CHECK_BREAK_INFO("RH", "", LEASE2); + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "", LEASE2, ls2.lease_epoch + 2); ZERO_STRUCT(break_info); ZERO_STRUCT(io); - smb2_lease_v2_create_share(&io, &ls, true, dname, + smb2_lease_v2_create_share(&io, &ls2t, true, dname, smb2_util_share_access("RWD"), LEASE2, NULL, smb2_util_lease_state("RHW"), - 0); + 0x222); io.in.create_disposition = NTCREATEX_DISP_OPEN; status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h5 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY); - CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0); + CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch+3); smb2_util_close(tree, h5); ZERO_STRUCT(w); @@ -1451,7 +1476,9 @@ static bool test_lease_v2_request(struct torture_context *tctx, * directory lease. */ smb2_util_close(tree, h4); - CHECK_BREAK_INFO("RH", "", LEASE2); + + CHECK_BREAK_INFO_V2(tree->session->transport, + "RH", "", LEASE2, ls2.lease_epoch+4); done: smb2_util_close(tree, h1); @@ -1505,26 +1532,26 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx, smb2_util_share_access("RWD"), LEASE1, NULL, smb2_util_lease_state("RHW"), - 0); - ls.lease_epoch = 0x4711; - + 0x4711); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0); - CHECK_VAL(io.out.lease_response_v2.lease_epoch, ls.lease_epoch + 1); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1); smb2_util_close(tree, h); smb2_util_unlink(tree, fname); - ls.lease_state = smb2_util_lease_state("RH"); + smb2_lease_v2_create_share(&io, &ls, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0); - CHECK_VAL(io.out.lease_response_v2.lease_epoch, ls.lease_epoch + 1); + CHECK_LEASE_V2(&io, "RWH", true, LEASE1, 0, 0, ls.lease_epoch + 1); smb2_util_close(tree, h); done: -- 1.9.1 From 627782543b810e26bde2de223f0fda4de4993bb0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 09:15:58 +0100 Subject: [PATCH 25/38] s4:torture/smb2: don't check the lease break connection against samba3 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit a9d46264ab3eb53cf4a70edf4e5dad2fe6d86e6c) --- source4/torture/smb2/lease.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index d10dcc5..2c5cbd0 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -25,6 +25,7 @@ #include "libcli/smb2/smb2_calls.h" #include "torture/torture.h" #include "torture/smb2/proto.h" +#include "torture/util.h" #include "libcli/smb/smbXcli_base.h" #define CHECK_VAL(v, correct) do { \ @@ -463,8 +464,10 @@ static struct { do { \ _CHECK_BREAK_INFO(__oldstate, __state, __key); \ CHECK_VAL(break_info.lease_break.new_epoch, __epoch); \ - CHECK_VAL((uintptr_t)break_info.lease_transport, \ - (uintptr_t)__transport); \ + if (!TARGET_IS_SAMBA3(tctx)) { \ + CHECK_VAL((uintptr_t)break_info.lease_transport, \ + (uintptr_t)__transport); \ + } \ } while(0) static void torture_lease_break_callback(struct smb2_request *req) -- 1.9.1 From 80408fab860985264c9ca91a72228d70432cb178 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:41:50 +0100 Subject: [PATCH 26/38] s4:torture/smb2: pass the expected flags to CHECK_LEASE() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 90c886e9656a1360ffdb8eaa80ad2b6c987f73ef) --- source4/torture/smb2/lease.c | 52 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 2c5cbd0..d81ddd3 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -52,7 +52,7 @@ CHECK_VAL((__io)->out.reserved2, 0); \ } while(0) -#define CHECK_LEASE(__io, __state, __oplevel, __key) \ +#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \ do { \ CHECK_VAL((__io)->out.lease_response.lease_version, 1); \ if (__oplevel) { \ @@ -67,7 +67,7 @@ CHECK_VAL((__io)->out.lease_response.lease_state, 0); \ } \ \ - CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \ + CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \ CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \ } while(0) @@ -144,7 +144,7 @@ static bool test_lease_request(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RHW", true, LEASE1); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); /* But will reject leases on directories. */ if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) { @@ -167,7 +167,7 @@ static bool test_lease_request(struct torture_context *tctx, h2 = io.out.file.handle; CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RHW", true, LEASE2); + CHECK_LEASE(&io, "RHW", true, LEASE2, 0); smb2_util_close(tree, h2); smb2_util_close(tree, h1); @@ -184,7 +184,7 @@ static bool test_lease_request(struct torture_context *tctx, h2 = io.out.file.handle; CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, request_results[i][1], true, LEASE1); + CHECK_LEASE(&io, request_results[i][1], true, LEASE1, 0); smb2_util_close(tree, io.out.file.handle); } @@ -225,7 +225,7 @@ static bool test_lease_upgrade(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RH", true, LEASE1); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); h = io.out.file.handle; /* Upgrades (sidegrades?) to RW leave us with an RH. */ @@ -233,7 +233,7 @@ static bool test_lease_upgrade(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RH", true, LEASE1); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); hnew = io.out.file.handle; smb2_util_close(tree, hnew); @@ -243,7 +243,7 @@ static bool test_lease_upgrade(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RHW", true, LEASE1); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); hnew = io.out.file.handle; smb2_util_close(tree, h); @@ -254,7 +254,7 @@ static bool test_lease_upgrade(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RHW", true, LEASE1); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); hnew = io.out.file.handle; smb2_util_close(tree, hnew); @@ -353,7 +353,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, t.initial, true, LEASE1); + CHECK_LEASE(&io, t.initial, true, LEASE1, 0); h = io.out.file.handle; /* Upgrade. */ @@ -361,7 +361,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, t.expected, true, LEASE1); + CHECK_LEASE(&io, t.expected, true, LEASE1, 0); hnew = io.out.file.handle; smb2_util_close(tree, hnew); @@ -603,7 +603,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, t.held1, true, LEASE1); + CHECK_LEASE(&io, t.held1, true, LEASE1, 0); h = io.out.file.handle; /* grab second lease */ @@ -611,7 +611,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, t.held2, true, LEASE2); + CHECK_LEASE(&io, t.held2, true, LEASE2, 0); h2 = io.out.file.handle; /* no break has happened */ @@ -623,7 +623,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx, status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, t.upgraded_to, true, LEASE1); + CHECK_LEASE(&io, t.upgraded_to, true, LEASE1, 0); hnew = io.out.file.handle; /* no break has happened */ @@ -775,7 +775,7 @@ static bool test_lease_break(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, held, true, LEASE1); + CHECK_LEASE(&io, held, true, LEASE1, 0); /* Possibly contend lease. */ smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend)); @@ -783,7 +783,7 @@ static bool test_lease_break(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, granted, true, LEASE2); + CHECK_LEASE(&io, granted, true, LEASE2, 0); if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) { CHECK_BREAK_INFO(held, brokento, LEASE1); @@ -802,7 +802,7 @@ static bool test_lease_break(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, brokento, true, LEASE1); + CHECK_LEASE(&io, brokento, true, LEASE1, 0); CHECK_VAL(break_info.count, 0); CHECK_VAL(break_info.failures, 0); @@ -853,14 +853,14 @@ static bool test_lease_nobreakself(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h1 = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "R", true, LEASE1); + CHECK_LEASE(&io, "R", true, LEASE1, 0); smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("R")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; - CHECK_LEASE(&io, "R", true, LEASE2); + CHECK_LEASE(&io, "R", true, LEASE2, 0); ZERO_STRUCT(break_info); @@ -879,7 +879,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx, smb2_util_lease_state("R")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); - CHECK_LEASE(&io, "R", true, LEASE2); + CHECK_LEASE(&io, "R", true, LEASE2, 0); smb2_util_close(tree, io.out.file.handle); /* Now break LEASE1 via h2 */ @@ -1023,7 +1023,7 @@ static bool test_lease_oplock(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, held, true, LEASE1); + CHECK_LEASE(&io, held, true, LEASE1, 0); /* Does an oplock contend the lease? */ smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend)); @@ -1074,7 +1074,7 @@ static bool test_lease_oplock(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, granted, true, LEASE1); + CHECK_LEASE(&io, granted, true, LEASE1, 0); if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) { CHECK_OPLOCK_BREAK(brokento); @@ -1133,14 +1133,14 @@ static bool test_lease_multibreak(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RH", true, LEASE1); + CHECK_LEASE(&io, "RH", true, LEASE1, 0); smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW")); status = smb2_create(tree, mem_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); h2 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RHW", true, LEASE1); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); /* Contend with LEASE2. */ smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW")); @@ -1148,7 +1148,7 @@ static bool test_lease_multibreak(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h3 = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "RH", true, LEASE2); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); /* Verify that we were only sent one break. */ CHECK_BREAK_INFO("RHW", "RH", LEASE1); @@ -1169,7 +1169,7 @@ static bool test_lease_multibreak(struct torture_context *tctx, CHECK_STATUS(status, NT_STATUS_OK); h = io.out.file.handle; CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); - CHECK_LEASE(&io, "R", true, LEASE1); + CHECK_LEASE(&io, "R", true, LEASE1, 0); /* Grab a level-II oplock. */ smb2_oplock_create(&io, fname, smb2_util_oplock_level("s")); -- 1.9.1 From b7c1a8ef599e7ec6e2361875ae6e5f784924edd0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:35:59 +0100 Subject: [PATCH 27/38] s4:torture/smb2: add smb2.lease.[v2_]complex1 tests These tests verify the lease state is consistent between two connections with the same client_guid. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 45c98b8069f6b94115616d7fddef515c76d7767e) --- source4/torture/smb2/lease.c | 273 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index d81ddd3..ac20946 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -1563,6 +1563,277 @@ done: return ret; } +static bool test_lease_complex1(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h, h2, h3; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_complex1.dat"; + bool ret = true; + uint32_t caps; + struct smb2_tree *tree1b = NULL; + struct smbcli_options options1; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + ZERO_STRUCT(break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "R", true, LEASE1, 0); + + /* Upgrade to RWH over connection 1b */ + ls1.lease_state = smb2_util_lease_state("RWH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RHW", true, LEASE1, 0); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Contend with LEASE2. */ + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("RHW")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE2, 0); + + /* Verify that we were only sent one break. */ + CHECK_BREAK_INFO("RHW", "RH", LEASE1); + + /* again RH over connection 1b doesn't change the epoch */ + ls1.lease_state = smb2_util_lease_state("RH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1a, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO("RH", "", LEASE2); + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h3; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1b, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO("RH", "", LEASE1); + + done: + smb2_util_close(tree1a, h); + smb2_util_close(tree1b, h2); + smb2_util_close(tree1b, h3); + + smb2_util_unlink(tree1a, fname); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_lease_v2_complex1(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1; + struct smb2_create io2; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h, h2, h3; + struct smb2_write w; + NTSTATUS status; + const char *fname = "lease_v2_complex1.dat"; + bool ret = true; + uint32_t caps; + enum protocol_types protocol; + struct smb2_tree *tree1b = NULL; + struct smbcli_options options1; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree1a->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + ZERO_STRUCT(break_info); + + /* Grab R lease over connection 1a */ + smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL, + smb2_util_lease_state("R"), 0x4711); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "R", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* Upgrade to RWH over connection 1b */ + ls1.lease_state = smb2_util_lease_state("RWH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls1.lease_epoch += 1; + CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Contend with LEASE2. */ + smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, + smb2_util_lease_state("RWH"), 0x11); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + ls2.lease_epoch += 1; + CHECK_LEASE_V2(&io2, "RH", true, LEASE2, + 0, 0, ls2.lease_epoch); + + /* Verify that we were only sent one break. */ + ls1.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RHW", "RH", LEASE1, ls1.lease_epoch); + + /* again RH over connection 1b doesn't change the epoch */ + ls1.lease_state = smb2_util_lease_state("RH"); + status = smb2_create(tree1b, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io1, "RH", true, LEASE1, + 0, 0, ls1.lease_epoch); + + /* close over connection 1b */ + status = smb2_util_close(tree1b, h2); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1a, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls2.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RH", "", LEASE2, ls2.lease_epoch); + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(w); + w.in.file.handle = h3; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree1b, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + ls1.lease_epoch += 1; + CHECK_BREAK_INFO_V2(tree1a->session->transport, + "RH", "", LEASE1, ls1.lease_epoch); + + done: + smb2_util_close(tree1a, h); + smb2_util_close(tree1b, h2); + smb2_util_close(tree1b, h3); + + smb2_util_unlink(tree1a, fname); + + talloc_free(mem_ctx); + + return ret; +} + struct torture_suite *torture_smb2_lease_init(void) { struct torture_suite *suite = @@ -1579,10 +1850,12 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "break", test_lease_break); torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock); torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak); + torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request); torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1); + torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 1.9.1 From 383c05b3495f73ab8c3c15e598302fe0ea636132 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 09:53:45 +0100 Subject: [PATCH 28/38] s4:torture/smb2: add smb2.lease.v2_epoch[2|3] tests They demonstrate that the lease version (v1 or v2) is selected by the first open. All following opens using the other version still get the lease version of the first open. This implies that the server has to remember the lease version more globaly. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit c18c84ca6ca04815e8ffedb3d6f8abadad06500b) --- source4/torture/smb2/lease.c | 210 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index ac20946..14ce739 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -1563,6 +1563,214 @@ done: return ret; } +static bool test_lease_v2_epoch2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1v2, ls1v2t, ls1v1; + struct smb2_handle hv2 = {}, hv1 = {}; + const char *fname = "lease_v2_epoch2.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("R"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "R", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1v2.lease_epoch + 2); + + smb2_util_close(tree, hv2); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RHW"), + 0x11); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 3); + + smb2_util_close(tree, hv2); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_BREAK_INFO_V2(tree->session->transport, + "RWH", "RH", LEASE1, ls1v2.lease_epoch + 4); + + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RHW")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RHW", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + +done: + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + +static bool test_lease_v2_epoch3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1v1 = {}, ls1v1t = {},ls1v2 = {}; + struct smb2_handle hv1 = {}, hv2 = {}; + const char *fname = "lease_v2_epoch3.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + enum protocol_types protocol; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + protocol = smbXcli_conn_protocol(tree->session->transport->conn); + if (protocol < PROTOCOL_SMB3_00) { + torture_skip(tctx, "v2 leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RW"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RW", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + + ZERO_STRUCT(io); + smb2_lease_create_share(&io, &ls1v1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + + smb2_util_close(tree, hv1); + + smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv1 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + smb2_util_close(tree, hv1); + smb2_util_close(tree, hv2); + + ZERO_STRUCT(io); + smb2_lease_v2_create_share(&io, &ls1v2, false, fname, + smb2_util_share_access("RWD"), + LEASE1, NULL, + smb2_util_lease_state("RWH"), + 0x4711); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + hv2 = io.out.file.handle; + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1); + smb2_util_close(tree, hv2); + +done: + smb2_util_close(tree, hv2); + smb2_util_close(tree, hv1); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -1855,6 +2063,8 @@ struct torture_suite *torture_smb2_lease_init(void) test_lease_v2_request_parent); torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request); torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1); + torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2); + torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3); torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 1.9.1 From 769bebc22049b756158ad6f7d57614e7d30cba1b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 29/38] s4:torture/smb2: make it possible to skip the automatic ack of lease breaks. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 0c239d32094f8cc20bd36b55a4d08f57d04bd263) --- source4/torture/smb2/lease.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 14ce739..b9df9ba 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -406,9 +406,10 @@ static bool test_lease_upgrade2(struct torture_context *tctx, CHECK_VAL((__lba)->out.lease.lease_duration, 0); \ } while(0) -static struct { +static struct torture_lease_break { struct smb2_lease_break lease_break; struct smb2_transport *lease_transport; + bool lease_skip_ack; struct smb2_lease_break_ack lease_break_ack; int count; int failures; @@ -446,8 +447,10 @@ static struct { CHECK_VAL(break_info.count, 1); \ CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \ (__state), (__key)); \ - if (break_info.lease_break.break_flags & \ - SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \ + if (!break_info.lease_skip_ack && \ + (break_info.lease_break.break_flags & \ + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED)) \ + { \ torture_wait_for_lease_break(tctx); \ CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \ (__state), (__key)); \ @@ -494,6 +497,10 @@ static bool torture_lease_handler(struct smb2_transport *transport, break_info.lease_break = *lb; break_info.count++; + if (break_info.lease_skip_ack) { + return true; + } + if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { ZERO_STRUCT(io); io.in.lease.lease_key = lb->current_lease.lease_key; -- 1.9.1 From 8a9494b7dbcf458fb6c11c1920eac2139f3c4ed2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 30/38] s4:torture/smb2: smb2.lease.breaking1 test This demonstrates a race case where the client reuses a lease, while the server already sent a break. The open succeeds with SMB2_LEASE_FLAG_BREAK_IN_PROGRESS being set. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit a7a59b16a4920181fd0e2484c24b448ff8642e88) --- source4/torture/smb2/lease.c | 108 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index b9df9ba..5678436 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -1778,6 +1778,113 @@ done: return ret; } +static bool test_lease_breaking1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking1.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + ZERO_STRUCT(break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We ack the lease break. + */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2065,6 +2172,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "break", test_lease_break); torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock); torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak); + torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From ea272569053a96cbabc2600d43bdd35a6bb3ae2d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 15 Nov 2014 11:58:01 +0100 Subject: [PATCH 31/38] s4:torture/smb2: smb2.lease.breaking2 test This demonstrates that a conflicting open with NTCREATEX_DISP_OVERWRITE breaks a lease to NONE. It also shows which error codes are generated for unexpected lease break acks. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 5a652dbb92231183070b2d139d3459be8fd89439) --- source4/torture/smb2/lease.c | 152 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 5678436..b694dd8 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -1885,6 +1885,157 @@ done: return ret; } +static bool test_lease_breaking2(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking2.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ZERO_STRUCT(break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We ack the lease break. + */ + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_WRITE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = + SMB2_LEASE_READ | SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_WRITE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_HANDLE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + ack.in.lease.lease_state = SMB2_LEASE_READ; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED); + + /* Try again with the correct state this time. */ + ack.in.lease.lease_state = SMB2_LEASE_NONE;; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); + + /* Get state of the original handle. */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io1, "", true, LEASE1, 0); + smb2_util_close(tree, io1.out.file.handle); + +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2173,6 +2324,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock); torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak); torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1); + torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From 7efb0fd5ca5e7057dfbd2ec29e305dab1c1a6374 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 32/38] s4:torture/smb2: smb2.lease.breaking3 test This demonstrates a race case where the client reuses a lease, while the server already sent a break. The open succeeds with SMB2_LEASE_FLAG_BREAK_IN_PROGRESS being set. This is more complex that smb2.lease.breaking[1-2] as it generates breaks from RWH => RH => R => NONE. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 8d16a2e36762f6da825106798689c96aff95437f) --- source4/torture/smb2/lease.c | 189 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index b694dd8..76715e7 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2036,6 +2036,194 @@ done: return ret; } +static bool test_lease_breaking3(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct smb2_request *req3 = NULL; + struct torture_lease_break break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking3.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + /* + * a conflicting open with NTCREATEX_DISP_OVERWRITE + * doesn't trigger an immediate lease break to none. + */ + break_info_tmp = break_info; + ZERO_STRUCT(break_info); + smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE); + io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req3 = smb2_create_send(tree, &io3); + torture_assert(tctx, req3 != NULL, "smb2_create_send"); + CHECK_NO_BREAK(tctx); + break_info = break_info_tmp; + + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + ZERO_STRUCT(break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + /* + * We ack the lease break, but defer acking the next break (to "R") + */ + break_info.lease_skip_ack = true; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); + + /* + * We got an additional break downgrading to just "R" + * while we defer the ack. + */ + CHECK_BREAK_INFO("RH", "R", LEASE1); + + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + ZERO_STRUCT(break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending"); + + /* + * We ack the downgrade to "R" and get an immediate break to none + */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1); + + /* + * We get the downgrade to none. + */ + CHECK_BREAK_INFO("R", "", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + torture_assert(tctx, req3->cancel.can_cancel, + "req3 can_cancel"); + + ZERO_STRUCT(break_info); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + status = smb2_create_recv(req3, tctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2325,6 +2513,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak); torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1); torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2); + torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From 913e783cde2517ddcc7efff2bc7991c1b0ae0735 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 15 Nov 2014 11:58:01 +0100 Subject: [PATCH 33/38] s4:torture/smb2: smb2.lease.breaking4 test This demonstrates that a confliciting open with NTCREATEX_DISP_OVERWRITE isn't delayed by a "RH" lease, even if a lease is in 'breaking' mode. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit b3a985ab6662cacb2ac399c667b48e03c0bd1bfe) --- source4/torture/smb2/lease.c | 146 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 76715e7..dff8a11 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2224,6 +2224,151 @@ done: return ret; } +static bool test_lease_breaking4(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls1t = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct torture_lease_break break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking4.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * We got a break from RH to NONE, we're supported to ack + * this downgrade + */ + CHECK_BREAK_INFO("RH", "", LEASE1); + + break_info_tmp = break_info; + ZERO_STRUCT(break_info); + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree, h2); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break, even if the lease is in breaking state. + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + smb2_util_close(tree, h2); + + CHECK_NO_BREAK(tctx); + + /* + * We now ask the server about the current lease state + * which should still be "RH", but with + * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS. + */ + smb2_lease_create_share(&io3, &ls1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + + /* + * We finally ack the lease break... + */ + CHECK_NO_BREAK(tctx); + break_info = break_info_tmp; + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + CHECK_NO_BREAK(tctx); + +done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2514,6 +2659,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1); torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2); torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3); + torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From 1ca6749c0384dff7cdd9676b8aae38e3b48cd11a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 10:25:45 +0100 Subject: [PATCH 34/38] s4:torture/smb2: smb2.lease.breaking5 test This is like breaking4, but with an initial "R" lease instead of "RH". Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit 6494597c0451944e2599736af116d6838e6aac4e) --- source4/torture/smb2/lease.c | 120 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index dff8a11..ff10d5c 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2369,6 +2369,125 @@ done: return ret; } +static bool test_lease_breaking5(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls1t = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_request *req2 = NULL; + struct torture_lease_break break_info_tmp = {}; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking5.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "R", true, LEASE1, 0); + + CHECK_NO_BREAK(tctx); + + /* + * a conflicting open is *not* blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE; + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * We got a break from RH to NONE, we're supported to ack + * this downgrade + */ + CHECK_BREAK_INFO("R", "", LEASE1); + + break_info_tmp = break_info; + ZERO_STRUCT(break_info); + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); + + /* + * We now ask the server about the current lease state + * which should still be "RH", but with + * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS. + */ + smb2_lease_create_share(&io3, &ls1t, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "", true, LEASE1, 0); + + /* + * We send an ack without without being asked. + */ + CHECK_NO_BREAK(tctx); + break_info = break_info_tmp; + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + ZERO_STRUCT(break_info); + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + CHECK_NO_BREAK(tctx); + +done: + smb2_util_close(tree, h1); + smb2_util_close(tree, h2); + smb2_util_close(tree, h3); + + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2660,6 +2779,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2); torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3); torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4); + torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From 1df98c5957b3879d89538fc098f36893e4a76160 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 14:00:24 +0100 Subject: [PATCH 35/38] s4:torture/smb2: smb2.lease.breaking6 test The client is allowed to downgrade a lease to a lower value than required. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (cherry picked from commit ea25f0d32a045e30d69aab4d84b15cf13a6e32ea) --- source4/torture/smb2/lease.c | 108 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index ff10d5c..5e788e1 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2488,6 +2488,113 @@ done: return ret; } +static bool test_lease_breaking6(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_lease ls1 = {}; + struct smb2_handle h1a = {}; + struct smb2_handle h1b = {}; + struct smb2_handle h2 = {}; + struct smb2_request *req2 = NULL; + struct smb2_lease_break_ack ack = {}; + const char *fname = "lease_breaking6.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * we defer acking the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1a = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* + * a conflicting open is blocked until we ack the + * lease break + */ + smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE); + req2 = smb2_create_send(tree, &io2); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + + /* + * we got the lease break, but defer the ack. + */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ZERO_STRUCT(break_info); + + /* + * a open using the same lease key is still works, + * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS + */ + status = smb2_create(tree, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1b = io1.out.file.handle; + CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS); + smb2_util_close(tree, h1b); + + CHECK_NO_BREAK(tctx); + + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + /* + * We are asked to break to "RH", but we are allowed to + * break to any of "RH", "R" or NONE. + */ + ack.in.lease.lease_state = SMB2_LEASE_NONE; + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1); + + torture_assert(tctx, req2->cancel.can_cancel, + "req2 can_cancel"); + + status = smb2_create_recv(req2, tctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); + + CHECK_NO_BREAK(tctx); +done: + smb2_util_close(tree, h1a); + smb2_util_close(tree, h1b); + smb2_util_close(tree, h2); + smb2_util_unlink(tree, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -2780,6 +2887,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3); torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4); torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5); + torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1 From 18f3b7f535ba070857d296d10ec3927531b7d85a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 14 Nov 2014 10:24:40 -0800 Subject: [PATCH 36/38] s3: leases - torture test for timeout of responding to lease break request. Passes against W2K12. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit f76c7c7404c1a67389b701bd1ab24d3b2938c212) --- source4/torture/smb2/lease.c | 130 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 5e788e1..e1bc9b3 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2866,6 +2866,135 @@ static bool test_lease_v2_complex1(struct torture_context *tctx, return ret; } +static bool test_lease_timeout(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h, hnew, h1b; + NTSTATUS status; + const char *fname = "lease_timeout.dat"; + bool ret = true; + struct smb2_lease_break_ack ack = {}; + struct smb2_request *req2 = NULL; + struct smb2_write w; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Grab a RWH lease. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * Just don't ack the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + /* Break with a RWH request. */ + smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH")); + req2 = smb2_create_send(tree, &io); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + /* Copy the break request. */ + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + + /* Now wait for the timeout and get the reply. */ + status = smb2_create_recv(req2, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + hnew = io.out.file.handle; + + /* Ack the break after the timeout... */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + /* Get state of the original handle. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree, io.out.file.handle); + + /* Write on the original handle and make sure it's still valid. */ + ZERO_STRUCT(break_info); + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, '1', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Causes new handle to break to NONE. */ + CHECK_BREAK_INFO("RH", "", LEASE2); + + /* Write on the new handle. */ + ZERO_STRUCT(break_info); + ZERO_STRUCT(w); + w.in.file.handle = hnew; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 1024); + memset(w.in.data.data, '2', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + /* No break - original handle was already NONE. */ + CHECK_NO_BREAK(tctx); + smb2_util_close(tree, hnew); + + /* Upgrade to R on LEASE1. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("R")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "R", true, LEASE1, 0); + h1b = io.out.file.handle; + smb2_util_close(tree, h1b); + + /* Upgrade to RWH on LEASE1. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h1b = io.out.file.handle; + smb2_util_close(tree, h1b); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + smb2_util_close(tree, h1b); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + + struct torture_suite *torture_smb2_lease_init(void) { struct torture_suite *suite = @@ -2896,6 +3025,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2); torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3); torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); + torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 1.9.1 From e68914cfcc84d706cbdd861b8d4c2bb818af8b9a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 17 Nov 2014 14:17:34 -0800 Subject: [PATCH 37/38] s4: smb2 : torture: Add new dynamic_share leases test. Depends on new share "dynamic_share" being set up containing an %R in the path= statement. Shows we will break leases and fail to grant new ones if we get a lease_key+client guid pair match on files with different fileid's, as can happen on dynamic shares. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (cherry picked from commit 62c6c79011d7e62423fa97d4cabd9de149af8311) --- selftest/target/Samba3.pm | 10 +++ source4/torture/smb2/lease.c | 190 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index ebe2c09..9a05464 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -876,6 +876,12 @@ sub provision($$$$$$) my $badnames_shrdir="$shrdir/badnames"; push(@dirs,$badnames_shrdir); + my $lease1_shrdir="$shrdir/SMB2_10"; + push(@dirs,$lease1_shrdir); + + my $lease2_shrdir="$shrdir/SMB3_00"; + push(@dirs,$lease2_shrdir); + # this gets autocreated by winbindd my $wbsockdir="$prefix_abs/winbindd"; my $wbsockprivdir="$lockdir/winbindd_privileged"; @@ -1220,6 +1226,10 @@ sub provision($$$$$$) [badname-tmp] path = $badnames_shrdir guest ok = yes + +[dynamic_share] + path = $shrdir/%R + guest ok = yes "; close(CONF); diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index e1bc9b3..db91548 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -27,6 +27,7 @@ #include "torture/smb2/proto.h" #include "torture/util.h" #include "libcli/smb/smbXcli_base.h" +#include "lib/param/param.h" #define CHECK_VAL(v, correct) do { \ if ((v) != (correct)) { \ @@ -2994,6 +2995,194 @@ static bool test_lease_timeout(struct torture_context *tctx, return ret; } +static bool test_lease_dynamic_share(struct torture_context *tctx, + struct smb2_tree *tree1a) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_handle h, h1, h2; + struct smb2_write w; + NTSTATUS status; + const char *fname = "dynamic_path.dat"; + bool ret = true; + uint32_t caps; + struct smb2_tree *tree_2_1 = NULL; + struct smb2_tree *tree_3_0 = NULL; + struct smbcli_options options2_1; + struct smbcli_options options3_0; + const char *orig_share = NULL; + + if (!TARGET_IS_SAMBA3(tctx)) { + torture_skip(tctx, "dynamic shares are not supported"); + return true; + } + + options2_1 = tree1a->session->transport->options; + options3_0 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* + * Save off original share name and change it to dynamic_share. + * This must have been pre-created with a dynamic path containing + * %R. + */ + + orig_share = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "share"); + orig_share = talloc_strdup(tctx->lp_ctx, orig_share); + if (orig_share == NULL) { + torture_result(tctx, TORTURE_FAIL, __location__ "no memory\n"); + ret = false; + goto done; + } + lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", "dynamic_share"); + + /* Set max protocol to SMB2.1 */ + options2_1.max_protocol = PROTOCOL_SMB2_10; + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options2_1, &tree_2_1)) { + torture_warning(tctx, "couldn't reconnect max protocol 2.1, bailing\n"); + ret = false; + goto done; + } + + tree_2_1->session->transport->lease.handler = torture_lease_handler; + tree_2_1->session->transport->lease.private_data = tree_2_1; + tree_2_1->session->transport->oplock.handler = torture_oplock_handler; + tree_2_1->session->transport->oplock.private_data = tree_2_1; + + smb2_util_unlink(tree_2_1, fname); + + /* Set max protocol to SMB3.0 */ + options3_0.max_protocol = PROTOCOL_SMB3_00; + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options3_0, &tree_3_0)) { + torture_warning(tctx, "couldn't reconnect max protocol 3.0, bailing\n"); + ret = false; + goto done; + } + + tree_3_0->session->transport->lease.handler = torture_lease_handler; + tree_3_0->session->transport->lease.private_data = tree_3_0; + tree_3_0->session->transport->oplock.handler = torture_oplock_handler; + tree_3_0->session->transport->oplock.private_data = tree_3_0; + + smb2_util_unlink(tree_3_0, fname); + + ZERO_STRUCT(break_info); + + /* Get RWH lease over connection 2_1 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2_1, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + + /* Write some data into it. */ + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, '1', w.in.data.length); + status = smb2_write(tree_2_1, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Open the same name over connection 3_0. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3_0, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io.out.file.handle; + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + + /* h1 should have replied with NONE. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + + /* We should have broken h to NONE. */ + CHECK_BREAK_INFO("RWH", "", LEASE1); + + /* Try to upgrade to RWH over connection 2_1 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2_1, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 4096); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + /* Should have been denied. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree_2_1, h2); + + /* Try to upgrade to RWH over connection 3_0 */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3_0, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io.out.file.handle; + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.size, 0); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + /* Should have been denied. */ + CHECK_LEASE(&io, "", true, LEASE1, 0); + smb2_util_close(tree_3_0, h2); + + /* Write some data into it. */ + w.in.file.handle = h1; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 1024); + memset(w.in.data.data, '2', w.in.data.length); + status = smb2_write(tree_3_0, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Close everything.. */ + smb2_util_close(tree_2_1, h); + smb2_util_close(tree_3_0, h1); + + /* And ensure we can get a lease ! */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_2_1, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + /* And the file is the right size. */ + CHECK_VAL(io.out.size, 4096); \ + /* Close it. */ + smb2_util_close(tree_2_1, h); + + /* And ensure we can get a lease ! */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree_3_0, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + /* And the file is the right size. */ + CHECK_VAL(io.out.size, 1024); \ + /* Close it. */ + smb2_util_close(tree_3_0, h); + + done: + + smb2_util_close(tree_2_1, h); + smb2_util_close(tree_3_0, h1); + smb2_util_close(tree_3_0, h2); + + smb2_util_unlink(tree_2_1, fname); + smb2_util_unlink(tree_3_0, fname); + + /* Set sharename back. */ + lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", orig_share); + + talloc_free(mem_ctx); + + return ret; +} + struct torture_suite *torture_smb2_lease_init(void) { @@ -3025,6 +3214,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2); torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3); torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); + torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share); torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 1.9.1 From 6781db5ecd298a44d6c7e88e07ca868f8801fb0c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 21 Nov 2014 21:28:14 -0800 Subject: [PATCH 38/38] s4: torture: leases - Add test for leases and blocking locks. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Thu Nov 27 19:08:24 CET 2014 on sn-devel-104 (cherry picked from commit c6a5eab3690d2926d66024a35e3c3e818d7e4935) --- source4/torture/smb2/lease.c | 173 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index db91548..c7b3592 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2596,6 +2596,178 @@ done: return ret; } +static bool test_lease_lock1(struct torture_context *tctx, + struct smb2_tree *tree1a, + struct smb2_tree *tree2) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io1 = {}; + struct smb2_create io2 = {}; + struct smb2_create io3 = {}; + struct smb2_lease ls1 = {}; + struct smb2_lease ls2 = {}; + struct smb2_lease ls3 = {}; + struct smb2_handle h1 = {}; + struct smb2_handle h2 = {}; + struct smb2_handle h3 = {}; + struct smb2_lock lck; + struct smb2_lock_element el[1]; + const char *fname = "locktest.dat"; + bool ret = true; + NTSTATUS status; + uint32_t caps; + struct smbcli_options options1; + struct smb2_tree *tree1b = NULL; + + options1 = tree1a->session->transport->options; + + caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + /* Set up handlers. */ + tree2->session->transport->lease.handler = torture_lease_handler; + tree2->session->transport->lease.private_data = tree2; + tree2->session->transport->oplock.handler = torture_oplock_handler; + tree2->session->transport->oplock.private_data = tree2; + + tree1a->session->transport->lease.handler = torture_lease_handler; + tree1a->session->transport->lease.private_data = tree1a; + tree1a->session->transport->oplock.handler = torture_oplock_handler; + tree1a->session->transport->oplock.private_data = tree1a; + + /* create a new connection (same client_guid) */ + if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + ret = false; + goto done; + } + + tree1b->session->transport->lease.handler = torture_lease_handler; + tree1b->session->transport->lease.private_data = tree1b; + tree1b->session->transport->oplock.handler = torture_oplock_handler; + tree1b->session->transport->oplock.private_data = tree1b; + + smb2_util_unlink(tree1a, fname); + + ZERO_STRUCT(break_info); + ZERO_STRUCT(lck); + + /* Open a handle on tree1a. */ + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access("RWD"), + LEASE1, + smb2_util_lease_state("RWH")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RWH", true, LEASE1, 0); + + /* Open a second handle on tree1b. */ + smb2_lease_create_share(&io2, &ls2, false, fname, + smb2_util_share_access("RWD"), + LEASE2, + smb2_util_lease_state("RWH")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + h2 = io2.out.file.handle; + CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io2, "RH", true, LEASE2, 0); + /* And LEASE1 got broken to RH. */ + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + ZERO_STRUCT(break_info); + + /* Now open a lease on a different client guid. */ + smb2_lease_create_share(&io3, &ls3, false, fname, + smb2_util_share_access("RWD"), + LEASE3, + smb2_util_lease_state("RWH")); + status = smb2_create(tree2, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + h3 = io3.out.file.handle; + CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io3, "RH", true, LEASE3, 0); + /* Doesn't break. */ + CHECK_NO_BREAK(tctx); + + lck.in.locks = el; + /* + * Try and get get an exclusive byte + * range lock on H1 (LEASE1). + */ + + lck.in.lock_count = 1; + lck.in.lock_sequence = 1; + lck.in.file.handle = h1; + el[0].offset = 0; + el[0].length = 1; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree1a, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* LEASE2 and LEASE3 should get broken to NONE. */ + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + torture_wait_for_lease_break(tctx); + + CHECK_VAL(break_info.failures, 0); \ + CHECK_VAL(break_info.count, 2); \ + + /* Get state of the H1 (LEASE1) */ + smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("")); + status = smb2_create(tree1a, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + /* Should still be RH. */ + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + smb2_util_close(tree1a, io1.out.file.handle); + + /* Get state of the H2 (LEASE2) */ + smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("")); + status = smb2_create(tree1b, mem_ctx, &io2); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io2, "", true, LEASE2, 0); + smb2_util_close(tree1b, io2.out.file.handle); + + /* Get state of the H3 (LEASE3) */ + smb2_lease_create(&io3, &ls3, false, fname, LEASE3, smb2_util_lease_state("")); + status = smb2_create(tree2, mem_ctx, &io3); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_LEASE(&io3, "", true, LEASE3, 0); + smb2_util_close(tree2, io3.out.file.handle); + + ZERO_STRUCT(break_info); + + /* + * Try and get get an exclusive byte + * range lock on H3 (LEASE3). + */ + lck.in.lock_count = 1; + lck.in.lock_sequence = 2; + lck.in.file.handle = h3; + el[0].offset = 100; + el[0].length = 1; + el[0].reserved = 0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; + status = smb2_lock(tree2, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + /* LEASE1 got broken to NONE. */ + CHECK_BREAK_INFO("RH", "", LEASE1); + ZERO_STRUCT(break_info); + +done: + smb2_util_close(tree1a, h1); + smb2_util_close(tree1b, h2); + smb2_util_close(tree2, h3); + + smb2_util_unlink(tree1a, fname); + talloc_free(mem_ctx); + return ret; +} + static bool test_lease_complex1(struct torture_context *tctx, struct smb2_tree *tree1a) { @@ -3206,6 +3378,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4); torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5); torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6); + torture_suite_add_2smb2_test(suite, "lock1", test_lease_lock1); torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1); torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent); -- 1.9.1