diff options
author | Jeremy Allison <jra@samba.org> | 2008-12-17 17:23:13 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2008-12-17 17:23:13 -0800 |
commit | 1a5fc6d4bc847d9896500b704c9a8d5ac7faa7d2 (patch) | |
tree | 3a6d51a6dc4794711a63e7447b020ffd6ec51a43 | |
parent | 43d0a4813bf919e7fe6ab82f6920015d1a57f7bc (diff) | |
download | samba-1a5fc6d4bc847d9896500b704c9a8d5ac7faa7d2.tar.gz samba-1a5fc6d4bc847d9896500b704c9a8d5ac7faa7d2.tar.bz2 samba-1a5fc6d4bc847d9896500b704c9a8d5ac7faa7d2.zip |
Fix bug #5979 - Level 2 oplocks being granted improperly,
Jeremy.
-rw-r--r-- | source3/locking/locking.c | 11 | ||||
-rw-r--r-- | source3/smbd/open.c | 161 | ||||
-rw-r--r-- | source3/smbd/oplock.c | 23 |
3 files changed, 124 insertions, 71 deletions
diff --git a/source3/locking/locking.c b/source3/locking/locking.c index a70f9d20fe..b342fa9b1e 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -1186,7 +1186,16 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) } e->op_mid = 0; - e->op_type = NO_OPLOCK; + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + /* + * Going from exclusive or batch, + * we always go through FAKE_LEVEL_II + * first. + */ + e->op_type = FAKE_LEVEL_II_OPLOCK; + } else { + e->op_type = NO_OPLOCK; + } lck->modified = True; return True; } diff --git a/source3/smbd/open.c b/source3/smbd/open.c index d22eda2bb5..f00361979d 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -742,12 +742,51 @@ static bool is_delete_request(files_struct *fsp) { } /* + * Send a break message to the oplock holder and delay the open for + * our client. + */ + +static NTSTATUS send_break_message(files_struct *fsp, + struct share_mode_entry *exclusive, + uint16 mid, + int oplock_request) +{ + NTSTATUS status; + char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; + + DEBUG(10, ("Sending break request to PID %s\n", + procid_str_static(&exclusive->pid))); + exclusive->op_mid = mid; + + /* Create the message. */ + share_mode_entry_to_message(msg, exclusive); + + /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We + don't want this set in the share mode struct pointed to by lck. */ + + if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) { + SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE); + } + + status = messaging_send_buf(smbd_messaging_context(), exclusive->pid, + MSG_SMB_BREAK_REQUEST, + (uint8 *)msg, + MSG_SMB_SHARE_MODE_ENTRY_SIZE); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("Could not send oplock break message: %s\n", + nt_errstr(status))); + } + + return status; +} + +/* * 1) No files open at all or internal open: Grant whatever the client wants. * * 2) Exclusive (or batch) oplock around: If the requested access is a delete * request, break if the oplock around is a batch oplock. If it's another * requested access type, break. - * + * * 3) Only level2 around: Grant level2 and do nothing else. */ @@ -757,20 +796,21 @@ static bool delay_for_oplocks(struct share_mode_lock *lck, int pass_number, int oplock_request) { + extern uint32 global_client_caps; int i; struct share_mode_entry *exclusive = NULL; - bool valid_entry = False; - bool delay_it = False; - bool have_level2 = False; - NTSTATUS status; - char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE]; + bool valid_entry = false; + bool have_level2 = false; + bool have_a_none_oplock = false; + bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) && + lp_level2_oplocks(SNUM(fsp->conn)); if (oplock_request & INTERNAL_OPEN_ONLY) { fsp->oplock_type = NO_OPLOCK; } if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) { - return False; + return false; } for (i=0; i<lck->num_share_modes; i++) { @@ -780,86 +820,80 @@ static bool delay_for_oplocks(struct share_mode_lock *lck, } /* At least one entry is not an invalid or deferred entry. */ - valid_entry = True; + valid_entry = true; if (pass_number == 1) { if (BATCH_OPLOCK_TYPE(lck->share_modes[i].op_type)) { - SMB_ASSERT(exclusive == NULL); + SMB_ASSERT(exclusive == NULL); exclusive = &lck->share_modes[i]; } } else { if (EXCLUSIVE_OPLOCK_TYPE(lck->share_modes[i].op_type)) { - SMB_ASSERT(exclusive == NULL); + SMB_ASSERT(exclusive == NULL); exclusive = &lck->share_modes[i]; } } - if (lck->share_modes[i].op_type == LEVEL_II_OPLOCK) { - SMB_ASSERT(exclusive == NULL); - have_level2 = True; + if (LEVEL_II_OPLOCK_TYPE(lck->share_modes[i].op_type)) { + SMB_ASSERT(exclusive == NULL); + have_level2 = true; } - } - if (!valid_entry) { - /* All entries are placeholders or deferred. - * Directly grant whatever the client wants. */ - if (fsp->oplock_type == NO_OPLOCK) { - /* Store a level2 oplock, but don't tell the client */ - fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + if (lck->share_modes[i].op_type == NO_OPLOCK) { + have_a_none_oplock = true; } - return False; } if (exclusive != NULL) { /* Found an exclusive oplock */ + bool delay_it = is_delete_request(fsp) ? + BATCH_OPLOCK_TYPE(exclusive->op_type) : true; SMB_ASSERT(!have_level2); - delay_it = is_delete_request(fsp) ? - BATCH_OPLOCK_TYPE(exclusive->op_type) : True; - } - - if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { - /* We can at most grant level2 as there are other - * level2 or NO_OPLOCK entries. */ - fsp->oplock_type = LEVEL_II_OPLOCK; + if (delay_it) { + send_break_message(fsp, exclusive, mid, oplock_request); + return true; + } } - if ((fsp->oplock_type == NO_OPLOCK) && have_level2) { - /* Store a level2 oplock, but don't tell the client */ - fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; - } + /* + * Match what was requested (fsp->oplock_type) with + * what was found in the existing share modes. + */ - if (!delay_it) { - return False; + if (!valid_entry) { + /* All entries are placeholders or deferred. + * Directly grant whatever the client wants. */ + if (fsp->oplock_type == NO_OPLOCK) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } + } else if (have_a_none_oplock) { + fsp->oplock_type = NO_OPLOCK; + } else if (have_level2) { + if (fsp->oplock_type == NO_OPLOCK || + fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) { + /* Store a level2 oplock, but don't tell the client */ + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; + } else { + fsp->oplock_type = LEVEL_II_OPLOCK; + } + } else { + /* This case can never happen. */ + SMB_ASSERT(1); } /* - * Send a break message to the oplock holder and delay the open for - * our client. + * Don't grant level2 to clients that don't want them + * or if we've turned them off. */ - - DEBUG(10, ("Sending break request to PID %s\n", - procid_str_static(&exclusive->pid))); - exclusive->op_mid = mid; - - /* Create the message. */ - share_mode_entry_to_message(msg, exclusive); - - /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We - don't want this set in the share mode struct pointed to by lck. */ - - if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) { - SSVAL(msg,6,exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE); + if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) { + fsp->oplock_type = FAKE_LEVEL_II_OPLOCK; } - status = messaging_send_buf(smbd_messaging_context(), exclusive->pid, - MSG_SMB_BREAK_REQUEST, - (uint8 *)msg, - MSG_SMB_SHARE_MODE_ENTRY_SIZE); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(3, ("Could not send oplock break message: %s\n", - nt_errstr(status))); - } + DEBUG(10,("delay_for_oplocks: oplock type 0x%x on file %s\n", + fsp->oplock_type, fsp->fsp_name)); - return True; + /* No delay. */ + return false; } bool request_timed_out(struct timeval request_time, @@ -1968,12 +2002,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, * file structs. */ - if ((fsp->oplock_type != NO_OPLOCK) && - (fsp->oplock_type != FAKE_LEVEL_II_OPLOCK)) { - if (!set_file_oplock(fsp, fsp->oplock_type)) { - /* Could not get the kernel oplock */ - fsp->oplock_type = NO_OPLOCK; - } + if (!set_file_oplock(fsp, fsp->oplock_type)) { + /* Could not get the kernel oplock */ + fsp->oplock_type = NO_OPLOCK; } if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED || info == FILE_WAS_SUPERSEDED) { diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 23411294df..c98d11426d 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); |