Latest leases patchset - getting there !
Stefan (metze) Metzmacher
metze at samba.org
Wed Dec 3 18:20:51 MST 2014
Hi Jeremy,
here's the patchset I'd propose for master.
Please review and push.
Thanks!
metze
-------------- next part --------------
From 5c7a0ddae1700406a613925ee52e68a262a84a05 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Mon, 1 Dec 2014 13:57:57 -0800
Subject: [PATCH 01/28] s4:torture:smb2: Add test that shows the client can
respond to a lease break over a different connection.
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 1f22e81964b7a1174e32cfa7b1486206b199b8c7 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 2 Dec 2014 12:58:28 -0800
Subject: [PATCH 02/28] s4:torture:smb2: Add smb2.lease.v2_breaking3 test.
This verifies the epoch handling in the multi step break.
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 9af2ccee9e1ea523bfa481c82d1007d85a1e7139 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 3 Dec 2014 22:32:33 +0100
Subject: [PATCH 03/28] s4:torture:smb2: let smb2.lease.[v2_]complex1 check the
R->NONE breaks
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 fcd2233f885f99af52b9f2fba9521f92b7e2174f Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 04/28] s3:smbd: factor out a send_break_to_none() helper
function
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 df2c17aaf4171cb2902f61ee01ba1e7fa6a4ff58 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:46:14 -0800
Subject: [PATCH 05/28] s3:smbd: Add fsp_client_guid() utility function to
return the connected client guid.
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 4d66ba3f548a80b34fcbead5bdefdddc231ac094 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 11:50:14 +0100
Subject: [PATCH 06/28] s3:smb2_server: allow smbd_smb2_send_break() with
session == NULL and tcon == NULL
In future we want to use this for lease breaks and they're not attached
to a session.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 138765e..4a3ea7d 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2735,14 +2735,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) +
@@ -2768,7 +2773,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 e6a30c75311978662b001a97319fd18baea5eeeb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 07/28] s3:smb2_server: add smbd_smb2_send_lease_break() helper
function
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 c67c3c3..d85e686 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -251,6 +251,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 4a3ea7d..3f23e2a 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2868,6 +2868,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 03aeda3b4c0b416876047085818681752c576cf4 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 3 Dec 2014 17:06:08 -0800
Subject: [PATCH 08/28] s3: leases: libsmbsharemodes no longer works with SMB2
leases inside our locking.tdb.
Remove it until a maintainer can be found.
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 at 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 ff058f22..caa5c9b 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 7bcef6a353367168fc848cfe15c70234604047a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 10 Oct 2014 16:36:54 -0700
Subject: [PATCH 09/28] s3:locking: add leases_db infrastructure
Will enable us to solve the dynamic share path problem
with leases on [homes].
We're also able to give the correct error codes when a
lease key is re-used with a different file name.
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/librpc/idl/leases_db.idl | 23 +++
source3/librpc/idl/wscript_build | 1 +
source3/librpc/wscript_build | 5 +
source3/locking/leases_db.c | 387 +++++++++++++++++++++++++++++++++++++++
source3/locking/leases_db.h | 46 +++++
source3/smbd/server.c | 5 +
source3/wscript_build | 6 +
7 files changed, 473 insertions(+)
create mode 100644 source3/librpc/idl/leases_db.idl
create mode 100644 source3/locking/leases_db.c
create mode 100644 source3/locking/leases_db.h
diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644
index 0000000..2ab1591
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,23 @@
+#include "idl_types.h"
+
+import "misc.idl";
+import "smb2_lease_struct.idl";
+import "file_id.idl";
+
+[
+ pointer_default(unique)
+]
+interface leases_db
+{
+ typedef [public] struct {
+ GUID client_guid;
+ smb2_lease_key lease_key;
+ } leases_db_key;
+
+ typedef [public] struct {
+ uint32 num_file_ids;
+ [size_is(num_file_ids)] file_id ids[];
+ [string,charset(UTF8)] char *filename;
+ [string,charset(UTF8)] char *stream_name;
+ } leases_db_value;
+}
diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build
index c38fe7b..f9b1bd7 100644
--- a/source3/librpc/idl/wscript_build
+++ b/source3/librpc/idl/wscript_build
@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
'''messaging.idl libnetapi.idl open_files.idl
perfcount.idl secrets.idl libnet_join.idl
smbXsrv.idl
+ leases_db.idl
''',
options='--includedir=%s --header --ndr-parser' % topinclude,
output_dir='../gen_ndr')
diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build
index 77ae048..38d9a81 100644
--- a/source3/librpc/wscript_build
+++ b/source3/librpc/wscript_build
@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH'
)
+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB',
+ source='gen_ndr/ndr_leases_db.c',
+ public_deps='ndr'
+ )
+
bld.SAMBA3_SUBSYSTEM('NDR_SECRETS',
source='gen_ndr/ndr_secrets.c',
public_deps='ndr'
diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..67c93ff
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,387 @@
+/*
+ Unix SMB/CIFS implementation.
+ Map lease keys to file ids
+ Copyright (C) Volker Lendecke 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "locking/leases_db.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "util_tdb.h"
+#include "ndr.h"
+#include "librpc/gen_ndr/ndr_leases_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
+
+/* the leases database handle */
+static struct db_context *leases_db;
+
+bool leases_db_init(bool read_only)
+{
+ if (leases_db) {
+ return true;
+ }
+
+ leases_db = db_open(NULL, lock_path("leases.tdb"), 0,
+ TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|
+ TDB_INCOMPATIBLE_HASH,
+ read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
+
+ if (leases_db == NULL) {
+ DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
+ return false;
+ }
+
+ return true;
+}
+
+static bool leases_db_key(TALLOC_CTX *mem_ctx,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ TDB_DATA *key)
+{
+ struct leases_db_key db_key = {
+ .client_guid = *client_guid,
+ .lease_key = *lease_key };
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("%s:\n", __func__));
+ NDR_PRINT_DEBUG(leases_db_key, &db_key);
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, mem_ctx, &db_key,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_key);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ return false;
+ }
+
+ *key = make_tdb_data(blob.data, blob.length);
+ return true;
+}
+
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id,
+ const char *filename,
+ const char *stream_name)
+{
+ TDB_DATA db_key, db_value;
+ DATA_BLOB blob;
+ struct db_record *rec;
+ NTSTATUS status;
+ bool ok;
+ struct leases_db_value new_value;
+ struct leases_db_value *value = NULL;
+ enum ndr_err_code ndr_err;
+
+ if (!leases_db_init(false)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ DEBUG(10, ("%s: leases_db_key failed\n", __func__));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+ TALLOC_FREE(db_key.dptr);
+ if (rec == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ db_value = dbwrap_record_get_value(rec);
+ if (db_value.dsize != 0) {
+ uint32_t i;
+
+ DEBUG(10, ("%s: record exists\n", __func__));
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ blob.data = db_value.dptr;
+ blob.length = db_value.dsize;
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ /* id must be unique. */
+ for (i = 0; i < value->num_file_ids; i++) {
+ if (file_id_equal(id, &value->ids[i])) {
+ status = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto out;
+ }
+ }
+
+ value->ids = talloc_realloc(value, value->ids, struct file_id,
+ value->num_file_ids + 1);
+ if (value->ids == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ value->ids[value->num_file_ids] = *id;
+ value->num_file_ids += 1;
+
+ } else {
+ DEBUG(10, ("%s: new record\n", __func__));
+
+ new_value = (struct leases_db_value) {
+ .num_file_ids = 1,
+ .ids = discard_const_p(struct file_id, id),
+ .filename = filename,
+ .stream_name = stream_name,
+ };
+ value = &new_value;
+ }
+
+ ndr_err = ndr_push_struct_blob(
+ &blob, talloc_tos(), value,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("%s:\n", __func__));
+ NDR_PRINT_DEBUG(leases_db_value, value);
+ }
+
+ db_value = make_tdb_data(blob.data, blob.length);
+
+ status = dbwrap_record_store(rec, db_value, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+ __func__, nt_errstr(status)));
+ }
+
+ out:
+
+ if (value != &new_value) {
+ TALLOC_FREE(value);
+ }
+ TALLOC_FREE(rec);
+ return status;
+}
+
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id)
+{
+ TDB_DATA db_key, db_value;
+ struct db_record *rec;
+ NTSTATUS status;
+ struct leases_db_value *value;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ uint32_t i;
+ bool ok;
+
+ if (!leases_db_init(false)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+ TALLOC_FREE(db_key.dptr);
+ if (rec == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ db_value = dbwrap_record_get_value(rec);
+ if (db_value.dsize == 0) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ blob.data = db_value.dptr;
+ blob.length = db_value.dsize;
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ /* id must exist. */
+ for (i = 0; i < value->num_file_ids; i++) {
+ if (file_id_equal(id, &value->ids[i])) {
+ break;
+ }
+ }
+
+ if (i == value->num_file_ids) {
+ status = NT_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ value->ids[i] = value->ids[value->num_file_ids-1];
+ value->num_file_ids -= 1;
+
+ if (value->num_file_ids == 0) {
+ DEBUG(10, ("%s: deleting record\n", __func__));
+ status = dbwrap_record_delete(rec);
+ } else {
+ DEBUG(10, ("%s: updating record\n", __func__));
+ ndr_err = ndr_push_struct_blob(
+ &blob, talloc_tos(), value,
+ (ndr_push_flags_fn_t)ndr_push_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("%s:\n", __func__));
+ NDR_PRINT_DEBUG(leases_db_value, value);
+ }
+
+ db_value = make_tdb_data(blob.data, blob.length);
+
+ status = dbwrap_record_store(rec, db_value, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+ __func__, nt_errstr(status)));
+ }
+ }
+
+ out:
+
+ TALLOC_FREE(value);
+ TALLOC_FREE(rec);
+ return status;
+}
+
+struct leases_db_fetch_state {
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids, const char *filename,
+ const char *stream_name, void *private_data);
+ void *private_data;
+ NTSTATUS status;
+};
+
+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+ struct leases_db_fetch_state *state =
+ (struct leases_db_fetch_state *)private_data;
+ DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
+ enum ndr_err_code ndr_err;
+ struct leases_db_value *value;
+
+ value = talloc(talloc_tos(), struct leases_db_value);
+ if (value == NULL) {
+ state->status = NT_STATUS_NO_MEMORY;
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob_all(
+ &blob, value, value,
+ (ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+ __func__, ndr_errstr(ndr_err)));
+ TALLOC_FREE(value);
+ state->status = ndr_map_error2ntstatus(ndr_err);
+ return;
+ }
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10, ("%s:\n", __func__));
+ NDR_PRINT_DEBUG(leases_db_value, value);
+ }
+
+ state->parser(value->num_file_ids,
+ value->ids, value->filename, value->stream_name,
+ state->private_data);
+
+ TALLOC_FREE(value);
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
+ const char *filename,
+ const char *stream_name,
+ void *private_data),
+ void *private_data)
+{
+ TDB_DATA db_key;
+ struct leases_db_fetch_state state;
+ NTSTATUS status;
+ bool ok;
+
+ if (!leases_db_init(true)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+ if (!ok) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state = (struct leases_db_fetch_state) {
+ .parser = parser,
+ .private_data = private_data,
+ .status = NT_STATUS_OK
+ };
+
+ status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
+ &state);
+ TALLOC_FREE(db_key.dptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return state.status;
+}
diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h
new file mode 100644
index 0000000..f570356
--- /dev/null
+++ b/source3/locking/leases_db.h
@@ -0,0 +1,46 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * leases.tdb functions
+ *
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LEASES_DB_H_
+#define _LEASES_DB_H_
+
+struct GUID;
+struct smb2_lease_key;
+struct file_id;
+
+bool leases_db_init(bool read_only);
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id,
+ const char *filename,
+ const char *stream_name);
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ const struct file_id *id);
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+ const struct smb2_lease_key *lease_key,
+ void (*parser)(uint32_t num_file_ids,
+ struct file_id *ids,
+ const char *filename,
+ const char *stream_name,
+ void *private_data),
+ void *private_data);
+
+#endif /* _LEASES_DB_H_ */
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 05104da..8207bf1 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;
@@ -1452,6 +1453,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 caa5c9b..480bedc 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -611,6 +611,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
LIBAFS
RPC_SERVICE
NDR_SMBXSRV
+ LEASES_DB
LIBASYS
sysquotas
ccan-hash
@@ -629,9 +630,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 016123af6ca0a7e75ac9694e8f802753275f2f13 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 27 Nov 2014 18:34:56 +0100
Subject: [PATCH 10/28] s3:open_files.idl: add data structures for SMB2.1 and
SMB3.0 leases.
Pair-Programmed-With: Volker Lendecke <vl at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 53d3edc..a6589db 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 6447c61..2ae7c33 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -154,7 +154,17 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
*/
for (i=0; i<d->num_share_modes; i++) {
- d->share_modes[i].stale = false;
+ struct share_mode_entry *e = &d->share_modes[i];
+
+ e->stale = false;
+ e->lease = NULL;
+ if (e->op_type != LEASE_OPLOCK) {
+ continue;
+ }
+ if (e->lease_idx >= d->num_leases) {
+ continue;
+ }
+ e->lease = &d->leases[e->lease_idx];
}
d->modified = false;
d->fresh = false;
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1952823..c14d2eb 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1568,7 +1568,8 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
req ? req->mid : 0,
- fsp->oplock_type);
+ fsp->oplock_type,
+ UINT32_MAX);
if (!ok) {
return NT_STATUS_NO_MEMORY;
}
@@ -3080,6 +3081,7 @@ static NTSTATUS open_directory(connection_struct *conn,
NTSTATUS status;
struct timespec mtimespec;
int info = 0;
+ bool ok;
if (is_ntfs_stream_smb_fname(smb_dname)) {
DEBUG(2, ("open_directory: %s is a stream name!\n",
@@ -3336,8 +3338,10 @@ static NTSTATUS open_directory(connection_struct *conn,
return status;
}
- if (!set_share_mode(lck, fsp, get_current_uid(conn),
- req ? req->mid : 0, NO_OPLOCK)) {
+ ok = set_share_mode(lck, fsp, get_current_uid(conn),
+ req ? req->mid : 0, NO_OPLOCK,
+ UINT32_MAX);
+ if (!ok) {
TALLOC_FREE(lck);
fd_close(fsp);
file_free(req, fsp);
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 4e1dae7..3864b7e 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -175,6 +175,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 7d6c93d0a07372ac14c0e34300275a2919257e15 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 14:32:19 -0700
Subject: [PATCH 11/28] s3:locking: ensure all share mode removal functions go
through a common lease refcount manager.
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
source3/locking/locking.c | 91 +++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 88 insertions(+), 3 deletions(-)
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 9194dd3..f96887e 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -46,6 +46,7 @@
#include "messages.h"
#include "util_tdb.h"
#include "../librpc/gen_ndr/ndr_open_files.h"
+#include "locking/leases_db.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_LOCKING
@@ -617,6 +618,86 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
}
/*
+ * See if we need to remove a lease being referred to by a
+ * share mode that is being marked stale or deleted.
+ */
+
+static void remove_share_mode_lease(struct share_mode_data *d,
+ struct share_mode_entry *e)
+{
+ struct GUID client_guid;
+ struct smb2_lease_key lease_key;
+ uint16_t op_type;
+ uint32_t lease_idx;
+ uint32_t i;
+
+ op_type = e->op_type;
+ e->op_type = NO_OPLOCK;
+
+ d->modified = true;
+
+ if (op_type != LEASE_OPLOCK) {
+ return;
+ }
+
+ /*
+ * This used to reference a lease. If there's no other one referencing
+ * it, remove it.
+ */
+
+ lease_idx = e->lease_idx;
+ e->lease_idx = UINT32_MAX;
+
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].stale) {
+ continue;
+ }
+ if (e == &d->share_modes[i]) {
+ /* Not ourselves. */
+ continue;
+ }
+ if (d->share_modes[i].lease_idx == lease_idx) {
+ break;
+ }
+ }
+ if (i < d->num_share_modes) {
+ /*
+ * Found another one
+ */
+ return;
+ }
+
+ memcpy(&client_guid,
+ &d->leases[lease_idx].client_guid,
+ sizeof(client_guid));
+ lease_key = d->leases[lease_idx].lease_key;
+
+ d->num_leases -= 1;
+ d->leases[lease_idx] = d->leases[d->num_leases];
+
+ /*
+ * We changed the lease array. Fix all references to it.
+ */
+ for (i=0; i<d->num_share_modes; i++) {
+ if (d->share_modes[i].lease_idx == d->num_leases) {
+ d->share_modes[i].lease_idx = lease_idx;
+ d->share_modes[i].lease = &d->leases[lease_idx];
+ }
+ }
+
+ {
+ NTSTATUS status;
+
+ status = leases_db_del(&client_guid,
+ &lease_key,
+ &e->id);
+
+ DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+ nt_errstr(status)));
+ }
+}
+
+/*
* In case d->share_modes[i] conflicts with something or otherwise is
* being used, we need to make sure the corresponding process still
* exists.
@@ -674,6 +755,8 @@ bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx)
}
}
+ remove_share_mode_lease(d, e);
+
d->modified = true;
return true;
}
@@ -782,6 +865,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
if (e == NULL) {
return False;
}
+ remove_share_mode_lease(lck->data, e);
*e = lck->data->share_modes[lck->data->num_share_modes-1];
lck->data->num_share_modes -= 1;
lck->data->modified = True;
@@ -829,6 +913,7 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
{
+ struct share_mode_data *d = lck->data;
struct share_mode_entry *e;
e = find_share_mode_entry(lck, fsp);
@@ -836,9 +921,9 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
return False;
}
- e->op_type = NO_OPLOCK;
- lck->data->modified = True;
- return True;
+ remove_share_mode_lease(d, e);
+ d->modified = True;
+ return true;
}
/*******************************************************************
--
1.9.1
From 90dfc565f0fecc377822b0f15664bcda531183de Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 27 Nov 2014 19:32:46 +0100
Subject: [PATCH 12/28] s3:locking: cleanup leases_db from
share_mode_cleanup_disconnected()
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 2ae7c33..138950f 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
@@ -638,6 +639,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: "
@@ -672,6 +683,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 5d55de98465981539f84a3ae560c01f38a85cb04 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 13/28] s3:locking: add downgrade_share_lease() helper function
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
source3/locking/locking.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
source3/locking/proto.h | 6 ++++
2 files changed, 86 insertions(+)
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index f96887e..d144f5c 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -944,6 +944,86 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
return True;
}
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+ struct share_mode_lock *lck,
+ const struct smb2_lease_key *key,
+ uint32_t new_lease_state,
+ struct share_mode_lease **_l)
+{
+ struct share_mode_data *d = lck->data;
+ struct share_mode_lease *l;
+ uint32_t i;
+
+ *_l = NULL;
+
+ for (i=0; i<d->num_leases; i++) {
+ if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid,
+ key,
+ &d->leases[i].client_guid,
+ &d->leases[i].lease_key)) {
+ break;
+ }
+ }
+ if (i == d->num_leases) {
+ DEBUG(10, ("lease not found\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ l = &d->leases[i];
+
+ if (!l->breaking) {
+ DEBUG(1, ("Attempt to break from %d to %d - but we're not in breaking state\n",
+ (int)l->current_state, (int)new_lease_state));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * Can't upgrade anything: l->breaking_to_requested (and l->current_state)
+ * must be a strict bitwise superset of new_lease_state
+ */
+ if ((new_lease_state & l->breaking_to_requested) != new_lease_state) {
+ DEBUG(1, ("Attempt to upgrade from %d to %d - expected %d\n",
+ (int)l->current_state, (int)new_lease_state,
+ (int)l->breaking_to_requested));
+ return NT_STATUS_REQUEST_NOT_ACCEPTED;
+ }
+
+ if (l->current_state != new_lease_state) {
+ l->current_state = new_lease_state;
+ d->modified = true;
+ }
+
+ if ((new_lease_state & ~l->breaking_to_required) != 0) {
+ DEBUG(5, ("lease state %d not fully broken from %d to %d\n",
+ (int)new_lease_state,
+ (int)l->current_state,
+ (int)l->breaking_to_required));
+ l->breaking_to_requested = l->breaking_to_required;
+ if (l->current_state & (~SMB2_LEASE_READ)) {
+ /*
+ * Here we break in steps, as windows does
+ * see the breaking3 and v2_breaking3 tests.
+ */
+ l->breaking_to_requested |= SMB2_LEASE_READ;
+ }
+ d->modified = true;
+ *_l = l;
+ return NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
+ }
+
+ DEBUG(10, ("breaking from %d to %d - expected %d\n",
+ (int)l->current_state, (int)new_lease_state,
+ (int)l->breaking_to_requested));
+
+ l->breaking_to_requested = 0;
+ l->breaking_to_required = 0;
+ l->breaking = false;
+
+ d->modified = true;
+
+ return NT_STATUS_OK;
+}
+
/****************************************************************************
Adds a delete on close token.
****************************************************************************/
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 6b60330..c4ea198 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -175,6 +175,12 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
struct files_struct *fsp);
bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
+struct share_mode_lease;
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+ struct share_mode_lock *lck,
+ const struct smb2_lease_key *key,
+ uint32_t new_lease_state,
+ struct share_mode_lease **_l);
bool get_delete_on_close_token(struct share_mode_lock *lck,
uint32_t name_hash,
const struct security_token **pp_nt_tok,
--
1.9.1
From 316f745c8a75d5deb5f7626f7419b8e6bc070d36 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 3 Dec 2014 17:57:37 +0100
Subject: [PATCH 14/28] s3:vfs.h: add more elements to struct fsp_lease
We'll need a reference to the smbd_server_connection as well
as a timeout handler.
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 698a0fba7cb5d7370a0113a7478e46c2d9173112 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 15/28] s3:smbd: add fsp_lease_type() and get_lease_type()
helper functions
These convert the oplock state into SMB2_LEASE_ flags.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 d3f1913d26cd3267696b2580aaf2ddc023e1d95c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 16/28] s3:smb2_create: allow durable handles with
SMB2_LEASE_HANDLE
We don't support real lease yet, but this makes use of fsp_lease_type()
which converts a batch oplock into and RWH lease.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 1e31cbf..f006a96 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -998,7 +998,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 b7ecf189ef2f3cd4bb78294b7a97eb0698dbf1a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 17/28] s3:smb2_create: validate durable reconnects with leases
We don't support leases yet, but prepares for the comming commits.
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 f006a96..e96e84a 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 0b7489b9d304af1cbbfc3e5e6d9a059ac3cf865a Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 18/28] s3:smbd: add file_find_one_fsp_from_lease_key() helper
function
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 e817512d05e1ee030d024eb6446c6bb60a1cd498 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 19/28] s3:smbd: add lease related helper functions to open.c
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/open.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++
source3/smbd/proto.h | 7 ++
2 files changed, 215 insertions(+)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index c14d2eb..1055c94 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -35,6 +35,7 @@
#include "serverid.h"
#include "messages.h"
#include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
extern const struct generic_mapping file_generic_mapping;
@@ -1490,6 +1491,213 @@ static bool file_has_brlocks(files_struct *fsp)
return (brl_num_locks(br_lck) > 0);
}
+int find_share_mode_lease(struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *key)
+{
+ uint32_t i;
+
+ for (i=0; i<d->num_leases; i++) {
+ struct share_mode_lease *l = &d->leases[i];
+
+ if (smb2_lease_equal(client_guid,
+ key,
+ &l->client_guid,
+ &l->lease_key)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
+ const struct smb2_lease_key *key,
+ const struct share_mode_lease *l)
+{
+ struct files_struct *fsp;
+
+ /*
+ * TODO: Measure how expensive this loop is with thousands of open
+ * handles...
+ */
+
+ for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id);
+ fsp != NULL;
+ fsp = file_find_di_next(fsp)) {
+
+ if (fsp == new_fsp) {
+ continue;
+ }
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ continue;
+ }
+ if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+ fsp->lease->ref_count += 1;
+ return fsp->lease;
+ }
+ }
+
+ /* Not found - must be leased in another smbd. */
+ new_fsp->lease = talloc_zero(new_fsp->conn->sconn, struct fsp_lease);
+ if (new_fsp->lease == NULL) {
+ return NULL;
+ }
+ new_fsp->lease->ref_count = 1;
+ new_fsp->lease->sconn = new_fsp->conn->sconn;
+ new_fsp->lease->lease.lease_key = *key;
+ new_fsp->lease->lease.lease_state = l->current_state;
+ /*
+ * We internally treat all leases as V2 and update
+ * the epoch, but when sending breaks it matters if
+ * the requesting lease was v1 or v2.
+ */
+ new_fsp->lease->lease.lease_version = l->lease_version;
+ new_fsp->lease->lease.lease_epoch = l->epoch;
+ return new_fsp->lease;
+}
+
+#if 0
+static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
+ struct share_mode_lock *lck,
+ const struct smb2_lease *lease,
+ uint32_t *p_lease_idx,
+ uint32_t granted)
+{
+ struct share_mode_data *d = lck->data;
+ const struct GUID *client_guid = fsp_client_guid(fsp);
+ struct share_mode_lease *tmp;
+ NTSTATUS status;
+ int idx;
+
+ idx = find_share_mode_lease(d, client_guid, &lease->lease_key);
+
+ if (idx != -1) {
+ struct share_mode_lease *l = &d->leases[idx];
+ bool do_upgrade;
+ uint32_t existing, requested;
+
+ fsp->lease = find_fsp_lease(fsp, &lease->lease_key, l);
+ if (fsp->lease == NULL) {
+ DEBUG(1, ("Did not find existing lease for file %s\n",
+ fsp_str_dbg(fsp)));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *p_lease_idx = idx;
+
+ /*
+ * Upgrade only if the requested lease is a strict upgrade.
+ */
+ existing = l->current_state;
+ requested = lease->lease_state;
+
+ /*
+ * Tricky: This test makes sure that "requested" is a
+ * strict bitwise superset of "existing".
+ */
+ do_upgrade = ((existing & requested) == existing);
+
+ /*
+ * Upgrade only if there's a change.
+ */
+ do_upgrade &= (granted != existing);
+
+ /*
+ * Upgrade only if other leases don't prevent what was asked
+ * for.
+ */
+ do_upgrade &= (granted == requested);
+
+ /*
+ * only upgrade if we are not in breaking state
+ */
+ do_upgrade &= !l->breaking;
+
+ DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+ "granted=%"PRIu32", do_upgrade=%d\n",
+ existing, requested, granted, (int)do_upgrade));
+
+ if (do_upgrade) {
+ l->current_state = granted;
+ l->epoch += 1;
+ }
+
+ /* Ensure we're in sync with current lease state. */
+ fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * Create new lease
+ */
+
+ tmp = talloc_realloc(d, d->leases, struct share_mode_lease,
+ d->num_leases+1);
+ if (tmp == NULL) {
+ /*
+ * See [MS-SMB2]
+ */
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ d->leases = tmp;
+
+ fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease);
+ if (fsp->lease == NULL) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+ fsp->lease->ref_count = 1;
+ fsp->lease->sconn = fsp->conn->sconn;
+ fsp->lease->lease.lease_version = lease->lease_version;
+ fsp->lease->lease.lease_key = lease->lease_key;
+ fsp->lease->lease.lease_state = granted;
+ fsp->lease->lease.lease_epoch = lease->lease_epoch + 1;
+
+ *p_lease_idx = d->num_leases;
+
+ d->leases[d->num_leases] = (struct share_mode_lease) {
+ .client_guid = *client_guid,
+ .lease_key = fsp->lease->lease.lease_key,
+ .current_state = fsp->lease->lease.lease_state,
+ .lease_version = fsp->lease->lease.lease_version,
+ .epoch = fsp->lease->lease.lease_epoch,
+ };
+
+ status = leases_db_add(client_guid, &lease->lease_key,
+ &fsp->file_id, fsp->fsp_name->base_name,
+ fsp->fsp_name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__,
+ nt_errstr(status)));
+ TALLOC_FREE(fsp->lease);
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ d->num_leases += 1;
+ d->modified = true;
+
+ return NT_STATUS_OK;
+}
+
+static bool is_same_lease(const files_struct *fsp,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ const struct smb2_lease *lease)
+{
+ if (e->op_type != LEASE_OPLOCK) {
+ return false;
+ }
+ if (lease == NULL) {
+ return false;
+ }
+
+ return smb2_lease_equal(fsp_client_guid(fsp),
+ &lease->lease_key,
+ &d->leases[e->lease_idx].client_guid,
+ &d->leases[e->lease_idx].lease_key);
+}
+#endif
+
static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
struct files_struct *fsp,
struct share_mode_lock *lck,
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7a4b4c9..55fbd3b 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -626,6 +626,13 @@ void msg_file_was_renamed(struct messaging_context *msg,
DATA_BLOB *data);
NTSTATUS open_streams_for_delete(connection_struct *conn,
const char *fname);
+int find_share_mode_lease(struct share_mode_data *d,
+ const struct GUID *client_guid,
+ const struct smb2_lease_key *key);
+struct share_mode_lease;
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+ const struct smb2_lease_key *key,
+ const struct share_mode_lease *l);
NTSTATUS create_file_default(connection_struct *conn,
struct smb_request *req,
uint16_t root_dir_fid,
--
1.9.1
From df694f0756f8f9bbe231758ba390cb0fff96f275 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 20/28] s3:smbd: add lease key validation functions to open.c
This makes sure a lease key can only be used for one specific
file.
This also handles the dynamic share file case [homes].
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
source3/smbd/open.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 192 insertions(+)
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1055c94..28f7408 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4034,6 +4034,179 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
}
/*
+ * If we already have a lease, it must match the new file id. [MS-SMB2]
+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is
+ * used for a different file name.
+ */
+
+struct lease_fname_match_state {
+ /* Input parameters. */
+ const struct smb_filename *fname;
+ bool file_existed;
+ struct file_id id;
+ /* Return parameters. */
+ uint32_t num_file_ids;
+ struct file_id *ids;
+ NTSTATUS match_status;
+};
+
+static void lease_fname_match_parser(
+ uint32_t num_file_ids,
+ struct file_id *ids, const char *filename, const char *stream_name,
+ void *private_data)
+{
+ struct lease_fname_match_state *state =
+ (struct lease_fname_match_state *)private_data;
+
+ if (!strequal(filename, state->fname->base_name) ||
+ !strequal(stream_name, state->fname->stream_name))
+ {
+ /* Names don't match lease key. */
+ state->match_status = NT_STATUS_INVALID_PARAMETER;
+ return;
+ }
+
+ if (state->file_existed &&
+ num_file_ids == 1 &&
+ file_id_equal(&ids[0],&state->id))
+ {
+ /* Common case - non-dynamic share. We're ok.. */
+ state->match_status = NT_STATUS_OK;
+ return;
+ }
+
+ /*
+ * More than one file id, or not equal, or new file
+ * being created and there's already an existing lease
+ * on this (client_guid, lease id) pair.
+ * Don't allow leases.
+ */
+
+ state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+ state->num_file_ids = num_file_ids;
+ state->ids = talloc_memdup(talloc_tos(),
+ ids,
+ num_file_ids * sizeof(struct file_id));
+ if (state->ids == NULL) {
+ state->match_status = NT_STATUS_NO_MEMORY;
+ }
+}
+
+static NTSTATUS lease_match(connection_struct *conn,
+ struct smb_request *req,
+ struct smb2_lease_key *lease_key,
+ const struct smb_filename *fname,
+ uint16_t *p_version,
+ uint16_t *p_epoch)
+{
+ struct smbd_server_connection *sconn = req->sconn;
+ struct lease_fname_match_state state = {
+ .fname = fname,
+ .match_status = NT_STATUS_OK
+ };
+ uint32_t i;
+ NTSTATUS status;
+
+ state.file_existed = VALID_STAT(fname->st);
+ if (state.file_existed) {
+ state.id = vfs_file_id_from_sbuf(conn, &fname->st);
+ } else {
+ memset(&state.id, '\0', sizeof(state.id));
+ }
+
+ status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
+ lease_key, lease_fname_match_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Not found or error means okay: We can make the lease pass
+ */
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ /*
+ * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller
+ * deal with it.
+ */
+ return state.match_status;
+ }
+
+ /* We have to break all existing leases. */
+ for (i = 0; i < state.num_file_ids; i++) {
+ struct share_mode_lock *lck;
+ struct share_mode_data *d;
+ uint32_t j;
+
+ if (file_id_equal(&state.ids[i], &state.id)) {
+ /* Don't need to break our own file. */
+ continue;
+ }
+
+ lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+ if (lck == NULL) {
+ /* Race condition - file already closed. */
+ continue;
+ }
+ d = lck->data;
+ for (j=0; j<d->num_share_modes; j++) {
+ struct share_mode_entry *e = &d->share_modes[j];
+ uint32_t e_lease_type = get_lease_type(d, e);
+ struct share_mode_lease *l = NULL;
+
+ if (share_mode_stale_pid(d, j)) {
+ continue;
+ }
+
+ if (e->op_type == LEASE_OPLOCK) {
+ l = &lck->data->leases[e->lease_idx];
+ if (!smb2_lease_key_equal(&l->lease_key,
+ lease_key)) {
+ continue;
+ }
+ *p_epoch = l->epoch;
+ *p_version = l->lease_version;
+ }
+
+ if (e_lease_type == SMB2_LEASE_NONE) {
+ continue;
+ }
+
+ send_break_message(conn->sconn->msg_ctx, e,
+ SMB2_LEASE_NONE);
+
+ /*
+ * Windows 7 and 8 lease clients
+ * are broken in that they will not
+ * respond to lease break requests
+ * whilst waiting for an outstanding
+ * open request on that lease handle
+ * on the same TCP connection, due
+ * to holding an internal inode lock.
+ *
+ * This means we can't reschedule
+ * ourselves here, but must return
+ * from the create.
+ *
+ * Work around:
+ *
+ * Send the breaks and then return
+ * SMB2_LEASE_NONE in the lease handle
+ * to cause them to acknowledge the
+ * lease break. Consulatation with
+ * Microsoft engineering confirmed
+ * this approach is safe.
+ */
+
+ }
+ TALLOC_FREE(lck);
+ }
+ /*
+ * Ensure we don't grant anything more so we
+ * never upgrade.
+ */
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
+}
+
+/*
* Wrapper around open_file_ntcreate and open_directory
*/
@@ -4089,6 +4262,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
oplock_request |= INTERNAL_OPEN_ONLY;
}
+ if (lease != NULL) {
+ uint16_t epoch = lease->lease_epoch;
+ uint16_t version = lease->lease_version;
+ status = lease_match(conn,
+ req,
+ &lease->lease_key,
+ smb_fname,
+ &version,
+ &epoch);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ /* Dynamic share file. No leases and update epoch... */
+ lease->lease_state = SMB2_LEASE_NONE;
+ lease->lease_epoch = epoch;
+ lease->lease_version = version;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
&& (access_mask & DELETE_ACCESS)
&& !is_ntfs_stream_smb_fname(smb_fname)) {
--
1.9.1
From 8dee5c41b9dcbfe7587d391a8dbf4afe8c31c952 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 21/28] s3:smbd: add lease related helper functions to oplock.c
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 1693f6e6d73f7b9142ed3d1d5ad0a39cdc7acae0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 22/28] s3:smbd: Implementation of SMB2.1 and SMB3.0 leases.
Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
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 d85e686..7726c24 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -305,7 +305,9 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
struct deferred_open_record;
/* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, int level);
+void send_break_message_smb2(files_struct *fsp,
+ uint32_t break_from,
+ uint32_t break_to);
struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
struct smb_request *req,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 28f7408..f5ad900 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1258,6 +1258,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
share_mode_entry_to_message(msg, exclusive);
/* Overload entry->op_type */
+ /*
+ * This is a cut from uint32 to uint16, but so far only the lower 3
+ * bits (LEASE_WRITE/HANDLE/READ are used anyway.
+ */
SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
status = messaging_send_buf(msg_ctx, exclusive->pid,
@@ -1377,56 +1381,19 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
static bool delay_for_oplock(files_struct *fsp,
int oplock_request,
+ const struct smb2_lease *lease,
struct share_mode_lock *lck,
bool have_sharing_violation,
- uint32_t create_disposition)
+ uint32_t create_disposition,
+ bool first_open_attempt)
{
struct share_mode_data *d = lck->data;
- struct share_mode_entry *entry;
- uint32_t num_non_stat_opens = 0;
uint32_t i;
- uint16_t break_to;
-
- if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
- return false;
- }
- for (i=0; i<d->num_share_modes; i++) {
- struct share_mode_entry *e = &d->share_modes[i];
- if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
- continue;
- }
- num_non_stat_opens += 1;
-
- /*
- * We found the a non-stat open, which in the exclusive/batch
- * case will be inspected further down.
- */
- entry = e;
- }
- if (num_non_stat_opens == 0) {
- /*
- * Nothing to wait for around
- */
- return false;
- }
- if (num_non_stat_opens != 1) {
- /*
- * More than one open around. There can't be any exclusive or
- * batch left, this is all level2.
- */
- return false;
- }
+ bool delay = false;
+ bool will_overwrite;
- if (server_id_is_disconnected(&entry->pid)) {
- /*
- * TODO: clean up.
- * This could be achieved by sending a break message
- * to ourselves. Special considerations for files
- * with delete_on_close flag set!
- *
- * For now we keep it simple and do not
- * allow delete on close for durable handles.
- */
+ if ((oplock_request & INTERNAL_OPEN_ONLY) ||
+ is_stat_open(fsp->access_mask)) {
return false;
}
@@ -1434,50 +1401,98 @@ static bool delay_for_oplock(files_struct *fsp,
case FILE_SUPERSEDE:
case FILE_OVERWRITE:
case FILE_OVERWRITE_IF:
- break_to = NO_OPLOCK;
+ will_overwrite = true;
break;
default:
- break_to = LEVEL_II_OPLOCK;
+ will_overwrite = false;
break;
}
- if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
- if (share_mode_stale_pid(d, 0)) {
- return false;
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+ struct share_mode_lease *l = NULL;
+ uint32_t e_lease_type = get_lease_type(d, e);
+ uint32_t break_to;
+ uint32_t delay_mask = 0;
+
+ if (e->op_type == LEASE_OPLOCK) {
+ l = &d->leases[e->lease_idx];
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return true;
- }
- if (have_sharing_violation) {
- /*
- * Non-batch exclusive is not broken if we have a sharing
- * violation
- */
- return false;
- }
- if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
- (break_to == NO_OPLOCK)) {
- if (share_mode_stale_pid(d, 0)) {
- return false;
+
+ if (have_sharing_violation) {
+ delay_mask = SMB2_LEASE_HANDLE;
+ } else {
+ delay_mask = SMB2_LEASE_WRITE;
}
- DEBUG(10, ("Asynchronously breaking level2 oplock for "
- "create_disposition=%u\n",
- (unsigned)create_disposition));
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return false;
- }
- if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
- /*
- * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
- */
- return false;
- }
- if (share_mode_stale_pid(d, 0)) {
- return false;
+
+ break_to = e_lease_type & ~delay_mask;
+
+ if (will_overwrite) {
+ /*
+ * we'll decide about SMB2_LEASE_READ later.
+ *
+ * Maybe the break will be defered
+ */
+ break_to &= ~SMB2_LEASE_HANDLE;
+ }
+
+ DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+ (unsigned)i, (unsigned)e_lease_type,
+ (unsigned)will_overwrite));
+
+ if (lease != NULL && l != NULL) {
+ bool ign;
+
+ ign = smb2_lease_equal(fsp_client_guid(fsp),
+ &lease->lease_key,
+ &l->client_guid,
+ &l->lease_key);
+ if (ign) {
+ continue;
+ }
+ }
+
+ if ((e_lease_type & ~break_to) == 0) {
+ if (l != NULL && l->breaking) {
+ delay = true;
+ }
+ continue;
+ }
+
+ if (share_mode_stale_pid(d, i)) {
+ continue;
+ }
+
+ if (will_overwrite) {
+ /*
+ * If we break anyway break to NONE directly.
+ * Otherwise vfs_set_filelen() will trigger the
+ * break.
+ */
+ break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
+ }
+
+ if (e->op_type != LEASE_OPLOCK) {
+ /*
+ * Oplocks only support breaking to R or NONE.
+ */
+ break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+ }
+
+ DEBUG(10, ("breaking from %d to %d\n",
+ (int)e_lease_type, (int)break_to));
+ send_break_message(fsp->conn->sconn->msg_ctx, e,
+ break_to);
+ if (e_lease_type & delay_mask) {
+ delay = true;
+ }
+ if (l != NULL && l->breaking && !first_open_attempt) {
+ delay = true;
+ }
+ continue;
}
- send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
- return true;
+ return delay;
}
static bool file_has_brlocks(files_struct *fsp)
@@ -1557,7 +1572,6 @@ struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
return new_fsp->lease;
}
-#if 0
static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
struct share_mode_lock *lck,
const struct smb2_lease *lease,
@@ -1696,88 +1710,151 @@ static bool is_same_lease(const files_struct *fsp,
&d->leases[e->lease_idx].client_guid,
&d->leases[e->lease_idx].lease_key);
}
-#endif
static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
struct files_struct *fsp,
struct share_mode_lock *lck,
- int oplock_request)
+ int oplock_request,
+ struct smb2_lease *lease)
{
- bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
- lp_level2_oplocks(SNUM(fsp->conn));
- bool got_level2_oplock, got_a_none_oplock;
+ struct share_mode_data *d = lck->data;
+ bool got_handle_lease = false;
+ bool got_oplock = false;
uint32_t i;
+ uint32_t granted;
+ uint32_t lease_idx = UINT32_MAX;
bool ok;
NTSTATUS status;
- /* Start by granting what the client asked for,
- but ensure no SAMBA_PRIVATE bits can be set. */
- fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
-
- if (fsp->oplock_type == NO_OPLOCK) {
- goto type_selected;
- }
-
if (oplock_request & INTERNAL_OPEN_ONLY) {
/* No oplocks on internal open. */
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
+ oplock_request = NO_OPLOCK;
+ DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+ fsp->oplock_type, fsp_str_dbg(fsp)));
+ }
+
+ if (oplock_request == LEASE_OPLOCK) {
+ if (lease == NULL) {
+ /*
+ * The SMB2 layer should have checked this
+ */
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ granted = lease->lease_state;
+
+ if (lp_kernel_oplocks(SNUM(fsp->conn))) {
+ DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
+ DEBUG(10, ("No read or write lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ if (granted == SMB2_LEASE_WRITE) {
+ DEBUG(10, ("pure write lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
+ DEBUG(10, ("write and handle lease requested\n"));
+ granted = SMB2_LEASE_NONE;
+ }
+ } else {
+ granted = map_oplock_to_lease_type(
+ oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
}
if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
fsp_str_dbg(fsp)));
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
+ granted &= ~SMB2_LEASE_READ;
}
- got_level2_oplock = false;
- got_a_none_oplock = false;
+ for (i=0; i<d->num_share_modes; i++) {
+ struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type;
- for (i=0; i<lck->data->num_share_modes; i++) {
- int op_type = lck->data->share_modes[i].op_type;
+ e_lease_type = get_lease_type(d, e);
- if (LEVEL_II_OPLOCK_TYPE(op_type)) {
- got_level2_oplock = true;
+ if ((granted & SMB2_LEASE_WRITE) &&
+ !is_same_lease(fsp, d, e, lease) &&
+ !share_mode_stale_pid(d, i)) {
+ /*
+ * Can grant only one writer
+ */
+ granted &= ~SMB2_LEASE_WRITE;
}
- if (op_type == NO_OPLOCK) {
- got_a_none_oplock = true;
+
+ if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+ !share_mode_stale_pid(d, i)) {
+ got_handle_lease = true;
+ }
+
+ if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+ !share_mode_stale_pid(d, i)) {
+ got_oplock = true;
}
}
- /*
- * Match what was requested (fsp->oplock_type) with
- * what was found in the existing share modes.
- */
+ if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) {
+ bool allow_level2 =
+ (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ lp_level2_oplocks(SNUM(fsp->conn));
- if (got_level2_oplock || got_a_none_oplock) {
- if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
- fsp->oplock_type = LEVEL_II_OPLOCK;
+ if (!allow_level2) {
+ granted = SMB2_LEASE_NONE;
}
}
- /*
- * Don't grant level2 to clients that don't want them
- * or if we've turned them off.
- */
- if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
- fsp->oplock_type = NO_OPLOCK;
- goto type_selected;
- }
+ if (oplock_request == LEASE_OPLOCK) {
+ if (got_oplock) {
+ granted &= ~SMB2_LEASE_HANDLE;
+ }
- type_selected:
- status = set_file_oplock(fsp);
- if (!NT_STATUS_IS_OK(status)) {
- /*
- * Could not get the kernel oplock
- */
- fsp->oplock_type = NO_OPLOCK;
+ fsp->oplock_type = LEASE_OPLOCK;
+
+ status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
+ granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+
+ }
+ *lease = fsp->lease->lease;
+ DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+ } else {
+ if (got_handle_lease) {
+ granted = SMB2_LEASE_NONE;
+ }
+
+ switch (granted) {
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+ fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+ fsp->oplock_type = EXCLUSIVE_OPLOCK;
+ break;
+ case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+ case SMB2_LEASE_READ:
+ fsp->oplock_type = LEVEL_II_OPLOCK;
+ break;
+ default:
+ fsp->oplock_type = NO_OPLOCK;
+ break;
+ }
+
+ status = set_file_oplock(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * Could not get the kernel oplock
+ */
+ fsp->oplock_type = NO_OPLOCK;
+ }
}
ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
req ? req->mid : 0,
fsp->oplock_type,
- UINT32_MAX);
+ lease_idx);
if (!ok) {
return NT_STATUS_NO_MEMORY;
}
@@ -2683,8 +2760,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
smb_panic("validate_oplock_types failed");
}
- if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
- schedule_defer_open(lck, fsp->file_id, request_time, req);
+ if (delay_for_oplock(fsp, 0, lease, lck, false,
+ create_disposition, first_open_attempt)) {
+ schedule_defer_open(lck, fsp->file_id, request_time,
+ req);
TALLOC_FREE(lck);
DEBUG(10, ("Sent oplock break request to kernel "
"oplock holder\n"));
@@ -2805,9 +2884,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
if ((req != NULL) &&
delay_for_oplock(
- fsp, oplock_request, lck,
+ fsp, oplock_request, lease, lck,
NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
- create_disposition)) {
+ create_disposition, first_open_attempt)) {
schedule_defer_open(lck, fsp->file_id, request_time, req);
TALLOC_FREE(lck);
fd_close(fsp);
@@ -3016,7 +3095,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
if (file_existed) {
/*
- * stat opens on existing files don't get oplocks.
+ * stat opens on existing files don't get oplocks or leases.
*
* Note that we check for stat open on the *open_access_mask*,
* i.e. the access mask we actually used to do the open,
@@ -3026,7 +3105,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* which adds FILE_WRITE_DATA to open_access_mask.
*/
if (is_stat_open(open_access_mask)) {
- oplock_request = NO_OPLOCK;
+ if (lease) {
+ lease->lease_state = SMB2_LEASE_NONE;
+ } else {
+ oplock_request = NO_OPLOCK;
+ }
}
}
@@ -3048,7 +3131,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
* Setup the oplock info in both the shared memory and
* file structs.
*/
- status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
+ status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
if (!NT_STATUS_IS_OK(status)) {
TALLOC_FREE(lck);
fd_close(fsp);
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 5ad881d..8f318f5 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -196,15 +196,10 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
for (i=0; i<d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
+ uint32_t e_lease_type = get_lease_type(d, e);
- if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+ if (e_lease_type & SMB2_LEASE_READ) {
num_read_oplocks += 1;
- continue;
- }
-
- if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
- num_read_oplocks += 1;
- continue;
}
}
@@ -772,14 +767,15 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
{
struct share_mode_entry msg;
files_struct *fsp;
- bool break_to_level2 = False;
bool use_kernel;
struct smbd_server_connection *sconn =
talloc_get_type_abort(private_data,
struct smbd_server_connection);
struct server_id self = messaging_server_id(sconn->msg_ctx);
struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+ uint16_t break_from;
uint16_t break_to;
+ bool break_needed = true;
if (data->data == NULL) {
DEBUG(0, ("Got NULL buffer\n"));
@@ -809,27 +805,118 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
return;
}
- if (fsp->sent_oplock_break != NO_BREAK_SENT) {
- /*
- * Nothing to do anymore
- */
+ break_from = fsp_lease_type(fsp);
+
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ if (fsp->sent_oplock_break != NO_BREAK_SENT) {
+ /*
+ * Nothing to do anymore
+ */
+ DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+ fsp->sent_oplock_break));
+ return;
+ }
+ }
+
+ if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
+ DEBUG(10, ("client_caps without level2 oplocks\n"));
+ break_to &= ~SMB2_LEASE_READ;
+ }
+
+ use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+ if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) {
+ DEBUG(10, ("Kernel oplocks don't allow level2\n"));
+ break_to &= ~SMB2_LEASE_READ;
+ }
+
+ if (!lp_level2_oplocks(SNUM(fsp->conn))) {
+ DEBUG(10, ("no level2 oplocks by config\n"));
+ break_to &= ~SMB2_LEASE_READ;
+ }
+
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ struct share_mode_lock *lck;
+ int idx;
+
+ lck = get_existing_share_mode_lock(
+ talloc_tos(), fsp->file_id);
+ if (lck == NULL) {
+ /*
+ * We hit a race here. Break messages are sent, and
+ * before we get to process this message, we have closed
+ * the file.
+ */
+ DEBUG(3, ("Did not find share_mode\n"));
+ return;
+ }
+
+ idx = find_share_mode_lease(
+ lck->data,
+ fsp_client_guid(fsp),
+ &fsp->lease->lease.lease_key);
+ if (idx != -1) {
+ struct share_mode_lease *l;
+ l = &lck->data->leases[idx];
+
+ break_from = l->current_state;
+ break_to &= l->current_state;
+
+ if (l->breaking) {
+ break_to &= l->breaking_to_required;
+ if (l->breaking_to_required != break_to) {
+ /*
+ * Note we don't increment the epoch
+ * here, which might be a bug in
+ * Windows too...
+ */
+ l->breaking_to_required = break_to;
+ lck->data->modified = true;
+ }
+ break_needed = false;
+ } else if (l->current_state == break_to) {
+ break_needed = false;
+ } else if (l->current_state == SMB2_LEASE_READ) {
+ l->current_state = SMB2_LEASE_NONE;
+ /* Need to increment the epoch */
+ l->epoch += 1;
+ lck->data->modified = true;
+ } else {
+ l->breaking = true;
+ l->breaking_to_required = break_to;
+ l->breaking_to_requested = break_to;
+ /* Need to increment the epoch */
+ l->epoch += 1;
+ lck->data->modified = true;
+ }
+
+ /* Ensure we're in sync with current lease state. */
+ fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+ }
+
+ TALLOC_FREE(lck);
+ }
+
+ if (!break_needed) {
+ DEBUG(10,("%s: skip break\n", __func__));
return;
}
- if (break_to == fsp->oplock_type) {
- DEBUG(3, ("Already downgraded oplock on %s: %s\n",
+ if ((break_from == SMB2_LEASE_NONE) && !break_needed) {
+ DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
file_id_string_tos(&fsp->file_id),
fsp_str_dbg(fsp)));
return;
}
- use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+ DEBUG(10, ("break_from=%u, break_to=%u\n",
+ (unsigned)break_from, (unsigned)break_to));
- if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
- (break_to != NO_OPLOCK) &&
- !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
- lp_level2_oplocks(SNUM(fsp->conn))) {
- break_to_level2 = True;
+ if ((break_from == break_to) && !break_needed) {
+ DEBUG(3, ("Already downgraded oplock to %u on %s: %s\n",
+ (unsigned)break_to,
+ file_id_string_tos(&fsp->file_id),
+ fsp_str_dbg(fsp)));
+ return;
}
/* Need to wait before sending a break
@@ -839,21 +926,31 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
}
if (sconn->using_smb2) {
- send_break_message_smb2(fsp, break_to_level2 ?
- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+ send_break_message_smb2(fsp, break_from, break_to);
} else {
- send_break_message_smb1(fsp, break_to_level2 ?
- OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+ send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
+ OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
}
- if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+ if ((break_from == SMB2_LEASE_READ) &&
+ (break_to == SMB2_LEASE_NONE)) {
/*
* This is an async break without a reply and thus no timeout
+ *
+ * leases are handled above.
*/
- remove_oplock(fsp);
+ if (fsp->oplock_type != LEASE_OPLOCK) {
+ remove_oplock(fsp);
+ }
+ return;
+ }
+ if (fsp->oplock_type == LEASE_OPLOCK) {
return;
}
- fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
+ fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
+ LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
add_oplock_timeout_handler(fsp);
}
@@ -908,7 +1005,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
}
if (sconn->using_smb2) {
- send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
+ send_break_message_smb2(fsp, 0, OPLOCKLEVEL_NONE);
} else {
send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
}
@@ -921,6 +1018,8 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
struct break_to_none_state {
struct smbd_server_connection *sconn;
struct file_id id;
+ struct smb2_lease_key lease_key;
+ struct GUID client_guid;
};
static void do_break_to_none(struct tevent_context *ctx,
struct tevent_immediate *im,
@@ -975,7 +1074,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
* anyway, so we postpone this into an immediate event.
*/
- state = talloc(sconn, struct break_to_none_state);
+ state = talloc_zero(sconn, struct break_to_none_state);
if (state == NULL) {
DEBUG(1, ("talloc failed\n"));
return;
@@ -983,6 +1082,14 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
state->sconn = sconn;
state->id = fsp->file_id;
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ state->client_guid = *fsp_client_guid(fsp);
+ state->lease_key = fsp->lease->lease.lease_key;
+ DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
+ state->lease_key.data[0],
+ state->lease_key.data[1]));
+ }
+
im = tevent_create_immediate(state);
if (im == NULL) {
DEBUG(1, ("tevent_create_immediate failed\n"));
@@ -1023,8 +1130,50 @@ static void do_break_to_none(struct tevent_context *ctx,
}
d = lck->data;
- DEBUG(10,("%s: num_share_modes = %d\n", __func__,
- lck->data->num_share_modes ));
+ /*
+ * Walk leases and oplocks separately: We have to send one break per
+ * lease. If we have multiple share_mode_entry having a common lease,
+ * we would break the lease twice if we don't walk the leases list
+ * separately.
+ */
+
+ for (i=0; i<d->num_leases; i++) {
+ struct share_mode_lease *l = &d->leases[i];
+ struct share_mode_entry *e;
+ uint32_t j;
+
+ if ((l->current_state & SMB2_LEASE_READ) == 0) {
+ continue;
+ }
+ if (smb2_lease_equal(&state->client_guid,
+ &state->lease_key,
+ &l->client_guid,
+ &l->lease_key)) {
+ DEBUG(10, ("Don't break our own lease\n"));
+ continue;
+ }
+
+ for (j=0; j<d->num_share_modes; j++) {
+ e = &d->share_modes[j];
+
+ if (!is_valid_share_mode_entry(e)) {
+ continue;
+ }
+ if (e->lease_idx == i) {
+ break;
+ }
+ }
+ if (j == d->num_share_modes) {
+ DEBUG(0, ("leases[%"PRIu32"] has no share mode\n",
+ i));
+ continue;
+ }
+
+ DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# "
+ "%"PRIu32"\n", i, j));
+
+ send_break_to_none(state->sconn->msg_ctx, e);
+ }
for(i = 0; i < d->num_share_modes; i++) {
struct share_mode_entry *e = &d->share_modes[i];
@@ -1032,6 +1181,12 @@ static void do_break_to_none(struct tevent_context *ctx,
if (!is_valid_share_mode_entry(e)) {
continue;
}
+ if (e->op_type == LEASE_OPLOCK) {
+ /*
+ * Took care of those in the loop above
+ */
+ continue;
+ }
/*
* As there could have been multiple writes waiting at the
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 4492456..126bf78 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -24,6 +24,10 @@
#include "smbd/globals.h"
#include "../libcli/smb/smb_common.h"
#include "../lib/util/tevent_ntstatus.h"
+#include "locking/leases_db.h"
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+ struct smbd_smb2_request *req);
static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@@ -45,6 +49,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
struct tevent_req *subreq;
status = smbd_smb2_request_verify_sizes(req, 0x18);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ /*
+ * Retry as a lease break
+ */
+ return smbd_smb2_request_process_lease_break(req);
+ }
if (!NT_STATUS_IS_OK(status)) {
return smbd_smb2_request_error(req, status);
}
@@ -222,16 +232,213 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
return NT_STATUS_OK;
}
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+ uint32_t in_lease_state);
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+ uint32_t *out_lease_state);
+
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+ struct smbd_smb2_request *req)
+{
+ NTSTATUS status;
+ const uint8_t *inbody;
+ struct smb2_lease_key in_lease_key;
+ uint32_t in_lease_state;
+ struct tevent_req *subreq;
+
+ status = smbd_smb2_request_verify_sizes(req, 0x24);
+ if (!NT_STATUS_IS_OK(status)) {
+ return smbd_smb2_request_error(req, status);
+ }
+
+ inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+ in_lease_key.data[0] = BVAL(inbody, 8);
+ in_lease_key.data[1] = BVAL(inbody, 16);
+ in_lease_state = IVAL(inbody, 24);
+
+ subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
+ in_lease_key, in_lease_state);
+ if (subreq == NULL) {
+ return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+ }
+ tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
+
+ return smbd_smb2_request_pending_queue(req, subreq, 500);
+}
+
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
+{
+ struct smbd_smb2_request *req = tevent_req_callback_data(
+ subreq, struct smbd_smb2_request);
+ const uint8_t *inbody;
+ struct smb2_lease_key in_lease_key;
+ uint32_t out_lease_state = 0;
+ DATA_BLOB outbody;
+ NTSTATUS status;
+ NTSTATUS error; /* transport error */
+
+ status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ error = smbd_smb2_request_error(req, status);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+ return;
+ }
+
+ inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+ in_lease_key.data[0] = BVAL(inbody, 8);
+ in_lease_key.data[1] = BVAL(inbody, 16);
+
+ outbody = smbd_smb2_generate_outbody(req, 0x24);
+ if (outbody.data == NULL) {
+ error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+ return;
+ }
+
+ SSVAL(outbody.data, 0x00, 0x24); /* struct size */
+ SSVAL(outbody.data, 0x02, 0); /* reserved */
+ SIVAL(outbody.data, 0x04, 0); /* flags, must be 0 */
+ SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
+ SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
+ SIVAL(outbody.data, 0x18, out_lease_state);
+ SBVAL(outbody.data, 0x1c, 0); /* leaseduration, must be 0 */
+
+ error = smbd_smb2_request_done(req, outbody, NULL);
+ if (!NT_STATUS_IS_OK(error)) {
+ smbd_server_connection_terminate(req->xconn,
+ nt_errstr(error));
+ return;
+ }
+}
+
+struct smbd_smb2_lease_break_state {
+ uint32_t lease_state;
+};
+
+struct lease_lookup_state {
+ TALLOC_CTX *mem_ctx;
+ /* Return parameters. */
+ uint32_t num_file_ids;
+ struct file_id *ids;
+ NTSTATUS status;
+};
+
+static void lease_parser(
+ uint32_t num_file_ids,
+ struct file_id *ids, const char *filename, const char *stream_name,
+ void *private_data)
+{
+ struct lease_lookup_state *lls =
+ (struct lease_lookup_state *)private_data;
+
+ lls->status = NT_STATUS_OK;
+ lls->num_file_ids = num_file_ids;
+ lls->ids = talloc_memdup(lls->mem_ctx,
+ ids,
+ num_file_ids * sizeof(struct file_id));
+ if (lls->ids == NULL) {
+ lls->status = NT_STATUS_NO_MEMORY;
+ }
+}
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+ TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+ struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+ uint32_t in_lease_state)
+{
+ struct tevent_req *req;
+ struct smbd_smb2_lease_break_state *state;
+ struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct smbd_smb2_lease_break_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->lease_state = in_lease_state;
+
+ /* Find any file ids with this lease key. */
+ status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
+ &in_lease_key,
+ lease_parser,
+ &lls);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ DEBUG(10, ("No record for lease key found\n"));
+ }
+ } else if (!NT_STATUS_IS_OK(lls.status)) {
+ status = lls.status;
+ } else if (lls.num_file_ids == 0) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ status = downgrade_lease(smb2_req->xconn,
+ lls.num_file_ids,
+ lls.ids,
+ &in_lease_key,
+ in_lease_state);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+ if (tevent_req_nterror(req, status)) {
+ DEBUG(10, ("downgrade_lease returned %s\n",
+ nt_errstr(status)));
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+ uint32_t *out_lease_state)
+{
+ struct smbd_smb2_lease_break_state *state = tevent_req_data(
+ req, struct smbd_smb2_lease_break_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *out_lease_state = state->lease_state;
+ return NT_STATUS_OK;
+}
+
/*********************************************************
Create and send an asynchronous
SMB2 OPLOCK_BREAK_NOTIFICATION.
*********************************************************/
-void send_break_message_smb2(files_struct *fsp, int level)
+void send_break_message_smb2(files_struct *fsp,
+ uint32_t break_from,
+ uint32_t break_to)
{
- uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ?
- SMB2_OPLOCK_LEVEL_II :
- SMB2_OPLOCK_LEVEL_NONE;
NTSTATUS status;
struct smbXsrv_connection *xconn = NULL;
struct smbXsrv_session *session = NULL;
@@ -257,7 +464,7 @@ void send_break_message_smb2(files_struct *fsp, int level)
"for file %s, %s, smb2 level %u session %llu not found\n",
fsp_str_dbg(fsp),
fsp_fnum_dbg(fsp),
- (unsigned int)smb2_oplock_level,
+ (unsigned int)break_to,
(unsigned long long)fsp->vuid));
return;
}
@@ -266,13 +473,35 @@ void send_break_message_smb2(files_struct *fsp, int level)
"for file %s, %s, smb2 level %u\n",
fsp_str_dbg(fsp),
fsp_fnum_dbg(fsp),
- (unsigned int)smb2_oplock_level ));
+ (unsigned int)break_to ));
- status = smbd_smb2_send_oplock_break(xconn,
- session,
- fsp->conn->tcon,
- fsp->op,
- smb2_oplock_level);
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ uint32_t break_flags = 0;
+ uint16_t new_epoch;
+
+ if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
+ break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
+ }
+
+ if (fsp->lease->lease.lease_version > 1) {
+ new_epoch = fsp->lease->lease.lease_epoch;
+ } else {
+ new_epoch = 0;
+ }
+
+ status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
+ &fsp->lease->lease.lease_key,
+ break_from, break_to);
+ } else {
+ uint8_t smb2_oplock_level;
+ smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
+ SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
+ status = smbd_smb2_send_oplock_break(xconn,
+ session,
+ fsp->conn->tcon,
+ fsp->op,
+ smb2_oplock_level);
+ }
if (!NT_STATUS_IS_OK(status)) {
smbd_server_connection_terminate(xconn,
nt_errstr(status));
--
1.9.1
From 9b16f7c19ae1a8cc361ee8c85b2d531b0f65f9e4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 23/28] s3:smb2_create: support leases and pass them down to
the VFS layer.
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>
Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
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 e96e84a..e0eef43 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,
@@ -1144,6 +1201,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 5623e0e8125be0a699d4f7e15d60b461133e6223 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 24/28] s3:param: Add "smb2 leases" parameter. Default "false".
This is currently marked as experimental!
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
docs-xml/smbdotconf/locking/kerneloplocks.xml | 1 +
docs-xml/smbdotconf/locking/oplocks.xml | 1 +
docs-xml/smbdotconf/locking/smb2leases.xml | 28 +++++++++++++++++++++++++++
lib/param/param_table.c | 9 +++++++++
source3/param/loadparm.c | 1 +
5 files changed, 40 insertions(+)
create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml
diff --git a/docs-xml/smbdotconf/locking/kerneloplocks.xml b/docs-xml/smbdotconf/locking/kerneloplocks.xml
index 8e3bba5..d8fe223 100644
--- a/docs-xml/smbdotconf/locking/kerneloplocks.xml
+++ b/docs-xml/smbdotconf/locking/kerneloplocks.xml
@@ -25,5 +25,6 @@
<related>oplocks</related>
<related>level2 oplocks</related>
+<related>smb2 leases</related>
<value type="default">no</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/oplocks.xml b/docs-xml/smbdotconf/locking/oplocks.xml
index a56e921..a5e163b 100644
--- a/docs-xml/smbdotconf/locking/oplocks.xml
+++ b/docs-xml/smbdotconf/locking/oplocks.xml
@@ -25,5 +25,6 @@
<related>kernel oplocks</related>
<related>level2 oplocks</related>
+<related>smb2 leases</related>
<value type="default">yes</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
new file mode 100644
index 0000000..0a734ec
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,28 @@
+<samba:parameter name="smb2 leases"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>
+ This boolean option tells <command moreinfo="none">smbd</command> whether to
+ globally negotiate SMB2 leases on file open requests. Leasing is an SMB2-only
+ feature which allows clients to aggressively cache files locally above and
+ beyond the caching allowed by SMB1 oplocks. This (experimental) parameter is
+ set to off by default until the SMB2 leasing code is declared fully stable.
+ </para>
+
+ <para>
+ This is only available with <smbconfoption name="oplocks">yes</smbconfoption>
+ and <smbconfoption name="kernel oplocks">no</smbconfoption>.
+ </para>
+
+ <para>
+ The Samba implementation of leases is currently marked as experimental!
+ </para>
+</description>
+
+<related>oplocks</related>
+<related>kernel oplocks</related>
+<related>level2 oplocks</related>
+<value type="default">no</value>
+</samba:parameter>
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 5dbcda8..18b0628 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -3023,6 +3023,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 8494c3f..da50e3a 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -855,6 +855,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 c02b61500fd75915ab77c2e5b05286fd0650e806 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 25/28] s3:smb2_negprot: announce support for SMB2.1 leases.
We only do this with "smb2 leases = yes"
and the default values for "oplocks = yes"
and "kernel oplocks = no".
Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 6904972..9a1ca9c 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 17afe43582545571f02fbee84e1f0efd6d9a56e2 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 26/28] selftest:Samba3: use "smb2 leases = yes"
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
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 797179f..50c2642 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1057,6 +1057,7 @@ sub provision($$$$$$)
kernel oplocks = no
kernel change notify = no
+ smb2 leases = yes
syslog = no
printing = bsd
--
1.9.1
From 5e70c549df1436beb68b853ae8770e45472d0d08 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:45:59 +0100
Subject: [PATCH 27/28] s3:smbd: document the interaction between "smb2 leases"
and "write cache size"
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
docs-xml/smbdotconf/locking/smb2leases.xml | 3 +++
docs-xml/smbdotconf/tuning/writecachesize.xml | 3 +++
source3/smbd/fileio.c | 5 +++++
3 files changed, 11 insertions(+)
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
index 0a734ec..9241bc8 100644
--- a/docs-xml/smbdotconf/locking/smb2leases.xml
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -16,6 +16,8 @@
and <smbconfoption name="kernel oplocks">no</smbconfoption>.
</para>
+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
+
<para>
The Samba implementation of leases is currently marked as experimental!
</para>
@@ -24,5 +26,6 @@
<related>oplocks</related>
<related>kernel oplocks</related>
<related>level2 oplocks</related>
+<related>write cache size</related>
<value type="default">no</value>
</samba:parameter>
diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index 12965b4..d7e2cc5 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -21,8 +21,11 @@
<para>The integer parameter specifies the size of this cache
(per oplocked file) in bytes.</para>
+
+ <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
</description>
+<related>smb2 leases</related>
<value type="default">0</value>
<value type="example">262144<comment> for a 256k cache size per file</comment></value>
</samba:parameter>
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index 9f3cc1a..2fe0432 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 d18d0e70e15da56506b1ee4bcda87d27a8561d74 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:47:01 +0100
Subject: [PATCH 28/28] docs-xml: document the interaction between "write cache
size" and "aio read/write size"
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
docs-xml/smbdotconf/tuning/writecachesize.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index d7e2cc5..e2fa17f 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -25,6 +25,8 @@
<para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
</description>
+<related>aio read size</related>
+<related>aio write size</related>
<related>smb2 leases</related>
<value type="default">0</value>
<value type="example">262144<comment> for a 256k cache size per file</comment></value>
--
1.9.1
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20141204/3a1847ec/attachment.pgp>
More information about the samba-technical
mailing list