Implement oplocks within SMB2. Plumb into the existing SMB1 oplock system.
authorJeremy Allison <jra@samba.org>
Sat, 24 Apr 2010 07:29:41 +0000 (00:29 -0700)
committerJeremy Allison <jra@samba.org>
Sat, 24 Apr 2010 07:29:41 +0000 (00:29 -0700)
Seems to work but needs more tests (to be added).

Jeremy.

source3/smbd/globals.h
source3/smbd/oplock.c
source3/smbd/smb2_break.c
source3/smbd/smb2_create.c
source3/smbd/smb2_server.c

index 4d1a13d3b10397b4b24fcce1b3fb3ce3e5d3fb6b..aa0018f087498dadcfd79166b90557a25e7091b0 100644 (file)
@@ -288,7 +288,6 @@ NTSTATUS smbd_smb2_request_done_ex(struct smbd_smb2_request *req,
        smbd_smb2_request_done_ex(req, NT_STATUS_OK, body, dyn, __location__)
 
 NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
-                                    uint64_t file_id_persistent,
                                     uint64_t file_id_volatile,
                                     uint8_t oplock_level);
 
@@ -324,7 +323,7 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
                                void *private_data);
 
 /* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, uint8_t level);
+void send_break_message_smb2(files_struct *fsp, int level);
 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
                                struct smb_request *req,
                                files_struct *fsp,
@@ -337,6 +336,8 @@ bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
                                uint64_t count,
                                uint32_t blocking_pid);
 /* From smbd/smb2_create.c */
+int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level);
+uint8_t map_samba_oplock_levels_to_smb2(int oplock_type);
 bool get_deferred_open_message_state_smb2(struct smbd_smb2_request *smb2req,
                        struct timeval *p_request_time,
                        void **pp_state);
index 2289787ddd54ebca9ceece3bebf1b3739743f555..c22a58910463a4dc98dea0677d824ac28a5e64e9 100644 (file)
@@ -215,7 +215,7 @@ bool should_notify_deferred_opens()
 ****************************************************************************/
 
 static char *new_break_message_smb1(TALLOC_CTX *mem_ctx,
-                                  files_struct *fsp, uint8 cmd)
+                                  files_struct *fsp, int cmd)
 {
        char *result = TALLOC_ARRAY(mem_ctx, char, smb_size + 8*2 + 0);
 
@@ -345,7 +345,7 @@ static void add_oplock_timeout_handler(files_struct *fsp)
        }
 
        fsp->oplock_timeout =
-               event_add_timed(smbd_event_context(), NULL,
+               event_add_timed(smbd_event_context(), fsp,
                                timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
                                oplock_timeout_handler, fsp);
 
@@ -354,7 +354,7 @@ static void add_oplock_timeout_handler(files_struct *fsp)
        }
 }
 
-static void send_break_message_smb1(files_struct *fsp, uint8_t level)
+static void send_break_message_smb1(files_struct *fsp, int level)
 {
        char *break_msg = new_break_message_smb1(talloc_tos(),
                                        fsp,
index 8bb1bfc27ac2942ee182f62c9859f919467875e7..d28bbf559aa0e120115db08686b14cb3c90a612b 100644 (file)
@@ -56,6 +56,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
        }
 
        in_oplock_level         = CVAL(inbody, 0x02);
+
+       if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
+                       in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
+               return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+       }
+
        /* 0x03 1 bytes reserved */
        /* 0x04 4 bytes reserved */
        in_file_id_persistent           = BVAL(inbody, 0x08);
@@ -123,7 +129,7 @@ static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
 
        SSVAL(outbody.data, 0x00, 0x18);        /* struct size */
        SCVAL(outbody.data, 0x02,
-             out_oplock_level);                /* oplock level */
+             out_oplock_level);                /* SMB2 oplock level */
        SCVAL(outbody.data, 0x03, 0);           /* reserved */
        SIVAL(outbody.data, 0x04, 0);           /* reserved */
        SBVAL(outbody.data, 0x08,
@@ -141,7 +147,7 @@ static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
 
 struct smbd_smb2_oplock_break_state {
        struct smbd_smb2_request *smb2req;
-       uint8_t out_oplock_level;
+       uint8_t out_oplock_level; /* SMB2 oplock level. */
 };
 
 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
@@ -154,7 +160,10 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
        struct smbd_smb2_oplock_break_state *state;
        struct smb_request *smbreq;
        connection_struct *conn = smb2req->tcon->compat_conn;
-       files_struct *fsp;
+       files_struct *fsp = NULL;
+       int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
+       bool break_to_none = (oplocklevel == NO_OPLOCK);
+       bool result;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbd_smb2_oplock_break_state);
@@ -164,8 +173,10 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
        state->smb2req = smb2req;
        state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
 
-       DEBUG(10,("smbd_smb2_oplock_break_send: file_id[0x%016llX]\n",
-                 (unsigned long long)in_file_id_volatile));
+       DEBUG(10,("smbd_smb2_oplock_break_send: file_id[0x%016llX] "
+               "samba level %d\n",
+               (unsigned long long)in_file_id_volatile,
+               oplocklevel));
 
        smbreq = smbd_smb2_fake_smb_request(smb2req);
        if (tevent_req_nomem(smbreq, req)) {
@@ -186,7 +197,31 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED);
+       DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
+               "for file %s fnum = %d\n",
+               (unsigned int)in_oplock_level,
+               fsp_str_dbg(fsp),
+               fsp->fnum ));
+
+       if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
+                       (break_to_none)) {
+               result = remove_oplock(fsp);
+               state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+       } else {
+               result = downgrade_oplock(fsp);
+               state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
+       }
+
+       if (!result) {
+               DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
+                       "oplock on file %s\n", fsp_str_dbg(fsp)));
+               /* Hmmm. Is this panic justified? */
+               smb_panic("internal tdb error");
+       }
+
+       reply_to_oplock_break_requests(fsp);
+
+       tevent_req_done(req);
        return tevent_req_post(req, ev);
 }
 
@@ -209,6 +244,27 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
        return NT_STATUS_OK;
 }
 
-void send_break_message_smb2(files_struct *fsp, uint8_t level)
+/*********************************************************
+ Create and send an asynchronous
+ SMB2 OPLOCK_BREAK_NOTIFICATION.
+*********************************************************/
+
+void send_break_message_smb2(files_struct *fsp, int level)
 {
+       uint8_t smb2_oplock_level = map_samba_oplock_levels_to_smb2(level);
+       NTSTATUS status;
+
+       DEBUG(10,("send_break_message_smb2: sending oplock break "
+               "for file %s, fnum = %d, smb2 level %u\n",
+               fsp_str_dbg(fsp),
+               fsp->fnum,
+               (unsigned int)smb2_oplock_level ));
+               
+       status = smbd_smb2_send_oplock_break(fsp->conn->sconn,
+                                       (uint64_t)fsp->fnum,
+                                       smb2_oplock_level);
+       if (!NT_STATUS_IS_OK(status)) {
+               smbd_server_connection_terminate(fsp->conn->sconn,
+                                nt_errstr(status));
+       }
 }
index d97d4af63a906bf5b74f71c483767c2ef005636e..6a118c3947d8fdd592262ff6eb9461a74912ae71 100644 (file)
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
 
+int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level)
+{
+       switch(in_oplock_level) {
+       case SMB2_OPLOCK_LEVEL_NONE:
+               return NO_OPLOCK;
+       case SMB2_OPLOCK_LEVEL_II:
+               return LEVEL_II_OPLOCK;
+       case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
+               return EXCLUSIVE_OPLOCK;
+       case SMB2_OPLOCK_LEVEL_BATCH:
+               return BATCH_OPLOCK;
+       case SMB2_OPLOCK_LEVEL_LEASE:
+               DEBUG(2,("map_smb2_oplock_levels_to_samba: "
+                       "LEASE_OPLOCK_REQUESTED\n"));
+               return NO_OPLOCK;
+       default:
+               DEBUG(2,("map_smb2_oplock_levels_to_samba: "
+                       "unknown level %u\n",
+                       (unsigned int)in_oplock_level));
+               return NO_OPLOCK;
+       }
+}
+
+uint8_t map_samba_oplock_levels_to_smb2(int oplock_type)
+{
+       if (BATCH_OPLOCK_TYPE(oplock_type)) {
+               return SMB2_OPLOCK_LEVEL_BATCH;
+       } else if (EXCLUSIVE_OPLOCK_TYPE(oplock_type)) {
+               return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+       } else if (LEVEL_II_OPLOCK_TYPE(oplock_type)) {
+               return SMB2_OPLOCK_LEVEL_II;
+       } else {
+               return SMB2_OPLOCK_LEVEL_NONE;
+       }
+}
+
 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        struct tevent_context *ev,
                        struct smbd_smb2_request *smb2req,
@@ -636,6 +672,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        return tevent_req_post(req, ev);
                }
 
+               in_file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS;
+
                status = SMB_VFS_CREATE_FILE(smb1req->conn,
                                             smb1req,
                                             0, /* root_dir_fid */
@@ -645,7 +683,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                             in_create_disposition,
                                             in_create_options,
                                             in_file_attributes,
-                                            0, /* oplock_request */
+                                            map_smb2_oplock_levels_to_samba(in_oplock_level),
                                             allocation_size,
                                             0, /* private_flags */
                                             sec_desc,
@@ -711,7 +749,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
        smb2req->compat_chain_fsp = smb1req->chain_fsp;
 
-       state->out_oplock_level = 0;
+       state->out_oplock_level = map_samba_oplock_levels_to_smb2(result->oplock_type);
+
        if ((in_create_disposition == FILE_SUPERSEDE)
            && (info == FILE_WAS_OVERWRITTEN)) {
                state->out_create_action = FILE_WAS_SUPERSEDED;
index 04cace8a591151031088ae201b87ce0b49e5760c..894042702bcc588d75574415c6ea094051e34773 100644 (file)
@@ -49,7 +49,7 @@ static const char *smb2_names[] = {
 
 const char *smb2_opcode_name(uint16_t opcode)
 {
-       if (opcode >= 0x12) {
+       if (opcode > 0x12) {
                return "Bad SMB2 opcode";
        }
        return smb2_names[opcode];
@@ -1427,7 +1427,6 @@ struct smbd_smb2_send_oplock_break_state {
 static void smbd_smb2_oplock_break_writev_done(struct tevent_req *subreq);
 
 NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
-                                    uint64_t file_id_persistent,
                                     uint64_t file_id_volatile,
                                     uint8_t oplock_level)
 {
@@ -1468,7 +1467,7 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbd_server_connection *sconn,
        SCVAL(body, 0x02, oplock_level);
        SCVAL(body, 0x03, 0);           /* reserved */
        SIVAL(body, 0x04, 0);           /* reserved */
-       SBVAL(body, 0x08, file_id_persistent);
+       SBVAL(body, 0x08, 0);           /* file_id_persistent */
        SBVAL(body, 0x10, file_id_volatile);
 
        subreq = tstream_writev_queue_send(state,