diff options
Diffstat (limited to 'source3/smbd/oplock.c')
-rw-r--r-- | source3/smbd/oplock.c | 54 |
1 files changed, 45 insertions, 9 deletions
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 23411294df..261d8fd670 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -104,7 +104,10 @@ void process_kernel_oplocks(struct messaging_context *msg_ctx, fd_set *pfds) bool set_file_oplock(files_struct *fsp, int oplock_type) { - if (koplocks && !koplocks->set_oplock(fsp, oplock_type)) { + if ((fsp->oplock_type != NO_OPLOCK) && + (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK) && + koplocks && + !koplocks->set_oplock(fsp, oplock_type)) { return False; } @@ -112,7 +115,7 @@ bool set_file_oplock(files_struct *fsp, int oplock_type) fsp->sent_oplock_break = NO_BREAK_SENT; if (oplock_type == LEVEL_II_OPLOCK) { level_II_oplocks_open++; - } else { + } else if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { exclusive_oplocks_open++; } @@ -145,10 +148,15 @@ void release_file_oplock(files_struct *fsp) SMB_ASSERT(exclusive_oplocks_open>=0); SMB_ASSERT(level_II_oplocks_open>=0); - - fsp->oplock_type = NO_OPLOCK; + + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* This doesn't matter for close. */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } else { + fsp->oplock_type = NO_OPLOCK; + } fsp->sent_oplock_break = NO_BREAK_SENT; - + flush_write_cache(fsp, OPLOCK_RELEASE_FLUSH); TALLOC_FREE(fsp->oplock_timeout); @@ -435,6 +443,11 @@ static void process_oplock_async_level2_break_message(struct messaging_context * /* Ensure we're really at level2 state. */ SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK); + DEBUG(10,("process_oplock_async_level2_break_message: sending break to " + "none message for fid %d, file %s\n", + fsp->fnum, + fsp->fsp_name)); + /* Now send a break to none message to our client. */ break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE); @@ -808,10 +821,33 @@ void release_level_2_oplocks_on_change(files_struct *fsp) share_mode_entry_to_message(msg, share_entry); - messaging_send_buf(smbd_messaging_context(), share_entry->pid, - MSG_SMB_ASYNC_LEVEL2_BREAK, - (uint8 *)msg, - MSG_SMB_SHARE_MODE_ENTRY_SIZE); + /* + * Deal with a race condition when breaking level2 + * oplocks. Don't send all the messages and release + * the lock, this allows someone else to come in and + * get a level2 lock before any of the messages are + * processed, and thus miss getting a break message. + * Ensure at least one entry (the one we're breaking) + * is processed immediately under the lock and becomes + * set as NO_OPLOCK to stop any waiter getting a level2. + * Bugid #5980. + */ + + if (procid_is_me(&share_entry->pid)) { + DATA_BLOB blob = data_blob_const(msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + process_oplock_async_level2_break_message(smbd_messaging_context(), + NULL, + MSG_SMB_ASYNC_LEVEL2_BREAK, + share_entry->pid, + &blob); + } else { + messaging_send_buf(smbd_messaging_context(), + share_entry->pid, + MSG_SMB_ASYNC_LEVEL2_BREAK, + (uint8 *)msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + } } /* We let the message receivers handle removing the oplock state |