summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2010-04-24 00:29:41 -0700
committerJeremy Allison <jra@samba.org>2010-04-24 00:29:41 -0700
commita796542a930dec93c2a747c4b015d8d650a081fd (patch)
tree62622992bf5d3599c12374de34768f7f1291a690
parent247273196ff8035ef16805f04fe7c32ef9a8be50 (diff)
downloadsamba-a796542a930dec93c2a747c4b015d8d650a081fd.tar.gz
Implement oplocks within SMB2. Plumb into the existing SMB1 oplock system.
Seems to work but needs more tests (to be added). Jeremy.
-rw-r--r--source3/smbd/globals.h5
-rw-r--r--source3/smbd/oplock.c6
-rw-r--r--source3/smbd/smb2_break.c70
-rw-r--r--source3/smbd/smb2_create.c43
-rw-r--r--source3/smbd/smb2_server.c5
5 files changed, 112 insertions, 17 deletions
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 4d1a13d3b10..aa0018f0874 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 2289787ddd5..c22a5891046 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 8bb1bfc27ac..d28bbf559aa 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 d97d4af63a9..6a118c3947d 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 04cace8a591..894042702bc 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,