summaryrefslogtreecommitdiff
path: root/source3/smbd/oplock.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/oplock.c')
-rw-r--r--source3/smbd/oplock.c54
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