s3:smb2_lock: use fsp->fnum as locking context
[ddiss/samba.git] / source3 / smbd / smb2_lock.c
index 63e43fa8329f1d59db2380623493d803c3892621..be403eefe8e50b8dc7dc93a9ea46e7bbbf173db1 100644 (file)
@@ -3,6 +3,7 @@
    Core SMB2 server
 
    Copyright (C) Stefan Metzmacher 2009
+   Copyright (C) Jeremy Allison 2010
 
    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
 */
 
 #include "includes.h"
+#include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
-#include "librpc/gen_ndr/messaging.h"
+#include "../lib/util/tevent_ntstatus.h"
+#include "messages.h"
 
 struct smbd_smb2_lock_element {
        uint64_t offset;
@@ -29,6 +32,17 @@ struct smbd_smb2_lock_element {
        uint32_t flags;
 };
 
+struct smbd_smb2_lock_state {
+       struct smbd_smb2_request *smb2req;
+       struct smb_request *smb1req;
+       struct blocking_lock_record *blr;
+       uint16_t lock_count;
+       struct smbd_lock_element *locks;
+};
+
+static void remove_pending_lock(struct smbd_smb2_lock_state *state,
+                               struct blocking_lock_record *blr);
+
 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                                                 struct tevent_context *ev,
                                                 struct smbd_smb2_request *smb2req,
@@ -44,8 +58,6 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
        const uint8_t *inhdr;
        const uint8_t *inbody;
        const int i = req->current_idx;
-       size_t expected_body_size = 0x30;
-       size_t body_size;
        uint32_t in_smbpid;
        uint16_t in_lock_count;
        uint64_t in_file_id_persistent;
@@ -54,19 +66,15 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
        struct tevent_req *subreq;
        const uint8_t *lock_buffer;
        uint16_t l;
+       NTSTATUS status;
 
-       inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
-       if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
-               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       status = smbd_smb2_request_verify_sizes(req, 0x30);
+       if (!NT_STATUS_IS_OK(status)) {
+               return smbd_smb2_request_error(req, status);
        }
-
+       inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
        inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
 
-       body_size = SVAL(inbody, 0x00);
-       if (body_size != expected_body_size) {
-               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
-       }
-
        in_smbpid                       = IVAL(inhdr, SMB2_HDR_PID);
 
        in_lock_count                   = CVAL(inbody, 0x02);
@@ -84,7 +92,7 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
 
        if (req->compat_chain_fsp) {
                /* skip check */
-       } else if (in_file_id_persistent != 0) {
+       } else if (in_file_id_persistent != in_file_id_volatile) {
                return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
        }
 
@@ -130,22 +138,32 @@ NTSTATUS smbd_smb2_request_process_lock(struct smbd_smb2_request *req)
 
 static void smbd_smb2_request_lock_done(struct tevent_req *subreq)
 {
-       struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
+       struct smbd_smb2_request *smb2req = tevent_req_callback_data(subreq,
                                        struct smbd_smb2_request);
        DATA_BLOB outbody;
        NTSTATUS status;
        NTSTATUS error; /* transport error */
 
-       if (req->cancelled) {
+       if (smb2req->cancelled) {
                const uint8_t *inhdr = (const uint8_t *)
-                       req->in.vector[req->current_idx].iov_base;
+                       smb2req->in.vector[smb2req->current_idx].iov_base;
                uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
+               struct smbd_smb2_lock_state *state;
 
                DEBUG(10,("smbd_smb2_request_lock_done: cancelled mid %llu\n",
                        (unsigned long long)mid ));
-               error = smbd_smb2_request_error(req, NT_STATUS_CANCELLED);
+
+               state = tevent_req_data(smb2req->subreq,
+                               struct smbd_smb2_lock_state);
+
+               SMB_ASSERT(state);
+               SMB_ASSERT(state->blr);
+
+               remove_pending_lock(state, state->blr);
+
+               error = smbd_smb2_request_error(smb2req, NT_STATUS_CANCELLED);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->sconn,
+                       smbd_server_connection_terminate(smb2req->sconn,
                                nt_errstr(error));
                        return;
                }
@@ -155,20 +173,20 @@ static void smbd_smb2_request_lock_done(struct tevent_req *subreq)
        status = smbd_smb2_lock_recv(subreq);
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
-               error = smbd_smb2_request_error(req, status);
+               error = smbd_smb2_request_error(smb2req, status);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->sconn,
+                       smbd_server_connection_terminate(smb2req->sconn,
                                                         nt_errstr(error));
                        return;
                }
                return;
        }
 
-       outbody = data_blob_talloc(req->out.vector, NULL, 0x04);
+       outbody = data_blob_talloc(smb2req->out.vector, NULL, 0x04);
        if (outbody.data == NULL) {
-               error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+               error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->sconn,
+                       smbd_server_connection_terminate(smb2req->sconn,
                                                         nt_errstr(error));
                        return;
                }
@@ -178,22 +196,14 @@ static void smbd_smb2_request_lock_done(struct tevent_req *subreq)
        SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
        SSVAL(outbody.data, 0x02, 0);           /* reserved */
 
-       error = smbd_smb2_request_done(req, outbody, NULL);
+       error = smbd_smb2_request_done(smb2req, outbody, NULL);
        if (!NT_STATUS_IS_OK(error)) {
-               smbd_server_connection_terminate(req->sconn,
+               smbd_server_connection_terminate(smb2req->sconn,
                                                 nt_errstr(error));
                return;
        }
 }
 
-struct smbd_smb2_lock_state {
-       struct smbd_smb2_request *smb2req;
-       struct smb_request *smb1req;
-       struct blocking_lock_record *blr;
-       uint16_t lock_count;
-       struct smbd_lock_element *locks;
-};
-
 static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                                                 struct tevent_context *ev,
                                                 struct smbd_smb2_request *smb2req,
@@ -284,12 +294,11 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                switch (in_locks[i].flags) {
                case SMB2_LOCK_FLAG_SHARED:
                case SMB2_LOCK_FLAG_EXCLUSIVE:
-                       if (i > 0) {
-                               tevent_req_nterror(req,
-                                                  NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
                        if (isunlock) {
+                               invalid = true;
+                               break;
+                       }
+                       if (i > 0) {
                                tevent_req_nterror(req,
                                                   NT_STATUS_INVALID_PARAMETER);
                                return tevent_req_post(req, ev);
@@ -299,9 +308,7 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                case SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
                case SMB2_LOCK_FLAG_EXCLUSIVE|SMB2_LOCK_FLAG_FAIL_IMMEDIATELY:
                        if (isunlock) {
-                               tevent_req_nterror(req,
-                                                  NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
+                               invalid = true;
                        }
                        break;
 
@@ -328,7 +335,7 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                        return tevent_req_post(req, ev);
                }
 
-               locks[i].smbpid = in_smbpid;
+               locks[i].smblctx = fsp->fnum;
                locks[i].offset = in_locks[i].offset;
                locks[i].count  = in_locks[i].length;
 
@@ -349,13 +356,12 @@ static struct tevent_req *smbd_smb2_lock_send(TALLOC_CTX *mem_ctx,
                }
 
                DEBUG(10,("smbd_smb2_lock_send: index %d offset=%llu, count=%llu, "
-                       "pid = %u type %d\n",
+                       "smblctx = %llu type %d\n",
                        i,
                        (unsigned long long)locks[i].offset,
                        (unsigned long long)locks[i].count,
-                       (unsigned int)locks[i].smbpid,
+                       (unsigned long long)locks[i].smblctx,
                        (int)locks[i].brltype ));
-
        }
 
        state->locks = locks;
@@ -444,8 +450,16 @@ static void received_unlock_msg(struct messaging_context *msg,
                                struct server_id server_id,
                                DATA_BLOB *data)
 {
+       struct smbd_server_connection *sconn;
+
        DEBUG(10,("received_unlock_msg (SMB2)\n"));
-       process_blocking_lock_queue_smb2(timeval_current());
+
+       sconn = msg_ctx_to_sconn(msg);
+       if (sconn == NULL) {
+               DEBUG(1, ("could not find sconn\n"));
+               return;
+       }
+       process_blocking_lock_queue_smb2(sconn, timeval_current());
 }
 
 /****************************************************************
@@ -497,11 +511,11 @@ static bool recalc_smb2_brl_timeout(struct smbd_server_connection *sconn)
                }
                if (timeval_is_zero(&blr->expire_time)) {
                        /*
-                        * If we're blocked on pid 0xFFFFFFFF this is
+                        * If we're blocked on pid 0xFFFFFFFFFFFFFFFFLL this is
                         * a POSIX lock, so calculate a timeout of
                         * 10 seconds into the future.
                         */
-                       if (blr->blocking_pid == 0xFFFFFFFF) {
+                       if (blr->blocking_smblctx == 0xFFFFFFFFFFFFFFFFLL) {
                                struct timeval psx_to = timeval_current_ofs(10, 0);
                                next_timeout = timeval_brl_min(&next_timeout, &psx_to);
                        }
@@ -569,14 +583,14 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
                                files_struct *fsp,
                                int lock_timeout,
                                int lock_num,
-                               uint32_t lock_pid,
+                               uint64_t smblctx,
                                enum brl_type lock_type,
                                enum brl_flavour lock_flav,
                                uint64_t offset,
                                uint64_t count,
-                               uint32_t blocking_pid)
+                               uint64_t blocking_smblctx)
 {
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = smb1req->sconn;
        struct smbd_smb2_request *smb2req = smb1req->smb2req;
        struct tevent_req *req = NULL;
        struct smbd_smb2_lock_state *state = NULL;
@@ -614,8 +628,8 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
        }
 
        blr->lock_num = lock_num;
-       blr->lock_pid = lock_pid;
-       blr->blocking_pid = blocking_pid;
+       blr->smblctx = smblctx;
+       blr->blocking_smblctx = blocking_smblctx;
        blr->lock_flav = lock_flav;
        blr->lock_type = lock_type;
        blr->offset = offset;
@@ -625,10 +639,10 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
        blr->blr_private = NULL;
 
        /* Add a pending lock record for this. */
-       status = brl_lock(smbd_messaging_context(),
+       status = brl_lock(sconn->msg_ctx,
                        br_lck,
-                       lock_pid,
-                       procid_self(),
+                       smblctx,
+                       sconn_server_id(sconn),
                        offset,
                        count,
                        lock_type == READ_LOCK ? PENDING_READ_LOCK : PENDING_WRITE_LOCK,
@@ -653,7 +667,7 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
 
        /* Ensure we'll receive messages when this is unlocked. */
        if (!sconn->smb2.locks.blocking_lock_unlock_state) {
-               messaging_register(smbd_messaging_context(), NULL,
+               messaging_register(sconn->msg_ctx, NULL,
                                MSG_SMB_UNLOCK, received_unlock_msg);
                sconn->smb2.locks.blocking_lock_unlock_state = true;
         }
@@ -668,23 +682,38 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
  Remove a pending lock record under lock.
 *****************************************************************/
 
-static void remove_pending_lock(TALLOC_CTX *mem_ctx, struct blocking_lock_record *blr)
+static void remove_pending_lock(struct smbd_smb2_lock_state *state,
+                       struct blocking_lock_record *blr)
 {
+       int i;
        struct byte_range_lock *br_lck = brl_get_locks(
-                               mem_ctx, blr->fsp);
+                               state, blr->fsp);
 
        DEBUG(10, ("remove_pending_lock: BLR = %p\n", blr));
 
        if (br_lck) {
                brl_lock_cancel(br_lck,
-                               blr->lock_pid,
-                               procid_self(),
+                               blr->smblctx,
+                               sconn_server_id(blr->fsp->conn->sconn),
                                blr->offset,
                                blr->count,
                                blr->lock_flav,
                                blr);
                TALLOC_FREE(br_lck);
        }
+
+       /* Remove the locks we already got. */
+
+       for(i = blr->lock_num - 1; i >= 0; i--) {
+               struct smbd_lock_element *e = &state->locks[i];
+
+               do_unlock(blr->fsp->conn->sconn->msg_ctx,
+                       blr->fsp,
+                       e->smblctx,
+                       e->count,
+                       e->offset,
+                       WINDOWS_LOCK);
+       }
 }
 
 /****************************************************************
@@ -695,7 +724,7 @@ static void remove_pending_lock(TALLOC_CTX *mem_ctx, struct blocking_lock_record
 static void reprocess_blocked_smb2_lock(struct smbd_smb2_request *smb2req,
                                struct timeval tv_curr)
 {
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        struct blocking_lock_record *blr = NULL;
        struct smbd_smb2_lock_state *state = NULL;
        files_struct *fsp = NULL;
@@ -717,16 +746,16 @@ static void reprocess_blocked_smb2_lock(struct smbd_smb2_request *smb2req,
                struct byte_range_lock *br_lck = NULL;
                struct smbd_lock_element *e = &state->locks[blr->lock_num];
 
-               br_lck = do_lock(smbd_messaging_context(),
+               br_lck = do_lock(fsp->conn->sconn->msg_ctx,
                                fsp,
-                               e->smbpid,
+                               e->smblctx,
                                e->count,
                                e->offset,
                                e->brltype,
                                WINDOWS_LOCK,
                                true,
                                &status,
-                               &blr->blocking_pid,
+                               &blr->blocking_smblctx,
                                blr);
 
                TALLOC_FREE(br_lck);
@@ -794,9 +823,9 @@ static void reprocess_blocked_smb2_lock(struct smbd_smb2_request *smb2req,
  the request queue.
 *****************************************************************/
 
-void process_blocking_lock_queue_smb2(struct timeval tv_curr)
+void process_blocking_lock_queue_smb2(
+       struct smbd_server_connection *sconn, struct timeval tv_curr)
 {
-       struct smbd_server_connection *sconn = smbd_server_conn;
        struct smbd_smb2_request *smb2req, *nextreq;
 
        for (smb2req = sconn->smb2.requests; smb2req; smb2req = nextreq) {
@@ -827,19 +856,18 @@ void process_blocking_lock_queue_smb2(struct timeval tv_curr)
 ****************************************************************************/
 
 void cancel_pending_lock_requests_by_fid_smb2(files_struct *fsp,
-                       struct byte_range_lock *br_lck)
+                       struct byte_range_lock *br_lck,
+                       enum file_close_type close_type)
 {
-       struct smbd_server_connection *sconn = smbd_server_conn;
+       struct smbd_server_connection *sconn = fsp->conn->sconn;
        struct smbd_smb2_request *smb2req, *nextreq;
 
        for (smb2req = sconn->smb2.requests; smb2req; smb2req = nextreq) {
                struct smbd_smb2_lock_state *state = NULL;
                files_struct *fsp_curr = NULL;
                int i = smb2req->current_idx;
-               uint64_t in_file_id_volatile;
                struct blocking_lock_record *blr = NULL;
                const uint8_t *inhdr;
-               const uint8_t *inbody;
 
                nextreq = smb2req->next;
 
@@ -853,14 +881,11 @@ void cancel_pending_lock_requests_by_fid_smb2(files_struct *fsp,
                }
 
                inhdr = (const uint8_t *)smb2req->in.vector[i].iov_base;
-               if (IVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_LOCK) {
+               if (SVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_LOCK) {
                        /* Not a lock call. */
                        continue;
                }
 
-               inbody = (const uint8_t *)smb2req->in.vector[i+1].iov_base;
-               in_file_id_volatile = BVAL(inbody, 0x10);
-
                state = tevent_req_data(smb2req->subreq,
                                struct smbd_smb2_lock_state);
                if (!state) {
@@ -868,7 +893,7 @@ void cancel_pending_lock_requests_by_fid_smb2(files_struct *fsp,
                        continue;
                }
 
-               fsp_curr = file_fsp(state->smb1req, (uint16_t)in_file_id_volatile);
+               fsp_curr = smb2req->compat_chain_fsp;
                if (fsp_curr == NULL) {
                        /* Strange - is this even possible ? */
                        continue;
@@ -883,15 +908,19 @@ void cancel_pending_lock_requests_by_fid_smb2(files_struct *fsp,
 
                /* Remove the entries from the lock db. */
                brl_lock_cancel(br_lck,
-                               blr->lock_pid,
-                               procid_self(),
+                               blr->smblctx,
+                               sconn_server_id(sconn),
                                blr->offset,
                                blr->count,
                                blr->lock_flav,
                                blr);
 
-               /* Finally cancel the request. */
-               smb2req->cancelled = true;
-               tevent_req_cancel(smb2req->subreq);
+               /* Finally end the request. */
+               if (close_type == SHUTDOWN_CLOSE) {
+                       tevent_req_done(smb2req->subreq);
+               } else {
+                       tevent_req_nterror(smb2req->subreq,
+                               NT_STATUS_RANGE_NOT_LOCKED);
+               }
        }
 }