r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[samba.git] / source3 / smbd / blocking.c
index f489a8e96b2bf41f0c7a951ff091ceba3e420787..ed1977e3bec9297a0c1da5550a64a4c47cc74afc 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
 
+extern int max_send;
+
 /****************************************************************************
  This is the structure to queue to implement blocking locks.
  notify. It consists of the requesting SMB and the expiry time.
@@ -37,6 +38,7 @@ typedef struct _blocking_lock_record {
        SMB_BIG_UINT offset;
        SMB_BIG_UINT count;
        uint32 lock_pid;
+       uint32 blocking_pid; /* PID that blocks us. */
        enum brl_flavour lock_flav;
        enum brl_type lock_type;
        char *inbuf;
@@ -49,6 +51,9 @@ static blocking_lock_record *blocking_lock_queue;
 /* dlink list we move cancelled lock records onto. */
 static blocking_lock_record *blocking_lock_cancelled_queue;
 
+/* The event that makes us process our blocking lock queue */
+static struct timed_event *brl_timeout;
+
 /****************************************************************************
  Destructor for the above structure.
 ****************************************************************************/
@@ -68,8 +73,78 @@ static BOOL in_chained_smb(void)
        return (chain_size != 0);
 }
 
-static void received_unlock_msg(int msg_type, struct process_id src,
-                               void *buf, size_t len);
+static void received_unlock_msg(struct messaging_context *msg,
+                               void *private_data,
+                               uint32_t msg_type,
+                               struct server_id server_id,
+                               DATA_BLOB *data);
+static void process_blocking_lock_queue(void);
+
+static void brl_timeout_fn(struct event_context *event_ctx,
+                          struct timed_event *te,
+                          const struct timeval *now,
+                          void *private_data)
+{
+       SMB_ASSERT(brl_timeout == te);
+       TALLOC_FREE(brl_timeout);
+
+       change_to_root_user();  /* TODO: Possibly run all timed events as
+                                * root */
+
+       process_blocking_lock_queue();
+}
+
+/****************************************************************************
+ After a change to blocking_lock_queue, recalculate the timed_event for the
+ next processing.
+****************************************************************************/
+
+static BOOL recalc_brl_timeout(void)
+{
+       blocking_lock_record *brl;
+       struct timeval next_timeout;
+
+       TALLOC_FREE(brl_timeout);
+
+       next_timeout = timeval_zero();  
+
+       for (brl = blocking_lock_queue; brl; brl = brl->next) {
+               if (timeval_is_zero(&brl->expire_time)) {
+                       /*
+                        * If we're blocked on pid 0xFFFFFFFF this is
+                        * a POSIX lock, so calculate a timeout of
+                        * 10 seconds into the future.
+                        */
+                        if (brl->blocking_pid == 0xFFFFFFFF) {
+                               struct timeval psx_to = timeval_current_ofs(10, 0);
+                               next_timeout = timeval_min(&next_timeout, &psx_to);
+                        }
+
+                       continue;
+               }
+
+               if (timeval_is_zero(&next_timeout)) {
+                       next_timeout = brl->expire_time;
+               }
+               else {
+                       next_timeout = timeval_min(&next_timeout,
+                                                  &brl->expire_time);
+               }
+       }
+
+       if (timeval_is_zero(&next_timeout)) {
+               return True;
+       }
+
+       if (!(brl_timeout = event_add_timed(smbd_event_context(), NULL,
+                                           next_timeout, "brl_timeout",
+                                           brl_timeout_fn, NULL))) {
+               return False;
+       }
+
+       return True;
+}
+
 
 /****************************************************************************
  Function to push a blocking lock request onto the lock queue.
@@ -83,7 +158,9 @@ BOOL push_blocking_lock_request( struct byte_range_lock *br_lck,
                uint32 lock_pid,
                enum brl_type lock_type,
                enum brl_flavour lock_flav,
-               SMB_BIG_UINT offset, SMB_BIG_UINT count)
+               SMB_BIG_UINT offset,
+               SMB_BIG_UINT count,
+               uint32 blocking_pid)
 {
        static BOOL set_lock_msg;
        blocking_lock_record *blr;
@@ -124,6 +201,7 @@ BOOL push_blocking_lock_request( struct byte_range_lock *br_lck,
        }
        blr->lock_num = lock_num;
        blr->lock_pid = lock_pid;
+       blr->blocking_pid = blocking_pid;
        blr->lock_flav = lock_flav;
        blr->lock_type = lock_type;
        blr->offset = offset;
@@ -132,14 +210,15 @@ BOOL push_blocking_lock_request( struct byte_range_lock *br_lck,
        blr->length = length;
 
        /* Add a pending lock record for this. */
-       status = brl_lock(br_lck,
+       status = brl_lock(smbd_messaging_context(), br_lck,
                        lock_pid,
                        procid_self(),
                        offset,
                        count,
                        lock_type == READ_LOCK ? PENDING_READ_LOCK : PENDING_WRITE_LOCK,
                        blr->lock_flav,
-                       lock_timeout ? True : False); /* blocking_lock. */
+                       lock_timeout ? True : False, /* blocking_lock. */
+                       NULL);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n"));
@@ -149,10 +228,12 @@ BOOL push_blocking_lock_request( struct byte_range_lock *br_lck,
        }
 
        DLIST_ADD_END(blocking_lock_queue, blr, blocking_lock_record *);
+       recalc_brl_timeout();
 
        /* Ensure we'll receive messages when this is unlocked. */
        if (!set_lock_msg) {
-               message_register(MSG_SMB_UNLOCK, received_unlock_msg);
+               messaging_register(smbd_messaging_context(), NULL,
+                                  MSG_SMB_UNLOCK, received_unlock_msg);
                set_lock_msg = True;
        }
 
@@ -172,13 +253,15 @@ BOOL push_blocking_lock_request( struct byte_range_lock *br_lck,
  Return a smd with a given size.
 *****************************************************************************/
 
-static void send_blocking_reply(char *outbuf, int outsize)
+static void send_blocking_reply(char *outbuf, int outsize, const char *inbuf)
 {
-       if(outsize > 4)
-               smb_setlen(outbuf,outsize - 4);
+       if(outsize > 4) {
+               smb_setlen(inbuf, outbuf,outsize - 4);
+       }
 
-       if (!send_smb(smbd_server_fd(),outbuf))
-               exit_server("send_blocking_reply: send_smb failed.");
+       if (!send_smb(smbd_server_fd(),outbuf)) {
+               exit_server_cleanly("send_blocking_reply: send_smb failed.");
+       }
 }
 
 /****************************************************************************
@@ -193,7 +276,7 @@ static void reply_lockingX_success(blocking_lock_record *blr)
        int outsize = 0;
 
        construct_reply_common(inbuf, outbuf);
-       set_message(outbuf,2,0,True);
+       set_message(inbuf,outbuf,2,0,True);
 
        /*
         * As this message is a lockingX call we must handle
@@ -207,7 +290,7 @@ static void reply_lockingX_success(blocking_lock_record *blr)
 
        outsize += chain_size;
 
-       send_blocking_reply(outbuf,outsize);
+       send_blocking_reply(outbuf,outsize,inbuf);
 }
 
 /****************************************************************************
@@ -242,7 +325,7 @@ static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS stat
 
        ERROR_NT(status);
        if (!send_smb(smbd_server_fd(),outbuf)) {
-               exit_server("generic_blocking_lock_error: send_smb failed.");
+               exit_server_cleanly("generic_blocking_lock_error: send_smb failed.");
        }
 }
 
@@ -288,7 +371,8 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status)
                 * request would never have been queued. JRA.
                 */
                
-               do_unlock(fsp,
+               do_unlock(smbd_messaging_context(),
+                       fsp,
                        lock_pid,
                        count,
                        offset,
@@ -320,7 +404,7 @@ static void blocking_lock_reply_error(blocking_lock_record *blr, NTSTATUS status
                        SCVAL(outbuf,smb_com,SMBtrans2);
                        ERROR_NT(status);
                        if (!send_smb(smbd_server_fd(),outbuf)) {
-                               exit_server("blocking_lock_reply_error: send_smb failed.");
+                               exit_server_cleanly("blocking_lock_reply_error: send_smb failed.");
                        }
                        break;
                }
@@ -368,7 +452,8 @@ static BOOL process_lockingX(blocking_lock_record *blr)
                 * request would never have been queued. JRA.
                 */
                errno = 0;
-               br_lck = do_lock(fsp,
+               br_lck = do_lock(smbd_messaging_context(),
+                               fsp,
                                lock_pid,
                                count,
                                offset, 
@@ -376,7 +461,8 @@ static BOOL process_lockingX(blocking_lock_record *blr)
                                        READ_LOCK : WRITE_LOCK),
                                WINDOWS_LOCK,
                                True,
-                               &status);
+                               &status,
+                               &blr->blocking_pid);
 
                TALLOC_FREE(br_lck);
 
@@ -425,19 +511,20 @@ Waiting....\n",
 
 static BOOL process_trans2(blocking_lock_record *blr)
 {
-       extern int max_send;
        char *inbuf = blr->inbuf;
        char *outbuf;
        char params[2];
        NTSTATUS status;
-       struct byte_range_lock *br_lck = do_lock(blr->fsp,
+       struct byte_range_lock *br_lck = do_lock(smbd_messaging_context(),
+                                               blr->fsp,
                                                blr->lock_pid,
                                                blr->count,
                                                blr->offset,
                                                blr->lock_type,
                                                blr->lock_flav,
                                                True,
-                                               &status);
+                                               &status,
+                                               &blr->blocking_pid);
        TALLOC_FREE(br_lck);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -459,7 +546,7 @@ static BOOL process_trans2(blocking_lock_record *blr)
        SCVAL(outbuf,smb_com,SMBtrans2);
        SSVAL(params,0,0);
        /* Fake up max_data_bytes here - we know it fits. */
-       send_trans2_replies(outbuf, max_send, params, 2, NULL, 0, 0xffff);
+       send_trans2_replies(inbuf, outbuf, max_send, params, 2, NULL, 0, 0xffff);
        return True;
 }
 
@@ -559,68 +646,45 @@ file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
 }
 
 /****************************************************************************
-  Set a flag as an unlock request affects one of our pending locks.
+ Is this mid a blocking lock request on the queue ?
 *****************************************************************************/
 
-static void received_unlock_msg(int msg_type, struct process_id src,
-                               void *buf, size_t len)
+BOOL blocking_lock_was_deferred(int mid)
 {
-       DEBUG(10,("received_unlock_msg\n"));
-       process_blocking_lock_queue();
+       blocking_lock_record *blr, *next = NULL;
+
+       for(blr = blocking_lock_queue; blr; blr = next) {
+               next = blr->next;
+               if(SVAL(blr->inbuf,smb_mid) == mid) {
+                       return True;
+               }
+       }
+       return False;
 }
 
 /****************************************************************************
- Return the number of milliseconds to the next blocking locks timeout, or default_timeout
+  Set a flag as an unlock request affects one of our pending locks.
 *****************************************************************************/
 
-unsigned int blocking_locks_timeout_ms(unsigned int default_timeout_ms)
+static void received_unlock_msg(struct messaging_context *msg,
+                               void *private_data,
+                               uint32_t msg_type,
+                               struct server_id server_id,
+                               DATA_BLOB *data)
 {
-       unsigned int timeout_ms = default_timeout_ms;
-       struct timeval tv_curr;
-       SMB_BIG_INT min_tv_dif_us = 0x7FFFFFFF; /* A large +ve number. */
-       blocking_lock_record *blr = blocking_lock_queue;
-
-       /* note that we avoid the GetTimeOfDay() syscall if there are no blocking locks */
-       if (!blr) {
-               return timeout_ms;
-       }
-
-       tv_curr = timeval_current();
-
-       for (; blr; blr = blr->next) {
-               SMB_BIG_INT tv_dif_us;
-
-               if (timeval_is_zero(&blr->expire_time)) {
-                       continue; /* Never timeout. */
-               }
-
-               tv_dif_us = usec_time_diff(&blr->expire_time, &tv_curr);
-               min_tv_dif_us = MIN(min_tv_dif_us, tv_dif_us);
-       }
-
-       if (min_tv_dif_us < 0) {
-               min_tv_dif_us = 0;
-       }
-
-       timeout_ms = (unsigned int)(min_tv_dif_us / (SMB_BIG_INT)1000);
-
-       if (timeout_ms < 1) {
-               timeout_ms = 1;
-       }
-
-       DEBUG(10,("blocking_locks_timeout_ms: returning %u\n", timeout_ms));
-
-       return timeout_ms;
+       DEBUG(10,("received_unlock_msg\n"));
+       process_blocking_lock_queue();
 }
 
 /****************************************************************************
  Process the blocking lock queue. Note that this is only called as root.
 *****************************************************************************/
 
-void process_blocking_lock_queue(void)
+static void process_blocking_lock_queue(void)
 {
        struct timeval tv_curr = timeval_current();
        blocking_lock_record *blr, *next = NULL;
+       BOOL recalc_timeout = False;
 
        /*
         * Go through the queue and see if we can get any of the locks.
@@ -672,6 +736,7 @@ void process_blocking_lock_queue(void)
                        blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
                        DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
+                       recalc_timeout = True;
                        continue;
                }
 
@@ -697,6 +762,7 @@ void process_blocking_lock_queue(void)
                        blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
                        DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
+                       recalc_timeout = True;
                        continue;
                }
 
@@ -721,6 +787,7 @@ void process_blocking_lock_queue(void)
                        blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
                        DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
+                       recalc_timeout = True;
                        change_to_root_user();
                        continue;
                }
@@ -746,9 +813,14 @@ void process_blocking_lock_queue(void)
 
                        DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
+                       recalc_timeout = True;
                }
                change_to_root_user();
        }
+
+       if (recalc_timeout) {
+               recalc_brl_timeout();
+       }
 }
 
 /****************************************************************************
@@ -757,21 +829,24 @@ void process_blocking_lock_queue(void)
 
 #define MSG_BLOCKING_LOCK_CANCEL_SIZE (sizeof(blocking_lock_record *) + sizeof(NTSTATUS))
 
-static void process_blocking_lock_cancel_message(int msg_type, struct process_id src,
-                                         void *buf, size_t len)
+static void process_blocking_lock_cancel_message(struct messaging_context *ctx,
+                                                void *private_data,
+                                                uint32_t msg_type,
+                                                struct server_id server_id,
+                                                DATA_BLOB *data)
 {
        NTSTATUS err;
-       const char *msg = (const char *)buf;
+       const char *msg = (const char *)data->data;
        blocking_lock_record *blr;
 
-       if (buf == NULL) {
-               smb_panic("process_blocking_lock_cancel_message: null msg\n");
+       if (data->data == NULL) {
+               smb_panic("process_blocking_lock_cancel_message: null msg");
        }
 
-       if (len != MSG_BLOCKING_LOCK_CANCEL_SIZE) {
+       if (data->length != MSG_BLOCKING_LOCK_CANCEL_SIZE) {
                DEBUG(0, ("process_blocking_lock_cancel_message: "
-                       "Got invalid msg len %d\n", (int)len));
-               smb_panic("process_blocking_lock_cancel_message: bad msg\n");
+                         "Got invalid msg len %d\n", (int)data->length));
+               smb_panic("process_blocking_lock_cancel_message: bad msg");
         }
 
        memcpy(&blr, msg, sizeof(blr));
@@ -803,8 +878,9 @@ BOOL blocking_lock_cancel(files_struct *fsp,
 
        if (!initialized) {
                /* Register our message. */
-               message_register(MSG_SMB_BLOCKING_LOCK_CANCEL,
-                               process_blocking_lock_cancel_message);
+               messaging_register(smbd_messaging_context(), NULL,
+                                  MSG_SMB_BLOCKING_LOCK_CANCEL,
+                                  process_blocking_lock_cancel_message);
 
                initialized = True;
        }
@@ -838,9 +914,9 @@ BOOL blocking_lock_cancel(files_struct *fsp,
        memcpy(msg, &blr, sizeof(blr));
        memcpy(&msg[sizeof(blr)], &err, sizeof(NTSTATUS));
 
-       message_send_pid(pid_to_procid(sys_getpid()),
-                       MSG_SMB_BLOCKING_LOCK_CANCEL,
-                       &msg, sizeof(msg), True);
+       messaging_send_buf(smbd_messaging_context(), procid_self(),
+                          MSG_SMB_BLOCKING_LOCK_CANCEL,
+                          (uint8 *)&msg, sizeof(msg));
 
        return True;
 }