The branch, v4-0-test has been updated via 914f0ac83bc396be0ca34c43e2ea01ecc1c3b826 (commit) via b781bb733c9a563457f87c94abe8c91b426c07ee (commit) via 2306394dcc22ff2be8581256a5cf91eef4993078 (commit) via 58189b87eade62b717c2c17c679e482786bf2098 (commit) via 7f545dbbf0186fe552e4c49a3f618862cb4771e7 (commit) via 5ffea702c3a1c92a797afab1a3cadf2f2a18729f (commit) via 60c4a4fc1afe88716ac63d3ea430e07fea7b9991 (commit) from c7f34f41c3f9f0c3f75a618dfaf566706014a6b4 (commit)
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v4-0-test - Log ----------------------------------------------------------------- commit 914f0ac83bc396be0ca34c43e2ea01ecc1c3b826 Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Sat Apr 19 00:13:42 2008 +0200 torture/smb2: add a simple SMB2-OPLOCK-BATCH1 test metze commit b781bb733c9a563457f87c94abe8c91b426c07ee Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Fri Apr 18 22:32:25 2008 +0200 ntvfs_generic: map RAW_LOCK_SMB2_BREAK to RAW_LOCK_GENERIC metze commit 2306394dcc22ff2be8581256a5cf91eef4993078 Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Fri Apr 18 22:30:12 2008 +0200 smb_server/smb2: handle incoming oplock releases metze commit 58189b87eade62b717c2c17c679e482786bf2098 Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Fri Apr 18 22:27:24 2008 +0200 libcli/smb2: make it possible to handle incoming oplock requests metze commit 7f545dbbf0186fe552e4c49a3f618862cb4771e7 Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Fri Apr 18 22:24:21 2008 +0200 libcli/smb2: add smb2_break() calls metze commit 5ffea702c3a1c92a797afab1a3cadf2f2a18729f Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Fri Apr 18 22:19:08 2008 +0200 libcli: define structure for SMB2 Break metze commit 60c4a4fc1afe88716ac63d3ea430e07fea7b9991 Author: Stefan Metzmacher <[EMAIL PROTECTED]> Date: Sat Apr 19 00:14:52 2008 +0200 ntvfs_generic: fix mapping the granted oplocks for SMB2 metze ----------------------------------------------------------------------- Summary of changes: source/libcli/raw/interfaces.h | 17 +++- source/libcli/smb2/{flush.c => break.c} | 42 ++++--- source/libcli/smb2/config.mk | 2 +- source/libcli/smb2/smb2.h | 11 ++ source/libcli/smb2/transport.c | 43 ++++++++ source/ntvfs/ntvfs_generic.c | 23 ++++- source/smb_server/smb2/fileio.c | 31 +++++- source/torture/smb2/config.mk | 3 +- source/torture/smb2/oplocks.c | 178 +++++++++++++++++++++++++++++++ source/torture/smb2/smb2.c | 1 + 10 files changed, 326 insertions(+), 25 deletions(-) copy source/libcli/smb2/{flush.c => break.c} (54%) create mode 100644 source/torture/smb2/oplocks.c Changeset truncated at 500 lines: diff --git a/source/libcli/raw/interfaces.h b/source/libcli/raw/interfaces.h index cf5a3aa..bad3743 100644 --- a/source/libcli/raw/interfaces.h +++ b/source/libcli/raw/interfaces.h @@ -1862,7 +1862,8 @@ enum smb_lock_level { RAW_LOCK_LOCK, RAW_LOCK_UNLOCK, RAW_LOCK_LOCKX, - RAW_LOCK_SMB2 + RAW_LOCK_SMB2, + RAW_LOCK_SMB2_BREAK }; /* the generic interface is defined to be equal to the lockingX interface */ @@ -1925,6 +1926,20 @@ union smb_lock { uint16_t unknown1; } out; } smb2; + + /* SMB2 Break */ + struct smb2_break { + enum smb_lock_level level; + struct { + union smb_handle file; + + /* static body buffer 24 (0x18) bytes */ + uint8_t oplock_level; + uint8_t reserved; + uint32_t reserved2; + /* struct smb2_handle handle; */ + } in, out; + } smb2_break; }; diff --git a/source/libcli/smb2/flush.c b/source/libcli/smb2/break.c similarity index 54% copy from source/libcli/smb2/flush.c copy to source/libcli/smb2/break.c index 116068e..fe0cceb 100644 --- a/source/libcli/smb2/flush.c +++ b/source/libcli/smb2/break.c @@ -1,20 +1,20 @@ -/* +/* Unix SMB/CIFS implementation. - SMB2 client flush handling + SMB2 client oplock break handling + + Copyright (C) Stefan Metzmacher 2008 - Copyright (C) Andrew Tridgell 2005 - 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 the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -24,17 +24,18 @@ #include "libcli/smb2/smb2_calls.h" /* - send a flush request + send a break request */ -struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io) +struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io) { struct smb2_request *req; - req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, false, 0); + req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0); if (req == NULL) return NULL; - SSVAL(req->out.body, 0x02, 0); /* pad? */ - SIVAL(req->out.body, 0x04, io->in.unknown); + SCVAL(req->out.body, 0x02, io->in.oplock_level); + SCVAL(req->out.body, 0x03, io->in.reserved); + SIVAL(req->out.body, 0x04, io->in.reserved2); smb2_push_handle(req->out.body+0x08, &io->in.file.handle); smb2_transport_send(req); @@ -44,16 +45,21 @@ struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush * /* - recv a flush reply + recv a break reply */ -NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io) +NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io) { - if (!smb2_request_receive(req) || + if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { return smb2_request_destroy(req); } - SMB2_CHECK_PACKET_RECV(req, 0x04, false); + SMB2_CHECK_PACKET_RECV(req, 0x18, false); + + io->out.oplock_level = CVAL(req->in.body, 0x02); + io->out.reserved = CVAL(req->in.body, 0x03); + io->out.reserved2 = IVAL(req->in.body, 0x04); + smb2_pull_handle(req->in.body+0x08, &io->out.file.handle); return smb2_request_destroy(req); } @@ -61,8 +67,8 @@ NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io) /* sync flush request */ -NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io) +NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io) { - struct smb2_request *req = smb2_flush_send(tree, io); - return smb2_flush_recv(req, io); + struct smb2_request *req = smb2_break_send(tree, io); + return smb2_break_recv(req, io); } diff --git a/source/libcli/smb2/config.mk b/source/libcli/smb2/config.mk index e95997d..18f6245 100644 --- a/source/libcli/smb2/config.mk +++ b/source/libcli/smb2/config.mk @@ -6,5 +6,5 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix libcli/smb2/, \ transport.o request.o negprot.o session.o tcon.o \ create.o close.o connect.o getinfo.o write.o read.o \ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \ - lock.o notify.o cancel.o keepalive.o) + lock.o notify.o cancel.o keepalive.o break.o) diff --git a/source/libcli/smb2/smb2.h b/source/libcli/smb2/smb2.h index 726df64..ae66a6e 100644 --- a/source/libcli/smb2/smb2.h +++ b/source/libcli/smb2/smb2.h @@ -21,6 +21,8 @@ #include "libcli/raw/request.h" +struct smb2_handle; + struct smb2_options { uint32_t timeout; }; @@ -58,6 +60,15 @@ struct smb2_transport { void *private; uint_t period; } idle; + + struct { + /* a oplock break request handler */ + bool (*handler)(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data); + /* private data passed to the oplock handler */ + void *private_data; + } oplock; }; diff --git a/source/libcli/smb2/transport.c b/source/libcli/smb2/transport.c index af19fcb..8eb60a0 100644 --- a/source/libcli/smb2/transport.c +++ b/source/libcli/smb2/transport.c @@ -140,6 +140,44 @@ void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status) } } +static bool smb2_handle_oplock_break(struct smb2_transport *transport, + const DATA_BLOB *blob) +{ + uint8_t *hdr; + uint16_t opcode; + uint64_t seqnum; + + hdr = blob->data+NBT_HDR_SIZE; + + if (blob->length < (SMB2_MIN_SIZE+0x18)) { + DEBUG(1,("Discarding smb2 oplock reply of size %u\n", + blob->length)); + return false; + } + + opcode = SVAL(hdr, SMB2_HDR_OPCODE); + seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); + + if ((opcode != SMB2_OP_BREAK) || + (seqnum != UINT64_MAX)) { + return false; + } + + if (transport->oplock.handler) { + uint8_t *body = hdr+SMB2_HDR_BODY; + struct smb2_handle h; + uint8_t level; + + level = CVAL(body, 0x02); + smb2_pull_handle(body+0x08, &h); + + transport->oplock.handler(transport, &h, level, + transport->oplock.private_data); + } + + return true; +} + /* we have a full request in our receive buffer - match it to a pending request and process @@ -167,6 +205,11 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob) goto error; } + if (smb2_handle_oplock_break(transport, &blob)) { + talloc_free(buffer); + return NT_STATUS_OK; + } + flags = IVAL(hdr, SMB2_HDR_FLAGS); seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); diff --git a/source/ntvfs/ntvfs_generic.c b/source/ntvfs/ntvfs_generic.c index 5d4bbf3..3653ad8 100644 --- a/source/ntvfs/ntvfs_generic.c +++ b/source/ntvfs/ntvfs_generic.c @@ -209,13 +209,13 @@ static NTSTATUS ntvfs_map_open_finish(struct ntvfs_module_context *ntvfs, case RAW_OPEN_SMB2: io->smb2.out.file.ntvfs = io2->generic.out.file.ntvfs; switch (io2->generic.out.oplock_level) { - case OPLOCK_BATCH: + case BATCH_OPLOCK_RETURN: io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; break; - case OPLOCK_EXCLUSIVE: + case EXCLUSIVE_OPLOCK_RETURN: io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; break; - case OPLOCK_LEVEL_II: + case LEVEL_II_OPLOCK_RETURN: io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_II; break; default: @@ -1043,6 +1043,23 @@ NTSTATUS ntvfs_map_lock(struct ntvfs_module_context *ntvfs, /* initialize output value */ lck->smb2.out.unknown1 = 0; break; + + case RAW_LOCK_SMB2_BREAK: + lck2->generic.level = RAW_LOCK_GENERIC; + lck2->generic.in.file.ntvfs = lck->smb2_break.in.file.ntvfs; + lck2->generic.in.mode = LOCKING_ANDX_OPLOCK_RELEASE | + ((lck->smb2_break.in.oplock_level << 8) & 0xFF00); + lck2->generic.in.timeout = 0; + lck2->generic.in.ulock_cnt = 0; + lck2->generic.in.lock_cnt = 0; + lck2->generic.in.locks = NULL; + + /* initialize output value */ + lck->smb2_break.out.oplock_level= lck->smb2_break.in.oplock_level; + lck->smb2_break.out.reserved = lck->smb2_break.in.reserved; + lck->smb2_break.out.reserved2 = lck->smb2_break.in.reserved2; + lck->smb2_break.out.file = lck->smb2_break.in.file; + break; } /* diff --git a/source/smb_server/smb2/fileio.c b/source/smb_server/smb2/fileio.c index af1a413..b6b35d3 100644 --- a/source/smb_server/smb2/fileio.c +++ b/source/smb_server/smb2/fileio.c @@ -410,7 +410,36 @@ void smb2srv_notify_recv(struct smb2srv_request *req) SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_notify(req->ntvfs, io)); } +static void smb2srv_break_send(struct ntvfs_request *ntvfs) +{ + struct smb2srv_request *req; + union smb_lock *io; + + SMB2SRV_CHECK_ASYNC_STATUS_ERR(io, union smb_lock); + SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x18, false, 0)); + + SCVAL(req->out.body, 0x02, io->smb2_break.out.oplock_level); + SCVAL(req->out.body, 0x03, io->smb2_break.out.reserved); + SIVAL(req->out.body, 0x04, io->smb2_break.out.reserved2); + smb2srv_push_handle(req->out.body, 0x08,io->smb2_break.out.file.ntvfs); + + smb2srv_send_reply(req); +} + void smb2srv_break_recv(struct smb2srv_request *req) { - smb2srv_send_error(req, NT_STATUS_NOT_IMPLEMENTED); + union smb_lock *io; + + SMB2SRV_CHECK_BODY_SIZE(req, 0x18, false); + SMB2SRV_TALLOC_IO_PTR(io, union smb_lock); + SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_break_send, NTVFS_ASYNC_STATE_MAY_ASYNC); + + io->smb2_break.level = RAW_LOCK_SMB2_BREAK; + io->smb2_break.in.oplock_level = CVAL(req->in.body, 0x02); + io->smb2_break.in.reserved = CVAL(req->in.body, 0x03); + io->smb2_break.in.reserved2 = IVAL(req->in.body, 0x04); + io->smb2_break.in.file.ntvfs = smb2srv_pull_handle(req, req->in.body, 0x08); + + SMB2SRV_CHECK_FILE_HANDLE(io->smb2_break.in.file.ntvfs); + SMB2SRV_CALL_NTVFS_BACKEND(ntvfs_lock(req->ntvfs, io)); } diff --git a/source/torture/smb2/config.mk b/source/torture/smb2/config.mk index 12d5edb..f3318bb 100644 --- a/source/torture/smb2/config.mk +++ b/source/torture/smb2/config.mk @@ -21,5 +21,6 @@ TORTURE_SMB2_OBJ_FILES = $(addprefix torture/smb2/, \ lock.o \ notify.o \ smb2.o \ - persistent_handles.o) + persistent_handles.o \ + oplocks.o) diff --git a/source/torture/smb2/oplocks.c b/source/torture/smb2/oplocks.c new file mode 100644 index 0000000..b0a1b31 --- /dev/null +++ b/source/torture/smb2/oplocks.c @@ -0,0 +1,178 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 oplocks + + Copyright (C) Stefan Metzmacher 2008 + + 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/security.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/smb2/proto.h" +#include "param/param.h" + +#define CHECK_VAL(v, correct) do { \ + if ((v) != (correct)) { \ + torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ + __location__, #v, (int)v, (int)correct); \ + ret = false; \ + }} while (0) + +#define CHECK_STATUS(status, correct) do { \ + if (!NT_STATUS_EQUAL(status, correct)) { \ + torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ + nt_errstr(status), nt_errstr(correct)); \ + ret = false; \ + goto done; \ + }} while (0) + +static struct { + struct smb2_handle handle; + uint8_t level; + struct smb2_break br; + int count; + int failures; +} break_info; + +static void torture_oplock_break_callback(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + ZERO_STRUCT(br); + status = smb2_break_recv(req, &break_info.br); + if (!NT_STATUS_IS_OK(status)) { + break_info.failures++; + } + + return; +} + +/* a oplock break request handler */ +static bool torture_oplock_handler(struct smb2_transport *transport, + const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + struct smb2_tree *tree = private_data; + const char *name; + struct smb2_request *req; + + break_info.handle = *handle; + break_info.level = level; + break_info.count++; + + switch (level) { + case SMB2_OPLOCK_LEVEL_II: + name = "level II"; + break; + case SMB2_OPLOCK_LEVEL_NONE: + name = "none"; + break; + default: + name = "unknown"; + break_info.failures++; + } + printf("Acking to %s [0x%02X] in oplock handler\n", + name, level); + + ZERO_STRUCT(break_info.br); + break_info.br.in.file.handle = *handle; + break_info.br.in.oplock_level = level; + break_info.br.in.reserved = 0; + break_info.br.in.reserved2 = 0; + + req = smb2_break_send(tree, &break_info.br); + req->async.fn = torture_oplock_break_callback; + req->async.private = NULL; + + return true; +} + +bool torture_smb2_oplock_batch1(struct torture_context *tctx, + struct smb2_tree *tree) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + struct smb2_handle h1, h2; + struct smb2_create io; + NTSTATUS status; + const char *fname = "oplock.dat"; + bool ret = true; + + tree->session->transport->oplock.handler = torture_oplock_handler; + tree->session->transport->oplock.private_data = tree; + + smb2_util_unlink(tree, fname); + + ZERO_STRUCT(break_info); + + ZERO_STRUCT(io); + io.in.security_flags = 0x00; + io.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.in.create_flags = 0x00000000; + io.in.reserved = 0x00000000; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_disposition = NTCREATEX_DISP_OPEN_IF; + io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io.in.fname = fname; + + status = smb2_create(tree, mem_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH); + /*CHECK_VAL(io.out.reserved, 0);*/ + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_CREATED); + CHECK_VAL(io.out.alloc_size, 0); + CHECK_VAL(io.out.size, 0); + CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); + CHECK_VAL(io.out.reserved2, 0); + CHECK_VAL(break_info.count, 0); + + h1 = io.out.file.handle; + + ZERO_STRUCT(io.in.blobs); + status = smb2_create(tree, mem_ctx, &io); + CHECK_VAL(break_info.count, 1); + CHECK_VAL(break_info.failures, 0); + CHECK_VAL(break_info.level, SMB2_OPLOCK_LEVEL_II); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_II); + /*CHECK_VAL(io.out.reserved, 0);*/ + CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); -- Samba Shared Repository