From ae5c09884d0f87f4a25a7af0445f8532708828be Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 14 Nov 2014 12:03:16 -0800 Subject: [PATCH 1/2] s3: leases - correctly cope with a client not responding to a lease break request. Just downgrade to the to_break value anyway. Signed-off-by: Jeremy Allison --- source3/locking/locking.c | 2 +- source3/smbd/oplock.c | 60 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/source3/locking/locking.c b/source3/locking/locking.c index b3ab9ad..a559c5f 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -1015,7 +1015,7 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn, if (!l->breaking) { DEBUG(0, ("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_INVALID_OPLOCK_PROTOCOL; + return NT_STATUS_UNSUCCESSFUL; } /* diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 240ff59..5c0d592 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -510,6 +510,32 @@ static files_struct *initial_break_processing( return fsp; } +struct lease_timeout_state { + files_struct *fsp; + uint16_t break_to; +}; + +static void lease_timeout_handler(struct tevent_context *ctx, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + struct lease_timeout_state *lts = + (struct lease_timeout_state *)private_data; + + DEBUG(0, ("lease break failed for file %s -- allowing anyway\n", + fsp_str_dbg(lts->fsp))); + (void)downgrade_lease(lts->fsp->conn->sconn->client->connections, + lts->fsp->file_id, + <s->fsp->lease->lease, + lts->break_to); + /* + * Remove the timed event handler. + * Must do this last as lts is hung off it. + */ + TALLOC_FREE(lts->fsp->oplock_timeout); +} + static void oplock_timeout_handler(struct tevent_context *ctx, struct tevent_timer *te, struct timeval now, @@ -517,11 +543,6 @@ static void oplock_timeout_handler(struct tevent_context *ctx, { files_struct *fsp = (files_struct *)private_data; - if (fsp->oplock_type == LEASE_OPLOCK) { - //TODO - SMB_ASSERT(false); - } - SMB_ASSERT(fsp->sent_oplock_break != NO_BREAK_SENT); /* Remove the timed event handler. */ @@ -535,7 +556,7 @@ static void oplock_timeout_handler(struct tevent_context *ctx, Add a timeout handler waiting for the client reply. *******************************************************************/ -static void add_oplock_timeout_handler(files_struct *fsp) +static void add_oplock_timeout_handler(files_struct *fsp, uint16_t break_to) { struct smbd_server_connection *sconn = fsp->conn->sconn; struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops; @@ -554,10 +575,29 @@ static void add_oplock_timeout_handler(files_struct *fsp) "around\n")); } - fsp->oplock_timeout = - tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp, + if (fsp->oplock_type == LEASE_OPLOCK) { + struct lease_timeout_state *lts = talloc_zero(fsp, + struct lease_timeout_state); + if (lts == NULL) { + return; + } + lts->fsp = fsp; + lts->break_to = break_to; + fsp->oplock_timeout = + tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp, + timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0), + lease_timeout_handler, lts); + if (fsp->oplock_timeout == NULL) { + talloc_free(lts); + } else { + talloc_move(fsp->oplock_timeout, <s); + } + } else { + fsp->oplock_timeout = + tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp, timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0), oplock_timeout_handler, fsp); + } if (fsp->oplock_timeout == NULL) { DEBUG(0, ("Could not add oplock timeout handler\n")); @@ -792,7 +832,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, } DEBUG(0,("%s: add_oplock_timeout_handler \n", __func__)); - add_oplock_timeout_handler(fsp); + add_oplock_timeout_handler(fsp, break_to); } /******************************************************************* @@ -853,7 +893,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx, fsp->sent_oplock_break = BREAK_TO_NONE_SENT; - add_oplock_timeout_handler(fsp); + add_oplock_timeout_handler(fsp, OPLOCKLEVEL_NONE); } struct break_to_none_state { -- 2.1.0.rc2.206.gedb03e5 From b1f78b2e3d7c829d0d1ef4caf7443893059f3fdf Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 14 Nov 2014 10:24:40 -0800 Subject: [PATCH 2/2] s3: leases - torture test for timeout of responding to lease break request. Passes against W2K12. Signed-off-by: Jeremy Allison --- source4/torture/smb2/lease.c | 92 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index 24c6544..fa50949 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -2358,6 +2358,97 @@ static bool test_lease_v2_complex1(struct torture_context *tctx, return ret; } +static bool test_lease_timeout(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_create io; + struct smb2_lease ls1; + struct smb2_lease ls2; + struct smb2_handle h, hnew; + NTSTATUS status; + const char *fname = "lease_timeout.dat"; + bool ret = true; + struct smb2_lease_break_ack ack = {}; + struct smb2_request *req2 = NULL; + struct smb2_write w; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree, fname); + + /* Grab a RWH lease. */ + smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH")); + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RWH", true, LEASE1, 0); + h = io.out.file.handle; + + tree->session->transport->lease.handler = torture_lease_handler; + tree->session->transport->lease.private_data = tree; + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + /* + * Just don't ack the lease break. + */ + ZERO_STRUCT(break_info); + break_info.lease_skip_ack = true; + + /* Break with a RWH request. */ + smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH")); + req2 = smb2_create_send(tree, &io); + torture_assert(tctx, req2 != NULL, "smb2_create_send"); + torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending"); + + CHECK_BREAK_INFO("RWH", "RH", LEASE1); + + /* Copy the break request. */ + ack.in.lease.lease_key = + break_info.lease_break.current_lease.lease_key; + ack.in.lease.lease_state = + break_info.lease_break.new_lease_state; + + /* Now wait for the timeout and get the reply. */ + status = smb2_create_recv(req2, tctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io, "RH", true, LEASE2, 0); + hnew = io.out.file.handle; + + /* Ack the break after the timeout... */ + status = smb2_lease_break_ack(tree, &ack); + CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL); + + /* Write on the original handle and make sure it's still valid. */ + ZERO_STRUCT(break_info); + ZERO_STRUCT(w); + w.in.file.handle = h; + w.in.offset = 0; + w.in.data = data_blob_talloc(mem_ctx, NULL, 4096); + memset(w.in.data.data, 'o', w.in.data.length); + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_BREAK_INFO("RH", "", LEASE2); + + done: + smb2_util_close(tree, h); + smb2_util_close(tree, hnew); + + smb2_util_unlink(tree, fname); + + talloc_free(mem_ctx); + + return ret; +} + + struct torture_suite *torture_smb2_lease_init(void) { struct torture_suite *suite = @@ -2384,6 +2475,7 @@ struct torture_suite *torture_smb2_lease_init(void) torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2); torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3); torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1); + torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 2.1.0.rc2.206.gedb03e5