r6225: get rid of warnings from my compiler about nested externs
[samba.git] / source3 / smbd / oplock.c
index 8525687793739cb1bd42bed9a63a7dac15207866..858092407f85daef129b83d033b74db30456bebf 100644 (file)
@@ -31,6 +31,9 @@ static int32 level_II_oplocks_open = 0;
 BOOL global_client_failed_oplock_break = False;
 BOOL global_oplock_break = False;
 
+extern struct timeval smb_last_time;
+extern uint32 global_client_caps;
+extern struct current_user current_user;
 extern int smb_read_error;
 
 static struct kernel_oplocks *koplocks;
@@ -388,6 +391,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n",
                                (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id));
                        break;
 
+               case RETRY_DEFERRED_OPEN_CMD:
+
+                       /* Request to retry and open that would return SHARING_VIOLATION. */
+                       if (msg_len != DEFERRED_OPEN_MSG_LEN) {
+                               DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n",
+                                       (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN));
+                               return False;
+                       }
+                       {
+                               uint16 mid;
+
+                               memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid));
+                               memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode));
+                               memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev));
+                               memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid));
+
+                               DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \
+pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n",
+                                       (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid));
+
+                               schedule_sharing_violation_open_smb_message(mid);
+                       }
+                       return True;
+
                /* 
                 * Keep this as a debug case - eventually we can remove it.
                 */
@@ -494,8 +521,6 @@ static void prepare_break_message(char *outbuf, files_struct *fsp, BOOL level2)
 
 static void wait_before_sending_break(BOOL local_request)
 {
-       extern struct timeval smb_last_time;
-
        if(local_request) {
                struct timeval cur_tv;
                long wait_left = (long)lp_oplock_break_wait_time();
@@ -574,13 +599,12 @@ static files_struct *initial_break_processing(SMB_DEV_T dev, SMB_INO_T inode, un
 
 /****************************************************************************
  Process a level II oplock break directly.
+ We must call this function with the share mode entry locked.
 ****************************************************************************/
 
-BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
+static BOOL oplock_break_level2(files_struct *fsp, BOOL local_request)
 {
-       extern uint32 global_client_caps;
        char outbuf[128];
-       BOOL got_lock = False;
        SMB_DEV_T dev = fsp->dev;
        SMB_INO_T inode = fsp->inode;
 
@@ -592,6 +616,8 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
         */
 
        if (global_client_caps & CAP_LEVEL_II_OPLOCKS) {
+               BOOL sign_state;
+
                /*
                 * If we are sending an oplock break due to an SMB sent
                 * by our own client we ensure that we wait at leat
@@ -603,34 +629,31 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
                wait_before_sending_break(local_request);
 
                /* Prepare the SMBlockingX message. */
-
                prepare_break_message( outbuf, fsp, False);
+
+               /* Save the server smb signing state. */
+               sign_state = srv_oplock_set_signing(False);
+
                if (!send_smb(smbd_server_fd(), outbuf))
                        exit_server("oplock_break_level2: send_smb failed.");
+
+               /* Restore the sign state to what it was. */
+               srv_oplock_set_signing(sign_state);
        }
 
        /*
         * Now we must update the shared memory structure to tell
         * everyone else we no longer have a level II oplock on 
-        * this open file. If local_request is true then token is
-        * the existing lock on the shared memory area.
+        * this open file. We must call this function with the share mode
+        * entry locked so we can change the entry directly.
         */
 
-       if(!local_request && lock_share_entry_fsp(fsp) == False) {
-               DEBUG(0,("oplock_break_level2: unable to lock share entry for file %s\n", fsp->fsp_name ));
-       } else {
-               got_lock = True;
-       }
-
        if(remove_share_oplock(fsp)==False) {
                DEBUG(0,("oplock_break_level2: unable to remove level II oplock for file %s\n", fsp->fsp_name ));
        }
 
        release_file_oplock(fsp);
 
-       if (!local_request && got_lock)
-               unlock_share_entry_fsp(fsp);
-
        if(level_II_oplocks_open < 0) {
                DEBUG(0,("oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR\n",
                        level_II_oplocks_open));
@@ -648,18 +671,18 @@ BOOL oplock_break_level2(files_struct *fsp, BOOL local_request, int token)
 
 /****************************************************************************
  Process an oplock break directly.
+ This is always called with the share mode lock *NOT* held.
 ****************************************************************************/
 
 static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id, BOOL local_request)
 {
-       extern uint32 global_client_caps;
-       extern struct current_user current_user;
        char *inbuf = NULL;
        char *outbuf = NULL;
        files_struct *fsp = NULL;
        time_t start_time;
        BOOL shutdown_server = False;
        BOOL oplock_timeout = False;
+       BOOL sign_state;
        connection_struct *saved_user_conn;
        connection_struct *saved_fsp_conn;
        int saved_vuid;
@@ -675,8 +698,18 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
         * Deal with a level II oplock going break to none separately.
         */
 
-       if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type))
-               return oplock_break_level2(fsp, local_request, -1);
+       if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) {
+               BOOL ret;
+               /* We must always call oplock_break_level2() with
+                  the share mode entry locked. */
+               if (lock_share_entry_fsp(fsp) == False) {
+                       DEBUG(0,("oplock_break: unable to lock share entry for file %s\n", fsp->fsp_name ));
+                       return False;
+               }
+               ret = oplock_break_level2(fsp, local_request);
+               unlock_share_entry_fsp(fsp);
+               return ret;
+       }
 
        /* Mark the oplock break as sent - we don't want to send twice! */
        if (fsp->sent_oplock_break) {
@@ -707,12 +740,12 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
         * messages crossing on the wire.
         */
 
-       if((inbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+       if((inbuf = (char *)SMB_MALLOC(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
                DEBUG(0,("oplock_break: malloc fail for input buffer.\n"));
                return False;
        }
 
-       if((outbuf = (char *)malloc(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
+       if((outbuf = (char *)SMB_MALLOC(BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN))==NULL) {
                DEBUG(0,("oplock_break: malloc fail for output buffer.\n"));
                SAFE_FREE(inbuf);
                return False;
@@ -742,8 +775,16 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
        /* Remember if we just sent a break to level II on this file. */
        fsp->sent_oplock_break = using_levelII? LEVEL_II_BREAK_SENT:EXCLUSIVE_BREAK_SENT;
 
-       if (!send_smb(smbd_server_fd(), outbuf))
+       /* Save the server smb signing state. */
+       sign_state = srv_oplock_set_signing(False);
+
+       if (!send_smb(smbd_server_fd(), outbuf)) {
+               srv_oplock_set_signing(sign_state);
                exit_server("oplock_break: send_smb failed.");
+       }
+
+       /* Restore the sign state to what it was. */
+       srv_oplock_set_signing(sign_state);
 
        /* We need this in case a readraw crosses on the wire. */
        global_oplock_break = True;
@@ -791,6 +832,9 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
                        } else if (smb_read_error == READ_ERROR) {
                                DEBUG( 0, ("oplock_break: receive_smb error (%s)\n", strerror(errno)) );
                                shutdown_server = True;
+                       } else if (smb_read_error == READ_BAD_SIG) {
+                               DEBUG( 0, ("oplock_break: bad signature from client\n" ));
+                               shutdown_server = True;
                        } else if (smb_read_error == READ_TIMEOUT) {
                                DEBUG( 0, ( "oplock_break: receive_smb timed out after %d seconds.\n", OPLOCK_BREAK_TIMEOUT ) );
                                oplock_timeout = True;
@@ -888,6 +932,9 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
                abort();
        }
 
+       /* We know we have no saved errors here. */
+       set_saved_error_triple(0, 0, NT_STATUS_OK);
+
        if( DEBUGLVL( 3 ) ) {
                dbgtext( "oplock_break: returning success for " );
                dbgtext( "dev = %x, inode = %.0f, file_id = %lu\n", (unsigned int)dev, (double)inode, file_id );
@@ -898,11 +945,12 @@ static BOOL oplock_break(SMB_DEV_T dev, SMB_INO_T inode, unsigned long file_id,
 }
 
 /****************************************************************************
-Send an oplock break message to another smbd process. If the oplock is held 
-by the local smbd then call the oplock break function directly.
+ Send an oplock break message to another smbd process. If the oplock is held 
+ by the local smbd then call the oplock break function directly.
+ This function is called with no share locks held.
 ****************************************************************************/
 
-BOOL request_oplock_break(share_mode_entry *share_entry, BOOL async)
+BOOL request_oplock_break(share_mode_entry *share_entry)
 {
        char op_break_msg[OPLOCK_BREAK_MSG_LEN];
        struct sockaddr_in addr_out;
@@ -943,7 +991,7 @@ dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
        /* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */
 
        if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
-               break_cmd_type = async ? ASYNC_LEVEL_II_OPLOCK_BREAK_CMD : LEVEL_II_OPLOCK_BREAK_CMD;
+               break_cmd_type = LEVEL_II_OPLOCK_BREAK_CMD;
        } else {
                break_cmd_type = OPLOCK_BREAK_CMD;
        }
@@ -961,7 +1009,7 @@ dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
        addr_out.sin_family = AF_INET;
    
        if( DEBUGLVL( 3 ) ) {
-               dbgtext( "request_oplock_break: sending a %s oplock break message to ", async ? "asynchronous" : "synchronous" );
+               dbgtext( "request_oplock_break: sending a synchronous oplock break message to " );
                dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port );
                dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
             (unsigned int)dev, (double)inode, file_id );
@@ -980,16 +1028,6 @@ dev = %x, inode = %.0f, file_id = %lu and no fsp found !\n",
                return False;
        }
 
-       /*
-        * If we just sent a message to a level II oplock share entry in async mode then
-        * we are done and may return.
-        */
-
-       if (LEVEL_II_OPLOCK_TYPE(share_entry->op_type) && async) {
-               DEBUG(3,("request_oplock_break: sent async break message to level II entry.\n"));
-               return True;
-       }
-
        /*
         * Now we must await the oplock broken message coming back
         * from the target smbd process. Timeout if it fails to
@@ -1102,6 +1140,57 @@ BOOL attempt_close_oplocked_file(files_struct *fsp)
        return False;
 }
 
+/****************************************************************************
+ Send an asynchronous oplock break message to another smbd process.
+****************************************************************************/
+
+static BOOL request_remote_level2_async_oplock_break(share_mode_entry *share_entry)
+{
+       char op_break_msg[OPLOCK_BREAK_MSG_LEN];
+       struct sockaddr_in addr_out;
+       pid_t pid = sys_getpid();
+       SMB_DEV_T dev = share_entry->dev;
+       SMB_INO_T inode = share_entry->inode;
+       unsigned long file_id = share_entry->share_file_id;
+
+       /* We need to send a ASYNC_LEVEL_II_OPLOCK_BREAK_CMD message to the port in the share mode entry. */
+
+       SSVAL(op_break_msg,OPBRK_MESSAGE_CMD_OFFSET,ASYNC_LEVEL_II_OPLOCK_BREAK_CMD);
+       memcpy(op_break_msg+OPLOCK_BREAK_PID_OFFSET,(char *)&pid,sizeof(pid));
+       memcpy(op_break_msg+OPLOCK_BREAK_DEV_OFFSET,(char *)&dev,sizeof(dev));
+       memcpy(op_break_msg+OPLOCK_BREAK_INODE_OFFSET,(char *)&inode,sizeof(inode));
+       memcpy(op_break_msg+OPLOCK_BREAK_FILEID_OFFSET,(char *)&file_id,sizeof(file_id));
+
+       /* Set the address and port. */
+       memset((char *)&addr_out,'\0',sizeof(addr_out));
+       addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       addr_out.sin_port = htons( share_entry->op_port );
+       addr_out.sin_family = AF_INET;
+   
+       if( DEBUGLVL( 3 ) ) {
+               dbgtext( "request_remote_level2_async_oplock_break: sending an asynchronous oplock break message to ");
+               dbgtext( "pid %d on port %d ", (int)share_entry->pid, share_entry->op_port );
+               dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+            (unsigned int)dev, (double)inode, file_id );
+       }
+
+       if(sys_sendto(oplock_sock,op_break_msg,OPLOCK_BREAK_MSG_LEN,0,
+                       (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+               if( DEBUGLVL( 0 ) ) {
+                       dbgtext( "request_remote_level2_async_oplock_break: failed when sending a oplock " );
+                       dbgtext( "break message to pid %d ", (int)share_entry->pid );
+                       dbgtext( "on port %d ", share_entry->op_port );
+                       dbgtext( "for dev = %x, inode = %.0f, file_id = %lu\n",
+                               (unsigned int)dev, (double)inode, file_id );
+                       dbgtext( "Error was %s\n", strerror(errno) );
+               }
+               return False;
+       }
+
+       DEBUG(3,("request_remote_level2_async_oplock_break: sent async break message to level II entry.\n"));
+       return True;
+}
+
 /****************************************************************************
  This function is called on any file modification or lock request. If a file
  is level 2 oplocked then it must tell all other level 2 holders to break to none.
@@ -1111,7 +1200,6 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
 {
        share_mode_entry *share_list = NULL;
        pid_t pid = sys_getpid();
-       int token = -1;
        int num_share_modes = 0;
        int i;
 
@@ -1178,7 +1266,7 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
 
                        DEBUG(10,("release_level_2_oplocks_on_change: breaking our own oplock.\n"));
 
-                       oplock_break_level2(new_fsp, True, token);
+                       oplock_break_level2(new_fsp, True);
 
                } else {
 
@@ -1188,7 +1276,7 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
                         */
 
                        DEBUG(10,("release_level_2_oplocks_on_change: breaking remote oplock (async).\n"));
-                       request_oplock_break(share_entry, True);
+                       request_remote_level2_async_oplock_break(share_entry);
                }
        }
 
@@ -1203,7 +1291,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp)
 }
 
 /****************************************************************************
-setup oplocks for this process
+ Send a 'retry your open' message to a process with a deferred open entry.
+****************************************************************************/
+
+BOOL send_deferred_open_retry_message(deferred_open_entry *entry)
+{
+       char de_msg[DEFERRED_OPEN_MSG_LEN];
+       struct sockaddr_in addr_out;
+       pid_t pid = sys_getpid();
+
+       memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN);
+       SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD);
+       memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid));
+       memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev));
+       memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode));
+       memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid));
+
+       /* Set the address and port. */
+       memset((char *)&addr_out,'\0',sizeof(addr_out));
+       addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+       addr_out.sin_port = htons( entry->port );
+       addr_out.sin_family = AF_INET;
+   
+       if( DEBUGLVL( 3 ) ) {
+               dbgtext( "send_deferred_open_retry_message: sending a message to ");
+               dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+               dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+                       (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+       }
+
+       if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0,
+                       (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) {
+               if( DEBUGLVL( 0 ) ) {
+                       dbgtext( "send_deferred_open_retry_message: failed sending a message to ");
+                       dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port );
+                       dbgtext( "for dev = %x, inode = %.0f, mid = %u\n",
+                               (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid );
+                       dbgtext( "Error was %s\n", strerror(errno) );
+               }
+               return False;
+       }
+       return True;
+}
+
+/****************************************************************************
+ Setup oplocks for this process.
 ****************************************************************************/
 
 BOOL init_oplocks(void)