From 20669d4a75386eef4fdcea07fb99812c4e09de13 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 26 Sep 2013 16:15:31 -0700 Subject: smbd: Fix raw.batch.exclusive[59] The level we have to break to depend on the breakers create_disposition: If we overwrite, we have to break to none. This patch overloads the "op_type" field in the break message we send across to the smbd holding the oplock with the oplock level we want to break to. Because it depends on the create_disposition in the breaking open, only the breaker can make that decision. We might want to use a different mechanism for this in the future, but for now using the op_type field seems acceptable to me. Signed-off-by: Volker Lendecke Reviewed-by: Stefan Metzmacher --- source3/smbd/open.c | 29 +++++++++++++++++++++++------ source3/smbd/oplock.c | 10 ++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) (limited to 'source3') diff --git a/source3/smbd/open.c b/source3/smbd/open.c index a9147f80b8..494145397d 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1168,7 +1168,8 @@ static NTSTATUS open_mode_check(connection_struct *conn, */ static NTSTATUS send_break_message(struct messaging_context *msg_ctx, - const struct share_mode_entry *exclusive) + const struct share_mode_entry *exclusive, + uint16_t break_to) { NTSTATUS status; char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; @@ -1179,6 +1180,9 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx, /* Create the message. */ share_mode_entry_to_message(msg, exclusive); + /* Overload entry->op_type */ + SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to); + status = messaging_send_buf(msg_ctx, exclusive->pid, MSG_SMB_BREAK_REQUEST, (uint8 *)msg, sizeof(msg)); @@ -1292,12 +1296,14 @@ static bool validate_oplock_types(struct share_mode_lock *lck) static bool delay_for_oplock(files_struct *fsp, int oplock_request, struct share_mode_lock *lck, - bool have_sharing_violation) + bool have_sharing_violation, + uint32_t create_disposition) { 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; @@ -1342,11 +1348,21 @@ static bool delay_for_oplock(files_struct *fsp, return false; } + switch (create_disposition) { + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + break_to = NO_OPLOCK; + break; + default: + break_to = LEVEL_II_OPLOCK; + break; + } + if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) { if (share_mode_stale_pid(d, 0)) { return false; } - send_break_message(fsp->conn->sconn->msg_ctx, entry); + send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to); return true; } if (have_sharing_violation) { @@ -1366,7 +1382,7 @@ static bool delay_for_oplock(files_struct *fsp, return false; } - send_break_message(fsp->conn->sconn->msg_ctx, entry); + send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to); return true; } @@ -2343,7 +2359,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, smb_panic("validate_oplock_types failed"); } - if (delay_for_oplock(fsp, 0, lck, false)) { + if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); DEBUG(10, ("Sent oplock break request to kernel " @@ -2455,7 +2471,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, if ((req != NULL) && delay_for_oplock( fsp, oplock_request, lck, - NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION))) { + NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION), + create_disposition)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); fd_close(fsp); diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index e2880c5de9..f1b89b4650 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -510,6 +510,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, 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_to; if (data->data == NULL) { DEBUG(0, ("Got NULL buffer\n")); @@ -523,9 +524,10 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, /* De-linearize incoming message. */ message_to_share_mode_entry(&msg, (char *)data->data); + break_to = msg.op_type; - DEBUG(10, ("Got oplock break message from pid %s: %s/%llu\n", - server_id_str(talloc_tos(), &src), + DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n", + (unsigned)break_to, server_id_str(talloc_tos(), &src), file_id_string_tos(&msg.id), (unsigned long long)msg.share_file_id)); @@ -545,8 +547,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, return; } - if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) && - !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + if (break_to == fsp->oplock_type) { DEBUG(3, ("Already downgraded oplock on %s: %s\n", file_id_string_tos(&fsp->file_id), fsp_str_dbg(fsp))); @@ -556,6 +557,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx, use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks; 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; -- cgit