The Samba-Bugzilla – Attachment 10481 Details for
Bug 10911
SMB2 leases are not yet supported.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Backports for v4-2-test part2
tmp42-02.diff.txt (text/plain), 148.68 KB, created by
Stefan Metzmacher
on 2014-12-04 07:29:15 UTC
(
hide
)
Description:
Backports for v4-2-test part2
Filename:
MIME Type:
Creator:
Stefan Metzmacher
Created:
2014-12-04 07:29:15 UTC
Size:
148.68 KB
patch
obsolete
>From cd80c59b85bc72e78866da0bf3bbb7800516cb0a Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Mon, 1 Dec 2014 13:57:57 -0800 >Subject: [PATCH 01/28] s4:torture:smb2: Add test that shows the client can > respond to a lease break over a different connection. > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit c5b481a8d5ed6c7c18c14479bc0263f37ded4702) >--- > source4/torture/smb2/lease.c | 111 +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 111 insertions(+) > >diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c >index c7b3592..9b25eb4 100644 >--- a/source4/torture/smb2/lease.c >+++ b/source4/torture/smb2/lease.c >@@ -3039,6 +3039,116 @@ static bool test_lease_v2_complex1(struct torture_context *tctx, > return ret; > } > >+static bool test_lease_v2_complex2(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; >+ struct smb2_request *req2 = NULL; >+ struct smb2_lease_break_ack ack = {}; >+ NTSTATUS status; >+ const char *fname = "lease_v2_complex2.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 RWH lease over connection 1a */ >+ smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL, >+ smb2_util_lease_state("RWH"), 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, "RWH", true, LEASE1, >+ 0, 0, ls1.lease_epoch); >+ >+ /* >+ * we defer acking the lease break. >+ */ >+ ZERO_STRUCT(break_info); >+ break_info.lease_skip_ack = true; >+ >+ /* Ask for RWH on connection 1b, different lease. */ >+ smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, >+ smb2_util_lease_state("RWH"), 0x11); >+ req2 = smb2_create_send(tree1b, &io2); >+ torture_assert(tctx, req2 != NULL, "smb2_create_send"); >+ >+ ls1.lease_epoch += 1; >+ >+ CHECK_BREAK_INFO_V2(tree1a->session->transport, >+ "RWH", "RH", LEASE1, ls1.lease_epoch); >+ >+ /* Send the break ACK on tree1b. */ >+ ack.in.lease.lease_key = >+ break_info.lease_break.current_lease.lease_key; >+ ack.in.lease.lease_state = SMB2_LEASE_HANDLE|SMB2_LEASE_READ; >+ >+ status = smb2_lease_break_ack(tree1b, &ack); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1); >+ >+ ZERO_STRUCT(break_info); >+ >+ status = smb2_create_recv(req2, tctx, &io2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_LEASE_V2(&io2, "RH", true, LEASE2, >+ 0, 0, ls2.lease_epoch+1); >+ h2 = io2.out.file.handle; >+ >+ done: >+ smb2_util_close(tree1a, h); >+ smb2_util_close(tree1b, h2); >+ >+ smb2_util_unlink(tree1a, fname); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+ > static bool test_lease_timeout(struct torture_context *tctx, > struct smb2_tree *tree) > { >@@ -3387,6 +3497,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, "v2_complex2", test_lease_v2_complex2); > torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share); > torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); > >-- >1.9.1 > > >From 9a2d67fb0a205d3bbca73dce34636db15a8f57d7 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 2 Dec 2014 12:58:28 -0800 >Subject: [PATCH 02/28] s4:torture:smb2: Add smb2.lease.v2_breaking3 test. > >This verifies the epoch handling in the multi step break. > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 99f9eee657fcc1c435d00a677395e5125070bf77) >--- > source4/torture/smb2/lease.c | 208 ++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 207 insertions(+), 1 deletion(-) > >diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c >index 9b25eb4..1665586 100644 >--- a/source4/torture/smb2/lease.c >+++ b/source4/torture/smb2/lease.c >@@ -2225,6 +2225,212 @@ done: > return ret; > } > >+static bool test_lease_v2_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 = "v2_lease_breaking3.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; >+ >+ /* >+ * we defer acking the lease break. >+ */ >+ ZERO_STRUCT(break_info); >+ break_info.lease_skip_ack = true; >+ >+ smb2_lease_v2_create_share(&io1, &ls1, false, fname, >+ smb2_util_share_access("RWD"), >+ LEASE1, NULL, >+ smb2_util_lease_state("RHW"), >+ 0x11); >+ 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); >+ /* Epoch increases on open. */ >+ ls1.lease_epoch += 1; >+ CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch); >+ >+ /* >+ * 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_V2(tree->session->transport, >+ "RWH", "RH", LEASE1, ls1.lease_epoch + 1); >+ >+ torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); >+ >+ /* On receiving a lease break, we must sync the new epoch. */ >+ ls1.lease_epoch = break_info.lease_break.new_epoch; >+ >+ /* >+ * 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_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); >+ 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_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); >+ 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_V2(tree->session->transport, >+ "RH", "R", LEASE1, ls1.lease_epoch); >+ /* On receiving a lease break, we must sync the new epoch. */ >+ ls1.lease_epoch = break_info.lease_break.new_epoch; >+ >+ 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_V2(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch); >+ 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_V2(tree->session->transport, >+ "R", "", LEASE1, ls1.lease_epoch); >+ >+ 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_breaking4(struct torture_context *tctx, > struct smb2_tree *tree) > { >@@ -3465,7 +3671,6 @@ static bool test_lease_dynamic_share(struct torture_context *tctx, > return ret; > } > >- > struct torture_suite *torture_smb2_lease_init(void) > { > struct torture_suite *suite = >@@ -3485,6 +3690,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, "v2_breaking3", test_lease_v2_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); >-- >1.9.1 > > >From b1a4923d3c795f051df76436a0bf36ff6ab6bef2 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Wed, 3 Dec 2014 22:32:33 +0100 >Subject: [PATCH 03/28] s4:torture:smb2: let smb2.lease.[v2_]complex1 check the > R->NONE breaks > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit 8c323f20dccb8d7a362e6ebb87fd6ec7739c336a) >--- > source4/torture/smb2/lease.c | 12 ++++++------ > 1 file changed, 6 insertions(+), 6 deletions(-) > >diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c >index 1665586..9d14aeb 100644 >--- a/source4/torture/smb2/lease.c >+++ b/source4/torture/smb2/lease.c >@@ -3040,12 +3040,12 @@ static bool test_lease_complex1(struct torture_context *tctx, > CHECK_STATUS(status, NT_STATUS_OK); > > /* Contend with LEASE2. */ >- smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("RHW")); >+ smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R")); > 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); >+ CHECK_LEASE(&io2, "R", true, LEASE2, 0); > > /* Verify that we were only sent one break. */ > CHECK_BREAK_INFO("RHW", "RH", LEASE1); >@@ -3073,7 +3073,7 @@ static bool test_lease_complex1(struct torture_context *tctx, > CHECK_STATUS(status, NT_STATUS_OK); > > ls2.lease_epoch += 1; >- CHECK_BREAK_INFO("RH", "", LEASE2); >+ CHECK_BREAK_INFO("R", "", LEASE2); > > ZERO_STRUCT(break_info); > >@@ -3178,13 +3178,13 @@ static bool test_lease_v2_complex1(struct torture_context *tctx, > > /* Contend with LEASE2. */ > smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL, >- smb2_util_lease_state("RWH"), 0x11); >+ smb2_util_lease_state("R"), 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, >+ CHECK_LEASE_V2(&io2, "R", true, LEASE2, > 0, 0, ls2.lease_epoch); > > /* Verify that we were only sent one break. */ >@@ -3217,7 +3217,7 @@ static bool test_lease_v2_complex1(struct torture_context *tctx, > > ls2.lease_epoch += 1; > CHECK_BREAK_INFO_V2(tree1a->session->transport, >- "RH", "", LEASE2, ls2.lease_epoch); >+ "R", "", LEASE2, ls2.lease_epoch); > > ZERO_STRUCT(break_info); > >-- >1.9.1 > > >From 1eed7892cefe6eb07609525669735a296306fda3 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 04/28] s3:smbd: factor out a send_break_to_none() helper > function > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit e761c80f65c690ce2d93c30bcd8d8d9d752096a8) >--- > source3/smbd/oplock.c | 40 ++++++++++++++++++++++++---------------- > 1 file changed, 24 insertions(+), 16 deletions(-) > >diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c >index aa99f68..62858c6 100644 >--- a/source3/smbd/oplock.c >+++ b/source3/smbd/oplock.c >@@ -667,14 +667,28 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, > tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state); > } > >+static void send_break_to_none(struct messaging_context *msg_ctx, >+ const struct share_mode_entry *e) >+{ >+ char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; >+ >+ share_mode_entry_to_message(msg, e); >+ /* Overload entry->op_type */ >+ SSVAL(msg, OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK); >+ >+ messaging_send_buf(msg_ctx, e->pid, MSG_SMB_BREAK_REQUEST, >+ (uint8 *)msg, sizeof(msg)); >+} >+ > static void do_break_to_none(struct tevent_context *ctx, > struct tevent_immediate *im, > void *private_data) > { > struct break_to_none_state *state = talloc_get_type_abort( > private_data, struct break_to_none_state); >- int i; >+ uint32_t i; > struct share_mode_lock *lck; >+ struct share_mode_data *d; > > lck = get_existing_share_mode_lock(talloc_tos(), state->id); > if (lck == NULL) { >@@ -682,15 +696,15 @@ static void do_break_to_none(struct tevent_context *ctx, > __func__, file_id_string_tos(&state->id))); > goto done; > } >+ d = lck->data; > > DEBUG(10,("%s: num_share_modes = %d\n", __func__, > lck->data->num_share_modes )); > >- for(i = 0; i < lck->data->num_share_modes; i++) { >- struct share_mode_entry *share_entry = &lck->data->share_modes[i]; >- char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; >+ for(i = 0; i < d->num_share_modes; i++) { >+ struct share_mode_entry *e = &d->share_modes[i]; > >- if (!is_valid_share_mode_entry(share_entry)) { >+ if (!is_valid_share_mode_entry(e)) { > continue; > } > >@@ -705,15 +719,15 @@ static void do_break_to_none(struct tevent_context *ctx, > * NO_OPLOCK states. JRA. > */ > >- DEBUG(10,("%s: share_entry[%i]->op_type == %d\n", __func__, >- i, share_entry->op_type )); >+ DEBUG(10, ("%s: share_entry[%i]->op_type == %d\n", __func__, >+ i, e->op_type )); > >- if (share_entry->op_type == NO_OPLOCK) { >+ if (e->op_type == NO_OPLOCK) { > continue; > } > > /* Paranoia .... */ >- if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) { >+ if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) { > DEBUG(0,("%s: PANIC. " > "share mode entry %d is an exlusive " > "oplock !\n", __func__, i )); >@@ -721,13 +735,7 @@ static void do_break_to_none(struct tevent_context *ctx, > abort(); > } > >- share_mode_entry_to_message(msg, share_entry); >- /* Overload entry->op_type */ >- SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK); >- >- messaging_send_buf(state->sconn->msg_ctx, share_entry->pid, >- MSG_SMB_BREAK_REQUEST, >- (uint8 *)msg, sizeof(msg)); >+ send_break_to_none(state->sconn->msg_ctx, e); > } > > /* We let the message receivers handle removing the oplock state >-- >1.9.1 > > >From 3f32056619d8edb3a08a7182271c71a213281a6d Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 4 Nov 2014 21:46:14 -0800 >Subject: [PATCH 05/28] s3:smbd: Add fsp_client_guid() utility function to > return the connected client guid. > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit c22d521d26a75a71f940088b5a01d7fa924efd88) >--- > source3/smbd/files.c | 5 +++++ > source3/smbd/proto.h | 1 + > 2 files changed, 6 insertions(+) > >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index a9e8357..7116628 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -740,3 +740,8 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, > smb_fname_str_dbg(fsp->fsp_name), > &fsp->name_hash); > } >+ >+const struct GUID *fsp_client_guid(const files_struct *fsp) >+{ >+ return &fsp->conn->sconn->client->connections->smb2.client.guid; >+} >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 1080895..0670597 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -387,6 +387,7 @@ NTSTATUS file_name_hash(connection_struct *conn, > const char *name, uint32_t *p_name_hash); > NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, > const struct smb_filename *smb_fname_in); >+const struct GUID *fsp_client_guid(const files_struct *fsp); > > /* The following definitions come from smbd/ipc.c */ > >-- >1.9.1 > > >From cd3fd8fd470d81d71d4dd55a339457807bb8a06e Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Thu, 13 Nov 2014 11:50:14 +0100 >Subject: [PATCH 06/28] s3:smb2_server: allow smbd_smb2_send_break() with > session == NULL and tcon == NULL > >In future we want to use this for lease breaks and they're not attached >to a session. > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit a9a39953c785e76f67ce72c776b36ad1e0130ded) >--- > source3/smbd/smb2_server.c | 13 +++++++++---- > 1 file changed, 9 insertions(+), 4 deletions(-) > >diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c >index 7100208..d17f74a 100644 >--- a/source3/smbd/smb2_server.c >+++ b/source3/smbd/smb2_server.c >@@ -2760,14 +2760,19 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn, > size_t body_len) > { > struct smbd_smb2_send_break_state *state; >- bool do_encryption = session->global->encryption_required; >+ bool do_encryption = false; >+ uint64_t session_wire_id = 0; > uint64_t nonce_high = 0; > uint64_t nonce_low = 0; > NTSTATUS status; > size_t statelen; > >- if (tcon->global->encryption_required) { >- do_encryption = true; >+ if (session != NULL) { >+ session_wire_id = session->global->session_wire_id; >+ do_encryption = session->global->encryption_required; >+ if (tcon->global->encryption_required) { >+ do_encryption = true; >+ } > } > > statelen = offsetof(struct smbd_smb2_send_break_state, body) + >@@ -2793,7 +2798,7 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn, > SIVAL(state->tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC); > SBVAL(state->tf, SMB2_TF_NONCE+0, nonce_low); > SBVAL(state->tf, SMB2_TF_NONCE+8, nonce_high); >- SBVAL(state->tf, SMB2_TF_SESSION_ID, session->global->session_wire_id); >+ SBVAL(state->tf, SMB2_TF_SESSION_ID, session_wire_id); > > SIVAL(state->hdr, 0, SMB2_MAGIC); > SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); >-- >1.9.1 > > >From cdea7f73b1233a82c316eae5886b7500d9114757 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 07/28] s3:smb2_server: add smbd_smb2_send_lease_break() helper > function > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 498e7220c553155ab6e7f383d9c4377ee92774ee) >--- > source3/smbd/globals.h | 6 ++++++ > source3/smbd/smb2_server.c | 23 +++++++++++++++++++++++ > 2 files changed, 29 insertions(+) > >diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h >index d0015d5..b4e232d 100644 >--- a/source3/smbd/globals.h >+++ b/source3/smbd/globals.h >@@ -250,6 +250,12 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn, > struct smbXsrv_tcon *tcon, > struct smbXsrv_open *op, > uint8_t oplock_level); >+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn, >+ uint16_t new_epoch, >+ uint32_t lease_flags, >+ struct smb2_lease_key *lease_key, >+ uint32_t current_lease_state, >+ uint32_t new_lease_state); > > NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > struct tevent_req *subreq, >diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c >index d17f74a..31ec70b 100644 >--- a/source3/smbd/smb2_server.c >+++ b/source3/smbd/smb2_server.c >@@ -2893,6 +2893,29 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn, > return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body)); > } > >+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn, >+ uint16_t new_epoch, >+ uint32_t lease_flags, >+ struct smb2_lease_key *lease_key, >+ uint32_t current_lease_state, >+ uint32_t new_lease_state) >+{ >+ uint8_t body[0x2c]; >+ >+ SSVAL(body, 0x00, sizeof(body)); >+ SSVAL(body, 0x02, new_epoch); >+ SIVAL(body, 0x04, lease_flags); >+ SBVAL(body, 0x08, lease_key->data[0]); >+ SBVAL(body, 0x10, lease_key->data[1]); >+ SIVAL(body, 0x18, current_lease_state); >+ SIVAL(body, 0x1c, new_lease_state); >+ SIVAL(body, 0x20, 0); /* BreakReason, MUST be 0 */ >+ SIVAL(body, 0x24, 0); /* AccessMaskHint, MUST be 0 */ >+ SIVAL(body, 0x28, 0); /* ShareMaskHint, MUST be 0 */ >+ >+ return smbd_smb2_send_break(xconn, NULL, NULL, body, sizeof(body)); >+} >+ > static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state) > { > NTSTATUS status; >-- >1.9.1 > > >From d5c4832b4cbf0d13e034aa1218a811fd1144d888 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Wed, 3 Dec 2014 17:06:08 -0800 >Subject: [PATCH 08/28] s3: leases: libsmbsharemodes no longer works with SMB2 > leases inside our locking.tdb. > >Remove it until a maintainer can be found. > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit a199214dfb5fc9c2b4c9e15ccb24198065c616b9) >--- > packaging/RHEL-CTDB/configure.rpm | 1 - > packaging/RHEL-CTDB/samba.spec.tmpl | 3 --- > packaging/RHEL/samba.spec.tmpl | 2 -- > source3/libsmb/smb_share_modes.c | 8 ++++++++ > source3/libsmb/smbsharemodes.pc.in | 11 ----------- > source3/wscript_build | 8 -------- > 6 files changed, 8 insertions(+), 25 deletions(-) > delete mode 100644 source3/libsmb/smbsharemodes.pc.in > >diff --git a/packaging/RHEL-CTDB/configure.rpm b/packaging/RHEL-CTDB/configure.rpm >index 398a3f9..62a326c 100755 >--- a/packaging/RHEL-CTDB/configure.rpm >+++ b/packaging/RHEL-CTDB/configure.rpm >@@ -53,7 +53,6 @@ CC="$CC" CFLAGS="-Wall -g -D_GNU_SOURCE -O3" ./configure -C \ > --enable-fhs \ > --with-pam_smbpass \ > --with-libsmbclient \ >- --with-libsmbsharemodes \ > --without-smbwrapper \ > --with-pam \ > --with-quotas \ >diff --git a/packaging/RHEL-CTDB/samba.spec.tmpl b/packaging/RHEL-CTDB/samba.spec.tmpl >index c1789f2..056c00f 100644 >--- a/packaging/RHEL-CTDB/samba.spec.tmpl >+++ b/packaging/RHEL-CTDB/samba.spec.tmpl >@@ -171,7 +171,6 @@ CFLAGS="$RPM_OPT_FLAGS $EXTRA -D_GNU_SOURCE" ./configure \ > --enable-fhs \ > --with-pam_smbpass \ > --with-libsmbclient \ >- --with-libsmbsharemodes \ > --without-smbwrapper \ > --with-pam \ > --with-quotas \ >@@ -544,8 +543,6 @@ exit 0 > %{_includedir}/libsmbclient.h > %{_libarchdir}/libsmbclient.* > %{_includedir}/smb_share_modes.h >-%{_libarchdir}/libsmbsharemodes.so >-%{_libarchdir}/libsmbsharemodes.so.0 > > %{_includedir}/netapi.h > %{_includedir}/wbclient.h >diff --git a/packaging/RHEL/samba.spec.tmpl b/packaging/RHEL/samba.spec.tmpl >index 05e46e1..ed37994 100644 >--- a/packaging/RHEL/samba.spec.tmpl >+++ b/packaging/RHEL/samba.spec.tmpl >@@ -167,7 +167,6 @@ CC="$CC" CFLAGS="$RPM_OPT_FLAGS $EXTRA -D_GNU_SOURCE" ./configure \ > --with-fhs \ > --with-pam_smbpass \ > --with-libsmbclient \ >- --with-libsmbsharemodes \ > --without-smbwrapper \ > --with-pam \ > --with-quotas \ >@@ -470,7 +469,6 @@ fi > %{_includedir}/libsmbclient.h > %{_libarchdir}/libsmbclient.* > %{_includedir}/smb_share_modes.h >-%{_libarchdir}/libsmbsharemodes.* > > %{_libarchdir}/samba/*.dat > %{_libarchdir}/samba/*.msg >diff --git a/source3/libsmb/smb_share_modes.c b/source3/libsmb/smb_share_modes.c >index f2decc1..bf21bf5 100644 >--- a/source3/libsmb/smb_share_modes.c >+++ b/source3/libsmb/smb_share_modes.c >@@ -2,6 +2,14 @@ > Samba share mode database library external interface library. > Used by non-Samba products needing access to the Samba share mode db. > >+ NOTICE FOR SAMBA 4.2.0 >+ >+ THIS CODE IS NON-FUNCTIONAL IN SAMBA 4.2.0 AND ABOVE DUE TO THE CHANGES IN >+ SHARE MODE DATABASE SCHEMA FOR SMB2 LEASES. >+ >+ CONTACT THE AUTHOR jra@samba.org IF YOU WISH TO RE-ENABLE >+ THIS CODE. >+ > Copyright (C) Jeremy Allison 2005 - 2006 > > sharemodes_procid functions (C) Copyright (C) Volker Lendecke 2005 >diff --git a/source3/libsmb/smbsharemodes.pc.in b/source3/libsmb/smbsharemodes.pc.in >deleted file mode 100644 >index fadc481..0000000 >--- a/source3/libsmb/smbsharemodes.pc.in >+++ /dev/null >@@ -1,11 +0,0 @@ >-prefix=@prefix@ >-exec_prefix=@exec_prefix@ >-libdir=@libdir@ >-includedir=@includedir@ >- >-Name: Samba libsmbsharemodes >-Description: A library >-Version: @PACKAGE_VERSION@ >-Libs: @LIB_RPATH@ -L${libdir} -lsmbsharemodes >-Cflags: -I${includedir} >-URL: http://www.samba.org/ >diff --git a/source3/wscript_build b/source3/wscript_build >index 54ba3a7..cfc519d 100755 >--- a/source3/wscript_build >+++ b/source3/wscript_build >@@ -55,14 +55,6 @@ bld.SAMBA3_LIBRARY('netapi', > pc_files='libnet/netapi.pc', > vnum='0') > >-bld.SAMBA3_LIBRARY('smbsharemodes', >- source='libsmb/smb_share_modes.c', >- public_deps='''talloc tdb_compat''', >- deps='''ccan-hash''', >- public_headers='include/smb_share_modes.h', >- pc_files='libsmb/smbsharemodes.pc', >- vnum='0') >- > bld.SAMBA3_LIBRARY('nss_wins', > source='../nsswitch/wins.c', > deps='''param libsmb LIBTSOCKET''', >-- >1.9.1 > > >From cdb7da6b5e2b8260d596e4defa2de409b8713e8b Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Fri, 10 Oct 2014 16:36:54 -0700 >Subject: [PATCH 09/28] s3:locking: add leases_db infrastructure > >Will enable us to solve the dynamic share path problem >with leases on [homes]. > >We're also able to give the correct error codes when a >lease key is re-used with a different file name. > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 14fac5dbc05823562760ac424522fb39817ec062) >--- > source3/librpc/idl/leases_db.idl | 23 +++ > source3/librpc/idl/wscript_build | 1 + > source3/librpc/wscript_build | 5 + > source3/locking/leases_db.c | 387 +++++++++++++++++++++++++++++++++++++++ > source3/locking/leases_db.h | 46 +++++ > source3/smbd/server.c | 5 + > source3/wscript_build | 6 + > 7 files changed, 473 insertions(+) > create mode 100644 source3/librpc/idl/leases_db.idl > create mode 100644 source3/locking/leases_db.c > create mode 100644 source3/locking/leases_db.h > >diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl >new file mode 100644 >index 0000000..2ab1591 >--- /dev/null >+++ b/source3/librpc/idl/leases_db.idl >@@ -0,0 +1,23 @@ >+#include "idl_types.h" >+ >+import "misc.idl"; >+import "smb2_lease_struct.idl"; >+import "file_id.idl"; >+ >+[ >+ pointer_default(unique) >+] >+interface leases_db >+{ >+ typedef [public] struct { >+ GUID client_guid; >+ smb2_lease_key lease_key; >+ } leases_db_key; >+ >+ typedef [public] struct { >+ uint32 num_file_ids; >+ [size_is(num_file_ids)] file_id ids[]; >+ [string,charset(UTF8)] char *filename; >+ [string,charset(UTF8)] char *stream_name; >+ } leases_db_value; >+} >diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build >index c38fe7b..f9b1bd7 100644 >--- a/source3/librpc/idl/wscript_build >+++ b/source3/librpc/idl/wscript_build >@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL', > '''messaging.idl libnetapi.idl open_files.idl > perfcount.idl secrets.idl libnet_join.idl > smbXsrv.idl >+ leases_db.idl > ''', > options='--includedir=%s --header --ndr-parser' % topinclude, > output_dir='../gen_ndr') >diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build >index 77ae048..38d9a81 100644 >--- a/source3/librpc/wscript_build >+++ b/source3/librpc/wscript_build >@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV', > public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH' > ) > >+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB', >+ source='gen_ndr/ndr_leases_db.c', >+ public_deps='ndr' >+ ) >+ > bld.SAMBA3_SUBSYSTEM('NDR_SECRETS', > source='gen_ndr/ndr_secrets.c', > public_deps='ndr' >diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c >new file mode 100644 >index 0000000..67c93ff >--- /dev/null >+++ b/source3/locking/leases_db.c >@@ -0,0 +1,387 @@ >+/* >+ Unix SMB/CIFS implementation. >+ Map lease keys to file ids >+ Copyright (C) Volker Lendecke 2013 >+ >+ This program is free software; you can redistribute it and/or modify >+ it under the terms of the GNU General Public License as published by >+ the Free Software Foundation; either version 3 of the License, or >+ (at your option) any later version. >+ >+ This program is distributed in the hope that it will be useful, >+ but WITHOUT ANY WARRANTY; without even the implied warranty of >+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ GNU General Public License for more details. >+ >+ You should have received a copy of the GNU General Public License >+ along with this program. If not, see <http://www.gnu.org/licenses/>. >+ >+*/ >+ >+#include "includes.h" >+#include "system/filesys.h" >+#include "locking/leases_db.h" >+#include "dbwrap/dbwrap.h" >+#include "dbwrap/dbwrap_open.h" >+#include "util_tdb.h" >+#include "ndr.h" >+#include "librpc/gen_ndr/ndr_leases_db.h" >+ >+#undef DBGC_CLASS >+#define DBGC_CLASS DBGC_LOCKING >+ >+/* the leases database handle */ >+static struct db_context *leases_db; >+ >+bool leases_db_init(bool read_only) >+{ >+ if (leases_db) { >+ return true; >+ } >+ >+ leases_db = db_open(NULL, lock_path("leases.tdb"), 0, >+ TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST| >+ TDB_INCOMPATIBLE_HASH, >+ read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644, >+ DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE); >+ >+ if (leases_db == NULL) { >+ DEBUG(1, ("ERROR: Failed to initialise leases database\n")); >+ return false; >+ } >+ >+ return true; >+} >+ >+static bool leases_db_key(TALLOC_CTX *mem_ctx, >+ const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ TDB_DATA *key) >+{ >+ struct leases_db_key db_key = { >+ .client_guid = *client_guid, >+ .lease_key = *lease_key }; >+ DATA_BLOB blob; >+ enum ndr_err_code ndr_err; >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("%s:\n", __func__)); >+ NDR_PRINT_DEBUG(leases_db_key, &db_key); >+ } >+ >+ ndr_err = ndr_push_struct_blob( >+ &blob, mem_ctx, &db_key, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_key); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ return false; >+ } >+ >+ *key = make_tdb_data(blob.data, blob.length); >+ return true; >+} >+ >+NTSTATUS leases_db_add(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id, >+ const char *filename, >+ const char *stream_name) >+{ >+ TDB_DATA db_key, db_value; >+ DATA_BLOB blob; >+ struct db_record *rec; >+ NTSTATUS status; >+ bool ok; >+ struct leases_db_value new_value; >+ struct leases_db_value *value = NULL; >+ enum ndr_err_code ndr_err; >+ >+ if (!leases_db_init(false)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ DEBUG(10, ("%s: leases_db_key failed\n", __func__)); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key); >+ TALLOC_FREE(db_key.dptr); >+ if (rec == NULL) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ db_value = dbwrap_record_get_value(rec); >+ if (db_value.dsize != 0) { >+ uint32_t i; >+ >+ DEBUG(10, ("%s: record exists\n", __func__)); >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ >+ blob.data = db_value.dptr; >+ blob.length = db_value.dsize; >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ /* id must be unique. */ >+ for (i = 0; i < value->num_file_ids; i++) { >+ if (file_id_equal(id, &value->ids[i])) { >+ status = NT_STATUS_OBJECT_NAME_COLLISION; >+ goto out; >+ } >+ } >+ >+ value->ids = talloc_realloc(value, value->ids, struct file_id, >+ value->num_file_ids + 1); >+ if (value->ids == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ value->ids[value->num_file_ids] = *id; >+ value->num_file_ids += 1; >+ >+ } else { >+ DEBUG(10, ("%s: new record\n", __func__)); >+ >+ new_value = (struct leases_db_value) { >+ .num_file_ids = 1, >+ .ids = discard_const_p(struct file_id, id), >+ .filename = filename, >+ .stream_name = stream_name, >+ }; >+ value = &new_value; >+ } >+ >+ ndr_err = ndr_push_struct_blob( >+ &blob, talloc_tos(), value, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("%s:\n", __func__)); >+ NDR_PRINT_DEBUG(leases_db_value, value); >+ } >+ >+ db_value = make_tdb_data(blob.data, blob.length); >+ >+ status = dbwrap_record_store(rec, db_value, 0); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n", >+ __func__, nt_errstr(status))); >+ } >+ >+ out: >+ >+ if (value != &new_value) { >+ TALLOC_FREE(value); >+ } >+ TALLOC_FREE(rec); >+ return status; >+} >+ >+NTSTATUS leases_db_del(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id) >+{ >+ TDB_DATA db_key, db_value; >+ struct db_record *rec; >+ NTSTATUS status; >+ struct leases_db_value *value; >+ enum ndr_err_code ndr_err; >+ DATA_BLOB blob; >+ uint32_t i; >+ bool ok; >+ >+ if (!leases_db_init(false)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key); >+ TALLOC_FREE(db_key.dptr); >+ if (rec == NULL) { >+ return NT_STATUS_NOT_FOUND; >+ } >+ db_value = dbwrap_record_get_value(rec); >+ if (db_value.dsize == 0) { >+ status = NT_STATUS_INTERNAL_ERROR; >+ goto out; >+ } >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ status = NT_STATUS_NO_MEMORY; >+ goto out; >+ } >+ >+ blob.data = db_value.dptr; >+ blob.length = db_value.dsize; >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ /* id must exist. */ >+ for (i = 0; i < value->num_file_ids; i++) { >+ if (file_id_equal(id, &value->ids[i])) { >+ break; >+ } >+ } >+ >+ if (i == value->num_file_ids) { >+ status = NT_STATUS_NOT_FOUND; >+ goto out; >+ } >+ >+ value->ids[i] = value->ids[value->num_file_ids-1]; >+ value->num_file_ids -= 1; >+ >+ if (value->num_file_ids == 0) { >+ DEBUG(10, ("%s: deleting record\n", __func__)); >+ status = dbwrap_record_delete(rec); >+ } else { >+ DEBUG(10, ("%s: updating record\n", __func__)); >+ ndr_err = ndr_push_struct_blob( >+ &blob, talloc_tos(), value, >+ (ndr_push_flags_fn_t)ndr_push_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ status = ndr_map_error2ntstatus(ndr_err); >+ goto out; >+ } >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("%s:\n", __func__)); >+ NDR_PRINT_DEBUG(leases_db_value, value); >+ } >+ >+ db_value = make_tdb_data(blob.data, blob.length); >+ >+ status = dbwrap_record_store(rec, db_value, 0); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n", >+ __func__, nt_errstr(status))); >+ } >+ } >+ >+ out: >+ >+ TALLOC_FREE(value); >+ TALLOC_FREE(rec); >+ return status; >+} >+ >+struct leases_db_fetch_state { >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, const char *filename, >+ const char *stream_name, void *private_data); >+ void *private_data; >+ NTSTATUS status; >+}; >+ >+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) >+{ >+ struct leases_db_fetch_state *state = >+ (struct leases_db_fetch_state *)private_data; >+ DATA_BLOB blob = { .data = data.dptr, .length = data.dsize }; >+ enum ndr_err_code ndr_err; >+ struct leases_db_value *value; >+ >+ value = talloc(talloc_tos(), struct leases_db_value); >+ if (value == NULL) { >+ state->status = NT_STATUS_NO_MEMORY; >+ return; >+ } >+ >+ ndr_err = ndr_pull_struct_blob_all( >+ &blob, value, value, >+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value); >+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { >+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n", >+ __func__, ndr_errstr(ndr_err))); >+ TALLOC_FREE(value); >+ state->status = ndr_map_error2ntstatus(ndr_err); >+ return; >+ } >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("%s:\n", __func__)); >+ NDR_PRINT_DEBUG(leases_db_value, value); >+ } >+ >+ state->parser(value->num_file_ids, >+ value->ids, value->filename, value->stream_name, >+ state->private_data); >+ >+ TALLOC_FREE(value); >+ state->status = NT_STATUS_OK; >+} >+ >+NTSTATUS leases_db_parse(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, >+ const char *filename, >+ const char *stream_name, >+ void *private_data), >+ void *private_data) >+{ >+ TDB_DATA db_key; >+ struct leases_db_fetch_state state; >+ NTSTATUS status; >+ bool ok; >+ >+ if (!leases_db_init(true)) { >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key); >+ if (!ok) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ state = (struct leases_db_fetch_state) { >+ .parser = parser, >+ .private_data = private_data, >+ .status = NT_STATUS_OK >+ }; >+ >+ status = dbwrap_parse_record(leases_db, db_key, leases_db_parser, >+ &state); >+ TALLOC_FREE(db_key.dptr); >+ if (!NT_STATUS_IS_OK(status)) { >+ return status; >+ } >+ return state.status; >+} >diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h >new file mode 100644 >index 0000000..f570356 >--- /dev/null >+++ b/source3/locking/leases_db.h >@@ -0,0 +1,46 @@ >+/* >+ * Unix SMB/CIFS implementation. >+ * leases.tdb functions >+ * >+ * 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 <http://www.gnu.org/licenses/>. >+ */ >+ >+#ifndef _LEASES_DB_H_ >+#define _LEASES_DB_H_ >+ >+struct GUID; >+struct smb2_lease_key; >+struct file_id; >+ >+bool leases_db_init(bool read_only); >+NTSTATUS leases_db_add(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id, >+ const char *filename, >+ const char *stream_name); >+NTSTATUS leases_db_del(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ const struct file_id *id); >+NTSTATUS leases_db_parse(const struct GUID *client_guid, >+ const struct smb2_lease_key *lease_key, >+ void (*parser)(uint32_t num_file_ids, >+ struct file_id *ids, >+ const char *filename, >+ const char *stream_name, >+ void *private_data), >+ void *private_data); >+ >+#endif /* _LEASES_DB_H_ */ >diff --git a/source3/smbd/server.c b/source3/smbd/server.c >index 8b9a1c1..0c00cfd 100644 >--- a/source3/smbd/server.c >+++ b/source3/smbd/server.c >@@ -47,6 +47,7 @@ > #include "../lib/util/pidfile.h" > #include "lib/smbd_shim.h" > #include "scavenger.h" >+#include "locking/leases_db.h" > > struct smbd_open_socket; > struct smbd_child_pid; >@@ -1450,6 +1451,10 @@ extern void build_options(bool screen); > if (!locking_init()) > exit_daemon("Samba cannot init locking", EACCES); > >+ if (!leases_db_init(false)) { >+ exit_daemon("Samba cannot init leases", EACCES); >+ } >+ > if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) { > exit_daemon("Samba cannot init notification", EACCES); > } >diff --git a/source3/wscript_build b/source3/wscript_build >index cfc519d..92ab5be 100755 >--- a/source3/wscript_build >+++ b/source3/wscript_build >@@ -610,6 +610,7 @@ bld.SAMBA3_LIBRARY('smbd_base', > LIBAFS > RPC_SERVICE > NDR_SMBXSRV >+ LEASES_DB > LIBASYS > sysquotas > ccan-hash >@@ -627,9 +628,14 @@ bld.SAMBA3_SUBSYSTEM('LOCKING', > deps=''' > tdb_compat > talloc >+ LEASES_DB > NDR_OPEN_FILES > FNAME_UTIL''') > >+bld.SAMBA3_SUBSYSTEM('LEASES_DB', >+ source='locking/leases_db.c', >+ deps='NDR_LEASES_DB') >+ > if bld.CONFIG_GET("WITH_PROFILE"): > bld.SAMBA3_SUBSYSTEM('PROFILE', > source='profile/profile.c', >-- >1.9.1 > > >From 10ac55c02b8e64f4dffc8e9043acdb69896fdcf2 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Thu, 27 Nov 2014 18:34:56 +0100 >Subject: [PATCH 10/28] s3:open_files.idl: add data structures for SMB2.1 and > SMB3.0 leases. > >Pair-Programmed-With: Volker Lendecke <vl@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 6b2f19a5e6e8b3eb2a44cd24408ba4f27cfb8745) >--- > source3/include/smb.h | 1 + > source3/librpc/idl/open_files.idl | 36 ++++++++++++++++++++++++++++++++++++ > source3/librpc/wscript_build | 2 +- > source3/locking/locking.c | 17 +++++++++++++++-- > source3/locking/proto.h | 5 +++-- > source3/locking/share_mode_lock.c | 12 +++++++++++- > source3/smbd/open.c | 10 +++++++--- > source3/utils/status.c | 9 +++++++++ > 8 files changed, 83 insertions(+), 9 deletions(-) > >diff --git a/source3/include/smb.h b/source3/include/smb.h >index aab4ff5..7bace88 100644 >--- a/source3/include/smb.h >+++ b/source3/include/smb.h >@@ -577,6 +577,7 @@ enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT, > #define EXCLUSIVE_OPLOCK OPLOCK_EXCLUSIVE > #define BATCH_OPLOCK OPLOCK_BATCH > #define LEVEL_II_OPLOCK OPLOCK_LEVEL_II >+#define LEASE_OPLOCK 0x100 > > /* The following are Samba-private. */ > #define INTERNAL_OPEN_ONLY 0x8 >diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl >index 4278301..0a9d5fd 100644 >--- a/source3/librpc/idl/open_files.idl >+++ b/source3/librpc/idl/open_files.idl >@@ -3,6 +3,8 @@ > import "server_id.idl"; > import "security.idl"; > import "file_id.idl"; >+import "smb2_lease_struct.idl"; >+import "misc.idl"; > > [ > pointer_default(unique) >@@ -10,10 +12,41 @@ import "file_id.idl"; > > interface open_files > { >+ typedef [public,flag(NDR_PAHEX)] struct { >+ GUID client_guid; >+ smb2_lease_key lease_key; >+ smb2_lease_state current_state; >+ /* >+ * 'breaking' indicates that we're waiting >+ * for a lease break ack from the client >+ * and breaking_to_requested and breaking_to_required >+ * have a meaning. >+ * >+ * breaking_to_requested is the value already sent to >+ * the client, the client needs to ack to this (or less). >+ * >+ * breaking_to_required is the internal value that needs to >+ * be reached before we can reset breaking = false, this >+ * may requires multiple roundtrips to the client, e.g. >+ * when the lease broken to a more reduced value, while >+ * the lease break is still in progress. >+ * >+ * The following can be assumed (if breaking == true): >+ * >+ * current_state > breaking_to_requested >= breaking_to_required >+ */ >+ boolean8 breaking; >+ smb2_lease_state breaking_to_requested; >+ smb2_lease_state breaking_to_required; >+ uint16 lease_version; >+ uint16 epoch; >+ } share_mode_lease; >+ > typedef [public] struct { > server_id pid; > hyper op_mid; > uint16 op_type; >+ uint32 lease_idx; > uint32 access_mask; > uint32 share_access; > uint32 private_options; >@@ -29,6 +62,7 @@ interface open_files > * to store this share_mode_entry on disk. > */ > [skip] boolean8 stale; >+ [skip] share_mode_lease *lease; > } share_mode_entry; > > typedef [public] struct { >@@ -43,6 +77,8 @@ interface open_files > [string,charset(UTF8)] char *stream_name; > uint32 num_share_modes; > [size_is(num_share_modes)] share_mode_entry share_modes[]; >+ uint32 num_leases; >+ [size_is(num_leases)] share_mode_lease leases[]; > uint32 num_delete_tokens; > [size_is(num_delete_tokens)] delete_token delete_tokens[]; > timespec old_write_time; >diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build >index 38d9a81..5c83cf2 100644 >--- a/source3/librpc/wscript_build >+++ b/source3/librpc/wscript_build >@@ -17,7 +17,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_MESSAGING', > > bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES', > source='gen_ndr/ndr_open_files.c', >- public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY' >+ public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT' > ) > > bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV', >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index a320068..9194dd3 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -608,6 +608,7 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e) > num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0); > num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0); > num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0); >+ num_props += (e->op_type == LEASE_OPLOCK); > > if ((num_props > 1) && serverid_exists(&e->pid)) { > smb_panic("Invalid share mode entry"); >@@ -693,11 +694,21 @@ void remove_stale_share_mode_entries(struct share_mode_data *d) > } > } > >-bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp, >- uid_t uid, uint64_t mid, uint16 op_type) >+bool set_share_mode(struct share_mode_lock *lck, struct files_struct *fsp, >+ uid_t uid, uint64_t mid, uint16_t op_type, >+ uint32_t lease_idx) > { > struct share_mode_data *d = lck->data; > struct share_mode_entry *tmp, *e; >+ struct share_mode_lease *lease = NULL; >+ >+ if (lease_idx == UINT32_MAX) { >+ lease = NULL; >+ } else if (lease_idx >= d->num_leases) { >+ return false; >+ } else { >+ lease = &d->leases[lease_idx]; >+ } > > tmp = talloc_realloc(d, d->share_modes, struct share_mode_entry, > d->num_share_modes+1); >@@ -716,6 +727,8 @@ bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp, > e->access_mask = fsp->access_mask; > e->op_mid = mid; > e->op_type = op_type; >+ e->lease_idx = lease_idx; >+ e->lease = lease; > e->time.tv_sec = fsp->open_time.tv_sec; > e->time.tv_usec = fsp->open_time.tv_usec; > e->id = fsp->file_id; >diff --git a/source3/locking/proto.h b/source3/locking/proto.h >index 8eccff8..6b60330 100644 >--- a/source3/locking/proto.h >+++ b/source3/locking/proto.h >@@ -166,8 +166,9 @@ void get_file_infos(struct file_id id, > struct timespec *write_time); > bool is_valid_share_mode_entry(const struct share_mode_entry *e); > bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx); >-bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp, >- uid_t uid, uint64_t mid, uint16 op_type); >+bool set_share_mode(struct share_mode_lock *lck, struct files_struct *fsp, >+ uid_t uid, uint64_t mid, uint16_t op_type, >+ uint32_t lease_idx); > void remove_stale_share_mode_entries(struct share_mode_data *d); > bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp); > bool mark_share_mode_disconnected(struct share_mode_lock *lck, >diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c >index 65409ac..2a130cb 100644 >--- a/source3/locking/share_mode_lock.c >+++ b/source3/locking/share_mode_lock.c >@@ -147,7 +147,17 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx, > */ > > for (i=0; i<d->num_share_modes; i++) { >- d->share_modes[i].stale = false; >+ struct share_mode_entry *e = &d->share_modes[i]; >+ >+ e->stale = false; >+ e->lease = NULL; >+ if (e->op_type != LEASE_OPLOCK) { >+ continue; >+ } >+ if (e->lease_idx >= d->num_leases) { >+ continue; >+ } >+ e->lease = &d->leases[e->lease_idx]; > } > d->modified = false; > d->fresh = false; >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index 1952823..c14d2eb 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -1568,7 +1568,8 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, > > ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn), > req ? req->mid : 0, >- fsp->oplock_type); >+ fsp->oplock_type, >+ UINT32_MAX); > if (!ok) { > return NT_STATUS_NO_MEMORY; > } >@@ -3080,6 +3081,7 @@ static NTSTATUS open_directory(connection_struct *conn, > NTSTATUS status; > struct timespec mtimespec; > int info = 0; >+ bool ok; > > if (is_ntfs_stream_smb_fname(smb_dname)) { > DEBUG(2, ("open_directory: %s is a stream name!\n", >@@ -3336,8 +3338,10 @@ static NTSTATUS open_directory(connection_struct *conn, > return status; > } > >- if (!set_share_mode(lck, fsp, get_current_uid(conn), >- req ? req->mid : 0, NO_OPLOCK)) { >+ ok = set_share_mode(lck, fsp, get_current_uid(conn), >+ req ? req->mid : 0, NO_OPLOCK, >+ UINT32_MAX); >+ if (!ok) { > TALLOC_FREE(lck); > fd_close(fsp); > file_free(req, fsp); >diff --git a/source3/utils/status.c b/source3/utils/status.c >index 2c21ee4..f8cbcf2 100644 >--- a/source3/utils/status.c >+++ b/source3/utils/status.c >@@ -177,6 +177,15 @@ static int print_share_mode(const struct share_mode_entry *e, > d_printf("BATCH "); > } else if (e->op_type & LEVEL_II_OPLOCK) { > d_printf("LEVEL_II "); >+ } else if (e->op_type == LEASE_OPLOCK) { >+ uint32_t lstate = e->lease->current_state; >+ d_printf("LEASE(%s%s%s)%s%s%s ", >+ (lstate & SMB2_LEASE_READ)?"R":"", >+ (lstate & SMB2_LEASE_WRITE)?"W":"", >+ (lstate & SMB2_LEASE_HANDLE)?"H":"", >+ (lstate & SMB2_LEASE_READ)?"":" ", >+ (lstate & SMB2_LEASE_WRITE)?"":" ", >+ (lstate & SMB2_LEASE_HANDLE)?"":" "); > } else { > d_printf("NONE "); > } >-- >1.9.1 > > >From ebe478eee1a03d4ab2a8a47868533f5cd2d05665 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Fri, 10 Oct 2014 14:32:19 -0700 >Subject: [PATCH 11/28] s3:locking: ensure all share mode removal functions go > through a common lease refcount manager. > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit a504b84ec147f64c428095519099104a2cb6ff1f) >--- > source3/locking/locking.c | 91 +++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 88 insertions(+), 3 deletions(-) > >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index 9194dd3..f96887e 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -46,6 +46,7 @@ > #include "messages.h" > #include "util_tdb.h" > #include "../librpc/gen_ndr/ndr_open_files.h" >+#include "locking/leases_db.h" > > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_LOCKING >@@ -617,6 +618,86 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e) > } > > /* >+ * See if we need to remove a lease being referred to by a >+ * share mode that is being marked stale or deleted. >+ */ >+ >+static void remove_share_mode_lease(struct share_mode_data *d, >+ struct share_mode_entry *e) >+{ >+ struct GUID client_guid; >+ struct smb2_lease_key lease_key; >+ uint16_t op_type; >+ uint32_t lease_idx; >+ uint32_t i; >+ >+ op_type = e->op_type; >+ e->op_type = NO_OPLOCK; >+ >+ d->modified = true; >+ >+ if (op_type != LEASE_OPLOCK) { >+ return; >+ } >+ >+ /* >+ * This used to reference a lease. If there's no other one referencing >+ * it, remove it. >+ */ >+ >+ lease_idx = e->lease_idx; >+ e->lease_idx = UINT32_MAX; >+ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].stale) { >+ continue; >+ } >+ if (e == &d->share_modes[i]) { >+ /* Not ourselves. */ >+ continue; >+ } >+ if (d->share_modes[i].lease_idx == lease_idx) { >+ break; >+ } >+ } >+ if (i < d->num_share_modes) { >+ /* >+ * Found another one >+ */ >+ return; >+ } >+ >+ memcpy(&client_guid, >+ &d->leases[lease_idx].client_guid, >+ sizeof(client_guid)); >+ lease_key = d->leases[lease_idx].lease_key; >+ >+ d->num_leases -= 1; >+ d->leases[lease_idx] = d->leases[d->num_leases]; >+ >+ /* >+ * We changed the lease array. Fix all references to it. >+ */ >+ for (i=0; i<d->num_share_modes; i++) { >+ if (d->share_modes[i].lease_idx == d->num_leases) { >+ d->share_modes[i].lease_idx = lease_idx; >+ d->share_modes[i].lease = &d->leases[lease_idx]; >+ } >+ } >+ >+ { >+ NTSTATUS status; >+ >+ status = leases_db_del(&client_guid, >+ &lease_key, >+ &e->id); >+ >+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__, >+ nt_errstr(status))); >+ } >+} >+ >+/* > * In case d->share_modes[i] conflicts with something or otherwise is > * being used, we need to make sure the corresponding process still > * exists. >@@ -674,6 +755,8 @@ bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx) > } > } > >+ remove_share_mode_lease(d, e); >+ > d->modified = true; > return true; > } >@@ -782,6 +865,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp) > if (e == NULL) { > return False; > } >+ remove_share_mode_lease(lck->data, e); > *e = lck->data->share_modes[lck->data->num_share_modes-1]; > lck->data->num_share_modes -= 1; > lck->data->modified = True; >@@ -829,6 +913,7 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck, > > bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > { >+ struct share_mode_data *d = lck->data; > struct share_mode_entry *e; > > e = find_share_mode_entry(lck, fsp); >@@ -836,9 +921,9 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > return False; > } > >- e->op_type = NO_OPLOCK; >- lck->data->modified = True; >- return True; >+ remove_share_mode_lease(d, e); >+ d->modified = True; >+ return true; > } > > /******************************************************************* >-- >1.9.1 > > >From f75656f46128707dfe219f014fc468e27f85db9b Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Thu, 27 Nov 2014 19:32:46 +0100 >Subject: [PATCH 12/28] s3:locking: cleanup leases_db from > share_mode_cleanup_disconnected() > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit 62e7e142aa350be8f25193bf9c39a19e354346ea) >--- > source3/locking/share_mode_lock.c | 12 ++++++++++++ > 1 file changed, 12 insertions(+) > >diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c >index 2a130cb..327ac79 100644 >--- a/source3/locking/share_mode_lock.c >+++ b/source3/locking/share_mode_lock.c >@@ -47,6 +47,7 @@ > #include "util_tdb.h" > #include "../librpc/gen_ndr/ndr_open_files.h" > #include "source3/lib/dbwrap/dbwrap_watch.h" >+#include "locking/leases_db.h" > > #undef DBGC_CLASS > #define DBGC_CLASS DBGC_LOCKING >@@ -631,6 +632,16 @@ bool share_mode_cleanup_disconnected(struct file_id fid, > } > } > >+ for (n=0; n < data->num_leases; n++) { >+ struct share_mode_lease *l = &data->leases[n]; >+ NTSTATUS status; >+ >+ status = leases_db_del(&l->client_guid, &l->lease_key, &fid); >+ >+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__, >+ nt_errstr(status))); >+ } >+ > ok = brl_cleanup_disconnected(fid, open_persistent_id); > if (!ok) { > DEBUG(10, ("share_mode_cleanup_disconnected: " >@@ -665,6 +676,7 @@ bool share_mode_cleanup_disconnected(struct file_id fid, > (unsigned long long)open_persistent_id)); > > data->num_share_modes = 0; >+ data->num_leases = 0; > data->modified = true; > > ret = true; >-- >1.9.1 > > >From 22d35cc76d6e793191e4fbf0ed7a48a802a69e11 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 13/28] s3:locking: add downgrade_share_lease() helper function > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 3e91b823e4447b936e0fafaf7807b665a51ded30) >--- > source3/locking/locking.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++ > source3/locking/proto.h | 6 ++++ > 2 files changed, 86 insertions(+) > >diff --git a/source3/locking/locking.c b/source3/locking/locking.c >index f96887e..d144f5c 100644 >--- a/source3/locking/locking.c >+++ b/source3/locking/locking.c >@@ -944,6 +944,86 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) > return True; > } > >+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, >+ struct share_mode_lock *lck, >+ const struct smb2_lease_key *key, >+ uint32_t new_lease_state, >+ struct share_mode_lease **_l) >+{ >+ struct share_mode_data *d = lck->data; >+ struct share_mode_lease *l; >+ uint32_t i; >+ >+ *_l = NULL; >+ >+ for (i=0; i<d->num_leases; i++) { >+ if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid, >+ key, >+ &d->leases[i].client_guid, >+ &d->leases[i].lease_key)) { >+ break; >+ } >+ } >+ if (i == d->num_leases) { >+ DEBUG(10, ("lease not found\n")); >+ return NT_STATUS_INVALID_PARAMETER; >+ } >+ >+ l = &d->leases[i]; >+ >+ if (!l->breaking) { >+ DEBUG(1, ("Attempt to break from %d to %d - but we're not in breaking state\n", >+ (int)l->current_state, (int)new_lease_state)); >+ return NT_STATUS_UNSUCCESSFUL; >+ } >+ >+ /* >+ * Can't upgrade anything: l->breaking_to_requested (and l->current_state) >+ * must be a strict bitwise superset of new_lease_state >+ */ >+ if ((new_lease_state & l->breaking_to_requested) != new_lease_state) { >+ DEBUG(1, ("Attempt to upgrade from %d to %d - expected %d\n", >+ (int)l->current_state, (int)new_lease_state, >+ (int)l->breaking_to_requested)); >+ return NT_STATUS_REQUEST_NOT_ACCEPTED; >+ } >+ >+ if (l->current_state != new_lease_state) { >+ l->current_state = new_lease_state; >+ d->modified = true; >+ } >+ >+ if ((new_lease_state & ~l->breaking_to_required) != 0) { >+ DEBUG(5, ("lease state %d not fully broken from %d to %d\n", >+ (int)new_lease_state, >+ (int)l->current_state, >+ (int)l->breaking_to_required)); >+ l->breaking_to_requested = l->breaking_to_required; >+ if (l->current_state & (~SMB2_LEASE_READ)) { >+ /* >+ * Here we break in steps, as windows does >+ * see the breaking3 and v2_breaking3 tests. >+ */ >+ l->breaking_to_requested |= SMB2_LEASE_READ; >+ } >+ d->modified = true; >+ *_l = l; >+ return NT_STATUS_OPLOCK_BREAK_IN_PROGRESS; >+ } >+ >+ DEBUG(10, ("breaking from %d to %d - expected %d\n", >+ (int)l->current_state, (int)new_lease_state, >+ (int)l->breaking_to_requested)); >+ >+ l->breaking_to_requested = 0; >+ l->breaking_to_required = 0; >+ l->breaking = false; >+ >+ d->modified = true; >+ >+ return NT_STATUS_OK; >+} >+ > /**************************************************************************** > Adds a delete on close token. > ****************************************************************************/ >diff --git a/source3/locking/proto.h b/source3/locking/proto.h >index 6b60330..c4ea198 100644 >--- a/source3/locking/proto.h >+++ b/source3/locking/proto.h >@@ -175,6 +175,12 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck, > struct files_struct *fsp); > bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp); > bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp); >+struct share_mode_lease; >+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, >+ struct share_mode_lock *lck, >+ const struct smb2_lease_key *key, >+ uint32_t new_lease_state, >+ struct share_mode_lease **_l); > bool get_delete_on_close_token(struct share_mode_lock *lck, > uint32_t name_hash, > const struct security_token **pp_nt_tok, >-- >1.9.1 > > >From 76a3c3ecb298cc7d6f767ed486692417c432fc21 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Wed, 3 Dec 2014 17:57:37 +0100 >Subject: [PATCH 14/28] s3:vfs.h: add more elements to struct fsp_lease > >We'll need a reference to the smbd_server_connection as well >as a timeout handler. > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit 7ca8785cae6529fe9b8f5546aaf3c15cca9458cd) >--- > source3/include/vfs.h | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/source3/include/vfs.h b/source3/include/vfs.h >index b0f00e8..e7dc079 100644 >--- a/source3/include/vfs.h >+++ b/source3/include/vfs.h >@@ -205,6 +205,8 @@ struct fd_handle { > > struct fsp_lease { > size_t ref_count; >+ struct smbd_server_connection *sconn; >+ struct tevent_timer *timeout; > struct smb2_lease lease; > }; > >-- >1.9.1 > > >From aff499eb7b38fdaf8433cb3141b3fa8fdaf70dc4 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 15/28] s3:smbd: add fsp_lease_type() and get_lease_type() > helper functions > >These convert the oplock state into SMB2_LEASE_ flags. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit b72fca52defa1fd0f7d10f2811d86bbc7c661f50) >--- > source3/smbd/files.c | 8 ++++++++ > source3/smbd/oplock.c | 30 ++++++++++++++++++++++++++++++ > source3/smbd/proto.h | 3 +++ > 3 files changed, 41 insertions(+) > >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index 7116628..d866d63 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -745,3 +745,11 @@ const struct GUID *fsp_client_guid(const files_struct *fsp) > { > return &fsp->conn->sconn->client->connections->smb2.client.guid; > } >+ >+uint32_t fsp_lease_type(struct files_struct *fsp) >+{ >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ return fsp->lease->lease.lease_state; >+ } >+ return map_oplock_to_lease_type(fsp->oplock_type); >+} >diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c >index 62858c6..5a12883 100644 >--- a/source3/smbd/oplock.c >+++ b/source3/smbd/oplock.c >@@ -148,6 +148,36 @@ static void downgrade_file_oplock(files_struct *fsp) > TALLOC_FREE(fsp->oplock_timeout); > } > >+uint32_t map_oplock_to_lease_type(uint16_t op_type) >+{ >+ uint32_t ret; >+ >+ switch(op_type) { >+ case BATCH_OPLOCK: >+ case BATCH_OPLOCK|EXCLUSIVE_OPLOCK: >+ ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE; >+ break; >+ case EXCLUSIVE_OPLOCK: >+ ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE; >+ break; >+ case LEVEL_II_OPLOCK: >+ ret = SMB2_LEASE_READ; >+ break; >+ default: >+ ret = SMB2_LEASE_NONE; >+ break; >+ } >+ return ret; >+} >+ >+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e) >+{ >+ if (e->op_type == LEASE_OPLOCK) { >+ return d->leases[e->lease_idx].current_state; >+ } >+ return map_oplock_to_lease_type(e->op_type); >+} >+ > bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck) > { > struct share_mode_data *d = lck->data; >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 0670597..67adbba 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -388,6 +388,7 @@ NTSTATUS file_name_hash(connection_struct *conn, > NTSTATUS fsp_set_smb_fname(struct files_struct *fsp, > const struct smb_filename *smb_fname_in); > const struct GUID *fsp_client_guid(const files_struct *fsp); >+uint32_t fsp_lease_type(struct files_struct *fsp); > > /* The following definitions come from smbd/ipc.c */ > >@@ -648,6 +649,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn, > > /* The following definitions come from smbd/oplock.c */ > >+uint32_t map_oplock_to_lease_type(uint16_t op_type); >+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e); > 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); >-- >1.9.1 > > >From b14420c3527660a3bdfd3bc2a6e71706b21f34b0 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 16/28] s3:smb2_create: allow durable handles with > SMB2_LEASE_HANDLE > >We don't support real lease yet, but this makes use of fsp_lease_type() >which converts a batch oplock into and RWH lease. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 81a5a9e8971fad928e0a1a022e7cb841d8dbadf5) >--- > source3/smbd/durable.c | 2 +- > source3/smbd/smb2_create.c | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index 9489cf1..c0d1883 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -168,7 +168,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, > return NT_STATUS_INVALID_PARAMETER; > } > >- if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) { >+ if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) { > return NT_STATUS_NOT_SUPPORTED; > } > >diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c >index 1e54801..6cd7928 100644 >--- a/source3/smbd/smb2_create.c >+++ b/source3/smbd/smb2_create.c >@@ -997,7 +997,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > } > > if (durable_requested && >- BATCH_OPLOCK_TYPE(result->oplock_type)) >+ (fsp_lease_type(result) & SMB2_LEASE_HANDLE)) > { > status = SMB_VFS_DURABLE_COOKIE(result, > op, >-- >1.9.1 > > >From 206f0e9b1e88d15d2c8d5635f15bc3c49c82a23c Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 17/28] s3:smb2_create: validate durable reconnects with leases > >We don't support leases yet, but prepares for the comming commits. > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit d78b18dd8c510591452ebd17810261cf89868d5c) >--- > source3/smbd/smb2_create.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 63 insertions(+) > >diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c >index 6cd7928..78ca4f7 100644 >--- a/source3/smbd/smb2_create.c >+++ b/source3/smbd/smb2_create.c >@@ -368,6 +368,57 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq) > } > } > >+static NTSTATUS smbd_smb2_create_durable_lease_check( >+ const char *requested_filename, const struct files_struct *fsp, >+ const struct smb2_lease *lease_ptr) >+{ >+ struct smb_filename *smb_fname = NULL; >+ NTSTATUS status; >+ >+ if (lease_ptr == NULL) { >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ return NT_STATUS_OK; >+ } >+ DEBUG(10, ("Reopened file has lease, but no lease " >+ "requested\n")); >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ DEBUG(10, ("Lease requested, but reopened file has no " >+ "lease\n")); >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ >+ if (!smb2_lease_key_equal(&lease_ptr->lease_key, >+ &fsp->lease->lease.lease_key)) { >+ DEBUG(10, ("Different lease key requested than found " >+ "in reopened file\n")); >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ >+ status = filename_convert(talloc_tos(), fsp->conn, false, >+ requested_filename, UCF_PREP_CREATEFILE, >+ NULL, &smb_fname); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("filename_convert returned %s\n", >+ nt_errstr(status))); >+ return status; >+ } >+ >+ if (!strequal(fsp->fsp_name->base_name, smb_fname->base_name)) { >+ DEBUG(10, ("Lease requested for file %s, reopened file " >+ "is named %s\n", smb_fname->base_name, >+ fsp->fsp_name->base_name)); >+ TALLOC_FREE(smb_fname); >+ return NT_STATUS_INVALID_PARAMETER; >+ } >+ >+ TALLOC_FREE(smb_fname); >+ >+ return NT_STATUS_OK; >+} >+ > struct smbd_smb2_create_state { > struct smbd_smb2_request *smb2req; > struct smb_request *smb1req; >@@ -599,6 +650,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > uint32_t durable_timeout_msec = 0; > bool do_durable_reconnect = false; > uint64_t persistent_id = 0; >+ struct smb2_lease *lease_ptr = NULL; > > exta = smb2_create_blob_find(&in_context_blobs, > SMB2_CREATE_TAG_EXTA); >@@ -857,6 +909,17 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > return tevent_req_post(req, ev); > } > >+ DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n", >+ (unsigned)result->oplock_type, lease_ptr)); >+ >+ status = smbd_smb2_create_durable_lease_check( >+ fname, result, lease_ptr); >+ if (!NT_STATUS_IS_OK(status)) { >+ close_file(smb1req, result, SHUTDOWN_CLOSE); >+ tevent_req_nterror(req, status); >+ return tevent_req_post(req, ev); >+ } >+ > data_blob_free(&op->global->backend_cookie); > op->global->backend_cookie = new_cookie; > >-- >1.9.1 > > >From 4d5fbb5f06b97dafb60f0bf732dda8c51a9d5b39 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 18/28] s3:smbd: add file_find_one_fsp_from_lease_key() helper > function > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit eb9fc01d207ce29e68027f8958d4e8534f89a9e5) >--- > source3/smbd/files.c | 18 ++++++++++++++++++ > source3/smbd/proto.h | 3 +++ > 2 files changed, 21 insertions(+) > >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index d866d63..91eddb8 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -387,6 +387,24 @@ files_struct *file_find_di_next(files_struct *start_fsp) > return NULL; > } > >+struct files_struct *file_find_one_fsp_from_lease_key( >+ struct smbd_server_connection *sconn, >+ const struct smb2_lease_key *lease_key) >+{ >+ struct files_struct *fsp; >+ >+ for (fsp = sconn->files; fsp; fsp=fsp->next) { >+ if ((fsp->lease != NULL) && >+ (fsp->lease->lease.lease_key.data[0] == >+ lease_key->data[0]) && >+ (fsp->lease->lease.lease_key.data[1] == >+ lease_key->data[1])) { >+ return fsp; >+ } >+ } >+ return NULL; >+} >+ > /**************************************************************************** > Find any fsp open with a pathname below that of an already open path. > ****************************************************************************/ >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 67adbba..7a4b4c9 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -369,6 +369,9 @@ files_struct *file_find_dif(struct smbd_server_connection *sconn, > files_struct *file_find_di_first(struct smbd_server_connection *sconn, > struct file_id id); > files_struct *file_find_di_next(files_struct *start_fsp); >+struct files_struct *file_find_one_fsp_from_lease_key( >+ struct smbd_server_connection *sconn, >+ const struct smb2_lease_key *lease_key); > bool file_find_subpath(files_struct *dir_fsp); > void file_sync_all(connection_struct *conn); > void fsp_free(files_struct *fsp); >-- >1.9.1 > > >From 8f7ee4cacad6acd4bce9063fae5af59f9d3f11d7 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 19/28] s3:smbd: add lease related helper functions to open.c > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit ffbac2112dee9933dd34263f590f0eae28efab45) >--- > source3/smbd/open.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++ > source3/smbd/proto.h | 7 ++ > 2 files changed, 215 insertions(+) > >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index c14d2eb..1055c94 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -35,6 +35,7 @@ > #include "serverid.h" > #include "messages.h" > #include "source3/lib/dbwrap/dbwrap_watch.h" >+#include "locking/leases_db.h" > > extern const struct generic_mapping file_generic_mapping; > >@@ -1490,6 +1491,213 @@ static bool file_has_brlocks(files_struct *fsp) > return (brl_num_locks(br_lck) > 0); > } > >+int find_share_mode_lease(struct share_mode_data *d, >+ const struct GUID *client_guid, >+ const struct smb2_lease_key *key) >+{ >+ uint32_t i; >+ >+ for (i=0; i<d->num_leases; i++) { >+ struct share_mode_lease *l = &d->leases[i]; >+ >+ if (smb2_lease_equal(client_guid, >+ key, >+ &l->client_guid, >+ &l->lease_key)) { >+ return i; >+ } >+ } >+ >+ return -1; >+} >+ >+struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp, >+ const struct smb2_lease_key *key, >+ const struct share_mode_lease *l) >+{ >+ struct files_struct *fsp; >+ >+ /* >+ * TODO: Measure how expensive this loop is with thousands of open >+ * handles... >+ */ >+ >+ for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id); >+ fsp != NULL; >+ fsp = file_find_di_next(fsp)) { >+ >+ if (fsp == new_fsp) { >+ continue; >+ } >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ continue; >+ } >+ if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) { >+ fsp->lease->ref_count += 1; >+ return fsp->lease; >+ } >+ } >+ >+ /* Not found - must be leased in another smbd. */ >+ new_fsp->lease = talloc_zero(new_fsp->conn->sconn, struct fsp_lease); >+ if (new_fsp->lease == NULL) { >+ return NULL; >+ } >+ new_fsp->lease->ref_count = 1; >+ new_fsp->lease->sconn = new_fsp->conn->sconn; >+ new_fsp->lease->lease.lease_key = *key; >+ new_fsp->lease->lease.lease_state = l->current_state; >+ /* >+ * We internally treat all leases as V2 and update >+ * the epoch, but when sending breaks it matters if >+ * the requesting lease was v1 or v2. >+ */ >+ new_fsp->lease->lease.lease_version = l->lease_version; >+ new_fsp->lease->lease.lease_epoch = l->epoch; >+ return new_fsp->lease; >+} >+ >+#if 0 >+static NTSTATUS grant_fsp_lease(struct files_struct *fsp, >+ struct share_mode_lock *lck, >+ const struct smb2_lease *lease, >+ uint32_t *p_lease_idx, >+ uint32_t granted) >+{ >+ struct share_mode_data *d = lck->data; >+ const struct GUID *client_guid = fsp_client_guid(fsp); >+ struct share_mode_lease *tmp; >+ NTSTATUS status; >+ int idx; >+ >+ idx = find_share_mode_lease(d, client_guid, &lease->lease_key); >+ >+ if (idx != -1) { >+ struct share_mode_lease *l = &d->leases[idx]; >+ bool do_upgrade; >+ uint32_t existing, requested; >+ >+ fsp->lease = find_fsp_lease(fsp, &lease->lease_key, l); >+ if (fsp->lease == NULL) { >+ DEBUG(1, ("Did not find existing lease for file %s\n", >+ fsp_str_dbg(fsp))); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ *p_lease_idx = idx; >+ >+ /* >+ * Upgrade only if the requested lease is a strict upgrade. >+ */ >+ existing = l->current_state; >+ requested = lease->lease_state; >+ >+ /* >+ * Tricky: This test makes sure that "requested" is a >+ * strict bitwise superset of "existing". >+ */ >+ do_upgrade = ((existing & requested) == existing); >+ >+ /* >+ * Upgrade only if there's a change. >+ */ >+ do_upgrade &= (granted != existing); >+ >+ /* >+ * Upgrade only if other leases don't prevent what was asked >+ * for. >+ */ >+ do_upgrade &= (granted == requested); >+ >+ /* >+ * only upgrade if we are not in breaking state >+ */ >+ do_upgrade &= !l->breaking; >+ >+ DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", " >+ "granted=%"PRIu32", do_upgrade=%d\n", >+ existing, requested, granted, (int)do_upgrade)); >+ >+ if (do_upgrade) { >+ l->current_state = granted; >+ l->epoch += 1; >+ } >+ >+ /* Ensure we're in sync with current lease state. */ >+ fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease); >+ return NT_STATUS_OK; >+ } >+ >+ /* >+ * Create new lease >+ */ >+ >+ tmp = talloc_realloc(d, d->leases, struct share_mode_lease, >+ d->num_leases+1); >+ if (tmp == NULL) { >+ /* >+ * See [MS-SMB2] >+ */ >+ return NT_STATUS_INSUFFICIENT_RESOURCES; >+ } >+ d->leases = tmp; >+ >+ fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease); >+ if (fsp->lease == NULL) { >+ return NT_STATUS_INSUFFICIENT_RESOURCES; >+ } >+ fsp->lease->ref_count = 1; >+ fsp->lease->sconn = fsp->conn->sconn; >+ fsp->lease->lease.lease_version = lease->lease_version; >+ fsp->lease->lease.lease_key = lease->lease_key; >+ fsp->lease->lease.lease_state = granted; >+ fsp->lease->lease.lease_epoch = lease->lease_epoch + 1; >+ >+ *p_lease_idx = d->num_leases; >+ >+ d->leases[d->num_leases] = (struct share_mode_lease) { >+ .client_guid = *client_guid, >+ .lease_key = fsp->lease->lease.lease_key, >+ .current_state = fsp->lease->lease.lease_state, >+ .lease_version = fsp->lease->lease.lease_version, >+ .epoch = fsp->lease->lease.lease_epoch, >+ }; >+ >+ status = leases_db_add(client_guid, &lease->lease_key, >+ &fsp->file_id, fsp->fsp_name->base_name, >+ fsp->fsp_name->stream_name); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__, >+ nt_errstr(status))); >+ TALLOC_FREE(fsp->lease); >+ return NT_STATUS_INSUFFICIENT_RESOURCES; >+ } >+ >+ d->num_leases += 1; >+ d->modified = true; >+ >+ return NT_STATUS_OK; >+} >+ >+static bool is_same_lease(const files_struct *fsp, >+ const struct share_mode_data *d, >+ const struct share_mode_entry *e, >+ const struct smb2_lease *lease) >+{ >+ if (e->op_type != LEASE_OPLOCK) { >+ return false; >+ } >+ if (lease == NULL) { >+ return false; >+ } >+ >+ return smb2_lease_equal(fsp_client_guid(fsp), >+ &lease->lease_key, >+ &d->leases[e->lease_idx].client_guid, >+ &d->leases[e->lease_idx].lease_key); >+} >+#endif >+ > static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, > struct files_struct *fsp, > struct share_mode_lock *lck, >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 7a4b4c9..55fbd3b 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -626,6 +626,13 @@ void msg_file_was_renamed(struct messaging_context *msg, > DATA_BLOB *data); > NTSTATUS open_streams_for_delete(connection_struct *conn, > const char *fname); >+int find_share_mode_lease(struct share_mode_data *d, >+ const struct GUID *client_guid, >+ const struct smb2_lease_key *key); >+struct share_mode_lease; >+struct fsp_lease *find_fsp_lease(files_struct *new_fsp, >+ const struct smb2_lease_key *key, >+ const struct share_mode_lease *l); > NTSTATUS create_file_default(connection_struct *conn, > struct smb_request *req, > uint16_t root_dir_fid, >-- >1.9.1 > > >From 2079cac1517ab1c0dd361d995f1933a3aa1ee893 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 20/28] s3:smbd: add lease key validation functions to open.c > >This makes sure a lease key can only be used for one specific >file. > >This also handles the dynamic share file case [homes]. > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 205ef314bc78a24651683f15e7cd84d8c4b96dee) >--- > source3/smbd/open.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 192 insertions(+) > >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index 1055c94..28f7408 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -4034,6 +4034,179 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) > } > > /* >+ * If we already have a lease, it must match the new file id. [MS-SMB2] >+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is >+ * used for a different file name. >+ */ >+ >+struct lease_fname_match_state { >+ /* Input parameters. */ >+ const struct smb_filename *fname; >+ bool file_existed; >+ struct file_id id; >+ /* Return parameters. */ >+ uint32_t num_file_ids; >+ struct file_id *ids; >+ NTSTATUS match_status; >+}; >+ >+static void lease_fname_match_parser( >+ uint32_t num_file_ids, >+ struct file_id *ids, const char *filename, const char *stream_name, >+ void *private_data) >+{ >+ struct lease_fname_match_state *state = >+ (struct lease_fname_match_state *)private_data; >+ >+ if (!strequal(filename, state->fname->base_name) || >+ !strequal(stream_name, state->fname->stream_name)) >+ { >+ /* Names don't match lease key. */ >+ state->match_status = NT_STATUS_INVALID_PARAMETER; >+ return; >+ } >+ >+ if (state->file_existed && >+ num_file_ids == 1 && >+ file_id_equal(&ids[0],&state->id)) >+ { >+ /* Common case - non-dynamic share. We're ok.. */ >+ state->match_status = NT_STATUS_OK; >+ return; >+ } >+ >+ /* >+ * More than one file id, or not equal, or new file >+ * being created and there's already an existing lease >+ * on this (client_guid, lease id) pair. >+ * Don't allow leases. >+ */ >+ >+ state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; >+ state->num_file_ids = num_file_ids; >+ state->ids = talloc_memdup(talloc_tos(), >+ ids, >+ num_file_ids * sizeof(struct file_id)); >+ if (state->ids == NULL) { >+ state->match_status = NT_STATUS_NO_MEMORY; >+ } >+} >+ >+static NTSTATUS lease_match(connection_struct *conn, >+ struct smb_request *req, >+ struct smb2_lease_key *lease_key, >+ const struct smb_filename *fname, >+ uint16_t *p_version, >+ uint16_t *p_epoch) >+{ >+ struct smbd_server_connection *sconn = req->sconn; >+ struct lease_fname_match_state state = { >+ .fname = fname, >+ .match_status = NT_STATUS_OK >+ }; >+ uint32_t i; >+ NTSTATUS status; >+ >+ state.file_existed = VALID_STAT(fname->st); >+ if (state.file_existed) { >+ state.id = vfs_file_id_from_sbuf(conn, &fname->st); >+ } else { >+ memset(&state.id, '\0', sizeof(state.id)); >+ } >+ >+ status = leases_db_parse(&sconn->client->connections->smb2.client.guid, >+ lease_key, lease_fname_match_parser, &state); >+ if (!NT_STATUS_IS_OK(status)) { >+ /* >+ * Not found or error means okay: We can make the lease pass >+ */ >+ return NT_STATUS_OK; >+ } >+ if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) { >+ /* >+ * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller >+ * deal with it. >+ */ >+ return state.match_status; >+ } >+ >+ /* We have to break all existing leases. */ >+ for (i = 0; i < state.num_file_ids; i++) { >+ struct share_mode_lock *lck; >+ struct share_mode_data *d; >+ uint32_t j; >+ >+ if (file_id_equal(&state.ids[i], &state.id)) { >+ /* Don't need to break our own file. */ >+ continue; >+ } >+ >+ lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]); >+ if (lck == NULL) { >+ /* Race condition - file already closed. */ >+ continue; >+ } >+ d = lck->data; >+ for (j=0; j<d->num_share_modes; j++) { >+ struct share_mode_entry *e = &d->share_modes[j]; >+ uint32_t e_lease_type = get_lease_type(d, e); >+ struct share_mode_lease *l = NULL; >+ >+ if (share_mode_stale_pid(d, j)) { >+ continue; >+ } >+ >+ if (e->op_type == LEASE_OPLOCK) { >+ l = &lck->data->leases[e->lease_idx]; >+ if (!smb2_lease_key_equal(&l->lease_key, >+ lease_key)) { >+ continue; >+ } >+ *p_epoch = l->epoch; >+ *p_version = l->lease_version; >+ } >+ >+ if (e_lease_type == SMB2_LEASE_NONE) { >+ continue; >+ } >+ >+ send_break_message(conn->sconn->msg_ctx, e, >+ SMB2_LEASE_NONE); >+ >+ /* >+ * Windows 7 and 8 lease clients >+ * are broken in that they will not >+ * respond to lease break requests >+ * whilst waiting for an outstanding >+ * open request on that lease handle >+ * on the same TCP connection, due >+ * to holding an internal inode lock. >+ * >+ * This means we can't reschedule >+ * ourselves here, but must return >+ * from the create. >+ * >+ * Work around: >+ * >+ * Send the breaks and then return >+ * SMB2_LEASE_NONE in the lease handle >+ * to cause them to acknowledge the >+ * lease break. Consulatation with >+ * Microsoft engineering confirmed >+ * this approach is safe. >+ */ >+ >+ } >+ TALLOC_FREE(lck); >+ } >+ /* >+ * Ensure we don't grant anything more so we >+ * never upgrade. >+ */ >+ return NT_STATUS_OPLOCK_NOT_GRANTED; >+} >+ >+/* > * Wrapper around open_file_ntcreate and open_directory > */ > >@@ -4089,6 +4262,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, > oplock_request |= INTERNAL_OPEN_ONLY; > } > >+ if (lease != NULL) { >+ uint16_t epoch = lease->lease_epoch; >+ uint16_t version = lease->lease_version; >+ status = lease_match(conn, >+ req, >+ &lease->lease_key, >+ smb_fname, >+ &version, >+ &epoch); >+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { >+ /* Dynamic share file. No leases and update epoch... */ >+ lease->lease_state = SMB2_LEASE_NONE; >+ lease->lease_epoch = epoch; >+ lease->lease_version = version; >+ } else if (!NT_STATUS_IS_OK(status)) { >+ goto fail; >+ } >+ } >+ > if ((conn->fs_capabilities & FILE_NAMED_STREAMS) > && (access_mask & DELETE_ACCESS) > && !is_ntfs_stream_smb_fname(smb_fname)) { >-- >1.9.1 > > >From 7dcf2c814337eda0b05bbedc518717a95347b9e1 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 21/28] s3:smbd: add lease related helper functions to oplock.c > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 91b2488c9ad25b35d717bca505acc9dd060be677) >--- > source3/smbd/oplock.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++ > source3/smbd/proto.h | 8 ++ > 2 files changed, 303 insertions(+) > >diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c >index 5a12883..5ad881d 100644 >--- a/source3/smbd/oplock.c >+++ b/source3/smbd/oplock.c >@@ -306,6 +306,301 @@ bool downgrade_oplock(files_struct *fsp) > return ret; > } > >+static void lease_timeout_handler(struct tevent_context *ctx, >+ struct tevent_timer *te, >+ struct timeval now, >+ void *private_data) >+{ >+ struct fsp_lease *lease = >+ talloc_get_type_abort(private_data, >+ struct fsp_lease); >+ struct files_struct *fsp; >+ struct share_mode_lock *lck; >+ uint16_t old_epoch = lease->lease.lease_epoch; >+ >+ fsp = file_find_one_fsp_from_lease_key(lease->sconn, >+ &lease->lease.lease_key); >+ if (fsp == NULL) { >+ /* race? */ >+ TALLOC_FREE(lease->timeout); >+ return; >+ } >+ >+ lck = get_existing_share_mode_lock( >+ talloc_tos(), fsp->file_id); >+ if (lck == NULL) { >+ /* race? */ >+ TALLOC_FREE(lease->timeout); >+ return; >+ } >+ >+ fsp_lease_update(lck, fsp_client_guid(fsp), lease); >+ >+ if (lease->lease.lease_epoch != old_epoch) { >+ /* >+ * If the epoch changed we need to wait for >+ * the next timeout to happen. >+ */ >+ DEBUG(10, ("lease break timeout race (epoch) for file %s - ignoring\n", >+ fsp_str_dbg(fsp))); >+ TALLOC_FREE(lck); >+ return; >+ } >+ >+ if (!(lease->lease.lease_flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)) { >+ /* >+ * If the epoch changed we need to wait for >+ * the next timeout to happen. >+ */ >+ DEBUG(10, ("lease break timeout race (flags) for file %s - ignoring\n", >+ fsp_str_dbg(fsp))); >+ TALLOC_FREE(lck); >+ return; >+ } >+ >+ DEBUG(1, ("lease break timed out for file %s -- replying anyway\n", >+ fsp_str_dbg(fsp))); >+ (void)downgrade_lease(lease->sconn->client->connections, >+ 1, >+ &fsp->file_id, >+ &lease->lease.lease_key, >+ SMB2_LEASE_NONE); >+ >+ TALLOC_FREE(lck); >+} >+ >+bool fsp_lease_update(struct share_mode_lock *lck, >+ const struct GUID *client_guid, >+ struct fsp_lease *lease) >+{ >+ struct share_mode_data *d = lck->data; >+ int idx; >+ struct share_mode_lease *l = NULL; >+ >+ idx = find_share_mode_lease(d, client_guid, &lease->lease.lease_key); >+ if (idx != -1) { >+ l = &d->leases[idx]; >+ } >+ >+ if (l == NULL) { >+ DEBUG(1, ("%s: Could not find lease entry\n", __func__)); >+ TALLOC_FREE(lease->timeout); >+ lease->lease.lease_state = SMB2_LEASE_NONE; >+ lease->lease.lease_epoch += 1; >+ lease->lease.lease_flags = 0; >+ return false; >+ } >+ >+ DEBUG(10,("%s: refresh lease state\n", __func__)); >+ >+ /* Ensure we're in sync with current lease state. */ >+ if (lease->lease.lease_epoch != l->epoch) { >+ DEBUG(10,("%s: cancel outdated timeout\n", __func__)); >+ TALLOC_FREE(lease->timeout); >+ } >+ lease->lease.lease_epoch = l->epoch; >+ lease->lease.lease_state = l->current_state; >+ >+ if (l->breaking) { >+ lease->lease.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS; >+ >+ if (lease->timeout == NULL) { >+ struct timeval t = timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0); >+ >+ DEBUG(10,("%s: setup timeout handler\n", __func__)); >+ >+ lease->timeout = tevent_add_timer(lease->sconn->ev_ctx, >+ lease, t, >+ lease_timeout_handler, >+ lease); >+ if (lease->timeout == NULL) { >+ DEBUG(0, ("%s: Could not add lease timeout handler\n", >+ __func__)); >+ } >+ } >+ } else { >+ lease->lease.lease_flags &= ~SMB2_LEASE_FLAG_BREAK_IN_PROGRESS; >+ TALLOC_FREE(lease->timeout); >+ } >+ >+ return true; >+} >+ >+struct downgrade_lease_additional_state { >+ struct tevent_immediate *im; >+ struct smbXsrv_connection *xconn; >+ uint32_t break_flags; >+ struct smb2_lease_key lease_key; >+ uint32_t break_from; >+ uint32_t break_to; >+ uint16_t new_epoch; >+}; >+ >+static void downgrade_lease_additional_trigger(struct tevent_context *ev, >+ struct tevent_immediate *im, >+ void *private_data) >+{ >+ struct downgrade_lease_additional_state *state = >+ talloc_get_type_abort(private_data, >+ struct downgrade_lease_additional_state); >+ struct smbXsrv_connection *xconn = state->xconn; >+ NTSTATUS status; >+ >+ status = smbd_smb2_send_lease_break(xconn, >+ state->new_epoch, >+ state->break_flags, >+ &state->lease_key, >+ state->break_from, >+ state->break_to); >+ TALLOC_FREE(state); >+ if (!NT_STATUS_IS_OK(status)) { >+ smbd_server_connection_terminate(xconn, >+ nt_errstr(status)); >+ return; >+ } >+} >+ >+struct downgrade_lease_fsps_state { >+ struct file_id id; >+ struct share_mode_lock *lck; >+ const struct smb2_lease_key *key; >+}; >+ >+static struct files_struct *downgrade_lease_fsps(struct files_struct *fsp, >+ void *private_data) >+{ >+ struct downgrade_lease_fsps_state *state = >+ (struct downgrade_lease_fsps_state *)private_data; >+ >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ return NULL; >+ } >+ if (!smb2_lease_key_equal(&fsp->lease->lease.lease_key, state->key)) { >+ return NULL; >+ } >+ if (!file_id_equal(&fsp->file_id, &state->id)) { >+ return NULL; >+ } >+ >+ fsp_lease_update(state->lck, fsp_client_guid(fsp), fsp->lease); >+ >+ return NULL; >+} >+ >+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn, >+ uint32_t num_file_ids, >+ const struct file_id *ids, >+ const struct smb2_lease_key *key, >+ uint32_t lease_state) >+{ >+ struct smbd_server_connection *sconn = xconn->client->sconn; >+ struct share_mode_lock *lck; >+ struct share_mode_lease *l = NULL; >+ const struct file_id id = ids[0]; >+ uint32_t i; >+ NTSTATUS status; >+ >+ DEBUG(10, ("%s: Downgrading %s to %x\n", __func__, >+ file_id_string_tos(&id), (unsigned)lease_state)); >+ >+ lck = get_existing_share_mode_lock(talloc_tos(), id); >+ if (lck == NULL) { >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ status = downgrade_share_lease(sconn, lck, key, lease_state, &l); >+ >+ DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__, >+ file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status))); >+ >+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) { >+ struct downgrade_lease_additional_state *state; >+ >+ state = talloc_zero(xconn, >+ struct downgrade_lease_additional_state); >+ if (state == NULL) { >+ TALLOC_FREE(lck); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ state->im = tevent_create_immediate(state); >+ if (state->im == NULL) { >+ TALLOC_FREE(state); >+ TALLOC_FREE(lck); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ state->xconn = xconn; >+ if (l->current_state & (~SMB2_LEASE_READ)) { >+ state->break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; >+ } >+ state->lease_key = l->lease_key; >+ state->break_from = l->current_state; >+ state->break_to = l->breaking_to_requested; >+ if (l->lease_version > 1) { >+ state->new_epoch = l->epoch; >+ } >+ >+ if (state->break_flags == 0) { >+ /* >+ * This is an async break without >+ * SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED >+ * >+ * we need to store NONE state in the >+ * database. >+ */ >+ l->current_state = 0; >+ l->breaking_to_requested = 0; >+ l->breaking_to_required = 0; >+ l->breaking = false; >+ >+ lck->data->modified = true; >+ } >+ >+ tevent_schedule_immediate(state->im, xconn->ev_ctx, >+ downgrade_lease_additional_trigger, >+ state); >+ } >+ >+ { >+ struct downgrade_lease_fsps_state state = { >+ .id = id, .lck = lck, .key = key, >+ }; >+ >+ files_forall(sconn, downgrade_lease_fsps, &state); >+ } >+ >+ TALLOC_FREE(lck); >+ DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__, >+ file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status))); >+ >+ /* >+ * Dynamic share case. Ensure other opens are copies. >+ * This will only be breaking to NONE. >+ */ >+ >+ for (i = 1; i < num_file_ids; i++) { >+ lck = get_existing_share_mode_lock(talloc_tos(), ids[i]); >+ if (lck == NULL) { >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ >+ { >+ struct downgrade_lease_fsps_state state = { >+ .id = ids[i], .lck = lck, .key = key, >+ }; >+ >+ files_forall(sconn, downgrade_lease_fsps, &state); >+ } >+ >+ DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__, >+ file_id_string_tos(&ids[i]), (unsigned)lease_state, nt_errstr(status))); >+ >+ TALLOC_FREE(lck); >+ } >+ >+ return status; >+} >+ > /**************************************************************************** > Set up an oplock break message. > ****************************************************************************/ >diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h >index 55fbd3b..702da2e 100644 >--- a/source3/smbd/proto.h >+++ b/source3/smbd/proto.h >@@ -667,6 +667,14 @@ 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); > bool downgrade_oplock(files_struct *fsp); >+bool fsp_lease_update(struct share_mode_lock *lck, >+ const struct GUID *client_guid, >+ struct fsp_lease *lease); >+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn, >+ uint32_t num_file_ids, >+ const struct file_id *ids, >+ const struct smb2_lease_key *key, >+ uint32_t lease_state); > void contend_level2_oplocks_begin(files_struct *fsp, > enum level2_contention_type type); > void contend_level2_oplocks_end(files_struct *fsp, >-- >1.9.1 > > >From 8b59787539c5ad5ecf352307643ac980e3b811da Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 22/28] s3:smbd: Implementation of SMB2.1 and SMB3.0 leases. > >Pair-Programmed-With: Jeremy Allison <jra@samba.org> >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Jeremy Allison <jra@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 02f2684dd8e88f1c55bf69fa8b2aa27c1c404a3d) >--- > source3/smbd/durable.c | 26 ++++ > source3/smbd/files.c | 8 ++ > source3/smbd/globals.h | 4 +- > source3/smbd/open.c | 353 ++++++++++++++++++++++++++++------------------ > source3/smbd/oplock.c | 217 ++++++++++++++++++++++++---- > source3/smbd/smb2_break.c | 251 ++++++++++++++++++++++++++++++-- > 6 files changed, 681 insertions(+), 178 deletions(-) > >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index c0d1883..d9b88a8 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, > fsp->aio_write_behind = false; > fsp->oplock_type = e->op_type; > >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ struct share_mode_lease *l = &lck->data->leases[e->lease_idx]; >+ struct smb2_lease_key key; >+ >+ key.data[0] = l->lease_key.data[0]; >+ key.data[1] = l->lease_key.data[1]; >+ >+ fsp->lease = find_fsp_lease(fsp, &key, l); >+ if (fsp->lease == NULL) { >+ TALLOC_FREE(lck); >+ fsp_free(fsp); >+ return NT_STATUS_NO_MEMORY; >+ } >+ >+ /* >+ * Ensure the existing client guid matches the >+ * stored one in the share_mode_lease. >+ */ >+ if (!GUID_equal(fsp_client_guid(fsp), >+ &l->client_guid)) { >+ TALLOC_FREE(lck); >+ fsp_free(fsp); >+ return NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ } >+ > fsp->initial_allocation_size = cookie.initial_allocation_size; > fsp->fh->position_information = cookie.position_information; > fsp->update_write_time_triggered = cookie.update_write_time_triggered; >diff --git a/source3/smbd/files.c b/source3/smbd/files.c >index 91eddb8..1ad601a 100644 >--- a/source3/smbd/files.c >+++ b/source3/smbd/files.c >@@ -490,6 +490,14 @@ void fsp_free(files_struct *fsp) > fsp->fh->ref_count--; > } > >+ if (fsp->lease != NULL) { >+ if (fsp->lease->ref_count == 1) { >+ TALLOC_FREE(fsp->lease); >+ } else { >+ fsp->lease->ref_count--; >+ } >+ } >+ > fsp->conn->num_files_open--; > > /* this is paranoia, just in case someone tries to reuse the >diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h >index b4e232d..eacc5b3 100644 >--- a/source3/smbd/globals.h >+++ b/source3/smbd/globals.h >@@ -304,7 +304,9 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx, > struct deferred_open_record; > > /* SMB1 -> SMB2 glue. */ >-void send_break_message_smb2(files_struct *fsp, int level); >+void send_break_message_smb2(files_struct *fsp, >+ uint32_t break_from, >+ uint32_t break_to); > struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req); > bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck, > struct smb_request *req, >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index 28f7408..f5ad900 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -1258,6 +1258,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx, > share_mode_entry_to_message(msg, exclusive); > > /* Overload entry->op_type */ >+ /* >+ * This is a cut from uint32 to uint16, but so far only the lower 3 >+ * bits (LEASE_WRITE/HANDLE/READ are used anyway. >+ */ > SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to); > > status = messaging_send_buf(msg_ctx, exclusive->pid, >@@ -1377,56 +1381,19 @@ static bool validate_oplock_types(struct share_mode_lock *lck) > > static bool delay_for_oplock(files_struct *fsp, > int oplock_request, >+ const struct smb2_lease *lease, > struct share_mode_lock *lck, > bool have_sharing_violation, >- uint32_t create_disposition) >+ uint32_t create_disposition, >+ bool first_open_attempt) > { > struct share_mode_data *d = lck->data; >- struct share_mode_entry *entry; >- uint32_t num_non_stat_opens = 0; > uint32_t i; >- uint16_t break_to; >- >- if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { >- return false; >- } >- for (i=0; i<d->num_share_modes; i++) { >- struct share_mode_entry *e = &d->share_modes[i]; >- if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) { >- continue; >- } >- num_non_stat_opens += 1; >- >- /* >- * We found the a non-stat open, which in the exclusive/batch >- * case will be inspected further down. >- */ >- entry = e; >- } >- if (num_non_stat_opens == 0) { >- /* >- * Nothing to wait for around >- */ >- return false; >- } >- if (num_non_stat_opens != 1) { >- /* >- * More than one open around. There can't be any exclusive or >- * batch left, this is all level2. >- */ >- return false; >- } >+ bool delay = false; >+ bool will_overwrite; > >- if (server_id_is_disconnected(&entry->pid)) { >- /* >- * TODO: clean up. >- * This could be achieved by sending a break message >- * to ourselves. Special considerations for files >- * with delete_on_close flag set! >- * >- * For now we keep it simple and do not >- * allow delete on close for durable handles. >- */ >+ if ((oplock_request & INTERNAL_OPEN_ONLY) || >+ is_stat_open(fsp->access_mask)) { > return false; > } > >@@ -1434,50 +1401,98 @@ static bool delay_for_oplock(files_struct *fsp, > case FILE_SUPERSEDE: > case FILE_OVERWRITE: > case FILE_OVERWRITE_IF: >- break_to = NO_OPLOCK; >+ will_overwrite = true; > break; > default: >- break_to = LEVEL_II_OPLOCK; >+ will_overwrite = false; > break; > } > >- if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) { >- if (share_mode_stale_pid(d, 0)) { >- return false; >+ for (i=0; i<d->num_share_modes; i++) { >+ struct share_mode_entry *e = &d->share_modes[i]; >+ struct share_mode_lease *l = NULL; >+ uint32_t e_lease_type = get_lease_type(d, e); >+ uint32_t break_to; >+ uint32_t delay_mask = 0; >+ >+ if (e->op_type == LEASE_OPLOCK) { >+ l = &d->leases[e->lease_idx]; > } >- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to); >- return true; >- } >- if (have_sharing_violation) { >- /* >- * Non-batch exclusive is not broken if we have a sharing >- * violation >- */ >- return false; >- } >- if (LEVEL_II_OPLOCK_TYPE(entry->op_type) && >- (break_to == NO_OPLOCK)) { >- if (share_mode_stale_pid(d, 0)) { >- return false; >+ >+ if (have_sharing_violation) { >+ delay_mask = SMB2_LEASE_HANDLE; >+ } else { >+ delay_mask = SMB2_LEASE_WRITE; > } >- DEBUG(10, ("Asynchronously breaking level2 oplock for " >- "create_disposition=%u\n", >- (unsigned)create_disposition)); >- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to); >- return false; >- } >- if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) { >- /* >- * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks >- */ >- return false; >- } >- if (share_mode_stale_pid(d, 0)) { >- return false; >+ >+ break_to = e_lease_type & ~delay_mask; >+ >+ if (will_overwrite) { >+ /* >+ * we'll decide about SMB2_LEASE_READ later. >+ * >+ * Maybe the break will be defered >+ */ >+ break_to &= ~SMB2_LEASE_HANDLE; >+ } >+ >+ DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n", >+ (unsigned)i, (unsigned)e_lease_type, >+ (unsigned)will_overwrite)); >+ >+ if (lease != NULL && l != NULL) { >+ bool ign; >+ >+ ign = smb2_lease_equal(fsp_client_guid(fsp), >+ &lease->lease_key, >+ &l->client_guid, >+ &l->lease_key); >+ if (ign) { >+ continue; >+ } >+ } >+ >+ if ((e_lease_type & ~break_to) == 0) { >+ if (l != NULL && l->breaking) { >+ delay = true; >+ } >+ continue; >+ } >+ >+ if (share_mode_stale_pid(d, i)) { >+ continue; >+ } >+ >+ if (will_overwrite) { >+ /* >+ * If we break anyway break to NONE directly. >+ * Otherwise vfs_set_filelen() will trigger the >+ * break. >+ */ >+ break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE); >+ } >+ >+ if (e->op_type != LEASE_OPLOCK) { >+ /* >+ * Oplocks only support breaking to R or NONE. >+ */ >+ break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE); >+ } >+ >+ DEBUG(10, ("breaking from %d to %d\n", >+ (int)e_lease_type, (int)break_to)); >+ send_break_message(fsp->conn->sconn->msg_ctx, e, >+ break_to); >+ if (e_lease_type & delay_mask) { >+ delay = true; >+ } >+ if (l != NULL && l->breaking && !first_open_attempt) { >+ delay = true; >+ } >+ continue; > } > >- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to); >- return true; >+ return delay; > } > > static bool file_has_brlocks(files_struct *fsp) >@@ -1557,7 +1572,6 @@ struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp, > return new_fsp->lease; > } > >-#if 0 > static NTSTATUS grant_fsp_lease(struct files_struct *fsp, > struct share_mode_lock *lck, > const struct smb2_lease *lease, >@@ -1696,88 +1710,151 @@ static bool is_same_lease(const files_struct *fsp, > &d->leases[e->lease_idx].client_guid, > &d->leases[e->lease_idx].lease_key); > } >-#endif > > static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, > struct files_struct *fsp, > struct share_mode_lock *lck, >- int oplock_request) >+ int oplock_request, >+ struct smb2_lease *lease) > { >- bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) && >- lp_level2_oplocks(SNUM(fsp->conn)); >- bool got_level2_oplock, got_a_none_oplock; >+ struct share_mode_data *d = lck->data; >+ bool got_handle_lease = false; >+ bool got_oplock = false; > uint32_t i; >+ uint32_t granted; >+ uint32_t lease_idx = UINT32_MAX; > 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; >- goto type_selected; >+ oplock_request = NO_OPLOCK; >+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n", >+ fsp->oplock_type, fsp_str_dbg(fsp))); >+ } >+ >+ if (oplock_request == LEASE_OPLOCK) { >+ if (lease == NULL) { >+ /* >+ * The SMB2 layer should have checked this >+ */ >+ return NT_STATUS_INTERNAL_ERROR; >+ } >+ >+ granted = lease->lease_state; >+ >+ if (lp_kernel_oplocks(SNUM(fsp->conn))) { >+ DEBUG(10, ("No lease granted because kernel oplocks are enabled\n")); >+ granted = SMB2_LEASE_NONE; >+ } >+ if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) { >+ DEBUG(10, ("No read or write lease requested\n")); >+ granted = SMB2_LEASE_NONE; >+ } >+ if (granted == SMB2_LEASE_WRITE) { >+ DEBUG(10, ("pure write lease requested\n")); >+ granted = SMB2_LEASE_NONE; >+ } >+ if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) { >+ DEBUG(10, ("write and handle lease requested\n")); >+ granted = SMB2_LEASE_NONE; >+ } >+ } else { >+ granted = map_oplock_to_lease_type( >+ oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK); > } > > 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; >- goto type_selected; >+ granted &= ~SMB2_LEASE_READ; > } > >- got_level2_oplock = false; >- got_a_none_oplock = false; >+ for (i=0; i<d->num_share_modes; i++) { >+ struct share_mode_entry *e = &d->share_modes[i]; >+ uint32_t e_lease_type; > >- for (i=0; i<lck->data->num_share_modes; i++) { >- int op_type = lck->data->share_modes[i].op_type; >+ e_lease_type = get_lease_type(d, e); > >- if (LEVEL_II_OPLOCK_TYPE(op_type)) { >- got_level2_oplock = true; >+ if ((granted & SMB2_LEASE_WRITE) && >+ !is_same_lease(fsp, d, e, lease) && >+ !share_mode_stale_pid(d, i)) { >+ /* >+ * Can grant only one writer >+ */ >+ granted &= ~SMB2_LEASE_WRITE; > } >- if (op_type == NO_OPLOCK) { >- got_a_none_oplock = true; >+ >+ if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease && >+ !share_mode_stale_pid(d, i)) { >+ got_handle_lease = true; >+ } >+ >+ if ((e->op_type != LEASE_OPLOCK) && !got_oplock && >+ !share_mode_stale_pid(d, i)) { >+ got_oplock = true; > } > } > >- /* >- * Match what was requested (fsp->oplock_type) with >- * what was found in the existing share modes. >- */ >+ if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) { >+ bool allow_level2 = >+ (global_client_caps & CAP_LEVEL_II_OPLOCKS) && >+ lp_level2_oplocks(SNUM(fsp->conn)); > >- if (got_level2_oplock || got_a_none_oplock) { >- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { >- fsp->oplock_type = LEVEL_II_OPLOCK; >+ if (!allow_level2) { >+ granted = SMB2_LEASE_NONE; > } > } > >- /* >- * Don't grant level2 to clients that don't want them >- * or if we've turned them off. >- */ >- if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) { >- fsp->oplock_type = NO_OPLOCK; >- goto type_selected; >- } >+ if (oplock_request == LEASE_OPLOCK) { >+ if (got_oplock) { >+ granted &= ~SMB2_LEASE_HANDLE; >+ } > >- type_selected: >- status = set_file_oplock(fsp); >- if (!NT_STATUS_IS_OK(status)) { >- /* >- * Could not get the kernel oplock >- */ >- fsp->oplock_type = NO_OPLOCK; >+ fsp->oplock_type = LEASE_OPLOCK; >+ >+ status = grant_fsp_lease(fsp, lck, lease, &lease_idx, >+ granted); >+ if (!NT_STATUS_IS_OK(status)) { >+ return status; >+ >+ } >+ *lease = fsp->lease->lease; >+ DEBUG(10, ("lease_state=%d\n", lease->lease_state)); >+ } else { >+ if (got_handle_lease) { >+ granted = SMB2_LEASE_NONE; >+ } >+ >+ switch (granted) { >+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE: >+ fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK; >+ break; >+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE: >+ fsp->oplock_type = EXCLUSIVE_OPLOCK; >+ break; >+ case SMB2_LEASE_READ|SMB2_LEASE_HANDLE: >+ case SMB2_LEASE_READ: >+ fsp->oplock_type = LEVEL_II_OPLOCK; >+ break; >+ default: >+ fsp->oplock_type = NO_OPLOCK; >+ break; >+ } >+ >+ 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, >- UINT32_MAX); >+ lease_idx); > if (!ok) { > return NT_STATUS_NO_MEMORY; > } >@@ -2683,8 +2760,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > smb_panic("validate_oplock_types failed"); > } > >- if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) { >- schedule_defer_open(lck, fsp->file_id, request_time, req); >+ if (delay_for_oplock(fsp, 0, lease, lck, false, >+ create_disposition, first_open_attempt)) { >+ schedule_defer_open(lck, fsp->file_id, request_time, >+ req); > TALLOC_FREE(lck); > DEBUG(10, ("Sent oplock break request to kernel " > "oplock holder\n")); >@@ -2805,9 +2884,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > > if ((req != NULL) && > delay_for_oplock( >- fsp, oplock_request, lck, >+ fsp, oplock_request, lease, lck, > NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION), >- create_disposition)) { >+ create_disposition, first_open_attempt)) { > schedule_defer_open(lck, fsp->file_id, request_time, req); > TALLOC_FREE(lck); > fd_close(fsp); >@@ -3016,7 +3095,7 @@ 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 or leases. > * > * Note that we check for stat open on the *open_access_mask*, > * i.e. the access mask we actually used to do the open, >@@ -3026,7 +3105,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > * which adds FILE_WRITE_DATA to open_access_mask. > */ > if (is_stat_open(open_access_mask)) { >- oplock_request = NO_OPLOCK; >+ if (lease) { >+ lease->lease_state = SMB2_LEASE_NONE; >+ } else { >+ oplock_request = NO_OPLOCK; >+ } > } > } > >@@ -3048,7 +3131,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > * Setup the oplock info in both the shared memory and > * file structs. > */ >- status = grant_fsp_oplock_type(req, fsp, lck, oplock_request); >+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease); > if (!NT_STATUS_IS_OK(status)) { > TALLOC_FREE(lck); > fd_close(fsp); >diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c >index 5ad881d..8f318f5 100644 >--- a/source3/smbd/oplock.c >+++ b/source3/smbd/oplock.c >@@ -196,15 +196,10 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck) > > for (i=0; i<d->num_share_modes; i++) { > struct share_mode_entry *e = &d->share_modes[i]; >+ uint32_t e_lease_type = get_lease_type(d, e); > >- if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) { >+ if (e_lease_type & SMB2_LEASE_READ) { > num_read_oplocks += 1; >- continue; >- } >- >- if (LEVEL_II_OPLOCK_TYPE(e->op_type)) { >- num_read_oplocks += 1; >- continue; > } > } > >@@ -772,14 +767,15 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, > { > struct share_mode_entry msg; > files_struct *fsp; >- bool break_to_level2 = False; > bool use_kernel; > struct smbd_server_connection *sconn = > talloc_get_type_abort(private_data, > struct smbd_server_connection); > struct server_id self = messaging_server_id(sconn->msg_ctx); > struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops; >+ uint16_t break_from; > uint16_t break_to; >+ bool break_needed = true; > > if (data->data == NULL) { > DEBUG(0, ("Got NULL buffer\n")); >@@ -809,27 +805,118 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, > return; > } > >- if (fsp->sent_oplock_break != NO_BREAK_SENT) { >- /* >- * Nothing to do anymore >- */ >+ break_from = fsp_lease_type(fsp); >+ >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ if (fsp->sent_oplock_break != NO_BREAK_SENT) { >+ /* >+ * Nothing to do anymore >+ */ >+ DEBUG(10, ("fsp->sent_oplock_break = %d\n", >+ fsp->sent_oplock_break)); >+ return; >+ } >+ } >+ >+ if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) { >+ DEBUG(10, ("client_caps without level2 oplocks\n")); >+ break_to &= ~SMB2_LEASE_READ; >+ } >+ >+ use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks; >+ if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) { >+ DEBUG(10, ("Kernel oplocks don't allow level2\n")); >+ break_to &= ~SMB2_LEASE_READ; >+ } >+ >+ if (!lp_level2_oplocks(SNUM(fsp->conn))) { >+ DEBUG(10, ("no level2 oplocks by config\n")); >+ break_to &= ~SMB2_LEASE_READ; >+ } >+ >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ struct share_mode_lock *lck; >+ int idx; >+ >+ lck = get_existing_share_mode_lock( >+ talloc_tos(), fsp->file_id); >+ if (lck == NULL) { >+ /* >+ * We hit a race here. Break messages are sent, and >+ * before we get to process this message, we have closed >+ * the file. >+ */ >+ DEBUG(3, ("Did not find share_mode\n")); >+ return; >+ } >+ >+ idx = find_share_mode_lease( >+ lck->data, >+ fsp_client_guid(fsp), >+ &fsp->lease->lease.lease_key); >+ if (idx != -1) { >+ struct share_mode_lease *l; >+ l = &lck->data->leases[idx]; >+ >+ break_from = l->current_state; >+ break_to &= l->current_state; >+ >+ if (l->breaking) { >+ break_to &= l->breaking_to_required; >+ if (l->breaking_to_required != break_to) { >+ /* >+ * Note we don't increment the epoch >+ * here, which might be a bug in >+ * Windows too... >+ */ >+ l->breaking_to_required = break_to; >+ lck->data->modified = true; >+ } >+ break_needed = false; >+ } else if (l->current_state == break_to) { >+ break_needed = false; >+ } else if (l->current_state == SMB2_LEASE_READ) { >+ l->current_state = SMB2_LEASE_NONE; >+ /* Need to increment the epoch */ >+ l->epoch += 1; >+ lck->data->modified = true; >+ } else { >+ l->breaking = true; >+ l->breaking_to_required = break_to; >+ l->breaking_to_requested = break_to; >+ /* Need to increment the epoch */ >+ l->epoch += 1; >+ lck->data->modified = true; >+ } >+ >+ /* Ensure we're in sync with current lease state. */ >+ fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease); >+ } >+ >+ TALLOC_FREE(lck); >+ } >+ >+ if (!break_needed) { >+ DEBUG(10,("%s: skip break\n", __func__)); > return; > } > >- if (break_to == fsp->oplock_type) { >- DEBUG(3, ("Already downgraded oplock on %s: %s\n", >+ if ((break_from == SMB2_LEASE_NONE) && !break_needed) { >+ DEBUG(3, ("Already downgraded oplock to none on %s: %s\n", > file_id_string_tos(&fsp->file_id), > fsp_str_dbg(fsp))); > return; > } > >- use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks; >+ DEBUG(10, ("break_from=%u, break_to=%u\n", >+ (unsigned)break_from, (unsigned)break_to)); > >- if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) && >- (break_to != NO_OPLOCK) && >- !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) && >- lp_level2_oplocks(SNUM(fsp->conn))) { >- break_to_level2 = True; >+ if ((break_from == break_to) && !break_needed) { >+ DEBUG(3, ("Already downgraded oplock to %u on %s: %s\n", >+ (unsigned)break_to, >+ file_id_string_tos(&fsp->file_id), >+ fsp_str_dbg(fsp))); >+ return; > } > > /* Need to wait before sending a break >@@ -839,21 +926,31 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, > } > > if (sconn->using_smb2) { >- send_break_message_smb2(fsp, break_to_level2 ? >- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); >+ send_break_message_smb2(fsp, break_from, break_to); > } else { >- send_break_message_smb1(fsp, break_to_level2 ? >- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); >+ send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ? >+ OPLOCKLEVEL_II : OPLOCKLEVEL_NONE); > } > >- if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) { >+ if ((break_from == SMB2_LEASE_READ) && >+ (break_to == SMB2_LEASE_NONE)) { > /* > * This is an async break without a reply and thus no timeout >+ * >+ * leases are handled above. > */ >- remove_oplock(fsp); >+ if (fsp->oplock_type != LEASE_OPLOCK) { >+ remove_oplock(fsp); >+ } >+ return; >+ } >+ if (fsp->oplock_type == LEASE_OPLOCK) { > return; > } >- fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; >+ >+ fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ? >+ LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT; >+ > add_oplock_timeout_handler(fsp); > } > >@@ -908,7 +1005,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, > } > > if (sconn->using_smb2) { >- send_break_message_smb2(fsp, OPLOCKLEVEL_NONE); >+ send_break_message_smb2(fsp, 0, OPLOCKLEVEL_NONE); > } else { > send_break_message_smb1(fsp, OPLOCKLEVEL_NONE); > } >@@ -921,6 +1018,8 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, > struct break_to_none_state { > struct smbd_server_connection *sconn; > struct file_id id; >+ struct smb2_lease_key lease_key; >+ struct GUID client_guid; > }; > static void do_break_to_none(struct tevent_context *ctx, > struct tevent_immediate *im, >@@ -975,7 +1074,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, > * anyway, so we postpone this into an immediate event. > */ > >- state = talloc(sconn, struct break_to_none_state); >+ state = talloc_zero(sconn, struct break_to_none_state); > if (state == NULL) { > DEBUG(1, ("talloc failed\n")); > return; >@@ -983,6 +1082,14 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp, > state->sconn = sconn; > state->id = fsp->file_id; > >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ state->client_guid = *fsp_client_guid(fsp); >+ state->lease_key = fsp->lease->lease.lease_key; >+ DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n", >+ state->lease_key.data[0], >+ state->lease_key.data[1])); >+ } >+ > im = tevent_create_immediate(state); > if (im == NULL) { > DEBUG(1, ("tevent_create_immediate failed\n")); >@@ -1023,8 +1130,50 @@ static void do_break_to_none(struct tevent_context *ctx, > } > d = lck->data; > >- DEBUG(10,("%s: num_share_modes = %d\n", __func__, >- lck->data->num_share_modes )); >+ /* >+ * Walk leases and oplocks separately: We have to send one break per >+ * lease. If we have multiple share_mode_entry having a common lease, >+ * we would break the lease twice if we don't walk the leases list >+ * separately. >+ */ >+ >+ for (i=0; i<d->num_leases; i++) { >+ struct share_mode_lease *l = &d->leases[i]; >+ struct share_mode_entry *e; >+ uint32_t j; >+ >+ if ((l->current_state & SMB2_LEASE_READ) == 0) { >+ continue; >+ } >+ if (smb2_lease_equal(&state->client_guid, >+ &state->lease_key, >+ &l->client_guid, >+ &l->lease_key)) { >+ DEBUG(10, ("Don't break our own lease\n")); >+ continue; >+ } >+ >+ for (j=0; j<d->num_share_modes; j++) { >+ e = &d->share_modes[j]; >+ >+ if (!is_valid_share_mode_entry(e)) { >+ continue; >+ } >+ if (e->lease_idx == i) { >+ break; >+ } >+ } >+ if (j == d->num_share_modes) { >+ DEBUG(0, ("leases[%"PRIu32"] has no share mode\n", >+ i)); >+ continue; >+ } >+ >+ DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# " >+ "%"PRIu32"\n", i, j)); >+ >+ send_break_to_none(state->sconn->msg_ctx, e); >+ } > > for(i = 0; i < d->num_share_modes; i++) { > struct share_mode_entry *e = &d->share_modes[i]; >@@ -1032,6 +1181,12 @@ static void do_break_to_none(struct tevent_context *ctx, > if (!is_valid_share_mode_entry(e)) { > continue; > } >+ if (e->op_type == LEASE_OPLOCK) { >+ /* >+ * Took care of those in the loop above >+ */ >+ continue; >+ } > > /* > * As there could have been multiple writes waiting at the >diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c >index 4492456..126bf78 100644 >--- a/source3/smbd/smb2_break.c >+++ b/source3/smbd/smb2_break.c >@@ -24,6 +24,10 @@ > #include "smbd/globals.h" > #include "../libcli/smb/smb_common.h" > #include "../lib/util/tevent_ntstatus.h" >+#include "locking/leases_db.h" >+ >+static NTSTATUS smbd_smb2_request_process_lease_break( >+ struct smbd_smb2_request *req); > > static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx, > struct tevent_context *ev, >@@ -45,6 +49,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req) > struct tevent_req *subreq; > > status = smbd_smb2_request_verify_sizes(req, 0x18); >+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { >+ /* >+ * Retry as a lease break >+ */ >+ return smbd_smb2_request_process_lease_break(req); >+ } > if (!NT_STATUS_IS_OK(status)) { > return smbd_smb2_request_error(req, status); > } >@@ -222,16 +232,213 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req, > return NT_STATUS_OK; > } > >+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq); >+ >+static struct tevent_req *smbd_smb2_lease_break_send( >+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, >+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key, >+ uint32_t in_lease_state); >+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req, >+ uint32_t *out_lease_state); >+ >+ >+static NTSTATUS smbd_smb2_request_process_lease_break( >+ struct smbd_smb2_request *req) >+{ >+ NTSTATUS status; >+ const uint8_t *inbody; >+ struct smb2_lease_key in_lease_key; >+ uint32_t in_lease_state; >+ struct tevent_req *subreq; >+ >+ status = smbd_smb2_request_verify_sizes(req, 0x24); >+ if (!NT_STATUS_IS_OK(status)) { >+ return smbd_smb2_request_error(req, status); >+ } >+ >+ inbody = SMBD_SMB2_IN_BODY_PTR(req); >+ >+ in_lease_key.data[0] = BVAL(inbody, 8); >+ in_lease_key.data[1] = BVAL(inbody, 16); >+ in_lease_state = IVAL(inbody, 24); >+ >+ subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req, >+ in_lease_key, in_lease_state); >+ if (subreq == NULL) { >+ return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); >+ } >+ tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req); >+ >+ return smbd_smb2_request_pending_queue(req, subreq, 500); >+} >+ >+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq) >+{ >+ struct smbd_smb2_request *req = tevent_req_callback_data( >+ subreq, struct smbd_smb2_request); >+ const uint8_t *inbody; >+ struct smb2_lease_key in_lease_key; >+ uint32_t out_lease_state = 0; >+ DATA_BLOB outbody; >+ NTSTATUS status; >+ NTSTATUS error; /* transport error */ >+ >+ status = smbd_smb2_lease_break_recv(subreq, &out_lease_state); >+ TALLOC_FREE(subreq); >+ if (!NT_STATUS_IS_OK(status)) { >+ error = smbd_smb2_request_error(req, status); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(req->xconn, >+ nt_errstr(error)); >+ return; >+ } >+ return; >+ } >+ >+ inbody = SMBD_SMB2_IN_BODY_PTR(req); >+ >+ in_lease_key.data[0] = BVAL(inbody, 8); >+ in_lease_key.data[1] = BVAL(inbody, 16); >+ >+ outbody = smbd_smb2_generate_outbody(req, 0x24); >+ if (outbody.data == NULL) { >+ error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(req->xconn, >+ nt_errstr(error)); >+ return; >+ } >+ return; >+ } >+ >+ SSVAL(outbody.data, 0x00, 0x24); /* struct size */ >+ SSVAL(outbody.data, 0x02, 0); /* reserved */ >+ SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */ >+ SBVAL(outbody.data, 0x08, in_lease_key.data[0]); >+ SBVAL(outbody.data, 0x10, in_lease_key.data[1]); >+ SIVAL(outbody.data, 0x18, out_lease_state); >+ SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */ >+ >+ error = smbd_smb2_request_done(req, outbody, NULL); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(req->xconn, >+ nt_errstr(error)); >+ return; >+ } >+} >+ >+struct smbd_smb2_lease_break_state { >+ uint32_t lease_state; >+}; >+ >+struct lease_lookup_state { >+ TALLOC_CTX *mem_ctx; >+ /* Return parameters. */ >+ uint32_t num_file_ids; >+ struct file_id *ids; >+ NTSTATUS status; >+}; >+ >+static void lease_parser( >+ uint32_t num_file_ids, >+ struct file_id *ids, const char *filename, const char *stream_name, >+ void *private_data) >+{ >+ struct lease_lookup_state *lls = >+ (struct lease_lookup_state *)private_data; >+ >+ lls->status = NT_STATUS_OK; >+ lls->num_file_ids = num_file_ids; >+ lls->ids = talloc_memdup(lls->mem_ctx, >+ ids, >+ num_file_ids * sizeof(struct file_id)); >+ if (lls->ids == NULL) { >+ lls->status = NT_STATUS_NO_MEMORY; >+ } >+} >+ >+static struct tevent_req *smbd_smb2_lease_break_send( >+ TALLOC_CTX *mem_ctx, struct tevent_context *ev, >+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key, >+ uint32_t in_lease_state) >+{ >+ struct tevent_req *req; >+ struct smbd_smb2_lease_break_state *state; >+ struct lease_lookup_state lls = {.mem_ctx = mem_ctx}; >+ NTSTATUS status; >+ >+ req = tevent_req_create(mem_ctx, &state, >+ struct smbd_smb2_lease_break_state); >+ if (req == NULL) { >+ return NULL; >+ } >+ state->lease_state = in_lease_state; >+ >+ /* Find any file ids with this lease key. */ >+ status = leases_db_parse(&smb2_req->xconn->smb2.client.guid, >+ &in_lease_key, >+ lease_parser, >+ &lls); >+ >+ if (!NT_STATUS_IS_OK(status)) { >+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { >+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ DEBUG(10, ("No record for lease key found\n")); >+ } >+ } else if (!NT_STATUS_IS_OK(lls.status)) { >+ status = lls.status; >+ } else if (lls.num_file_ids == 0) { >+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND; >+ } >+ >+ if (!NT_STATUS_IS_OK(status)) { >+ tevent_req_nterror(req, status); >+ return tevent_req_post(req, ev); >+ } >+ >+ status = downgrade_lease(smb2_req->xconn, >+ lls.num_file_ids, >+ lls.ids, >+ &in_lease_key, >+ in_lease_state); >+ >+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) { >+ tevent_req_done(req); >+ return tevent_req_post(req, ev); >+ } >+ if (tevent_req_nterror(req, status)) { >+ DEBUG(10, ("downgrade_lease returned %s\n", >+ nt_errstr(status))); >+ return tevent_req_post(req, ev); >+ } >+ >+ tevent_req_done(req); >+ return tevent_req_post(req, ev); >+} >+ >+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req, >+ uint32_t *out_lease_state) >+{ >+ struct smbd_smb2_lease_break_state *state = tevent_req_data( >+ req, struct smbd_smb2_lease_break_state); >+ NTSTATUS status; >+ >+ if (tevent_req_is_nterror(req, &status)) { >+ return status; >+ } >+ *out_lease_state = state->lease_state; >+ return NT_STATUS_OK; >+} >+ > /********************************************************* > Create and send an asynchronous > SMB2 OPLOCK_BREAK_NOTIFICATION. > *********************************************************/ > >-void send_break_message_smb2(files_struct *fsp, int level) >+void send_break_message_smb2(files_struct *fsp, >+ uint32_t break_from, >+ uint32_t break_to) > { >- uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ? >- SMB2_OPLOCK_LEVEL_II : >- SMB2_OPLOCK_LEVEL_NONE; > NTSTATUS status; > struct smbXsrv_connection *xconn = NULL; > struct smbXsrv_session *session = NULL; >@@ -257,7 +464,7 @@ void send_break_message_smb2(files_struct *fsp, int level) > "for file %s, %s, smb2 level %u session %llu not found\n", > fsp_str_dbg(fsp), > fsp_fnum_dbg(fsp), >- (unsigned int)smb2_oplock_level, >+ (unsigned int)break_to, > (unsigned long long)fsp->vuid)); > return; > } >@@ -266,13 +473,35 @@ void send_break_message_smb2(files_struct *fsp, int level) > "for file %s, %s, smb2 level %u\n", > fsp_str_dbg(fsp), > fsp_fnum_dbg(fsp), >- (unsigned int)smb2_oplock_level )); >+ (unsigned int)break_to )); > >- status = smbd_smb2_send_oplock_break(xconn, >- session, >- fsp->conn->tcon, >- fsp->op, >- smb2_oplock_level); >+ if (fsp->oplock_type == LEASE_OPLOCK) { >+ uint32_t break_flags = 0; >+ uint16_t new_epoch; >+ >+ if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) { >+ break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; >+ } >+ >+ if (fsp->lease->lease.lease_version > 1) { >+ new_epoch = fsp->lease->lease.lease_epoch; >+ } else { >+ new_epoch = 0; >+ } >+ >+ status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags, >+ &fsp->lease->lease.lease_key, >+ break_from, break_to); >+ } else { >+ uint8_t smb2_oplock_level; >+ smb2_oplock_level = (break_to & SMB2_LEASE_READ) ? >+ SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE; >+ status = smbd_smb2_send_oplock_break(xconn, >+ session, >+ fsp->conn->tcon, >+ fsp->op, >+ smb2_oplock_level); >+ } > if (!NT_STATUS_IS_OK(status)) { > smbd_server_connection_terminate(xconn, > nt_errstr(status)); >-- >1.9.1 > > >From 35a80927c53e2127725b92ae1f155361604abc59 Mon Sep 17 00:00:00 2001 >From: Volker Lendecke <vl@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 23/28] s3:smb2_create: support leases and pass them down to > the VFS layer. > >Pair-Programmed-With: Stefan Metzmacher <metze@samba.org> > >Signed-off-by: Volker Lendecke <vl@samba.org> >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit e2d80a89d53fdd6794a344649c3c9728db93adce) >--- > source3/smbd/smb2_create.c | 91 ++++++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 87 insertions(+), 4 deletions(-) > >diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c >index 78ca4f7..26d285c 100644 >--- a/source3/smbd/smb2_create.c >+++ b/source3/smbd/smb2_create.c >@@ -25,6 +25,7 @@ > #include "smbd/globals.h" > #include "../libcli/smb/smb_common.h" > #include "../librpc/gen_ndr/ndr_security.h" >+#include "../librpc/gen_ndr/ndr_smb2_lease_struct.h" > #include "../lib/util/tevent_ntstatus.h" > #include "messages.h" > >@@ -40,9 +41,7 @@ int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level) > case SMB2_OPLOCK_LEVEL_BATCH: > return BATCH_OPLOCK; > case SMB2_OPLOCK_LEVEL_LEASE: >- DEBUG(2,("map_smb2_oplock_levels_to_samba: " >- "LEASE_OPLOCK_REQUESTED\n")); >- return NO_OPLOCK; >+ return LEASE_OPLOCK; > default: > DEBUG(2,("map_smb2_oplock_levels_to_samba: " > "unknown level %u\n", >@@ -59,6 +58,8 @@ static uint8_t map_samba_oplock_levels_to_smb2(int oplock_type) > return SMB2_OPLOCK_LEVEL_EXCLUSIVE; > } else if (oplock_type == LEVEL_II_OPLOCK) { > return SMB2_OPLOCK_LEVEL_II; >+ } else if (oplock_type == LEASE_OPLOCK) { >+ return SMB2_OPLOCK_LEVEL_LEASE; > } else { > return SMB2_OPLOCK_LEVEL_NONE; > } >@@ -368,6 +369,11 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq) > } > } > >+static bool smb2_lease_key_valid(const struct smb2_lease_key *key) >+{ >+ return ((key->data[0] != 0) || (key->data[1] != 0)); >+} >+ > static NTSTATUS smbd_smb2_create_durable_lease_check( > const char *requested_filename, const struct files_struct *fsp, > const struct smb2_lease *lease_ptr) >@@ -467,6 +473,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > struct smb2_create_blob *dh2c = NULL; > struct smb2_create_blob *dhnq = NULL; > struct smb2_create_blob *dh2q = NULL; >+ struct smb2_create_blob *rqls = NULL; > struct smbXsrv_open *op = NULL; > > ZERO_STRUCT(out_context_blobs); >@@ -513,6 +520,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > SMB2_CREATE_TAG_DH2Q); > dh2c = smb2_create_blob_find(&in_context_blobs, > SMB2_CREATE_TAG_DH2C); >+ if (smb2req->xconn->smb2.server.capabilities & SMB2_CAP_LEASING) { >+ rqls = smb2_create_blob_find(&in_context_blobs, >+ SMB2_CREATE_TAG_RQLS); >+ } > > if ((dhnc && dh2c) || (dhnc && dh2q) || (dh2c && dhnq) || > (dh2q && dh2c)) >@@ -552,6 +563,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > num_blobs_allowed = 1; > } > >+ if (rqls != NULL) { >+ num_blobs_allowed += 1; >+ } >+ > if (in_context_blobs.num_blobs != num_blobs_allowed) { > tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); > return tevent_req_post(req, ev); >@@ -584,6 +599,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > > num_blobs_allowed = 1; > >+ if (rqls != NULL) { >+ num_blobs_allowed += 1; >+ } >+ > if (in_context_blobs.num_blobs != num_blobs_allowed) { > tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND); > return tevent_req_post(req, ev); >@@ -650,7 +669,9 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > uint32_t durable_timeout_msec = 0; > bool do_durable_reconnect = false; > uint64_t persistent_id = 0; >+ struct smb2_lease lease; > struct smb2_lease *lease_ptr = NULL; >+ ssize_t lease_len = -1; > > exta = smb2_create_blob_find(&in_context_blobs, > SMB2_CREATE_TAG_EXTA); >@@ -850,6 +871,34 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > } > } > >+ if (rqls) { >+ lease_len = smb2_lease_pull( >+ rqls->data.data, rqls->data.length, &lease); >+ if (lease_len == -1) { >+ tevent_req_nterror( >+ req, NT_STATUS_INVALID_PARAMETER); >+ return tevent_req_post(req, ev); >+ } >+ lease_ptr = &lease; >+ >+ if (DEBUGLEVEL >= 10) { >+ DEBUG(10, ("Got lease request size %d\n", >+ (int)lease_len)); >+ NDR_PRINT_DEBUG(smb2_lease, lease_ptr); >+ } >+ >+ if (!smb2_lease_key_valid(&lease.lease_key)) { >+ lease_ptr = NULL; >+ requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; >+ } >+ >+ if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) && >+ (lease.lease_version != 1)) { >+ DEBUG(10, ("v2 lease key only for SMB3\n")); >+ lease_ptr = NULL; >+ } >+ } >+ > /* these are ignored for SMB2 */ > in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */ > in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */ >@@ -935,6 +984,14 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > } else { > struct smb_filename *smb_fname = NULL; > >+ if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) { >+ if (lease_ptr == NULL) { >+ requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE; >+ } >+ } else { >+ lease_ptr = NULL; >+ } >+ > /* > * For a DFS path the function parse_dfs_path() > * will do the path processing. >@@ -1005,7 +1062,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > in_create_options, > in_file_attributes, > map_smb2_oplock_levels_to_samba(requested_oplock_level), >- NULL, >+ lease_ptr, > allocation_size, > 0, /* private_flags */ > sec_desc, >@@ -1143,6 +1200,32 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, > return tevent_req_post(req, ev); > } > } >+ >+ if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) { >+ uint8_t buf[52]; >+ >+ lease = result->lease->lease; >+ >+ lease_len = sizeof(buf); >+ if (lease.lease_version == 1) { >+ lease_len = 32; >+ } >+ >+ if (!smb2_lease_push(&lease, buf, lease_len)) { >+ tevent_req_nterror( >+ req, NT_STATUS_INTERNAL_ERROR); >+ return tevent_req_post(req, ev); >+ } >+ >+ status = smb2_create_blob_add( >+ state, &out_context_blobs, >+ SMB2_CREATE_TAG_RQLS, >+ data_blob_const(buf, lease_len)); >+ if (!NT_STATUS_IS_OK(status)) { >+ tevent_req_nterror(req, status); >+ return tevent_req_post(req, ev); >+ } >+ } > } > > smb2req->compat_chain_fsp = smb1req->chain_fsp; >-- >1.9.1 > > >From 8ddcd67dea6c8381dd314a7cc99be6474a8a95f7 Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 14 Oct 2014 10:34:53 -0700 >Subject: [PATCH 24/28] s3:param: Add "smb2 leases" parameter. Default "false". > >This is currently marked as experimental! > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 44d2612822a39b980fe2b85b2a780de2f3902155) >--- > docs-xml/smbdotconf/locking/kerneloplocks.xml | 1 + > docs-xml/smbdotconf/locking/oplocks.xml | 1 + > docs-xml/smbdotconf/locking/smb2leases.xml | 28 +++++++++++++++++++++++++++ > lib/param/param_table.c | 9 +++++++++ > source3/param/loadparm.c | 1 + > 5 files changed, 40 insertions(+) > create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml > >diff --git a/docs-xml/smbdotconf/locking/kerneloplocks.xml b/docs-xml/smbdotconf/locking/kerneloplocks.xml >index 8e3bba5..d8fe223 100644 >--- a/docs-xml/smbdotconf/locking/kerneloplocks.xml >+++ b/docs-xml/smbdotconf/locking/kerneloplocks.xml >@@ -25,5 +25,6 @@ > > <related>oplocks</related> > <related>level2 oplocks</related> >+<related>smb2 leases</related> > <value type="default">no</value> > </samba:parameter> >diff --git a/docs-xml/smbdotconf/locking/oplocks.xml b/docs-xml/smbdotconf/locking/oplocks.xml >index a56e921..a5e163b 100644 >--- a/docs-xml/smbdotconf/locking/oplocks.xml >+++ b/docs-xml/smbdotconf/locking/oplocks.xml >@@ -25,5 +25,6 @@ > > <related>kernel oplocks</related> > <related>level2 oplocks</related> >+<related>smb2 leases</related> > <value type="default">yes</value> > </samba:parameter> >diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml >new file mode 100644 >index 0000000..0a734ec >--- /dev/null >+++ b/docs-xml/smbdotconf/locking/smb2leases.xml >@@ -0,0 +1,28 @@ >+<samba:parameter name="smb2 leases" >+ context="G" >+ type="boolean" >+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> >+<description> >+ <para> >+ This boolean option tells <command moreinfo="none">smbd</command> whether to >+ globally negotiate SMB2 leases on file open requests. Leasing is an SMB2-only >+ feature which allows clients to aggressively cache files locally above and >+ beyond the caching allowed by SMB1 oplocks. This (experimental) parameter is >+ set to off by default until the SMB2 leasing code is declared fully stable. >+ </para> >+ >+ <para> >+ This is only available with <smbconfoption name="oplocks">yes</smbconfoption> >+ and <smbconfoption name="kernel oplocks">no</smbconfoption>. >+ </para> >+ >+ <para> >+ The Samba implementation of leases is currently marked as experimental! >+ </para> >+</description> >+ >+<related>oplocks</related> >+<related>kernel oplocks</related> >+<related>level2 oplocks</related> >+<value type="default">no</value> >+</samba:parameter> >diff --git a/lib/param/param_table.c b/lib/param/param_table.c >index 53c299c..c57f783 100644 >--- a/lib/param/param_table.c >+++ b/lib/param/param_table.c >@@ -3009,6 +3009,15 @@ struct parm_struct parm_table[] = { > .flags = FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL, > }, > { >+ .label = "smb2 leases", >+ .type = P_BOOL, >+ .p_class = P_GLOBAL, >+ .offset = GLOBAL_VAR(smb2_leases), >+ .special = NULL, >+ .enum_list = NULL, >+ .flags = FLAG_ADVANCED, >+ }, >+ { > .label = "locking", > .type = P_BOOL, > .p_class = P_LOCAL, >diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c >index d2afac7..a0f3eef 100644 >--- a/source3/param/loadparm.c >+++ b/source3/param/loadparm.c >@@ -854,6 +854,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals) > Globals.smb2_max_write = DEFAULT_SMB2_MAX_WRITE; > Globals.smb2_max_trans = DEFAULT_SMB2_MAX_TRANSACT; > Globals.ismb2_max_credits = DEFAULT_SMB2_MAX_CREDITS; >+ Globals.smb2_leases = false; > > string_set(Globals.ctx, &Globals.ncalrpc_dir, get_dyn_NCALRPCDIR()); > >-- >1.9.1 > > >From e44f2ffa3733dd68d97a31564abb4fcc8248c39f Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 28 Oct 2014 15:31:46 -0700 >Subject: [PATCH 25/28] s3:smb2_negprot: announce support for SMB2.1 leases. > >We only do this with "smb2 leases = yes" >and the default values for "oplocks = yes" >and "kernel oplocks = no". > >Signed-off-by: Volker Lendecke <vl@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 556bf2ee006d14eb206c701bd8d375efb940140b) >--- > source3/smbd/smb2_negprot.c | 8 ++++++++ > 1 file changed, 8 insertions(+) > >diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c >index 12ff7b4..1f7f1a9 100644 >--- a/source3/smbd/smb2_negprot.c >+++ b/source3/smbd/smb2_negprot.c >@@ -230,6 +230,14 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) > capabilities |= SMB2_CAP_DFS; > } > >+ if (protocol >= PROTOCOL_SMB2_10 && >+ lp_smb2_leases() && >+ lp_oplocks(GLOBAL_SECTION_SNUM) && >+ !lp_kernel_oplocks(GLOBAL_SECTION_SNUM)) >+ { >+ capabilities |= SMB2_CAP_LEASING; >+ } >+ > if ((protocol >= PROTOCOL_SMB2_24) && > (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) && > (in_capabilities & SMB2_CAP_ENCRYPTION)) { >-- >1.9.1 > > >From be751d736261065c67bc7c5d81b4198a372f9b2e Mon Sep 17 00:00:00 2001 >From: Jeremy Allison <jra@samba.org> >Date: Tue, 14 Oct 2014 10:34:53 -0700 >Subject: [PATCH 26/28] selftest:Samba3: use "smb2 leases = yes" > >Signed-off-by: Jeremy Allison <jra@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 7eae9460a357071a776fecc6f3cf8cb5cb0961a3) >--- > selftest/knownfail | 13 ------------- > selftest/target/Samba3.pm | 1 + > 2 files changed, 1 insertion(+), 13 deletions(-) > >diff --git a/selftest/knownfail b/selftest/knownfail >index 1c4f446..af7e7fd 100644 >--- a/selftest/knownfail >+++ b/selftest/knownfail >@@ -189,25 +189,12 @@ > ^samba3.smb2.notify.valid-req > ^samba3.smb2.notify.dir > ^samba3.smb2.notify.rec >-^samba3.smb2.durable-open.lock-lease > ^samba3.smb2.durable-open.delete_on_close2 >-^samba3.smb2.durable-v2-open.open-lease >-^samba3.smb2.durable-v2-open.persistent-open-lease > ^samba3.smb2.durable-v2-open.app-instance > ^samba4.smb2.ioctl.req_resume_key\(dc\) # not supported by s4 ntvfs server > ^samba4.smb2.ioctl.copy_chunk_\w*\(dc\) # not supported by s4 ntvfs server > ^samba3.smb2.dir.one > ^samba3.smb2.dir.modify >-^samba3.smb2.lease.request >-^samba3.smb2.lease.upgrade >-^samba3.smb2.lease.break >-^samba3.smb2.lease.oplock >-^samba3.smb2.lease.multibreak >-^samba3.smb2.lease.v2_request >-^samba3.smb2.lease.v2_request_parent >-^samba3.smb2.lease.break_twice >-^samba3.smb2.lease.nobreakself >-^samba3.smb2.lease.v2_epoch1 > ^samba3.smb2.oplock.batch20 > ^samba3.smb2.oplock.stream1 > ^samba3.smb2.streams.rename >diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm >index 9a05464..a495685 100755 >--- a/selftest/target/Samba3.pm >+++ b/selftest/target/Samba3.pm >@@ -1054,6 +1054,7 @@ sub provision($$$$$$) > > kernel oplocks = no > kernel change notify = no >+ smb2 leases = yes > > syslog = no > printing = bsd >-- >1.9.1 > > >From 9d48a012c4319e7116a9b11c0fb9cf6db6cc875a Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Sat, 8 Nov 2014 09:45:59 +0100 >Subject: [PATCH 27/28] s3:smbd: document the interaction between "smb2 leases" > and "write cache size" > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> >(cherry picked from commit 5e251adbed792a53652ccaa1c3277a6ebb3af6f0) >--- > docs-xml/smbdotconf/locking/smb2leases.xml | 3 +++ > docs-xml/smbdotconf/tuning/writecachesize.xml | 3 +++ > source3/smbd/fileio.c | 5 +++++ > 3 files changed, 11 insertions(+) > >diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml >index 0a734ec..9241bc8 100644 >--- a/docs-xml/smbdotconf/locking/smb2leases.xml >+++ b/docs-xml/smbdotconf/locking/smb2leases.xml >@@ -16,6 +16,8 @@ > and <smbconfoption name="kernel oplocks">no</smbconfoption>. > </para> > >+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para> >+ > <para> > The Samba implementation of leases is currently marked as experimental! > </para> >@@ -24,5 +26,6 @@ > <related>oplocks</related> > <related>kernel oplocks</related> > <related>level2 oplocks</related> >+<related>write cache size</related> > <value type="default">no</value> > </samba:parameter> >diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml >index 12965b4..d7e2cc5 100644 >--- a/docs-xml/smbdotconf/tuning/writecachesize.xml >+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml >@@ -21,8 +21,11 @@ > > <para>The integer parameter specifies the size of this cache > (per oplocked file) in bytes.</para> >+ >+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para> > </description> > >+<related>smb2 leases</related> > <value type="default">0</value> > <value type="example">262144<comment> for a 256k cache size per file</comment></value> > </samba:parameter> >diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c >index 37c3f66..6ba7285 100644 >--- a/source3/smbd/fileio.c >+++ b/source3/smbd/fileio.c >@@ -339,6 +339,11 @@ ssize_t write_file(struct smb_request *req, > if (!fsp->modified && > EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && > (wcp == NULL)) { >+ /* >+ * Note: no write cache with leases! >+ * as the handles would have to share the write cache >+ * that's possible but an improvement for another day... >+ */ > setup_write_cache(fsp, fsp->fsp_name->st.st_ex_size); > wcp = fsp->wcp; > } >-- >1.9.1 > > >From 4e9620b9246b7026c24ccf6cc551abc1faeae5c6 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Sat, 8 Nov 2014 09:47:01 +0100 >Subject: [PATCH 28/28] docs-xml: document the interaction between "write cache > size" and "aio read/write size" > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Jeremy Allison <jra@samba.org> > >Autobuild-User(master): Jeremy Allison <jra@samba.org> >Autobuild-Date(master): Thu Dec 4 08:09:15 CET 2014 on sn-devel-104 > >(cherry picked from commit c261072a31bed4a7f67930fed54816312a158891) >--- > docs-xml/smbdotconf/tuning/writecachesize.xml | 2 ++ > 1 file changed, 2 insertions(+) > >diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml >index d7e2cc5..e2fa17f 100644 >--- a/docs-xml/smbdotconf/tuning/writecachesize.xml >+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml >@@ -25,6 +25,8 @@ > <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para> > </description> > >+<related>aio read size</related> >+<related>aio write size</related> > <related>smb2 leases</related> > <value type="default">0</value> > <value type="example">262144<comment> for a 256k cache size per file</comment></value> >-- >1.9.1 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Flags:
jra
:
review+
vl
:
review+
Actions:
View
Attachments on
bug 10911
:
10394
|
10396
|
10397
|
10399
|
10400
|
10401
|
10406
|
10407
|
10411
|
10412
|
10415
|
10416
|
10417
|
10418
|
10421
|
10478
|
10479
| 10481 |
10484
|
10486
|
10488
|
10492
|
10493
|
10507
|
10546