From 0a50a825ffc8a59beebcada8171e26552713bc73 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Tue, 23 Sep 2014 14:08:10 -0700 Subject: [PATCH 01/47] 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 56c6796..dff1ca9 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -2475,7 +2475,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"); @@ -3149,6 +3149,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 510c931dd6aec59c738e804e055d49b082197854 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 5 Nov 2014 10:12:20 -0800 Subject: [PATCH 02/47] 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 a629c80ab48e3cd8628c5d35cfd67134b5379502 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 22:56:41 +0200 Subject: [PATCH 03/47] 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 c77b01b95bfd244825478b21f891e950c92aa373 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 22 Sep 2014 21:21:36 +0200 Subject: [PATCH 04/47] 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 dcaf1e19c17621aeb197095a2d0743881de213e1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 29 Oct 2014 13:55:16 +0100 Subject: [PATCH 05/47] 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 b040c40224d343ba828a5ec73ae563f08b76a894 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 4 Nov 2014 21:44:45 -0800 Subject: [PATCH 06/47] 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 0383d40d126f5a5510e7ba297c392d68435d35e1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 23:34:14 +0200 Subject: [PATCH 07/47] 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 e446f99c41504b09e39344a5fd96721d0679e0c2 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 28 Oct 2014 15:27:09 -0700 Subject: [PATCH 08/47] 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 d13e10222aa0646c9b7f5f4185476cf97c367060 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 22 Oct 2014 17:53:01 -0700 Subject: [PATCH 09/47] 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 5a5f50ef8a4feb67dafa4748be92f8dccea3bf2f Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 28 Oct 2014 15:27:09 -0700 Subject: [PATCH 10/47] 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 9476f54289b9d68100584d2e3a94c900f1cd05c9 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 23 Sep 2014 18:49:46 +0200 Subject: [PATCH 11/47] 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 f25df70387163256d32a92c018780ce903dc8ce0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 11:06:16 +0100 Subject: [PATCH 12/47] 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 94cfbca9e9a21d874a2d9eb9c116f4a5f23ea9d5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 13 Nov 2014 12:10:46 +0100 Subject: [PATCH 13/47] 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 6fe1ac6225612f4ff64ea3ff14a288389e7fcccb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:30:31 +0100 Subject: [PATCH 14/47] 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 653ab64c9f6287dd2645b06e990c156d1b313ed2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:32:40 +0100 Subject: [PATCH 15/47] 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 f9d96b58..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 50174c6a9505f332c5010b0ae5de059b86930a54 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:35:38 +0100 Subject: [PATCH 16/47] 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 99b554704ad8bf878682a954e3fdd2de192d2d88 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:38:09 +0100 Subject: [PATCH 17/47] 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 1456551d58c8d9cc1e93f162c6c32b4d1bc7952d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:33:13 +0100 Subject: [PATCH 18/47] 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 7ddbd00fb1d07b78c6947397f96556828ca36ba9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 11:13:15 +0100 Subject: [PATCH 19/47] 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 42816ea794eafd4ad0e8a8479dd8a6c55b5784f3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:19:49 +0100 Subject: [PATCH 20/47] 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 d2c5788d1c9938de3be52ff4798d8ec7cedbb71b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:23:31 +0100 Subject: [PATCH 21/47] 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 5ecabad0eece3d5407f7d1cf56b21ec0fd66c141 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 07:24:16 +0100 Subject: [PATCH 22/47] 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 347935fdf21f1b4ff43f89733950ed6a210c6efd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 17:00:44 +0100 Subject: [PATCH 23/47] 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 020683b9145ee9743168b3e03a26f2527fcd39a7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:35:07 +0100 Subject: [PATCH 24/47] 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 1bb44e1e78a081a94cfd801eec2cb5c1fee300ca Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 09:15:58 +0100 Subject: [PATCH 25/47] 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 ca9280609486831bcced6e702a0bbd133da99c25 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:41:50 +0100 Subject: [PATCH 26/47] 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 a44fd172e0dc8d1c7e60224a8c702ec5fc3fa525 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 11 Nov 2014 19:35:59 +0100 Subject: [PATCH 27/47] 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 12b952de4535b2668906856a51ed6fa2edd1f3a7 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 09:53:45 +0100 Subject: [PATCH 28/47] 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 1e70b66bea69655e13a04774b70aa0d9d1aed26f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 29/47] 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 e20ccc7513e8de84d2beb8f64f7b2be24eb8480d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 30/47] 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 11c88c5e13d29336cff25622dd6373283204e96b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 15 Nov 2014 11:58:01 +0100 Subject: [PATCH 31/47] 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 e284ab1a816085e43c0817bbec90359b0a50ef07 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 12 Nov 2014 15:43:56 +0100 Subject: [PATCH 32/47] 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 d2b64b8c6d1fcec11536d490718eb616c58aedb2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sat, 15 Nov 2014 11:58:01 +0100 Subject: [PATCH 33/47] 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 7a9d877a5f258608c2a42b3c9704f7ef81ba80fc Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 10:25:45 +0100 Subject: [PATCH 34/47] 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 d8613c211d1dbd2103f9828dee24bb2459ee39dd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 26 Nov 2014 14:00:24 +0100 Subject: [PATCH 35/47] 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 81d28774200bc9614b28cd1410b439b15b3fe877 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 14 Nov 2014 10:24:40 -0800 Subject: [PATCH 36/47] 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 18a5e8e016d9a9adbdf08da463f2504552b21411 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Mon, 17 Nov 2014 14:17:34 -0800 Subject: [PATCH 37/47] 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 ce27b2e11f87cdaa34e09a2438cf43d759e964ca Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 21 Nov 2014 21:28:14 -0800 Subject: [PATCH 38/47] 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 From c6545a39727d713ba0ad678625c6a19861a33187 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 29 Sep 2014 15:06:37 +0000 Subject: [PATCH 39/47] profiling: Make "struct profile_header" static Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit 7c1f6c7f61f27ec8e115cdce1e2ac15addd8fb84) --- source3/include/smbprofile.h | 7 ------- source3/profile/profile.c | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index 8ecbc30..9ffcfc4 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -876,13 +876,6 @@ struct profile_stats { unsigned writecache_allocated_write_caches; }; -struct profile_header { - int prof_shm_magic; - int prof_shm_version; - struct profile_stats stats; -}; - -extern struct profile_header *profile_h; extern struct profile_stats *profile_p; extern bool do_profile_flag; extern bool do_profile_times; diff --git a/source3/profile/profile.c b/source3/profile/profile.c index 5f9433f..e0e4684 100644 --- a/source3/profile/profile.c +++ b/source3/profile/profile.c @@ -34,7 +34,13 @@ static int shm_id; static bool read_only; #endif -struct profile_header *profile_h; +struct profile_header { + int prof_shm_magic; + int prof_shm_version; + struct profile_stats stats; +}; + +static struct profile_header *profile_h; struct profile_stats *profile_p; bool do_profile_flag = False; -- 1.9.1 From 59e8e40d8f75043ddb93c04fdb154cbd0ac5fee1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 30 Sep 2014 13:06:02 +0000 Subject: [PATCH 40/47] profiling: Fix a typo Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit d523e0b7ed67d1697a1b8ab5f58902afd2a496b3) --- source3/include/smbprofile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index 9ffcfc4..fd17b7a 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -835,7 +835,7 @@ enum profile_stats_values #define smb2_break_count __profile_stats_value(PR_VALUE_SMB2_BREAK, count) #define smb2_break_time __profile_stats_value(PR_VALUE_SMB2_BREAK, time) - /* This mist remain the last value. */ + /* This must remain the last value. */ PR_VALUE_MAX }; /* enum profile_stats_values */ -- 1.9.1 From ccc0f2e9354a810a6f668196b25163f63cf28437 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Oct 2014 12:52:05 +0000 Subject: [PATCH 41/47] profiling: Move some #defines to profile.c Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit 0e1b60e8c3325a1e5ea77f420326b254d5404389) --- source3/include/smbprofile.h | 4 ---- source3/profile/profile.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index fd17b7a..3040aa0 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -24,10 +24,6 @@ /* this file defines the profile structure in the profile shared memory area */ -#define PROF_SHMEM_KEY ((key_t)0x07021999) -#define PROF_SHM_MAGIC 0x6349985 -#define PROF_SHM_VERSION 13 - /* time values in the following structure are in microseconds */ #define __profile_stats_value(which, domain) domain[which] diff --git a/source3/profile/profile.c b/source3/profile/profile.c index e0e4684..9ef2c69 100644 --- a/source3/profile/profile.c +++ b/source3/profile/profile.c @@ -25,6 +25,10 @@ #include "messages.h" #include "smbprofile.h" +#define PROF_SHMEM_KEY ((key_t)0x07021999) +#define PROF_SHM_MAGIC 0x6349985 +#define PROF_SHM_VERSION 13 + #ifdef WITH_PROFILE #define IPC_PERMS ((S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH) #endif /* WITH_PROFILE */ -- 1.9.1 From 1f3bc5f5c93bca86d76672675a0bc00bcfc36332 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Oct 2014 13:05:07 +0000 Subject: [PATCH 42/47] profiling: Fix a typo Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit a33b445a7d7567b27f5247cb6060edbbeecf4d52) --- source3/include/smbprofile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index 3040aa0..97e7c70 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -880,7 +880,7 @@ extern bool do_profile_times; /* these are helper macros - do not call them directly in the code * use the DO_PROFILE_* START_PROFILE and END_PROFILE ones - * below which test for the profile flage first + * below which test for the profile flags first */ #define INC_PROFILE_COUNT(x) profile_p->x++ #define DEC_PROFILE_COUNT(x) profile_p->x-- -- 1.9.1 From 1c3337f4ab2742daa0506f985e8c2cd0feb581a0 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 30 Sep 2014 15:08:20 +0000 Subject: [PATCH 43/47] profiling: Only compile utils/status_profile.c if profiling is enabled This conditional compile avoids some #ifdef WITH_PROFILE, which makes the code more readable Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit 17c7f454813be526876ac750ceb6fd1422577495) --- source3/utils/status_profile.c | 21 --------------------- source3/utils/status_profile_dummy.c | 33 +++++++++++++++++++++++++++++++++ source3/wscript_build | 11 ++++++++--- 3 files changed, 41 insertions(+), 24 deletions(-) create mode 100644 source3/utils/status_profile_dummy.c diff --git a/source3/utils/status_profile.c b/source3/utils/status_profile.c index 0923bae..153fba6 100644 --- a/source3/utils/status_profile.c +++ b/source3/utils/status_profile.c @@ -24,7 +24,6 @@ bool status_profile_dump(bool be_verbose); bool status_profile_rates(bool be_verbose); -#ifdef WITH_PROFILE static void profile_separator(const char * title) { char line[79 + 1]; @@ -39,14 +38,12 @@ static void profile_separator(const char * title) line[sizeof(line) - 1] = '\0'; d_printf("%s\n", line); } -#endif /******************************************************************* dump the elements of the profile structure ******************************************************************/ bool status_profile_dump(bool verbose) { -#ifdef WITH_PROFILE if (!profile_setup(NULL, True)) { fprintf(stderr,"Failed to initialise profile memory\n"); return False; @@ -437,16 +434,9 @@ bool status_profile_dump(bool verbose) d_printf("smb2_break_count: %u\n", profile_p->smb2_break_count); d_printf("smb2_break_time: %u\n", profile_p->smb2_break_time); -#else /* WITH_PROFILE */ - - fprintf(stderr, "Profile data unavailable\n"); -#endif /* WITH_PROFILE */ - return True; } -#ifdef WITH_PROFILE - /* Convert microseconds to milliseconds. */ #define usec_to_msec(s) ((s) / 1000) /* Convert microseconds to seconds. */ @@ -566,14 +556,3 @@ bool status_profile_rates(bool verbose) return True; } - -#else /* WITH_PROFILE */ - -bool status_profile_rates(bool verbose) -{ - fprintf(stderr, "Profile data unavailable\n"); - return False; -} - -#endif /* WITH_PROFILE */ - diff --git a/source3/utils/status_profile_dummy.c b/source3/utils/status_profile_dummy.c new file mode 100644 index 0000000..c58f696 --- /dev/null +++ b/source3/utils/status_profile_dummy.c @@ -0,0 +1,33 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2013 by Volker Lendecke + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "smbprofile.h" + +bool status_profile_dump(bool be_verbose) +{ + fprintf(stderr, "Profile data unavailable\n"); + return true; +} + +bool status_profile_rates(bool be_verbose) +{ + fprintf(stderr, "Profile data unavailable\n"); + return true; +} diff --git a/source3/wscript_build b/source3/wscript_build index 7394a57..d3979e8 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -1164,10 +1164,15 @@ bld.SAMBA3_BINARY('smbta-util', secrets3 param''') +smbstatus_source = 'utils/status.c smbd/notify_internal.c' + +if bld.CONFIG_GET("WITH_PROFILE"): + smbstatus_source += ' utils/status_profile.c' +else: + smbstatus_source += ' utils/status_profile_dummy.c' + bld.SAMBA3_BINARY('smbstatus', - source='''utils/status.c - utils/status_profile.c - smbd/notify_internal.c''', + source=smbstatus_source, deps=''' talloc param -- 1.9.1 From ad6bffc4d3a3ead5cf02dc14ba0ad56628f8f02d Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 30 Sep 2014 15:08:20 +0000 Subject: [PATCH 44/47] profiling: Only compile profile/profile.c if profiling is enabled This conditional compile avoids some #ifdef WITH_PROFILE, which makes the code more readable Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit c7c300f9710f1f207b9fa0274648ba2dc11c1ffd) --- source3/profile/profile.c | 17 +---------------- source3/profile/profile_dummy.c | 31 +++++++++++++++++++++++++++++++ source3/wscript_build | 11 ++++++++--- 3 files changed, 40 insertions(+), 19 deletions(-) create mode 100644 source3/profile/profile_dummy.c diff --git a/source3/profile/profile.c b/source3/profile/profile.c index 9ef2c69..9367ff4 100644 --- a/source3/profile/profile.c +++ b/source3/profile/profile.c @@ -29,14 +29,10 @@ #define PROF_SHM_MAGIC 0x6349985 #define PROF_SHM_VERSION 13 -#ifdef WITH_PROFILE #define IPC_PERMS ((S_IRUSR | S_IWUSR) | S_IRGRP | S_IROTH) -#endif /* WITH_PROFILE */ -#ifdef WITH_PROFILE static int shm_id; static bool read_only; -#endif struct profile_header { int prof_shm_magic; @@ -55,7 +51,6 @@ Set a profiling level. ****************************************************************************/ void set_profile_level(int level, struct server_id src) { -#ifdef WITH_PROFILE switch (level) { case 0: /* turn off profiling */ do_profile_flag = False; @@ -81,13 +76,8 @@ void set_profile_level(int level, struct server_id src) (int)procid_to_pid(&src))); break; } -#else /* WITH_PROFILE */ - DEBUG(1,("INFO: Profiling support unavailable in this build.\n")); -#endif /* WITH_PROFILE */ } -#ifdef WITH_PROFILE - /**************************************************************************** receive a set profile level message ****************************************************************************/ @@ -119,11 +109,8 @@ static void reqprofile_message(struct messaging_context *msg_ctx, { int level; -#ifdef WITH_PROFILE level = 1 + (do_profile_flag?2:0) + (do_profile_times?4:0); -#else - level = 0; -#endif + DEBUG(1,("INFO: Received REQ_PROFILELEVEL message from PID %u\n", (unsigned int)procid_to_pid(&src))); messaging_send_buf(msg_ctx, src, MSG_PROFILELEVEL, @@ -413,5 +400,3 @@ bool profile_setup(struct messaging_context *msg_ctx, bool rdonly) SMB_ASSERT(val < PR_VALUE_MAX); return valnames[val]; } - -#endif /* WITH_PROFILE */ diff --git a/source3/profile/profile_dummy.c b/source3/profile/profile_dummy.c new file mode 100644 index 0000000..1f820ec --- /dev/null +++ b/source3/profile/profile_dummy.c @@ -0,0 +1,31 @@ +/* + * Unix SMB/CIFS implementation. + * profile.c implementation if profiles are not enabled + * Copyright (C) Volker Lendecke 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "includes.h" +#include "smbprofile.h" + +bool profile_setup(struct messaging_context *msg_ctx, bool rdonly) +{ + return true; +} + +void set_profile_level(int level, struct server_id src) +{ + DEBUG(1,("INFO: Profiling support unavailable in this build.\n")); +} diff --git a/source3/wscript_build b/source3/wscript_build index d3979e8..54ba3a7 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -638,9 +638,14 @@ bld.SAMBA3_SUBSYSTEM('LOCKING', NDR_OPEN_FILES FNAME_UTIL''') -bld.SAMBA3_SUBSYSTEM('PROFILE', - source='profile/profile.c', - deps='samba-util') +if bld.CONFIG_GET("WITH_PROFILE"): + bld.SAMBA3_SUBSYSTEM('PROFILE', + source='profile/profile.c', + deps='samba-util') +else: + bld.SAMBA3_SUBSYSTEM('PROFILE', + source='profile/profile_dummy.c', + deps='') bld.SAMBA3_SUBSYSTEM('PRINTBASE', source='''printing/notify.c printing/printing_db.c''', -- 1.9.1 From 004037fc7e9da4006ab9b23a1f1c4d69efbe461a Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Oct 2014 13:36:22 +0000 Subject: [PATCH 45/47] profiling: Remove some #ifdefs The DO_PROFILE_INC thingies already #define to nothing without WITH_PROFILE, and any sane compiler will just not compile the if-condition if there is no body to be executed. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit 5254a7e1e5ab216a679bfbf3584bb8a16f502a2b) --- source3/smbd/fileio.c | 6 ------ source3/smbd/server.c | 2 -- 2 files changed, 8 deletions(-) diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index b0da7a2..91b3102 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -345,12 +345,10 @@ ssize_t write_file(struct smb_request *req, mark_file_modified(fsp); -#ifdef WITH_PROFILE DO_PROFILE_INC(writecache_total_writes); if (!fsp->oplock_type) { DO_PROFILE_INC(writecache_non_oplock_writes); } -#endif /* * If this file is level II oplocked then we need @@ -826,13 +824,11 @@ n = %u, wcp->offset=%.0f, wcp->data_size=%u\n", */ if (n) { -#ifdef WITH_PROFILE if (wcp->data_size) { DO_PROFILE_INC(writecache_abutted_writes); } else { DO_PROFILE_INC(writecache_init_writes); } -#endif if ((wcp->data_size == 0) && (pos > wcp->file_size) @@ -997,11 +993,9 @@ ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason) DEBUG(9,("flushing write cache: fd = %d, off=%.0f, size=%u\n", fsp->fh->fd, (double)wcp->offset, (unsigned int)data_size)); -#ifdef WITH_PROFILE if(data_size == wcp->alloc_size) { DO_PROFILE_INC(writecache_num_perfect_writes); } -#endif ret = real_write_file(NULL, fsp, wcp->data, wcp->offset, data_size); diff --git a/source3/smbd/server.c b/source3/smbd/server.c index 658f268..8b9a1c1 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -1304,7 +1304,6 @@ extern void build_options(bool screen); init_structs(); -#ifdef WITH_PROFILE if (!profile_setup(msg_ctx, False)) { DEBUG(0,("ERROR: failed to setup profiling\n")); return -1; @@ -1317,7 +1316,6 @@ extern void build_options(bool screen); src.pid = getpid(); set_profile_level(pl, src); } -#endif if (!is_daemon && !is_a_socket(0)) { if (!interactive) { -- 1.9.1 From a7295493f6ce0756b0a1dce33b681d1b8b5515fd Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Oct 2014 13:40:35 +0000 Subject: [PATCH 46/47] profiling: Remove a big DEBUG statement I would like to have the freedom to play with the profiling implementation. This is kindof in the way. This code is from pre-SVN days: > commit 7914e9351abb5271ebb4990c3b1fe495d15a4eda > Author: Jeremy Allison > AuthorDate: Thu Oct 5 18:50:18 2000 +0000 > Commit: Jeremy Allison > CommitDate: Thu Oct 5 18:50:18 2000 +0000 > > Herb's fixes for profiling & compiler warnings. > Jeremy. Herb, please speak up quickly if you still need this :-) Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit f74ac712aea8d354b70966ffd3f617c65a17d8d0) --- source3/smbd/fileio.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 91b3102..37c3f66 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -362,31 +362,6 @@ ssize_t write_file(struct smb_request *req, contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE); contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE); -#ifdef WITH_PROFILE - if (profile_p && profile_p->writecache_total_writes % 500 == 0) { - DEBUG(3,("WRITECACHE: initwrites=%u abutted=%u total=%u \ -nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n", - profile_p->writecache_init_writes, - profile_p->writecache_abutted_writes, - profile_p->writecache_total_writes, - profile_p->writecache_non_oplock_writes, - profile_p->writecache_allocated_write_caches, - profile_p->writecache_num_write_caches, - profile_p->writecache_direct_writes, - profile_p->writecache_num_perfect_writes, - profile_p->writecache_read_hits )); - - DEBUG(3,("WRITECACHE: Flushes SEEK=%d, READ=%d, WRITE=%d, READRAW=%d, OPLOCK=%d, CLOSE=%d, SYNC=%d\n", - profile_p->writecache_flushed_writes[SAMBA_SEEK_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_READ_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_WRITE_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_READRAW_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_OPLOCK_RELEASE_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_CLOSE_FLUSH], - profile_p->writecache_flushed_writes[SAMBA_SYNC_FLUSH] )); - } -#endif - if (wcp && req->unread_bytes) { /* If we're using receivefile don't * deal with a write cache. -- 1.9.1 From 23de486e5d2e178cd6ecadbf18d6fb64960b42c4 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 2 Oct 2014 13:45:01 +0000 Subject: [PATCH 47/47] profiling: Make WITH_PROFILE span more in smbprofile.h Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Oct 3 22:17:46 CEST 2014 on sn-devel-104 (cherry picked from commit 5496270d0f615aa39de0a083ec74fb40a988f64b) --- source3/include/smbprofile.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index 97e7c70..a37ac4f 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -21,6 +21,8 @@ */ +#ifdef WITH_PROFILE + /* this file defines the profile structure in the profile shared memory area */ @@ -876,8 +878,6 @@ extern struct profile_stats *profile_p; extern bool do_profile_flag; extern bool do_profile_times; -#ifdef WITH_PROFILE - /* these are helper macros - do not call them directly in the code * use the DO_PROFILE_* START_PROFILE and END_PROFILE ones * below which test for the profile flags first @@ -964,6 +964,7 @@ static inline uint64_t profile_timestamp(void) #define START_PROFILE_BYTES(x,n) #define END_PROFILE_STAMP(x, _stamp) #define END_PROFILE(x) + #endif /* WITH_PROFILE */ /* The following definitions come from profile/profile.c */ -- 1.9.1