From cd80c59b85bc72e78866da0bf3bbb7800516cb0a Mon Sep 17 00:00:00 2001 From: Jeremy Allison 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 Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison (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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison (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 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 Signed-off-by: Volker Lendecke Signed-off-by: Stefan Metzmacher (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 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 Reviewed-by: Stefan Metzmacher (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 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 Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 . + +*/ + +#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 . + */ + +#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 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 Signed-off-by: Volker Lendecke Signed-off-by: Stefan Metzmacher (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; inum_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 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 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (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; inum_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; inum_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 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 Reviewed-by: Jeremy Allison (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 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 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (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; inum_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 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 Reviewed-by: Jeremy Allison (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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (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; inum_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 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 Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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; jnum_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 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 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (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 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 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Volker Lendecke Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher (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; inum_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; inum_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; inum_share_modes; i++) { + struct share_mode_entry *e = &d->share_modes[i]; + uint32_t e_lease_type; - for (i=0; idata->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; inum_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; inum_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; jnum_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 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 Signed-off-by: Volker Lendecke Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison (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 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 Reviewed-by: Stefan Metzmacher (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 @@ oplocks level2 oplocks +smb2 leases no 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 @@ kernel oplocks level2 oplocks +smb2 leases yes 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 @@ + + + + This boolean option tells smbd 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. + + + + This is only available with yes + and no. + + + + The Samba implementation of leases is currently marked as experimental! + + + +oplocks +kernel oplocks +level2 oplocks +no + 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 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 Reviewed-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 Date: Tue, 14 Oct 2014 10:34:53 -0700 Subject: [PATCH 26/28] selftest:Samba3: use "smb2 leases = yes" Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher (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 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 Reviewed-by: Jeremy Allison (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 no. + Note that the write cache won't be used for file handles with a smb2 write lease. + The Samba implementation of leases is currently marked as experimental! @@ -24,5 +26,6 @@ oplocks kernel oplocks level2 oplocks +write cache size no 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 @@ The integer parameter specifies the size of this cache (per oplocked file) in bytes. + + Note that the write cache won't be used for file handles with a smb2 write lease. +smb2 leases 0 262144 for a 256k cache size per file 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 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 Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison 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 @@ Note that the write cache won't be used for file handles with a smb2 write lease. +aio read size +aio write size smb2 leases 0 262144 for a 256k cache size per file -- 1.9.1