From a796542a930dec93c2a747c4b015d8d650a081fd Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 24 Apr 2010 00:29:41 -0700 Subject: Implement oplocks within SMB2. Plumb into the existing SMB1 oplock system. Seems to work but needs more tests (to be added). Jeremy. --- source3/smbd/globals.h | 5 ++-- source3/smbd/oplock.c | 6 ++-- source3/smbd/smb2_break.c | 70 +++++++++++++++++++++++++++++++++++++++++----- source3/smbd/smb2_create.c | 43 ++++++++++++++++++++++++++-- source3/smbd/smb2_server.c | 5 ++-- 5 files changed, 112 insertions(+), 17 deletions(-) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 4d1a13d3b1..aa0018f087 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -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); diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 2289787ddd..c22a589104 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -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, diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c index 8bb1bfc27a..d28bbf559a 100644 --- a/source3/smbd/smb2_break.c +++ b/source3/smbd/smb2_break.c @@ -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)); + } } diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index d97d4af63a..6a118c3947 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -22,6 +22,42 @@ #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; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 04cace8a59..894042702b 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -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, -- cgit