r17098: Samba3 now cleanly passes Samba4 RAW-LOCK torture
[samba.git] / source3 / smbd / blocking.c
index a8db498ef5a2aaa64871d0098c18aca3277eb2f5..941e87d3ad70b9098f54afe2c0c3b4cefa68924c 100644 (file)
@@ -19,6 +19,8 @@
 */
 
 #include "includes.h"
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
 
 /****************************************************************************
  This is the structure to queue to implement blocking locks.
@@ -34,22 +36,25 @@ typedef struct _blocking_lock_record {
        int lock_num;
        SMB_BIG_UINT offset;
        SMB_BIG_UINT count;
-       uint16 lock_pid;
+       uint32 lock_pid;
        enum brl_flavour lock_flav;
        enum brl_type lock_type;
        char *inbuf;
        int length;
 } blocking_lock_record;
 
+/* dlink list we store pending lock records on. */
 static blocking_lock_record *blocking_lock_queue;
 
+/* dlink list we move cancelled lock records onto. */
+static blocking_lock_record *blocking_lock_cancelled_queue;
+
 /****************************************************************************
  Destructor for the above structure.
 ****************************************************************************/
 
 static void free_blocking_lock_record(blocking_lock_record *blr)
 {
-       DLIST_REMOVE(blocking_lock_queue, blr);
        SAFE_FREE(blr->inbuf);
        SAFE_FREE(blr);
 }
@@ -74,14 +79,13 @@ BOOL push_blocking_lock_request( char *inbuf, int length,
                files_struct *fsp,
                int lock_timeout,
                int lock_num,
-               uint16 lock_pid,
+               uint32 lock_pid,
                enum brl_type lock_type,
                enum brl_flavour lock_flav,
                SMB_BIG_UINT offset, SMB_BIG_UINT count)
 {
        static BOOL set_lock_msg;
        blocking_lock_record *blr, *tmp;
-       BOOL my_lock_ctx = False;
        struct byte_range_lock *br_lck = NULL;
        NTSTATUS status;
 
@@ -121,8 +125,9 @@ BOOL push_blocking_lock_request( char *inbuf, int length,
        memcpy(blr->inbuf, inbuf, length);
        blr->length = length;
 
-       br_lck = brl_get_locks(blr->fsp);
+       br_lck = brl_get_locks(NULL, blr->fsp);
        if (!br_lck) {
+               DLIST_REMOVE(blocking_lock_queue, blr);
                free_blocking_lock_record(blr);
                return False;
        }
@@ -135,11 +140,12 @@ BOOL push_blocking_lock_request( char *inbuf, int length,
                        count,
                        PENDING_LOCK,
                        blr->lock_flav,
-                       &my_lock_ctx);
-       byte_range_lock_destructor(br_lck);
+                       lock_timeout);
+       TALLOC_FREE(br_lck);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(0,("push_blocking_lock_request: failed to add PENDING_LOCK record.\n"));
+               DLIST_REMOVE(blocking_lock_queue, blr);
                free_blocking_lock_record(blr);
                return False;
        }
@@ -220,9 +226,24 @@ static void generic_blocking_lock_error(blocking_lock_record *blr, NTSTATUS stat
                status = NT_STATUS_FILE_LOCK_CONFLICT;
        }
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) {
+               /* Store the last lock error. */
+               files_struct *fsp = blr->fsp;
+
+               fsp->last_lock_failure.context.smbpid = blr->lock_pid;
+               fsp->last_lock_failure.context.tid = fsp->conn->cnum;
+               fsp->last_lock_failure.context.pid = procid_self();
+               fsp->last_lock_failure.start = blr->offset;
+               fsp->last_lock_failure.size = blr->count;
+               fsp->last_lock_failure.fnum = fsp->fnum;
+               fsp->last_lock_failure.lock_type = READ_LOCK; /* Don't care. */
+               fsp->last_lock_failure.lock_flav = blr->lock_flav;
+       }
+
        ERROR_NT(status);
-       if (!send_smb(smbd_server_fd(),outbuf))
+       if (!send_smb(smbd_server_fd(),outbuf)) {
                exit_server("generic_blocking_lock_error: send_smb failed.");
+       }
 }
 
 /****************************************************************************
@@ -236,7 +257,7 @@ static void reply_lockingX_error(blocking_lock_record *blr, NTSTATUS status)
        files_struct *fsp = blr->fsp;
        uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
        SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT) 0;
-       uint16 lock_pid;
+       uint32 lock_pid;
        unsigned char locktype = CVAL(inbuf,smb_vwv3);
        BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
        char *data;
@@ -335,7 +356,6 @@ static BOOL process_lockread(blocking_lock_record *blr)
        size_t numtoread;
        NTSTATUS status;
        files_struct *fsp = blr->fsp;
-       BOOL my_lock_ctx = False;
 
        numtoread = SVAL(inbuf,smb_vwv1);
        startpos = (SMB_BIG_UINT)IVAL(inbuf,smb_vwv2);
@@ -343,13 +363,13 @@ static BOOL process_lockread(blocking_lock_record *blr)
        numtoread = MIN(BUFFER_SIZE-outsize,numtoread);
        data = smb_buf(outbuf) + 3;
  
-       status = do_lock_spin(fsp,
-                               SVAL(inbuf,smb_pid),
-                               (SMB_BIG_UINT)numtoread,
-                               startpos,
-                               READ_LOCK,
-                               WINDOWS_LOCK,
-                               &my_lock_ctx);
+       status = do_lock(fsp,
+                       (uint32)SVAL(inbuf,smb_pid),
+                       (SMB_BIG_UINT)numtoread,
+                       startpos,
+                       READ_LOCK,
+                       WINDOWS_LOCK,
+                       (int32)-1);
 
        if (NT_STATUS_V(status)) {
                if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
@@ -410,19 +430,18 @@ static BOOL process_lock(blocking_lock_record *blr)
        SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
        NTSTATUS status;
        files_struct *fsp = blr->fsp;
-       BOOL my_lock_ctx = False;
 
        count = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv1);
        offset = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
 
        errno = 0;
-       status = do_lock_spin(fsp,
-                               SVAL(inbuf,smb_pid),
-                               count,
-                               offset,
-                               WRITE_LOCK,
-                               WINDOWS_LOCK,
-                               &my_lock_ctx);
+       status = do_lock(fsp,
+                       (uint32)SVAL(inbuf,smb_pid),
+                       count,
+                       offset,
+                       WRITE_LOCK,
+                       WINDOWS_LOCK,
+                       (int32)-1);
 
        if (NT_STATUS_IS_ERR(status)) {
                if (!NT_STATUS_EQUAL(status,NT_STATUS_LOCK_NOT_GRANTED) &&
@@ -471,10 +490,9 @@ static BOOL process_lockingX(blocking_lock_record *blr)
        uint16 num_ulocks = SVAL(inbuf,smb_vwv6);
        uint16 num_locks = SVAL(inbuf,smb_vwv7);
        SMB_BIG_UINT count = (SMB_BIG_UINT)0, offset = (SMB_BIG_UINT)0;
-       uint16 lock_pid;
+       uint32 lock_pid;
        BOOL large_file_format = (locktype & LOCKING_ANDX_LARGE_FILES);
        char *data;
-       BOOL my_lock_ctx = False;
        NTSTATUS status = NT_STATUS_OK;
 
        data = smb_buf(inbuf) + ((large_file_format ? 20 : 10)*num_ulocks);
@@ -496,13 +514,14 @@ static BOOL process_lockingX(blocking_lock_record *blr)
                 * request would never have been queued. JRA.
                 */
                errno = 0;
-               status = do_lock_spin(fsp,
+               status = do_lock(fsp,
                                lock_pid,
                                count,
                                offset, 
-                               ((locktype & 1) ? READ_LOCK : WRITE_LOCK),
+                               ((locktype & LOCKING_ANDX_SHARED_LOCK) ?
+                                       READ_LOCK : WRITE_LOCK),
                                WINDOWS_LOCK,
-                               &my_lock_ctx);
+                               (int32)-1);
 
                if (NT_STATUS_IS_ERR(status)) {
                        break;
@@ -552,7 +571,6 @@ static BOOL process_trans2(blocking_lock_record *blr)
        extern int max_send;
        char *inbuf = blr->inbuf;
        char *outbuf;
-       BOOL my_lock_ctx = False;
        char params[2];
        NTSTATUS status;
 
@@ -562,7 +580,7 @@ static BOOL process_trans2(blocking_lock_record *blr)
                        blr->offset,
                        blr->lock_type,
                        blr->lock_flav,
-                       &my_lock_ctx);
+                       (int32)-1);
 
        if (!NT_STATUS_IS_OK(status)) {
                if (ERROR_WAS_LOCK_DENIED(status)) {
@@ -615,33 +633,41 @@ static BOOL blocking_lock_record_process(blocking_lock_record *blr)
 }
 
 /****************************************************************************
Delete entries by fnum from the blocking lock pending queue.
Cancel entries by fnum from the blocking lock pending queue.
 *****************************************************************************/
 
-void remove_pending_lock_requests_by_fid(files_struct *fsp)
+void cancel_pending_lock_requests_by_fid(files_struct *fsp, struct byte_range_lock *br_lck)
 {
        blocking_lock_record *blr, *next = NULL;
 
        for(blr = blocking_lock_queue; blr; blr = next) {
                next = blr->next;
                if(blr->fsp->fnum == fsp->fnum) {
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       unsigned char locktype = 0;
+
+                       if (blr->com_type == SMBlockingX) {
+                               locktype = CVAL(blr->inbuf,smb_vwv3);
+                       }
 
                        if (br_lck) {
                                DEBUG(10,("remove_pending_lock_requests_by_fid - removing request type %d for \
 file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
 
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
 
+                               blocking_lock_cancel(fsp,
+                                       blr->lock_pid,
+                                       blr->offset,
+                                       blr->count,
+                                       blr->lock_flav,
+                                       locktype,
+                                       NT_STATUS_RANGE_NOT_LOCKED);
                        }
-
-                       free_blocking_lock_record(blr);
                }
        }
 }
@@ -658,22 +684,23 @@ void remove_pending_lock_requests_by_mid(int mid)
                next = blr->next;
                if(SVAL(blr->inbuf,smb_mid) == mid) {
                        files_struct *fsp = blr->fsp;
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp);
 
                        if (br_lck) {
                                DEBUG(10,("remove_pending_lock_requests_by_mid - removing request type %d for \
 file %s fnum = %d\n", blr->com_type, fsp->fsp_name, fsp->fnum ));
 
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
+                               TALLOC_FREE(br_lck);
                        }
 
                        blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+                       DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
                }
        }
@@ -754,7 +781,7 @@ void process_blocking_lock_queue(time_t t)
                        fsp->fnum, fsp->fsp_name ));
 
                if((blr->expire_time != -1) && (blr->expire_time <= t)) {
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp);
 
                        /*
                         * Lock expired - throw away all previously
@@ -765,63 +792,66 @@ void process_blocking_lock_queue(time_t t)
                                DEBUG(5,("process_blocking_lock_queue: pending lock fnum = %d for file %s timed out.\n",
                                        fsp->fnum, fsp->fsp_name ));
 
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
+                               TALLOC_FREE(br_lck);
                        }
 
                        blocking_lock_reply_error(blr,NT_STATUS_FILE_LOCK_CONFLICT);
+                       DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
                        continue;
                }
 
                if(!change_to_user(conn,vuid)) {
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp);
 
                        /*
                         * Remove the entry and return an error to the client.
                         */
 
                        if (br_lck) {
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
+                               TALLOC_FREE(br_lck);
                        }
 
                        DEBUG(0,("process_blocking_lock_queue: Unable to become user vuid=%d.\n",
                                vuid ));
                        blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+                       DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
                        continue;
                }
 
                if(!set_current_service(conn,SVAL(blr->inbuf,smb_flg),True)) {
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp);
 
                        /*
                         * Remove the entry and return an error to the client.
                         */
 
                        if (br_lck) {
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
+                               TALLOC_FREE(br_lck);
                        }
 
                        DEBUG(0,("process_blocking_lock_queue: Unable to become service Error was %s.\n", strerror(errno) ));
                        blocking_lock_reply_error(blr,NT_STATUS_ACCESS_DENIED);
+                       DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
                        change_to_root_user();
                        continue;
@@ -834,20 +864,118 @@ void process_blocking_lock_queue(time_t t)
                 */
 
                if(blocking_lock_record_process(blr)) {
-                       struct byte_range_lock *br_lck = brl_get_locks(fsp);
+                       struct byte_range_lock *br_lck = brl_get_locks(NULL, fsp);
 
                        if (br_lck) {
-                               brl_remove_pending_lock(br_lck,
+                               brl_lock_cancel(br_lck,
                                        blr->lock_pid,
                                        procid_self(),
                                        blr->offset,
                                        blr->count,
                                        blr->lock_flav);
-                               byte_range_lock_destructor(br_lck);
+                               TALLOC_FREE(br_lck);
                        }
 
+                       DLIST_REMOVE(blocking_lock_queue, blr);
                        free_blocking_lock_record(blr);
                }
                change_to_root_user();
        }
 }
+
+/****************************************************************************
+ Handle a cancel message. Lock already moved onto the cancel queue.
+*****************************************************************************/
+
+#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)
+{
+       NTSTATUS err;
+       const char *msg = (const char *)buf;
+       blocking_lock_record *blr;
+
+       if (buf == NULL) {
+               smb_panic("process_blocking_lock_cancel_message: null msg\n");
+       }
+
+       if (len != 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");
+        }
+
+       memcpy(&blr, msg, sizeof(blr));
+       memcpy(&err, &msg[sizeof(blr)], sizeof(NTSTATUS));
+
+       DEBUG(10,("process_blocking_lock_cancel_message: returning error %s\n",
+               nt_errstr(err) ));
+
+       blocking_lock_reply_error(blr, err);
+       DLIST_REMOVE(blocking_lock_cancelled_queue, blr);
+       free_blocking_lock_record(blr);
+}
+
+/****************************************************************************
+ Send ourselves a blocking lock cancelled message. Handled asynchronously above.
+*****************************************************************************/
+
+BOOL blocking_lock_cancel(files_struct *fsp,
+                       uint32 lock_pid,
+                       SMB_BIG_UINT offset,
+                       SMB_BIG_UINT count,
+                       enum brl_flavour lock_flav,
+                       unsigned char locktype,
+                        NTSTATUS err)
+{
+       static BOOL initialized;
+       char msg[MSG_BLOCKING_LOCK_CANCEL_SIZE];
+       blocking_lock_record *blr;
+
+       if (!initialized) {
+               /* Register our message. */
+               message_register(MSG_SMB_BLOCKING_LOCK_CANCEL,
+                               process_blocking_lock_cancel_message);
+
+               initialized = True;
+       }
+
+       for (blr = blocking_lock_queue; blr; blr = blr->next) {
+               if (fsp == blr->fsp &&
+                               lock_pid == blr->lock_pid &&
+                               offset == blr->offset &&
+                               count == blr->count &&
+                               lock_flav == blr->lock_flav) {
+                       break;
+               }
+       }
+
+       if (!blr) {
+               return False;
+       }
+
+       /* Check the flags are right. */
+       if (blr->com_type == SMBlockingX &&
+               (locktype & LOCKING_ANDX_LARGE_FILES) !=
+                       (CVAL(blr->inbuf,smb_vwv3) & LOCKING_ANDX_LARGE_FILES)) {
+               return False;
+       }
+
+       /* Move to cancelled queue. */
+       DLIST_REMOVE(blocking_lock_queue, blr);
+       DLIST_ADD(blocking_lock_cancelled_queue, blr);
+
+       /* Create the message. */
+       memcpy(msg, &blr, sizeof(blr));
+       memcpy(&msg[sizeof(blr)], &err, sizeof(NTSTATUS));
+
+       /* Don't need to be root here as we're only ever
+               sending to ourselves. */
+
+       message_send_pid(pid_to_procid(sys_getpid()),
+                       MSG_SMB_BLOCKING_LOCK_CANCEL,
+                       &msg, sizeof(msg), True);
+
+       return True;
+}