r13198: Fix issues exposed by Jerry's testing on 64-bit Solaris
authorJeremy Allison <jra@samba.org>
Fri, 27 Jan 2006 23:30:30 +0000 (23:30 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 16:06:17 +0000 (11:06 -0500)
(I hope). Separate 3.0.21b patch sent to Jerry.
Jeremy.
(This used to be commit 837e7ea7e40cedc6b01e023445feb4a90c4bf8b9)

source3/include/smb.h
source3/locking/locking.c
source3/smbd/oplock.c

index f899a71dc6dedddeaceccd3984c041bb1af4f019..fba02565d8fecdddba13152f852776de5b471bb4 100644 (file)
@@ -549,7 +549,6 @@ struct current_user
 #define NO_BREAK_SENT 0
 #define BREAK_TO_NONE_SENT 1
 #define LEVEL_II_BREAK_SENT 2
-#define ASYNC_LEVEL_II_BREAK_SENT 3
 
 typedef struct {
        fstring smb_name; /* user name from the client */
index be1e83121b7377c470c73ed6f0d1c5f0a594f35b..e31ead30e4e920419c2fa320401d916f6bd65273 100644 (file)
@@ -988,28 +988,6 @@ BOOL downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
        return True;
 }
 
-
-/*******************************************************************
- We've just told all the smbd's that our level2 or fake level2 has been
- written to.
-********************************************************************/
-BOOL remove_all_share_oplocks(struct share_mode_lock *lck, files_struct *fsp)
-{
-       int i;
-       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) {
-                       continue;
-               }
-               e->op_type = NO_OPLOCK;
-               lck->modified = True;
-       }
-       return True;
-}
-
 /****************************************************************************
  Deal with the internal needs of setting the delete on close flag. Note that
  as the tdb locking is recursive, it is safe to call this from within 
index 234b62e8ae47713745de198cd5cede390166e350..86f5e1a47cf033b1977d4886d00cb4f7e11e7586 100644 (file)
@@ -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
+               /* 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,