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.c193
1 files changed, 138 insertions, 55 deletions
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 234b62e8ae..86f5e1a47c 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -372,12 +372,129 @@ static void oplock_timeout_handler(struct timed_event *te,
{
files_struct *fsp = private_data;
- DEBUG(0, ("Oplock break failed -- replying anyway\n"));
+ DEBUG(0, ("Oplock break failed for file %s -- replying anyway\n", fsp->fsp_name));
global_client_failed_oplock_break = True;
remove_oplock(fsp);
reply_to_oplock_break_requests(fsp);
}
+/*******************************************************************
+ Add a timeout handler waiting for the client reply.
+*******************************************************************/
+
+static void add_oplock_timeout_handler(files_struct *fsp)
+{
+ if (fsp->oplock_timeout != NULL) {
+ DEBUG(0, ("Logic problem -- have an oplock event hanging "
+ "around\n"));
+ }
+
+ fsp->oplock_timeout =
+ add_timed_event(NULL,
+ timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
+ "oplock_timeout_handler",
+ oplock_timeout_handler, fsp);
+
+ if (fsp->oplock_timeout == NULL) {
+ DEBUG(0, ("Could not add oplock timeout handler\n"));
+ }
+}
+
+/*******************************************************************
+ This handles the case of a write triggering a break to none
+ message on a level2 oplock.
+ When we get this message we may be in any of three states :
+ NO_OPLOCK, LEVEL_II, FAKE_LEVEL2. We only send a message to
+ the client for LEVEL2.
+*******************************************************************/
+
+static void process_oplock_async_level2_break_message(int msg_type, struct process_id src,
+ void *buf, size_t len)
+{
+ struct share_mode_entry msg;
+ files_struct *fsp;
+ char *break_msg;
+ BOOL break_to_level2 = False;
+ BOOL sign_state;
+
+ if (buf == NULL) {
+ DEBUG(0, ("Got NULL buffer\n"));
+ return;
+ }
+
+ if (len != MSG_SMB_SHARE_MODE_ENTRY_SIZE) {
+ DEBUG(0, ("Got invalid msg len %d\n", (int)len));
+ return;
+ }
+
+ /* De-linearize incoming message. */
+ message_to_share_mode_entry(&msg, buf);
+
+ DEBUG(10, ("Got oplock async level 2 break message from pid %d: 0x%x/%.0f/%d\n",
+ (int)procid_to_pid(&src), (unsigned int)msg.dev, (double)msg.inode,
+ (int)msg.share_file_id));
+
+ fsp = initial_break_processing(msg.dev, msg.inode,
+ msg.share_file_id);
+
+ if (fsp == NULL) {
+ /* We hit a race here. Break messages are sent, and before we
+ * get to process this message, we have closed the file.
+ * No need to reply as this is an async message. */
+ DEBUG(3, ("process_oplock_async_level2_break_message: Did not find fsp, ignoring\n"));
+ return;
+ }
+
+ if (fsp->oplock_type == NO_OPLOCK) {
+ /* We already got a "break to none" message and we've handled it.
+ * just ignore. */
+ DEBUG(3, ("process_oplock_async_level2_break_message: already broken to none, ignoring.\n"));
+ return;
+ }
+
+ if (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
+ /* Don't tell the client, just downgrade. */
+ DEBUG(3, ("process_oplock_async_level2_break_message: downgrading fake level 2 oplock.\n"));
+ remove_oplock(fsp);
+ return;
+ }
+
+ /* Ensure we're really at level2 state. */
+ SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK);
+
+ /* Now send a break to none message to our client. */
+
+ break_msg = new_break_smb_message(NULL, fsp, OPLOCKLEVEL_NONE);
+ if (break_msg == NULL) {
+ exit_server("Could not talloc break_msg\n");
+ }
+
+ /* Need to wait before sending a break message if we sent ourselves this message. */
+ if (procid_to_pid(&src) == sys_getpid()) {
+ wait_before_sending_break();
+ }
+
+ /* Save the server smb signing state. */
+ sign_state = srv_oplock_set_signing(False);
+
+ show_msg(break_msg);
+ if (!send_smb(smbd_server_fd(), break_msg)) {
+ exit_server("oplock_break: send_smb failed.");
+ }
+
+ /* Restore the sign state to what it was. */
+ srv_oplock_set_signing(sign_state);
+
+ talloc_free(break_msg);
+
+ /* Async level2 request, don't send a reply, just remove the oplock. */
+ remove_oplock(fsp);
+}
+
+/*******************************************************************
+ This handles the generic oplock break message from another smbd.
+*******************************************************************/
+
static void process_oplock_break_message(int msg_type, struct process_id src,
void *buf, size_t len)
{
@@ -408,7 +525,7 @@ static void process_oplock_break_message(int msg_type, struct process_id src,
msg.share_file_id);
if (fsp == NULL) {
- /* We hit race here. Break messages are sent, and before we
+ /* a We hit race here. Break messages are sent, and before we
* get to process this message, we have closed the file. Reply
* with 'ok, oplock broken' */
DEBUG(3, ("Did not find fsp\n"));
@@ -447,8 +564,7 @@ static void process_oplock_break_message(int msg_type, struct process_id src,
return;
}
- if ((msg_type == MSG_SMB_BREAK_REQUEST) &&
- (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+ if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
!koplocks && /* NOTE: we force levelII off for kernel oplocks -
* this will change when it is supported */
lp_level2_oplocks(SNUM(fsp->conn))) {
@@ -461,7 +577,7 @@ static void process_oplock_break_message(int msg_type, struct process_id src,
exit_server("Could not talloc break_msg\n");
}
- /* Need to wait before sending a break message to a file of our own */
+ /* Need to wait before sending a break message if we sent ourselves this message. */
if (procid_to_pid(&src) == sys_getpid()) {
wait_before_sending_break();
}
@@ -479,34 +595,20 @@ static void process_oplock_break_message(int msg_type, struct process_id src,
talloc_free(break_msg);
- if (msg_type == MSG_SMB_BREAK_REQUEST) {
- fsp->sent_oplock_break = break_to_level2 ?
- LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
- } else {
- /* Async level2 request, don't send a reply */
- fsp->sent_oplock_break = ASYNC_LEVEL_II_BREAK_SENT;
- }
+ fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
msg.pid = src;
ADD_TO_ARRAY(NULL, struct share_mode_entry, msg,
&fsp->pending_break_messages,
&fsp->num_pending_break_messages);
- if (fsp->oplock_timeout != NULL) {
- DEBUG(0, ("Logic problem -- have an oplock event hanging "
- "around\n"));
- }
-
- fsp->oplock_timeout =
- add_timed_event(NULL,
- timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
- "oplock_timeout_handler",
- oplock_timeout_handler, fsp);
-
- if (fsp->oplock_timeout == NULL) {
- DEBUG(0, ("Could not add oplock timeout handler\n"));
- }
+ add_oplock_timeout_handler(fsp);
}
+/*******************************************************************
+ This handles the kernel oplock break message.
+*******************************************************************/
+
static void process_kernel_oplock_break(int msg_type, struct process_id src,
void *buf, size_t len)
{
@@ -570,6 +672,8 @@ static void process_kernel_oplock_break(int msg_type, struct process_id src,
talloc_free(break_msg);
fsp->sent_oplock_break = BREAK_TO_NONE_SENT;
+
+ add_oplock_timeout_handler(fsp);
}
void reply_to_oplock_break_requests(files_struct *fsp)
@@ -591,6 +695,7 @@ void reply_to_oplock_break_requests(files_struct *fsp)
SAFE_FREE(fsp->pending_break_messages);
fsp->num_pending_break_messages = 0;
if (fsp->oplock_timeout != NULL) {
+ /* Remove the timed event handler. */
talloc_free(fsp->oplock_timeout);
fsp->oplock_timeout = NULL;
}
@@ -679,29 +784,6 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
DEBUG(10,("release_level_2_oplocks_on_change: num_share_modes = %d\n",
lck->num_share_modes ));
- if (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK) {
- /* See if someone else has already downgraded us, then we
- don't have to do anything */
- for (i=0; i<lck->num_share_modes; i++) {
- struct share_mode_entry *e = &lck->share_modes[i];
-
- if (!is_valid_share_mode_entry(e)) {
- continue;
- }
-
- if ((e->op_type == NO_OPLOCK) &&
- (e->share_file_id == fsp->file_id) &&
- (e->dev == fsp->dev) &&
- (e->inode == fsp->inode) &&
- (procid_is_me(&e->pid))) {
- /* We're done */
- fsp->oplock_type = NO_OPLOCK;
- talloc_free(lck);
- return;
- }
- }
- }
-
for(i = 0; i < lck->num_share_modes; i++) {
struct share_mode_entry *share_entry = &lck->share_modes[i];
char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
@@ -714,7 +796,7 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
* As there could have been multiple writes waiting at the
* lock_share_entry gate we may not be the first to
* enter. Hence the state of the op_types in the share mode
- * entries may be partly NO_OPLOCK and partly LEVEL_II
+ * entries may be partly NO_OPLOCK and partly LEVEL_II or FAKE_LEVEL_II
* oplock. It will do no harm to re-send break messages to
* those smbd's that are still waiting their turn to remove
* their LEVEL_II state, and also no harm to ignore existing
@@ -725,8 +807,7 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
"share_entry[%i]->op_type == %d\n",
i, share_entry->op_type ));
- if ((share_entry->op_type == NO_OPLOCK) ||
- (share_entry->op_type == FAKE_LEVEL_II_OPLOCK)) {
+ if (share_entry->op_type == NO_OPLOCK) {
continue;
}
@@ -747,7 +828,9 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
unbecome_root();
}
- remove_all_share_oplocks(lck, fsp);
+ /* We let the message receivers handle removing the oplock state
+ in the share mode lock db. */
+
talloc_free(lck);
}
@@ -795,12 +878,12 @@ void message_to_share_mode_entry(struct share_mode_entry *e, char *msg)
BOOL init_oplocks(void)
{
- DEBUG(3,("open_oplock_ipc: opening loopback UDP socket.\n"));
+ DEBUG(3,("open_oplock_ipc: initializing messages.\n"));
message_register(MSG_SMB_BREAK_REQUEST,
process_oplock_break_message);
message_register(MSG_SMB_ASYNC_LEVEL2_BREAK,
- process_oplock_break_message);
+ process_oplock_async_level2_break_message);
message_register(MSG_SMB_BREAK_RESPONSE,
process_oplock_break_response);
message_register(MSG_SMB_KERNEL_BREAK,