From fafe329f052789cbeaa075f0c1885559f7d0df7a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 15:17:01 -0700 Subject: [PATCH 01/57] s3:param: Correctly set up cli_maxprotocol, cli_minprotocol in our parameter block. Set to PROTOCOL_NT1, PROTOCOL_CORE by default. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9829 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/include/proto.h | 2 ++ source3/param/loadparm.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/source3/include/proto.h b/source3/include/proto.h index 13b1350..01e5727 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1188,6 +1188,8 @@ int lp_deadtime(void); bool lp_getwd_cache(void); int lp_srv_maxprotocol(void); int lp_srv_minprotocol(void); +int lp_cli_maxprotocol(void); +int lp_cli_minprotocol(void); int lp_security(void); int lp__server_role(void); int lp__security(void); diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index fa2f9b6..229ebd8 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -828,6 +828,8 @@ static void init_globals(bool reinit_globals) Globals.open_files_db_hash_size = SMB_OPEN_DATABASE_TDB_HASH_SIZE; Globals.srv_maxprotocol = PROTOCOL_SMB3_00; Globals.srv_minprotocol = PROTOCOL_LANMAN1; + Globals.cli_maxprotocol = PROTOCOL_NT1; + Globals.cli_minprotocol = PROTOCOL_CORE; Globals.security = SEC_USER; Globals.bEncryptPasswords = true; Globals.clientSchannel = Auto; -- 1.8.3 From 6824e459a4eba81263aafdb783dd6808680bb961 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 13:08:49 +0200 Subject: [PATCH 02/57] s3:client: avoid interpret_protocol() lp_set_cmdline("client max protocol",...) and lp_cli_maxprotocol() are the more generic solution. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/client/client.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source3/client/client.c b/source3/client/client.c index 1f63052..44e1efd 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -58,7 +58,7 @@ const char *cmd_ptr = NULL; static int io_bufsize = 524288; static int name_type = 0x20; -static int max_protocol = PROTOCOL_NT1; +static int max_protocol = -1; static int process_tok(char *tok); static int cmd_help(void); @@ -5456,7 +5456,7 @@ static int do_message_op(struct user_auth_info *a_info) } break; case 'm': - max_protocol = interpret_protocol(poptGetOptArg(pc), max_protocol); + lp_set_cmdline("client max protocol", poptGetOptArg(pc)); break; case 'T': /* We must use old option processing for this. Find the @@ -5574,6 +5574,8 @@ static int do_message_op(struct user_auth_info *a_info) /* Ensure we have a password (or equivalent). */ set_cmdline_auth_info_getpass(auth_info); + max_protocol = lp_cli_maxprotocol(); + if (tar_type) { if (cmdstr) process_command_string(cmdstr); -- 1.8.3 From fac7050dc66ecde75603a20d405d7f9b7d28089b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 13:10:13 +0200 Subject: [PATCH 03/57] s3:torture: avoid interpret_protocol() lp_set_cmdline("client max protocol",...) and lp_cli_maxprotocol() are the more generic solution. https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/torture/masktest.c | 5 +++-- source3/torture/torture.c | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/torture/masktest.c b/source3/torture/masktest.c index 8332e80..bca35aa 100644 --- a/source3/torture/masktest.c +++ b/source3/torture/masktest.c @@ -27,7 +27,7 @@ static fstring password; static fstring username; static int got_pass; -static int max_protocol = PROTOCOL_NT1; +static int max_protocol = -1; static bool showall = False; static bool old_list = False; static const char *maskchars = "<>\"?*abc."; @@ -510,7 +510,7 @@ static void usage(void) verbose++; break; case 'M': - max_protocol = interpret_protocol(optarg, max_protocol); + lp_set_cmdline("client max protocol", optarg); break; case 'U': fstrcpy(username,optarg); @@ -548,6 +548,7 @@ static void usage(void) argc -= optind; argv += optind; + max_protocol = lp_cli_maxprotocol(); cli = connect_one(share); if (!cli) { diff --git a/source3/torture/torture.c b/source3/torture/torture.c index b0b498f..4a8e3ed 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -46,7 +46,6 @@ extern char *optarg; extern int optind; fstring host, workgroup, share, password, username, myname; -static int max_protocol = PROTOCOL_NT1; static const char *sockops="TCP_NODELAY"; int torture_nprocs=1; static int port_to_use=0; @@ -9759,7 +9758,7 @@ static void usage(void) fstrcpy(workgroup,optarg); break; case 'm': - max_protocol = interpret_protocol(optarg, max_protocol); + lp_set_cmdline("client max protocol", optarg); break; case 'N': torture_nprocs = atoi(optarg); -- 1.8.3 From 1f388b345abd9783f5e0df3b2fb9a2574732db2e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 13:11:41 +0200 Subject: [PATCH 04/57] s3:lib: remove unused interpret_protocol() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/include/proto.h | 1 - source3/lib/util.c | 25 ------------------------- 2 files changed, 26 deletions(-) diff --git a/source3/include/proto.h b/source3/include/proto.h index 01e5727..078d039 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -401,7 +401,6 @@ void add_to_large_array(TALLOC_CTX *mem_ctx, size_t element_size, ssize_t *array_size); char *get_myname(TALLOC_CTX *ctx); char *get_mydnsdomname(TALLOC_CTX *ctx); -int interpret_protocol(const char *str,int def); char *automount_lookup(TALLOC_CTX *ctx, const char *user_name); char *automount_lookup(TALLOC_CTX *ctx, const char *user_name); bool process_exists(const struct server_id pid); diff --git a/source3/lib/util.c b/source3/lib/util.c index 93aab3c..bf6c8c5 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -559,31 +559,6 @@ char *get_mydnsdomname(TALLOC_CTX *ctx) } } -/**************************************************************************** - Interpret a protocol description string, with a default. -****************************************************************************/ - -int interpret_protocol(const char *str,int def) -{ - if (strequal(str,"NT1")) - return(PROTOCOL_NT1); - if (strequal(str,"LANMAN2")) - return(PROTOCOL_LANMAN2); - if (strequal(str,"LANMAN1")) - return(PROTOCOL_LANMAN1); - if (strequal(str,"CORE")) - return(PROTOCOL_CORE); - if (strequal(str,"COREPLUS")) - return(PROTOCOL_COREPLUS); - if (strequal(str,"CORE+")) - return(PROTOCOL_COREPLUS); - - DEBUG(0,("Unrecognised protocol level %s\n",str)); - - return(def); -} - - #if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT)) /****************************************************************** Remove any mount options such as -rsize=2048,wsize=2048 etc. -- 1.8.3 From 0e82981dd6ca13599d65f0dad005a4f4b03d69c4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 Aug 2013 11:35:44 -0700 Subject: [PATCH 05/57] s3:libsmb: Modify cli_start_connection_connected() to use lp_cli_minprotocol()/lp_cli_maxprotocol() instead of hard coding PROTOCOL_CORE, PROTOCOL_NT1. https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/cliconnect.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 07d9a8a..14cdf96 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -3118,7 +3118,8 @@ static void cli_start_connection_connected(struct tevent_req *subreq) subreq = smbXcli_negprot_send(state, state->ev, state->cli->conn, state->cli->timeout, - PROTOCOL_CORE, PROTOCOL_NT1); + lp_cli_minprotocol(), + lp_cli_maxprotocol()); if (tevent_req_nomem(subreq, req)) { return; } -- 1.8.3 From c5b1aaba133bc982ad3688f649b70d334099ff6e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 9 Aug 2013 11:15:48 -0700 Subject: [PATCH 06/57] s3:libsmb: Ensure we ask for DEFAULT_SMB2_MAX_CREDITS on successful negprot. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/cliconnect.c | 10 ++++++++++ source3/libsmb/clidfs.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 14cdf96..f963edf 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -3130,6 +3130,8 @@ static void cli_start_connection_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); + struct cli_start_connection_state *state = tevent_req_data( + req, struct cli_start_connection_state); NTSTATUS status; status = smbXcli_negprot_recv(subreq); @@ -3137,6 +3139,13 @@ static void cli_start_connection_done(struct tevent_req *subreq) if (tevent_req_nterror(req, status)) { return; } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(state->cli->conn, + DEFAULT_SMB2_MAX_CREDITS); + } + tevent_req_done(req); } @@ -3151,6 +3160,7 @@ static NTSTATUS cli_start_connection_recv(struct tevent_req *req, return status; } *output_cli = state->cli; + return NT_STATUS_OK; } diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index 95f8817..1ed6ac5 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -160,6 +160,11 @@ static NTSTATUS do_connect(TALLOC_CTX *ctx, return status; } + if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS); + } + username = get_cmdline_auth_info_username(auth_info); password = get_cmdline_auth_info_password(auth_info); -- 1.8.3 From e11d697af570258597b7cd6568a1c50b14160b3d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 13:46:34 +0200 Subject: [PATCH 07/57] s3:lib/netapi: make use of lp_cli_maxprotocol() https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/lib/netapi/cm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/lib/netapi/cm.c b/source3/lib/netapi/cm.c index cbc93d9..1cfdccf 100644 --- a/source3/lib/netapi/cm.c +++ b/source3/lib/netapi/cm.c @@ -111,7 +111,7 @@ static WERROR libnetapi_open_ipc_connection(struct libnetapi_ctx *ctx, server_name, "IPC$", auth_info, false, false, - PROTOCOL_NT1, + lp_cli_maxprotocol(), 0, 0x20, &cli_ipc); if (NT_STATUS_IS_OK(status)) { cli_set_username(cli_ipc, ctx->username); -- 1.8.3 From f0f0a782fa414ede5edb878673a9612120250fca Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 13:48:31 +0200 Subject: [PATCH 08/57] s3:winbindd: make use of lp_cli_{min,max}protocol() This changes winbindd back to use NT1 as defeault. https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/winbindd/winbindd_cm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source3/winbindd/winbindd_cm.c b/source3/winbindd/winbindd_cm.c index d89ca95..0d4b774 100644 --- a/source3/winbindd/winbindd_cm.c +++ b/source3/winbindd/winbindd_cm.c @@ -841,8 +841,9 @@ static NTSTATUS cm_prepare_connection(const struct winbindd_domain *domain, cli_set_timeout(*cli, 10000); /* 10 seconds */ - result = smbXcli_negprot((*cli)->conn, (*cli)->timeout, PROTOCOL_CORE, - PROTOCOL_LATEST); + result = smbXcli_negprot((*cli)->conn, (*cli)->timeout, + lp_cli_minprotocol(), + lp_cli_maxprotocol()); if (!NT_STATUS_IS_OK(result)) { DEBUG(1, ("cli_negprot failed: %s\n", nt_errstr(result))); -- 1.8.3 From 9490237628c86ce2997f21a4649a267b66e63cc1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 14:00:45 +0200 Subject: [PATCH 09/57] s3:libsmb: use lp_cli_minprotocol() in do_connect() https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clidfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c index 1ed6ac5..1d92843 100644 --- a/source3/libsmb/clidfs.c +++ b/source3/libsmb/clidfs.c @@ -150,7 +150,8 @@ static NTSTATUS do_connect(TALLOC_CTX *ctx, } DEBUG(4,(" session request ok\n")); - status = smbXcli_negprot(c->conn, c->timeout, PROTOCOL_CORE, + status = smbXcli_negprot(c->conn, c->timeout, + lp_cli_minprotocol(), max_protocol); if (!NT_STATUS_IS_OK(status)) { -- 1.8.3 From 95a8b98fdc8affc46073060e27eae16a176d49a3 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Sun, 11 Aug 2013 14:01:36 +0200 Subject: [PATCH 10/57] s3:libsmb: make use of lp_cli_{min,max}protocol() in SMBC_server_internal() https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/libsmb_server.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c index 5c665bf..35b31d8 100644 --- a/source3/libsmb/libsmb_server.c +++ b/source3/libsmb/libsmb_server.c @@ -449,9 +449,9 @@ SMBC_server_internal(TALLOC_CTX *ctx, cli_set_timeout(c, smbc_getTimeout(context)); - status = smbXcli_negprot(c->conn, c->timeout, PROTOCOL_CORE, - PROTOCOL_NT1); - + status = smbXcli_negprot(c->conn, c->timeout, + lp_cli_minprotocol(), + lp_cli_maxprotocol()); if (!NT_STATUS_IS_OK(status)) { cli_shutdown(c); errno = ETIMEDOUT; -- 1.8.3 From 24de5d88131062912e3df4ffe6381778bc769b0c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 Aug 2013 11:43:16 -0700 Subject: [PATCH 11/57] s3:smbcacls: Add -m option to smbcacls. https://bugzilla.samba.org/show_bug.cgi?id=9514 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher Reviewed-by: Stefan Metzmacher --- source3/utils/smbcacls.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c index d3d60bc..20ce73d 100644 --- a/source3/utils/smbcacls.c +++ b/source3/utils/smbcacls.c @@ -1385,6 +1385,7 @@ static struct cli_state *connect_one(struct user_auth_info *auth_info, }, { "test-args", 't', POPT_ARG_NONE, &test_args, 1, "Test arguments"}, { "domain-sid", 0, POPT_ARG_STRING, &domain_sid, 0, "Domain SID for sddl", "SID"}, + { "max-protocol", 'm', POPT_ARG_STRING, NULL, 'm', "Set the max protocol level", "LEVEL" }, POPT_COMMON_SAMBA POPT_COMMON_CONNECTION POPT_COMMON_CREDENTIALS @@ -1455,6 +1456,9 @@ static struct cli_state *connect_one(struct user_auth_info *auth_info, owner_username = poptGetOptArg(pc); change_mode = REQUEST_INHERIT; break; + case 'm': + lp_set_cmdline("client max protocol", poptGetOptArg(pc)); + break; } } -- 1.8.3 From 6f9e8b32dcede5416f6e8ab8788191b89a28627a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 08:12:08 +0200 Subject: [PATCH 12/57] libcli/smb: fix the credit handling on a SMB1 => SMB2 negotiate Our cur_credit value had 1 credit too many in the case of an SMB1 => SMB2 upgrade. When we max out the credits the server disconnected the connection. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 7397f83..3b24f9a 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -4405,9 +4405,14 @@ static NTSTATUS smbXcli_negprot_dispatch_incoming(struct smbXcli_conn *conn, /* * we got an SMB2 answer, which consumed sequence number 0 - * so we need to use 1 as the next one + * so we need to use 1 as the next one. + * + * we also need to set the current credits to 0 + * as we consumed the initial one. The SMB2 answer + * hopefully grant us a new credit. */ conn->smb2.mid = 1; + conn->smb2.cur_credits = 0; tevent_req_set_callback(subreq, smbXcli_negprot_smb2_done, req); conn->dispatch_incoming = smb2cli_conn_dispatch_incoming; return smb2cli_conn_dispatch_incoming(conn, tmp_mem, inbuf); -- 1.8.3 From 3f90a9d4227e942e45c50b61e03469659bdb53d2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 14:41:24 -0700 Subject: [PATCH 13/57] libcli/smb: Fix smb2cli_write_recv() and smb2cli_write() to return the bytes written. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- libcli/smb/smb2cli_write.c | 30 +++++++++++++++++++++++++----- libcli/smb/smbXcli_base.h | 6 ++++-- source3/libsmb/cli_np_tstream.c | 7 +++++-- source3/torture/test_smb2.c | 14 +++++++------- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/libcli/smb/smb2cli_write.c b/libcli/smb/smb2cli_write.c index 8e65460..89137bd 100644 --- a/libcli/smb/smb2cli_write.c +++ b/libcli/smb/smb2cli_write.c @@ -26,6 +26,7 @@ struct smb2cli_write_state { uint8_t fixed[48]; uint8_t dyn_pad[1]; + uint32_t written; }; static void smb2cli_write_done(struct tevent_req *subreq); @@ -94,7 +95,11 @@ static void smb2cli_write_done(struct tevent_req *subreq) struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct smb2cli_write_state *state = + tevent_req_data(req, + struct smb2cli_write_state); NTSTATUS status; + struct iovec *iov; static const struct smb2cli_req_expected_response expected[] = { { .status = NT_STATUS_OK, @@ -102,18 +107,32 @@ static void smb2cli_write_done(struct tevent_req *subreq) } }; - status = smb2cli_req_recv(subreq, NULL, NULL, + status = smb2cli_req_recv(subreq, state, &iov, expected, ARRAY_SIZE(expected)); TALLOC_FREE(subreq); if (tevent_req_nterror(req, status)) { return; } + state->written = IVAL(iov[1].iov_base, 4); tevent_req_done(req); } -NTSTATUS smb2cli_write_recv(struct tevent_req *req) +NTSTATUS smb2cli_write_recv(struct tevent_req *req, uint32_t *written) { - return tevent_req_simple_recv_ntstatus(req); + struct smb2cli_write_state *state = + tevent_req_data(req, + struct smb2cli_write_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + if (written) { + *written = state->written; + } + tevent_req_received(req); + return NT_STATUS_OK; } NTSTATUS smb2cli_write(struct smbXcli_conn *conn, @@ -126,7 +145,8 @@ NTSTATUS smb2cli_write(struct smbXcli_conn *conn, uint64_t fid_volatile, uint32_t remaining_bytes, uint32_t flags, - const uint8_t *data) + const uint8_t *data, + uint32_t *written) { TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; @@ -155,7 +175,7 @@ NTSTATUS smb2cli_write(struct smbXcli_conn *conn, if (!tevent_req_poll_ntstatus(req, ev, &status)) { goto fail; } - status = smb2cli_write_recv(req); + status = smb2cli_write_recv(req, written); fail: TALLOC_FREE(frame); return status; diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index f7b60d3..997869b 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -429,7 +429,8 @@ struct tevent_req *smb2cli_write_send(TALLOC_CTX *mem_ctx, uint32_t remaining_bytes, uint32_t flags, const uint8_t *data); -NTSTATUS smb2cli_write_recv(struct tevent_req *req); +NTSTATUS smb2cli_write_recv(struct tevent_req *req, + uint32_t *written); NTSTATUS smb2cli_write(struct smbXcli_conn *conn, uint32_t timeout_msec, struct smbXcli_session *session, @@ -440,7 +441,8 @@ NTSTATUS smb2cli_write(struct smbXcli_conn *conn, uint64_t fid_volatile, uint32_t remaining_bytes, uint32_t flags, - const uint8_t *data); + const uint8_t *data, + uint32_t *written); struct tevent_req *smb2cli_flush_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/libsmb/cli_np_tstream.c b/source3/libsmb/cli_np_tstream.c index c7ec664..07835a5 100644 --- a/source3/libsmb/cli_np_tstream.c +++ b/source3/libsmb/cli_np_tstream.c @@ -527,8 +527,11 @@ static void tstream_cli_np_writev_write_done(struct tevent_req *subreq) if (cli_nps->is_smb1) { status = cli_write_andx_recv(subreq, &written); } else { - status = smb2cli_write_recv(subreq); - written = cli_nps->write.ofs; // TODO: get the value from the server + uint32_t smb2_written; + status = smb2cli_write_recv(subreq, &smb2_written); + if (NT_STATUS_IS_OK(status)) { + written = smb2_written; + } } TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { diff --git a/source3/torture/test_smb2.c b/source3/torture/test_smb2.c index ec695da..7ca9f49 100644 --- a/source3/torture/test_smb2.c +++ b/source3/torture/test_smb2.c @@ -91,7 +91,7 @@ bool run_smb2_basic(int dummy) status = smb2cli_write(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_write returned %s\n", nt_errstr(status)); return false; @@ -349,7 +349,7 @@ bool run_smb2_session_reconnect(int dummy) status = smb2cli_write(cli1->conn, cli1->timeout, cli1->smb2.session, cli1->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_write returned %s\n", nt_errstr(status)); return false; @@ -545,7 +545,7 @@ bool run_smb2_session_reconnect(int dummy) status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session, cli2->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) && !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) { @@ -605,7 +605,7 @@ bool run_smb2_session_reconnect(int dummy) status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session, cli1->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_FILE_CLOSED) && !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) { @@ -670,7 +670,7 @@ bool run_smb2_session_reconnect(int dummy) status = smb2cli_write(cli2->conn, cli2->timeout, cli2->smb2.session, cli2->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_write returned %s\n", nt_errstr(status)); return false; @@ -765,7 +765,7 @@ bool run_smb2_tcon_dependence(int dummy) status = smb2cli_write(cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_write returned %s\n", nt_errstr(status)); return false; @@ -1172,7 +1172,7 @@ bool run_smb2_multi_channel(int dummy) status = smb2cli_write(cli1->conn, cli1->timeout, cli1->smb2.session, cli1->smb2.tcon, strlen(hello), 0, fid_persistent, - fid_volatile, 0, 0, (const uint8_t *)hello); + fid_volatile, 0, 0, (const uint8_t *)hello, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_write returned %s\n", nt_errstr(status)); return false; -- 1.8.3 From 99b8b1ad4e7489f68a8399b3741084d08acc5eb5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 15:01:50 -0700 Subject: [PATCH 14/57] libcli/smb: Change smb2cli_create() and smb2cli_create_recv() to return a parameter blob of the newly opened/created file. Will use in the smb2 client code. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- libcli/smb/smb2_create_blob.h | 12 +++++++++++ libcli/smb/smb2cli_create.c | 39 ++++++++++++++++----------------- libcli/smb/smbXcli_base.h | 7 ++++-- source3/libsmb/cli_np_tstream.c | 3 ++- source3/torture/test_smb2.c | 48 +++++++++++++++++++++++++++-------------- 5 files changed, 69 insertions(+), 40 deletions(-) diff --git a/libcli/smb/smb2_create_blob.h b/libcli/smb/smb2_create_blob.h index 008befe..2f915b3 100644 --- a/libcli/smb/smb2_create_blob.h +++ b/libcli/smb/smb2_create_blob.h @@ -33,6 +33,18 @@ struct smb2_create_blobs { struct smb2_create_blob *blobs; }; +struct smb2_create_returns { + uint8_t oplock_level; + uint32_t create_action; + NTTIME creation_time; + NTTIME last_access_time; + NTTIME last_write_time; + NTTIME change_time; + uint64_t allocation_size; + uint64_t end_of_file; + uint32_t file_attributes; +}; + /* parse a set of SMB2 create blobs */ diff --git a/libcli/smb/smb2cli_create.c b/libcli/smb/smb2cli_create.c index 627bdcb..020a468 100644 --- a/libcli/smb/smb2cli_create.c +++ b/libcli/smb/smb2cli_create.c @@ -27,17 +27,9 @@ struct smb2cli_create_state { uint8_t fixed[56]; - uint8_t oplock_level; - uint32_t create_action; - NTTIME creation_time; - NTTIME last_access_time; - NTTIME last_write_time; - NTTIME change_time; - uint64_t allocation_size; - uint64_t end_of_file; - uint32_t file_attributes; uint64_t fid_persistent; uint64_t fid_volatile; + struct smb2_create_returns cr; struct smb2_create_blobs blobs; }; @@ -179,15 +171,15 @@ static void smb2cli_create_done(struct tevent_req *subreq) body = (uint8_t *)iov[1].iov_base; - state->oplock_level = CVAL(body, 2); - state->create_action = IVAL(body, 4); - state->creation_time = BVAL(body, 8); - state->last_access_time = BVAL(body, 16); - state->last_write_time = BVAL(body, 24); - state->change_time = BVAL(body, 32); - state->allocation_size = BVAL(body, 40); - state->end_of_file = BVAL(body, 48); - state->file_attributes = IVAL(body, 56); + state->cr.oplock_level = CVAL(body, 2); + state->cr.create_action = IVAL(body, 4); + state->cr.creation_time = BVAL(body, 8); + state->cr.last_access_time = BVAL(body, 16); + state->cr.last_write_time = BVAL(body, 24); + state->cr.change_time = BVAL(body, 32); + state->cr.allocation_size = BVAL(body, 40); + state->cr.end_of_file = BVAL(body, 48); + state->cr.file_attributes = IVAL(body, 56); state->fid_persistent = BVAL(body, 64); state->fid_volatile = BVAL(body, 72); @@ -213,7 +205,8 @@ static void smb2cli_create_done(struct tevent_req *subreq) NTSTATUS smb2cli_create_recv(struct tevent_req *req, uint64_t *fid_persistent, - uint64_t *fid_volatile) + uint64_t *fid_volatile, + struct smb2_create_returns *cr) { struct smb2cli_create_state *state = tevent_req_data(req, @@ -225,6 +218,9 @@ NTSTATUS smb2cli_create_recv(struct tevent_req *req, } *fid_persistent = state->fid_persistent; *fid_volatile = state->fid_volatile; + if (cr) { + *cr = state->cr; + } return NT_STATUS_OK; } @@ -242,7 +238,8 @@ NTSTATUS smb2cli_create(struct smbXcli_conn *conn, uint32_t create_options, struct smb2_create_blobs *blobs, uint64_t *fid_persistent, - uint64_t *fid_volatile) + uint64_t *fid_volatile, + struct smb2_create_returns *cr) { TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; @@ -273,7 +270,7 @@ NTSTATUS smb2cli_create(struct smbXcli_conn *conn, if (!tevent_req_poll_ntstatus(req, ev, &status)) { goto fail; } - status = smb2cli_create_recv(req, fid_persistent, fid_volatile); + status = smb2cli_create_recv(req, fid_persistent, fid_volatile, cr); fail: TALLOC_FREE(frame); return status; diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 997869b..a497e13 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -28,6 +28,7 @@ struct smb_trans_enc_state; struct GUID; struct iovec; struct smb2_create_blobs; +struct smb2_create_returns; struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx, int fd, @@ -353,7 +354,8 @@ struct tevent_req *smb2cli_create_send( struct smb2_create_blobs *blobs); NTSTATUS smb2cli_create_recv(struct tevent_req *req, uint64_t *fid_persistent, - uint64_t *fid_volatile); + uint64_t *fid_volatile, + struct smb2_create_returns *cr); NTSTATUS smb2cli_create(struct smbXcli_conn *conn, uint32_t timeout_msec, struct smbXcli_session *session, @@ -368,7 +370,8 @@ NTSTATUS smb2cli_create(struct smbXcli_conn *conn, uint32_t create_options, struct smb2_create_blobs *blobs, uint64_t *fid_persistent, - uint64_t *fid_volatile); + uint64_t *fid_volatile, + struct smb2_create_returns *cr); struct tevent_req *smb2cli_close_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/libsmb/cli_np_tstream.c b/source3/libsmb/cli_np_tstream.c index 07835a5..f3a9962 100644 --- a/source3/libsmb/cli_np_tstream.c +++ b/source3/libsmb/cli_np_tstream.c @@ -208,7 +208,8 @@ static void tstream_cli_np_open_done(struct tevent_req *subreq) } else { status = smb2cli_create_recv(subreq, &state->fid_persistent, - &state->fid_volatile); + &state->fid_volatile, + NULL); } TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { diff --git a/source3/torture/test_smb2.c b/source3/torture/test_smb2.c index 7ca9f49..3bcd2ed 100644 --- a/source3/torture/test_smb2.c +++ b/source3/torture/test_smb2.c @@ -83,7 +83,8 @@ bool run_smb2_basic(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create returned %s\n", nt_errstr(status)); return false; @@ -145,7 +146,8 @@ bool run_smb2_basic(int dummy) FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create returned %s\n", nt_errstr(status)); return false; @@ -341,7 +343,8 @@ bool run_smb2_session_reconnect(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create on cli1 %s\n", nt_errstr(status)); return false; @@ -575,7 +578,8 @@ bool run_smb2_session_reconnect(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) && !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) { printf("smb2cli_create on cli2 %s\n", nt_errstr(status)); @@ -635,7 +639,8 @@ bool run_smb2_session_reconnect(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED) && !NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_NAME_DELETED)) { @@ -662,7 +667,8 @@ bool run_smb2_session_reconnect(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create on cli2 %s\n", nt_errstr(status)); return false; @@ -757,7 +763,8 @@ bool run_smb2_tcon_dependence(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create on cli %s\n", nt_errstr(status)); return false; @@ -1164,7 +1171,8 @@ bool run_smb2_multi_channel(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create on cli2 %s\n", nt_errstr(status)); return false; @@ -1324,7 +1332,8 @@ bool run_smb2_multi_channel(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; @@ -1341,7 +1350,8 @@ bool run_smb2_multi_channel(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; @@ -1358,7 +1368,8 @@ bool run_smb2_multi_channel(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; @@ -1489,7 +1500,8 @@ bool run_smb2_session_reauth(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; @@ -1508,7 +1520,8 @@ bool run_smb2_session_reauth(int dummy) FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &dir_persistent, - &dir_volatile); + &dir_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create returned %s\n", nt_errstr(status)); return false; @@ -1691,7 +1704,8 @@ bool run_smb2_session_reauth(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; @@ -1710,7 +1724,8 @@ bool run_smb2_session_reauth(int dummy) FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &dir_persistent, - &dir_volatile); + &dir_volatile, + NULL); if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) { printf("smb2cli_create returned %s\n", nt_errstr(status)); return false; @@ -1865,7 +1880,8 @@ bool run_smb2_session_reauth(int dummy) FILE_DELETE_ON_CLOSE, /* create_options, */ NULL, /* smb2_create_blobs *blobs */ &fid_persistent, - &fid_volatile); + &fid_volatile, + NULL); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_create %s\n", nt_errstr(status)); return false; -- 1.8.3 From ef9c90ad9d4ff9c9ef74af4a6181ec8e925056e0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 09:50:29 +0200 Subject: [PATCH 15/57] libcli/smb: calculate the credit charge on the input and output dyn_len Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 3b24f9a..7d7df14 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -246,6 +246,11 @@ struct smbXcli_req_state { */ struct iovec *recv_iov; + /* + * the expected max for the response dyn_len + */ + uint32_t max_dyn_len; + uint16_t credit_charge; bool should_sign; @@ -2788,7 +2793,12 @@ NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, } if (state->conn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) { - charge = (MAX(state->smb2.dyn_len, 1) - 1)/ 65536 + 1; + uint32_t max_dyn_len = 1; + + max_dyn_len = MAX(max_dyn_len, state->smb2.dyn_len); + max_dyn_len = MAX(max_dyn_len, state->smb2.max_dyn_len); + + charge = (max_dyn_len - 1)/ 65536 + 1; } else { charge = 1; } -- 1.8.3 From 003fd7a2b5ca58ce668c4d84d5940de3597098ff Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 09:54:42 +0200 Subject: [PATCH 16/57] libcli/smb: pass max_dyn_len to smb2cli_req_create() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 11 ++++++++--- libcli/smb/smbXcli_base.h | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 7d7df14..0271691 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -2495,7 +2495,7 @@ static bool smb2cli_req_cancel(struct tevent_req *req) 0, /* timeout */ tcon, session, fixed, fixed_len, - NULL, 0); + NULL, 0, 0); if (subreq == NULL) { return false; } @@ -2544,7 +2544,8 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len) + uint32_t dyn_len, + uint32_t max_dyn_len) { struct tevent_req *req; struct smbXcli_req_state *state; @@ -2617,6 +2618,7 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, state->smb2.fixed_len = fixed_len; state->smb2.dyn = dyn; state->smb2.dyn_len = dyn_len; + state->smb2.max_dyn_len = max_dyn_len; if (state->smb2.should_encrypt) { SIVAL(state->smb2.transform, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC); @@ -2986,12 +2988,15 @@ struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, { struct tevent_req *req; NTSTATUS status; + uint32_t max_dyn_len = 0; req = smb2cli_req_create(mem_ctx, ev, conn, cmd, additional_flags, clear_flags, timeout_msec, tcon, session, - fixed, fixed_len, dyn, dyn_len); + fixed, fixed_len, + dyn, dyn_len, + max_dyn_len); if (req == NULL) { return NULL; } diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index a497e13..017c0f0 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -218,7 +218,8 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len); + uint32_t dyn_len, + uint32_t max_dyn_len); void smb2cli_req_set_notify_async(struct tevent_req *req); NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs, int num_reqs); -- 1.8.3 From a5b4aafa171f364694ab15e7600f1afd3402a9b1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 10:25:52 +0200 Subject: [PATCH 17/57] libcli/smb: pass max_dyn_len to smb2cli_req_send() This way we can calculate the correct credit charge for requests with large output buffers. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smb2cli_close.c | 3 ++- libcli/smb/smb2cli_create.c | 13 ++++++++++++- libcli/smb/smb2cli_echo.c | 3 ++- libcli/smb/smb2cli_flush.c | 3 ++- libcli/smb/smb2cli_ioctl.c | 13 ++++++++++++- libcli/smb/smb2cli_query_directory.c | 3 ++- libcli/smb/smb2cli_query_info.c | 3 ++- libcli/smb/smb2cli_read.c | 3 ++- libcli/smb/smb2cli_session.c | 6 ++++-- libcli/smb/smb2cli_set_info.c | 3 ++- libcli/smb/smb2cli_write.c | 3 ++- libcli/smb/smbXcli_base.c | 7 ++++--- libcli/smb/smbXcli_base.h | 3 ++- source3/libsmb/smb2cli_tcon.c | 6 ++++-- source4/libcli/smb2/transport.c | 9 ++++++--- 15 files changed, 60 insertions(+), 21 deletions(-) diff --git a/libcli/smb/smb2cli_close.c b/libcli/smb/smb2cli_close.c index ed15a20..5e31056 100644 --- a/libcli/smb/smb2cli_close.c +++ b/libcli/smb/smb2cli_close.c @@ -60,7 +60,8 @@ struct tevent_req *smb2cli_close_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - NULL, 0); + NULL, 0, /* dyn* */ + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_create.c b/libcli/smb/smb2cli_create.c index 020a468..3f8d672 100644 --- a/libcli/smb/smb2cli_create.c +++ b/libcli/smb/smb2cli_create.c @@ -62,6 +62,7 @@ struct tevent_req *smb2cli_create_send( size_t blobs_offset; uint8_t *dyn; size_t dyn_len; + size_t max_dyn_len; req = tevent_req_create(mem_ctx, &state, struct smb2cli_create_state); @@ -129,13 +130,23 @@ struct tevent_req *smb2cli_create_send( data_blob_free(&blob); } + /* + * We use max_dyn_len = 0 + * as we don't explicitly ask for any output length. + * + * But it's still possible for the server to return + * large create blobs. + */ + max_dyn_len = 0; + subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_CREATE, 0, 0, /* flags */ timeout_msec, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + max_dyn_len); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_echo.c b/libcli/smb/smb2cli_echo.c index 29cbf26..39c592c 100644 --- a/libcli/smb/smb2cli_echo.c +++ b/libcli/smb/smb2cli_echo.c @@ -53,7 +53,8 @@ struct tevent_req *smb2cli_echo_send(TALLOC_CTX *mem_ctx, NULL, /* tcon */ NULL, /* session */ state->fixed, sizeof(state->fixed), - NULL, 0); + NULL, 0, /* dyn* */ + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_flush.c b/libcli/smb/smb2cli_flush.c index 0ca2699..f014720 100644 --- a/libcli/smb/smb2cli_flush.c +++ b/libcli/smb/smb2cli_flush.c @@ -58,7 +58,8 @@ struct tevent_req *smb2cli_flush_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - NULL, 0); + NULL, 0, /* dyn* */ + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_ioctl.c b/libcli/smb/smb2cli_ioctl.c index 15a990c..8de7635 100644 --- a/libcli/smb/smb2cli_ioctl.c +++ b/libcli/smb/smb2cli_ioctl.c @@ -61,6 +61,8 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx, uint32_t output_buffer_offset = 0; uint32_t output_buffer_length = 0; uint32_t pad_length = 0; + uint64_t tmp64; + uint32_t max_dyn_len = 0; req = tevent_req_create(mem_ctx, &state, struct smb2cli_ioctl_state); @@ -70,6 +72,14 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx, state->max_input_length = in_max_input_length; state->max_output_length = in_max_output_length; + tmp64 = in_max_input_length; + tmp64 += in_max_output_length; + if (tmp64 > UINT32_MAX) { + max_dyn_len = UINT32_MAX; + } else { + max_dyn_len = tmp64; + } + if (in_input_buffer) { input_buffer_offset = SMB2_HDR_BODY+0x38; input_buffer_length = in_input_buffer->length; @@ -139,7 +149,8 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + max_dyn_len); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_query_directory.c b/libcli/smb/smb2cli_query_directory.c index 32f5bee..bccc529 100644 --- a/libcli/smb/smb2cli_query_directory.c +++ b/libcli/smb/smb2cli_query_directory.c @@ -93,7 +93,8 @@ struct tevent_req *smb2cli_query_directory_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + outbuf_len); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_query_info.c b/libcli/smb/smb2cli_query_info.c index 9ec16b5..454f25a 100644 --- a/libcli/smb/smb2cli_query_info.c +++ b/libcli/smb/smb2cli_query_info.c @@ -96,7 +96,8 @@ struct tevent_req *smb2cli_query_info_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + in_max_output_length); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_read.c b/libcli/smb/smb2cli_read.c index 0adb403..4a31622 100644 --- a/libcli/smb/smb2cli_read.c +++ b/libcli/smb/smb2cli_read.c @@ -72,7 +72,8 @@ struct tevent_req *smb2cli_read_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - state->dyn_pad, sizeof(state->dyn_pad)); + state->dyn_pad, sizeof(state->dyn_pad), + length); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_session.c b/libcli/smb/smb2cli_session.c index 537c171..4418a0d 100644 --- a/libcli/smb/smb2cli_session.c +++ b/libcli/smb/smb2cli_session.c @@ -102,7 +102,8 @@ struct tevent_req *smb2cli_session_setup_send(TALLOC_CTX *mem_ctx, NULL, /* tcon */ session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + UINT16_MAX); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -258,7 +259,8 @@ struct tevent_req *smb2cli_logoff_send(TALLOC_CTX *mem_ctx, NULL, /* tcon */ session, state->fixed, sizeof(state->fixed), - NULL, 0); + NULL, 0, /* dyn* */ + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_set_info.c b/libcli/smb/smb2cli_set_info.c index d5c7e58..6871370 100644 --- a/libcli/smb/smb2cli_set_info.c +++ b/libcli/smb/smb2cli_set_info.c @@ -88,7 +88,8 @@ struct tevent_req *smb2cli_set_info_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smb2cli_write.c b/libcli/smb/smb2cli_write.c index 89137bd..6d0a0aa 100644 --- a/libcli/smb/smb2cli_write.c +++ b/libcli/smb/smb2cli_write.c @@ -82,7 +82,8 @@ struct tevent_req *smb2cli_write_send(TALLOC_CTX *mem_ctx, tcon, session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 0271691..2562442 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -2984,11 +2984,11 @@ struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len) + uint32_t dyn_len, + uint32_t max_dyn_len) { struct tevent_req *req; NTSTATUS status; - uint32_t max_dyn_len = 0; req = smb2cli_req_create(mem_ctx, ev, conn, cmd, additional_flags, clear_flags, @@ -4260,7 +4260,8 @@ static struct tevent_req *smbXcli_negprot_smb2_subreq(struct smbXcli_negprot_sta state->timeout_msec, NULL, NULL, /* tcon, session */ state->smb2.fixed, sizeof(state->smb2.fixed), - state->smb2.dyn, dialect_count*2); + state->smb2.dyn, dialect_count*2, + UINT16_MAX); /* max_dyn_len */ } static void smbXcli_negprot_smb2_done(struct tevent_req *subreq) diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 017c0f0..4ce39c0 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -242,7 +242,8 @@ struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx, const uint8_t *fixed, uint16_t fixed_len, const uint8_t *dyn, - uint32_t dyn_len); + uint32_t dyn_len, + uint32_t max_dyn_len); NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct iovec **piov, const struct smb2cli_req_expected_response *expected, diff --git a/source3/libsmb/smb2cli_tcon.c b/source3/libsmb/smb2cli_tcon.c index ab97f8d..b3136fa 100644 --- a/source3/libsmb/smb2cli_tcon.c +++ b/source3/libsmb/smb2cli_tcon.c @@ -85,7 +85,8 @@ struct tevent_req *smb2cli_tcon_send(TALLOC_CTX *mem_ctx, NULL, /* tcon */ cli->smb2.session, state->fixed, sizeof(state->fixed), - dyn, dyn_len); + dyn, dyn_len, + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } @@ -211,7 +212,8 @@ struct tevent_req *smb2cli_tdis_send(TALLOC_CTX *mem_ctx, cli->smb2.tcon, cli->smb2.session, state->fixed, sizeof(state->fixed), - NULL, 0); + NULL, 0, /* dyn* */ + 0); /* max_dyn_len */ if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index bdab523..2ad16a9 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -151,7 +151,8 @@ void smb2_transport_send(struct smb2_request *req) NULL, /* body */ 0, /* body_fixed */ NULL, /* dyn */ - 0); /* dyn_len */ + 0, /* dyn_len */ + 0); /* max_dyn_len */ if (subreq != NULL) { smbXcli_req_set_pending(subreq); tevent_req_set_callback(subreq, @@ -190,7 +191,8 @@ void smb2_transport_send(struct smb2_request *req) tcon, session, body.data, body.length, - dyn.data, dyn.length); + dyn.data, dyn.length, + 0); /* max_dyn_len */ if (req->subreq == NULL) { req->state = SMB2_REQUEST_ERROR; req->status = NT_STATUS_NO_MEMORY; @@ -347,7 +349,8 @@ static void smb2_transport_break_handler(struct tevent_req *subreq) NULL, /* body */ 0, /* body_fixed */ NULL, /* dyn */ - 0); /* dyn_len */ + 0, /* dyn_len */ + 0); /* max_dyn_len */ if (subreq != NULL) { smbXcli_req_set_pending(subreq); tevent_req_set_callback(subreq, -- 1.8.3 From e00f67958bcd42f8e1c82a1ceafa8ac93066edbd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 14:05:05 +0200 Subject: [PATCH 18/57] libcli/smb: add smb1cli_conn_req_possible() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 17 +++++++++++++++++ libcli/smb/smbXcli_base.h | 1 + 2 files changed, 18 insertions(+) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 2562442..9e8725c 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -595,6 +595,23 @@ uint32_t smb1cli_conn_max_xmit(struct smbXcli_conn *conn) return conn->smb1.max_xmit; } +bool smb1cli_conn_req_possible(struct smbXcli_conn *conn) +{ + size_t pending; + uint16_t possible = conn->smb1.server.max_mux; + + pending = tevent_queue_length(conn->outgoing); + if (pending >= possible) { + return false; + } + pending += talloc_array_length(conn->pending); + if (pending >= possible) { + return false; + } + + return true; +} + uint32_t smb1cli_conn_server_session_key(struct smbXcli_conn *conn) { return conn->smb1.server.session_key; diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 4ce39c0..6b3156f 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -69,6 +69,7 @@ bool smbXcli_req_set_pending(struct tevent_req *req); uint32_t smb1cli_conn_capabilities(struct smbXcli_conn *conn); uint32_t smb1cli_conn_max_xmit(struct smbXcli_conn *conn); +bool smb1cli_conn_req_possible(struct smbXcli_conn *conn); uint32_t smb1cli_conn_server_session_key(struct smbXcli_conn *conn); const uint8_t *smb1cli_conn_server_challenge(struct smbXcli_conn *conn); uint16_t smb1cli_conn_server_security_mode(struct smbXcli_conn *conn); -- 1.8.3 From 953c1add63463abb03a631f1b9544f2e7c5810f0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 15:49:19 +0200 Subject: [PATCH 19/57] libcli/smb: add smb2cli_conn_req_possible() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- libcli/smb/smbXcli_base.c | 22 ++++++++++++++++++++++ libcli/smb/smbXcli_base.h | 1 + 2 files changed, 23 insertions(+) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index 9e8725c..1176bb8 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -2453,6 +2453,28 @@ bool smbXcli_conn_has_async_calls(struct smbXcli_conn *conn) || (talloc_array_length(conn->pending) != 0)); } +bool smb2cli_conn_req_possible(struct smbXcli_conn *conn, uint32_t *max_dyn_len) +{ + uint16_t credits = 1; + + if (conn->smb2.cur_credits == 0) { + if (max_dyn_len != NULL) { + *max_dyn_len = 0; + } + return false; + } + + if (conn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) { + credits = conn->smb2.cur_credits; + } + + if (max_dyn_len != NULL) { + *max_dyn_len = credits * 65536; + } + + return true; +} + uint32_t smb2cli_conn_server_capabilities(struct smbXcli_conn *conn) { return conn->smb2.server.capabilities; diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index 6b3156f..a7cfcc3 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -199,6 +199,7 @@ NTSTATUS smb1cli_echo_recv(struct tevent_req *req); NTSTATUS smb1cli_echo(struct smbXcli_conn *conn, uint32_t timeout_msec, uint16_t num_echos, DATA_BLOB data); +bool smb2cli_conn_req_possible(struct smbXcli_conn *conn, uint32_t *max_dyn_len); uint32_t smb2cli_conn_server_capabilities(struct smbXcli_conn *conn); uint16_t smb2cli_conn_server_security_mode(struct smbXcli_conn *conn); uint32_t smb2cli_conn_max_trans_size(struct smbXcli_conn *conn); -- 1.8.3 From 05cb37fe0037511b50edda474d117e08abd93ef2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 14:10:59 +0200 Subject: [PATCH 20/57] s3:libsmb: rewrite cli_push* to use smb1cli_conn_req_possible() This works out if it's possible to ship the next request dynamically instead of relying on fixed values. The default window size is 16 MByte. We limit the number of outstanding chunks/requests to 256. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 288 +++++++++++++++++++++++++++--------------- 1 file changed, 187 insertions(+), 101 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 47e7f1b..550c52b 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -1092,13 +1092,7 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, return status; } -struct cli_push_write_state { - struct tevent_req *req;/* This is the main request! Not the subreq */ - uint32_t idx; - off_t ofs; - uint8_t *buf; - size_t size; -}; +struct cli_push_chunk; struct cli_push_state { struct tevent_context *ev; @@ -1106,7 +1100,6 @@ struct cli_push_state { uint16_t fnum; uint16_t mode; off_t start_offset; - size_t window_size; size_t (*source)(uint8_t *buf, size_t n, void *priv); void *priv; @@ -1118,62 +1111,32 @@ struct cli_push_state { /* * Outstanding requests + * + * The maximum is 256: + * - which would be a window of 256 MByte + * for SMB2 with multi-credit + * or smb1 unix extentions. */ - uint32_t pending; - uint16_t max_reqs; - uint32_t num_reqs; - struct cli_push_write_state **reqs; + uint16_t max_chunks; + uint16_t num_chunks; + uint16_t num_waiting; + struct cli_push_chunk *chunks; }; -static void cli_push_written(struct tevent_req *req); - -static bool cli_push_write_setup(struct tevent_req *req, - struct cli_push_state *state, - uint32_t idx) -{ - struct cli_push_write_state *substate; +struct cli_push_chunk { + struct cli_push_chunk *prev, *next; + struct tevent_req *req;/* This is the main request! Not the subreq */ struct tevent_req *subreq; + off_t ofs; + uint8_t *buf; + size_t total_size; + size_t tmp_size; + bool done; +}; - substate = talloc(state->reqs, struct cli_push_write_state); - if (!substate) { - return false; - } - substate->req = req; - substate->idx = idx; - substate->ofs = state->next_offset; - substate->buf = talloc_array(substate, uint8_t, state->chunk_size); - if (!substate->buf) { - talloc_free(substate); - return false; - } - substate->size = state->source(substate->buf, - state->chunk_size, - state->priv); - if (substate->size == 0) { - state->eof = true; - /* nothing to send */ - talloc_free(substate); - return true; - } - - subreq = cli_writeall_send(substate, - state->ev, state->cli, - state->fnum, state->mode, - substate->buf, - substate->ofs, - substate->size); - if (!subreq) { - talloc_free(substate); - return false; - } - tevent_req_set_callback(subreq, cli_push_written, substate); - - state->reqs[idx] = substate; - state->pending += 1; - state->next_offset += substate->size; - - return true; -} +static void cli_push_setup_chunks(struct tevent_req *req); +static void cli_push_chunk_ship(struct cli_push_chunk *chunk); +static void cli_push_chunk_done(struct tevent_req *subreq); struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, @@ -1185,8 +1148,8 @@ struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, { struct tevent_req *req; struct cli_push_state *state; - uint32_t i; size_t page_size = 1024; + uint64_t tmp64; req = tevent_req_create(mem_ctx, &state, struct cli_push_state); if (req == NULL) { @@ -1199,8 +1162,6 @@ struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->mode = mode; state->source = source; state->priv = priv; - state->eof = false; - state->pending = 0; state->next_offset = start_offset; state->chunk_size = cli_write_max_bufsize(cli, mode, 14); @@ -1208,77 +1169,202 @@ struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->chunk_size &= ~(page_size - 1); } - state->max_reqs = smbXcli_conn_max_requests(cli->conn); - if (window_size == 0) { - window_size = state->max_reqs * state->chunk_size; + /* + * We use 16 MByte as default window size. + */ + window_size = 16 * 1024 * 1024; } - state->num_reqs = window_size/state->chunk_size; + + tmp64 = window_size/state->chunk_size; if ((window_size % state->chunk_size) > 0) { - state->num_reqs += 1; + tmp64 += 1; } - state->num_reqs = MIN(state->num_reqs, state->max_reqs); - state->num_reqs = MAX(state->num_reqs, 1); + tmp64 = MAX(tmp64, 1); + tmp64 = MIN(tmp64, 256); + state->max_chunks = tmp64; - state->reqs = talloc_zero_array(state, struct cli_push_write_state *, - state->num_reqs); - if (state->reqs == NULL) { - goto failed; + /* + * We defer the callback because of the complex + * substate/subfunction logic + */ + tevent_req_defer_callback(req, ev); + + cli_push_setup_chunks(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); } - for (i=0; inum_reqs; i++) { - if (!cli_push_write_setup(req, state, i)) { - goto failed; + return req; +} + +static void cli_push_setup_chunks(struct tevent_req *req) +{ + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); + struct cli_push_chunk *chunk, *next = NULL; + size_t i; + + for (chunk = state->chunks; chunk; chunk = next) { + /* + * Note that chunk might be removed from this call. + */ + next = chunk->next; + cli_push_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } + } + + for (i = state->num_chunks; i < state->max_chunks; i++) { + + if (state->num_waiting > 0) { + return; } if (state->eof) { break; } + + chunk = talloc_zero(state, struct cli_push_chunk); + if (tevent_req_nomem(chunk, req)) { + return; + } + chunk->req = req; + chunk->ofs = state->next_offset; + chunk->buf = talloc_array(chunk, + uint8_t, + state->chunk_size); + if (tevent_req_nomem(chunk->buf, req)) { + return; + } + chunk->total_size = state->source(chunk->buf, + state->chunk_size, + state->priv); + if (chunk->total_size == 0) { + /* nothing to send */ + talloc_free(chunk); + state->eof = true; + break; + } + state->next_offset += chunk->total_size; + + DLIST_ADD_END(state->chunks, chunk, NULL); + state->num_chunks++; + state->num_waiting++; + + cli_push_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } } - if (state->pending == 0) { - tevent_req_done(req); - return tevent_req_post(req, ev); + if (!state->eof) { + return; } - return req; + if (state->num_chunks > 0) { + return; + } + + tevent_req_done(req); +} + +static void cli_push_chunk_ship(struct cli_push_chunk *chunk) +{ + struct tevent_req *req = chunk->req; + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); + bool ok; + const uint8_t *buf; + off_t ofs; + size_t size; + + if (chunk->done) { + DLIST_REMOVE(state->chunks, chunk); + SMB_ASSERT(state->num_chunks > 0); + state->num_chunks--; + TALLOC_FREE(chunk); + + return; + } + + if (chunk->subreq != NULL) { + return; + } + + SMB_ASSERT(state->num_waiting > 0); + + buf = chunk->buf + chunk->tmp_size; + ofs = chunk->ofs + chunk->tmp_size; + size = chunk->total_size - chunk->tmp_size; + + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + return; + } + + chunk->subreq = cli_write_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + state->mode, + buf, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + tevent_req_set_callback(chunk->subreq, + cli_push_chunk_done, + chunk); - failed: - tevent_req_nterror(req, NT_STATUS_NO_MEMORY); - return tevent_req_post(req, ev); + state->num_waiting--; + return; } -static void cli_push_written(struct tevent_req *subreq) +static void cli_push_chunk_done(struct tevent_req *subreq) { - struct cli_push_write_state *substate = tevent_req_callback_data( - subreq, struct cli_push_write_state); - struct tevent_req *req = substate->req; - struct cli_push_state *state = tevent_req_data( - req, struct cli_push_state); + struct cli_push_chunk *chunk = + tevent_req_callback_data(subreq, + struct cli_push_chunk); + struct tevent_req *req = chunk->req; + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); NTSTATUS status; - uint32_t idx = substate->idx; + size_t expected = chunk->total_size - chunk->tmp_size; + size_t written; - state->reqs[idx] = NULL; - state->pending -= 1; + chunk->subreq = NULL; - status = cli_writeall_recv(subreq, NULL); + status = cli_write_andx_recv(subreq, &written); TALLOC_FREE(subreq); - TALLOC_FREE(substate); if (tevent_req_nterror(req, status)) { return; } - if (!state->eof) { - if (!cli_push_write_setup(req, state, idx)) { - tevent_req_nterror(req, NT_STATUS_NO_MEMORY); - return; - } + if (written > expected) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } - if (state->pending == 0) { - tevent_req_done(req); + if (written == 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); return; } + + chunk->tmp_size += written; + + if (chunk->tmp_size == chunk->total_size) { + chunk->done = true; + } else { + state->num_waiting++; + } + + cli_push_setup_chunks(req); } NTSTATUS cli_push_recv(struct tevent_req *req) -- 1.8.3 From 600ff103193db2c6b47ed478f385690995d6ad95 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 18:03:50 +0200 Subject: [PATCH 21/57] s3:libsmb: rewrite cli_pull* to use smb1cli_conn_req_possible() This works out if it's possible to ship the next request dynamically instead of relying on fixed values. The default window size is 16 MByte. We limit the number of outstanding chunks/requests to 256. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 417 +++++++++++++++++++++++++----------------- 1 file changed, 246 insertions(+), 171 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 550c52b..dd5d4c2 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -388,23 +388,9 @@ static NTSTATUS cli_readall_recv(struct tevent_req *req, ssize_t *received, return NT_STATUS_OK; } -struct cli_pull_subreq { - struct tevent_req *req; - ssize_t received; - uint8_t *buf; -}; - -/* - * Parallel read support. - * - * cli_pull sends as many read&x requests as the server would allow via - * max_mux at a time. When replies flow back in, the data is written into - * the callback function "sink" in the right order. - */ +struct cli_pull_chunk; struct cli_pull_state { - struct tevent_req *req; - struct tevent_context *ev; struct cli_state *cli; uint16_t fnum; @@ -415,54 +401,49 @@ struct cli_pull_state { void *priv; size_t chunk_size; + off_t next_offset; + off_t remaining; /* - * Outstanding requests - */ - uint16_t max_reqs; - int num_reqs; - struct cli_pull_subreq *reqs; - - /* - * For how many bytes did we send requests already? - */ - off_t requested; - - /* - * Next request index to push into "sink". This walks around the "req" - * array, taking care that the requests are pushed to "sink" in the - * right order. If necessary (i.e. replies don't come in in the right - * order), replies are held back in "reqs". + * How many bytes did we push into "sink"? */ - int top_req; + off_t pushed; /* - * How many bytes did we push into "sink"? + * Outstanding requests + * + * The maximum is 256: + * - which would be a window of 256 MByte + * for SMB2 with multi-credit + * or smb1 unix extentions. */ - - off_t pushed; + uint16_t max_chunks; + uint16_t num_chunks; + uint16_t num_waiting; + struct cli_pull_chunk *chunks; }; -static char *cli_pull_print(struct tevent_req *req, TALLOC_CTX *mem_ctx) -{ - struct cli_pull_state *state = tevent_req_data( - req, struct cli_pull_state); - char *result; - - result = tevent_req_default_print(req, mem_ctx); - if (result == NULL) { - return NULL; - } - - return talloc_asprintf_append_buffer( - result, "num_reqs=%d, top_req=%d", - state->num_reqs, state->top_req); -} +struct cli_pull_chunk { + struct cli_pull_chunk *prev, *next; + struct tevent_req *req;/* This is the main request! Not the subreq */ + struct tevent_req *subreq; + off_t ofs; + uint8_t *buf; + size_t total_size; + size_t tmp_size; + bool done; +}; -static void cli_pull_read_done(struct tevent_req *read_req); +static void cli_pull_setup_chunks(struct tevent_req *req); +static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk); +static void cli_pull_chunk_done(struct tevent_req *subreq); /* - * Prepare an async pull request + * Parallel read support. + * + * cli_pull sends as many read&x requests as the server would allow via + * max_mux at a time. When replies flow back in, the data is written into + * the callback function "sink" in the right order. */ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, @@ -476,16 +457,13 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, { struct tevent_req *req; struct cli_pull_state *state; - int i; size_t page_size = 1024; + uint64_t tmp64; req = tevent_req_create(mem_ctx, &state, struct cli_pull_state); if (req == NULL) { return NULL; } - tevent_req_set_print_fn(req, cli_pull_print); - state->req = req; - state->cli = cli; state->ev = ev; state->fnum = fnum; @@ -493,9 +471,8 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, state->size = size; state->sink = sink; state->priv = priv; - - state->pushed = 0; - state->top_req = 0; + state->next_offset = start_offset; + state->remaining = size; if (size == 0) { tevent_req_done(req); @@ -507,155 +484,251 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, state->chunk_size &= ~(page_size - 1); } - state->max_reqs = smbXcli_conn_max_requests(cli->conn); + if (window_size == 0) { + /* + * We use 16 MByte as default window size. + */ + window_size = 16 * 1024 * 1024; + } + + tmp64 = window_size/state->chunk_size; + if ((window_size % state->chunk_size) > 0) { + tmp64 += 1; + } + tmp64 = MAX(tmp64, 1); + tmp64 = MIN(tmp64, 256); + state->max_chunks = tmp64; + + /* + * We defer the callback because of the complex + * substate/subfunction logic + */ + tevent_req_defer_callback(req, ev); + + cli_pull_setup_chunks(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} - state->num_reqs = MAX(window_size/state->chunk_size, 1); - state->num_reqs = MIN(state->num_reqs, state->max_reqs); +static void cli_pull_setup_chunks(struct tevent_req *req) +{ + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + struct cli_pull_chunk *chunk, *next = NULL; + size_t i; - state->reqs = talloc_zero_array(state, struct cli_pull_subreq, - state->num_reqs); - if (state->reqs == NULL) { - goto failed; + for (chunk = state->chunks; chunk; chunk = next) { + /* + * Note that chunk might be removed from this call. + */ + next = chunk->next; + cli_pull_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } } - state->requested = 0; + for (i = state->num_chunks; i < state->max_chunks; i++) { - for (i=0; inum_reqs; i++) { - struct cli_pull_subreq *subreq = &state->reqs[i]; - off_t size_left; - size_t request_thistime; + if (state->num_waiting > 0) { + return; + } - if (state->requested >= size) { - state->num_reqs = i; + if (state->remaining == 0) { break; } - size_left = size - state->requested; - request_thistime = MIN(size_left, state->chunk_size); + chunk = talloc_zero(state, struct cli_pull_chunk); + if (tevent_req_nomem(chunk, req)) { + return; + } + chunk->req = req; + chunk->ofs = state->next_offset; + chunk->total_size = MIN(state->remaining, state->chunk_size); + state->next_offset += chunk->total_size; + state->remaining -= chunk->total_size; - subreq->req = cli_readall_send( - state->reqs, ev, cli, fnum, - state->start_offset + state->requested, - request_thistime); + DLIST_ADD_END(state->chunks, chunk, NULL); + state->num_chunks++; + state->num_waiting++; - if (subreq->req == NULL) { - goto failed; + cli_pull_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; } - tevent_req_set_callback(subreq->req, cli_pull_read_done, req); - state->requested += request_thistime; } - return req; -failed: - TALLOC_FREE(req); - return NULL; -} + if (state->remaining > 0) { + return; + } -/* - * Handle incoming read replies, push the data into sink and send out new - * requests if necessary. - */ + if (state->num_chunks > 0) { + return; + } + + tevent_req_done(req); +} -static void cli_pull_read_done(struct tevent_req *subreq) +static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk) { - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct cli_pull_state *state = tevent_req_data( - req, struct cli_pull_state); - struct cli_pull_subreq *pull_subreq = NULL; - NTSTATUS status; - int i; + struct tevent_req *req = chunk->req; + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + bool ok; + off_t ofs; + size_t size; - for (i = 0; i < state->num_reqs; i++) { - pull_subreq = &state->reqs[i]; - if (subreq == pull_subreq->req) { - break; + if (chunk->done) { + NTSTATUS status; + + if (chunk != state->chunks) { + /* + * this chunk is not the + * first one in the list. + * + * which means we should not + * push it into the sink yet. + */ + return; + } + + if (chunk->tmp_size == 0) { + /* + * we git a short read, we're done + */ + tevent_req_done(req); + return; } + + status = state->sink((char *)chunk->buf, + chunk->tmp_size, + state->priv); + if (tevent_req_nterror(req, status)) { + return; + } + state->pushed += chunk->tmp_size; + + if (chunk->tmp_size < chunk->total_size) { + /* + * we git a short read, we're done + */ + tevent_req_done(req); + return; + } + + DLIST_REMOVE(state->chunks, chunk); + SMB_ASSERT(state->num_chunks > 0); + state->num_chunks--; + TALLOC_FREE(chunk); + + return; } - if (i == state->num_reqs) { - /* Huh -- received something we did not send?? */ - tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + + if (chunk->subreq != NULL) { return; } - status = cli_readall_recv(subreq, &pull_subreq->received, - &pull_subreq->buf); - if (!NT_STATUS_IS_OK(status)) { - tevent_req_nterror(state->req, status); + SMB_ASSERT(state->num_waiting > 0); + + ofs = chunk->ofs + chunk->tmp_size; + size = chunk->total_size - chunk->tmp_size; + + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { return; } - /* - * This loop is the one to take care of out-of-order replies. All - * pending requests are in state->reqs, state->reqs[top_req] is the - * one that is to be pushed next. If however a request later than - * top_req is replied to, then we can't push yet. If top_req is - * replied to at a later point then, we need to push all the finished - * requests. - */ + chunk->subreq = cli_read_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + tevent_req_set_callback(chunk->subreq, + cli_pull_chunk_done, + chunk); - while (state->reqs[state->top_req].req != NULL) { - struct cli_pull_subreq *top_subreq; + state->num_waiting--; + return; +} - DEBUG(11, ("cli_pull_read_done: top_req = %d\n", - state->top_req)); +static void cli_pull_chunk_done(struct tevent_req *subreq) +{ + struct cli_pull_chunk *chunk = + tevent_req_callback_data(subreq, + struct cli_pull_chunk); + struct tevent_req *req = chunk->req; + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + NTSTATUS status; + size_t expected = chunk->total_size - chunk->tmp_size; + ssize_t received; + uint8_t *buf = NULL; - top_subreq = &state->reqs[state->top_req]; + chunk->subreq = NULL; - if (tevent_req_is_in_progress(top_subreq->req)) { - DEBUG(11, ("cli_pull_read_done: top request not yet " - "done\n")); - return; - } + status = cli_read_andx_recv(subreq, &received, &buf); + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { + received = 0; + status = NT_STATUS_OK; + } + if (tevent_req_nterror(req, status)) { + return; + } + + if (received > expected) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } - DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already " - "pushed\n", (int)top_subreq->received, - (int)state->pushed)); + if (received == 0) { + /* + * We got EOF we're done + */ + chunk->done = true; + cli_pull_setup_chunks(req); + return; + } - status = state->sink((char *)top_subreq->buf, - top_subreq->received, state->priv); - if (tevent_req_nterror(state->req, status)) { + if (received == chunk->total_size) { + /* + * We got it in the first run. + * + * We don't call TALLOC_FREE(subreq) + * here and keep the returned buffer. + */ + chunk->buf = buf; + } else if (chunk->buf == NULL) { + chunk->buf = talloc_array(chunk, uint8_t, chunk->total_size); + if (tevent_req_nomem(chunk->buf, req)) { return; } - state->pushed += top_subreq->received; - - TALLOC_FREE(state->reqs[state->top_req].req); - - if (state->requested < state->size) { - struct tevent_req *new_req; - off_t size_left; - size_t request_thistime; - - size_left = state->size - state->requested; - request_thistime = MIN(size_left, state->chunk_size); - - DEBUG(10, ("cli_pull_read_done: Requesting %d bytes " - "at %d, position %d\n", - (int)request_thistime, - (int)(state->start_offset - + state->requested), - state->top_req)); - - new_req = cli_readall_send( - state->reqs, state->ev, state->cli, - state->fnum, - state->start_offset + state->requested, - request_thistime); - - if (tevent_req_nomem(new_req, state->req)) { - return; - } - tevent_req_set_callback(new_req, cli_pull_read_done, - req); - - state->reqs[state->top_req].req = new_req; - state->requested += request_thistime; - } + } - state->top_req = (state->top_req+1) % state->num_reqs; + if (received != chunk->total_size) { + uint8_t *p = chunk->buf + chunk->tmp_size; + memcpy(p, buf, received); + TALLOC_FREE(subreq); } - tevent_req_done(req); + chunk->tmp_size += received; + + if (chunk->tmp_size == chunk->total_size) { + chunk->done = true; + } else { + state->num_waiting++; + } + + cli_pull_setup_chunks(req); } NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received) @@ -665,9 +738,11 @@ NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received) NTSTATUS status; if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); return status; } *received = state->pushed; + tevent_req_received(req); return NT_STATUS_OK; } -- 1.8.3 From de97c6e42c5cac70d9b9b22d02af8996a71a9349 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 18:23:55 +0200 Subject: [PATCH 22/57] s3:libsmb: remove unused cli_readall* Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 119 ------------------------------------------ 1 file changed, 119 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index dd5d4c2..10dc79b 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -269,125 +269,6 @@ NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, return NT_STATUS_OK; } -struct cli_readall_state { - struct tevent_context *ev; - struct cli_state *cli; - uint16_t fnum; - off_t start_offset; - size_t size; - size_t received; - uint8_t *buf; -}; - -static void cli_readall_done(struct tevent_req *subreq); - -static struct tevent_req *cli_readall_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli, - uint16_t fnum, - off_t offset, size_t size) -{ - struct tevent_req *req, *subreq; - struct cli_readall_state *state; - - req = tevent_req_create(mem_ctx, &state, struct cli_readall_state); - if (req == NULL) { - return NULL; - } - state->ev = ev; - state->cli = cli; - state->fnum = fnum; - state->start_offset = offset; - state->size = size; - state->received = 0; - state->buf = NULL; - - subreq = cli_read_andx_send(state, ev, cli, fnum, offset, size); - if (tevent_req_nomem(subreq, req)) { - return tevent_req_post(req, ev); - } - tevent_req_set_callback(subreq, cli_readall_done, req); - return req; -} - -static void cli_readall_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct cli_readall_state *state = tevent_req_data( - req, struct cli_readall_state); - ssize_t received; - uint8_t *buf; - NTSTATUS status; - - status = cli_read_andx_recv(subreq, &received, &buf); - if (tevent_req_nterror(req, status)) { - return; - } - - if (received == 0) { - /* EOF */ - tevent_req_done(req); - return; - } - - if ((state->received == 0) && (received == state->size)) { - /* Ideal case: Got it all in one run */ - state->buf = buf; - state->received += received; - tevent_req_done(req); - return; - } - - /* - * We got a short read, issue a read for the - * rest. Unfortunately we have to allocate the buffer - * ourselves now, as our caller expects to receive a single - * buffer. cli_read_andx does it from the buffer received from - * the net, but with a short read we have to put it together - * from several reads. - */ - - if (state->buf == NULL) { - state->buf = talloc_array(state, uint8_t, state->size); - if (tevent_req_nomem(state->buf, req)) { - return; - } - } - memcpy(state->buf + state->received, buf, received); - state->received += received; - - TALLOC_FREE(subreq); - - if (state->received >= state->size) { - tevent_req_done(req); - return; - } - - subreq = cli_read_andx_send(state, state->ev, state->cli, state->fnum, - state->start_offset + state->received, - state->size - state->received); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, cli_readall_done, req); -} - -static NTSTATUS cli_readall_recv(struct tevent_req *req, ssize_t *received, - uint8_t **rcvbuf) -{ - struct cli_readall_state *state = tevent_req_data( - req, struct cli_readall_state); - NTSTATUS status; - - if (tevent_req_is_nterror(req, &status)) { - return status; - } - *received = state->received; - *rcvbuf = state->buf; - return NT_STATUS_OK; -} - struct cli_pull_chunk; struct cli_pull_state { -- 1.8.3 From 269027441abf55bec029eea3b8913a67e279870d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 10:46:28 +0200 Subject: [PATCH 23/57] s3:client: use the default io size Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/client/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/client/client.c b/source3/client/client.c index 44e1efd..ebdcbad 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -55,7 +55,7 @@ static bool grepable = false; static char *cmdstr = NULL; const char *cmd_ptr = NULL; -static int io_bufsize = 524288; +static int io_bufsize = 0; /* we use the default size */ static int name_type = 0x20; static int max_protocol = -1; -- 1.8.3 From ede4bd179886721ffc3e34b18c9ce1c710180a4e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 10:47:11 +0200 Subject: [PATCH 24/57] s3:client: fix compiler warning Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/client/client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/client/client.c b/source3/client/client.c index ebdcbad..d302d43 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -5338,6 +5338,7 @@ static int do_message_op(struct user_auth_info *a_info) int main(int argc,char *argv[]) { + const char **const_argv = discard_const_p(const char *, argv); char *base_directory = NULL; int opt; char *query_host = NULL; @@ -5388,7 +5389,7 @@ static int do_message_op(struct user_auth_info *a_info) popt_common_set_auth_info(auth_info); /* skip argv(0) */ - pc = poptGetContext("smbclient", argc, (const char **) argv, long_options, 0); + pc = poptGetContext("smbclient", argc, const_argv, long_options, 0); poptSetOtherOptionHelp(pc, "service "); while ((opt = poptGetNextOpt(pc)) != -1) { -- 1.8.3 From c1caffdb4ae1bc30bf401a5c18d85d124262a966 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 13:48:55 -0700 Subject: [PATCH 25/57] s3:lib: Factor read_ea_list_entry() and read_nttrans_ea_list() out so they can be used by the SMB2 client code. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/lib/util_ea.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ source3/lib/util_ea.h | 36 ++++++++++++++ source3/smbd/nttrans.c | 44 +---------------- source3/smbd/trans2.c | 62 +----------------------- source3/wscript_build | 3 +- 5 files changed, 166 insertions(+), 105 deletions(-) create mode 100644 source3/lib/util_ea.c create mode 100644 source3/lib/util_ea.h diff --git a/source3/lib/util_ea.c b/source3/lib/util_ea.c new file mode 100644 index 0000000..81684da --- /dev/null +++ b/source3/lib/util_ea.c @@ -0,0 +1,126 @@ +/* + Unix SMB/CIFS implementation. + SMB Extended attribute buffer handling + Copyright (C) Jeremy Allison 2005-2013 + Copyright (C) Tim Prouty 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 . +*/ + +#include "includes.h" +#include "lib/util_ea.h" + +/**************************************************************************** + Read one EA list entry from the buffer. +****************************************************************************/ + +struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used) +{ + struct ea_list *eal = talloc_zero(ctx, struct ea_list); + uint16 val_len; + unsigned int namelen; + size_t converted_size; + + if (!eal) { + return NULL; + } + + if (data_size < 6) { + return NULL; + } + + eal->ea.flags = CVAL(pdata,0); + namelen = CVAL(pdata,1); + val_len = SVAL(pdata,2); + + if (4 + namelen + 1 + val_len > data_size) { + return NULL; + } + + /* Ensure the name is null terminated. */ + if (pdata[namelen + 4] != '\0') { + return NULL; + } + if (!pull_ascii_talloc(ctx, &eal->ea.name, pdata + 4, &converted_size)) { + DEBUG(0,("read_ea_list_entry: pull_ascii_talloc failed: %s", + strerror(errno))); + } + if (!eal->ea.name) { + return NULL; + } + + eal->ea.value = data_blob_talloc(eal, NULL, (size_t)val_len + 1); + if (!eal->ea.value.data) { + return NULL; + } + + memcpy(eal->ea.value.data, pdata + 4 + namelen + 1, val_len); + + /* Ensure we're null terminated just in case we print the value. */ + eal->ea.value.data[val_len] = '\0'; + /* But don't count the null. */ + eal->ea.value.length--; + + if (pbytes_used) { + *pbytes_used = 4 + namelen + 1 + val_len; + } + + DEBUG(10,("read_ea_list_entry: read ea name %s\n", eal->ea.name)); + dump_data(10, eal->ea.value.data, eal->ea.value.length); + + return eal; +} + +/**************************************************************************** + Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. +****************************************************************************/ + +struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) +{ + struct ea_list *ea_list_head = NULL; + size_t offset = 0; + + if (data_size < 4) { + return NULL; + } + + while (offset + 4 <= data_size) { + size_t next_offset = IVAL(pdata,offset); + struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset + 4, data_size - offset - 4, NULL); + + if (!eal) { + return NULL; + } + + DLIST_ADD_END(ea_list_head, eal, struct ea_list *); + if (next_offset == 0) { + break; + } + + /* Integer wrap protection for the increment. */ + if (offset + next_offset < offset) { + break; + } + + offset += next_offset; + + /* Integer wrap protection for while loop. */ + if (offset + 4 < offset) { + break; + } + + } + + return ea_list_head; +} diff --git a/source3/lib/util_ea.h b/source3/lib/util_ea.h new file mode 100644 index 0000000..54423ac --- /dev/null +++ b/source3/lib/util_ea.h @@ -0,0 +1,36 @@ +/* + Unix SMB/CIFS implementation. + SMB Extended attribute buffer handling + Copyright (C) Jeremy Allison 2005-2013 + Copyright (C) Tim Prouty 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 . +*/ + +#ifndef __LIB_UTIL_EA_H__ +#define __LIB_UTIL_EA_H__ + +/**************************************************************************** + Read one EA list entry from a buffer. +****************************************************************************/ + +struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used); + +/**************************************************************************** + Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. +****************************************************************************/ + +struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size); + +#endif /* __LIB_UTIL_EA_H__ */ diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index bcba29a..94be9f6 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -29,6 +29,7 @@ #include "auth.h" #include "smbprofile.h" #include "libsmb/libsmb.h" +#include "lib/util_ea.h" extern const struct generic_mapping file_generic_mapping; @@ -966,49 +967,6 @@ NTSTATUS set_sd_blob(files_struct *fsp, uint8_t *data, uint32_t sd_len, } /**************************************************************************** - Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. -****************************************************************************/ - -struct ea_list *read_nttrans_ea_list(TALLOC_CTX *ctx, const char *pdata, size_t data_size) -{ - struct ea_list *ea_list_head = NULL; - size_t offset = 0; - - if (data_size < 4) { - return NULL; - } - - while (offset + 4 <= data_size) { - size_t next_offset = IVAL(pdata,offset); - struct ea_list *eal = read_ea_list_entry(ctx, pdata + offset + 4, data_size - offset - 4, NULL); - - if (!eal) { - return NULL; - } - - DLIST_ADD_END(ea_list_head, eal, struct ea_list *); - if (next_offset == 0) { - break; - } - - /* Integer wrap protection for the increment. */ - if (offset + next_offset < offset) { - break; - } - - offset += next_offset; - - /* Integer wrap protection for while loop. */ - if (offset + 4 < offset) { - break; - } - - } - - return ea_list_head; -} - -/**************************************************************************** Reply to a NT_TRANSACT_CREATE call (needs to process SD's). ****************************************************************************/ diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index da0b1ea..2bff483 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -38,6 +38,7 @@ #include "smbprofile.h" #include "rpc_server/srv_pipe_hnd.h" #include "printing.h" +#include "lib/util_ea.h" #define DIR_ENTRY_SAFETY_MARGIN 4096 @@ -749,67 +750,6 @@ static struct ea_list *read_ea_name_list(TALLOC_CTX *ctx, const char *pdata, siz } /**************************************************************************** - Read one EA list entry from the buffer. -****************************************************************************/ - -struct ea_list *read_ea_list_entry(TALLOC_CTX *ctx, const char *pdata, size_t data_size, size_t *pbytes_used) -{ - struct ea_list *eal = talloc_zero(ctx, struct ea_list); - uint16 val_len; - unsigned int namelen; - size_t converted_size; - - if (!eal) { - return NULL; - } - - if (data_size < 6) { - return NULL; - } - - eal->ea.flags = CVAL(pdata,0); - namelen = CVAL(pdata,1); - val_len = SVAL(pdata,2); - - if (4 + namelen + 1 + val_len > data_size) { - return NULL; - } - - /* Ensure the name is null terminated. */ - if (pdata[namelen + 4] != '\0') { - return NULL; - } - if (!pull_ascii_talloc(ctx, &eal->ea.name, pdata + 4, &converted_size)) { - DEBUG(0,("read_ea_list_entry: pull_ascii_talloc failed: %s", - strerror(errno))); - } - if (!eal->ea.name) { - return NULL; - } - - eal->ea.value = data_blob_talloc(eal, NULL, (size_t)val_len + 1); - if (!eal->ea.value.data) { - return NULL; - } - - memcpy(eal->ea.value.data, pdata + 4 + namelen + 1, val_len); - - /* Ensure we're null terminated just in case we print the value. */ - eal->ea.value.data[val_len] = '\0'; - /* But don't count the null. */ - eal->ea.value.length--; - - if (pbytes_used) { - *pbytes_used = 4 + namelen + 1 + val_len; - } - - DEBUG(10,("read_ea_list_entry: read ea name %s\n", eal->ea.name)); - dump_data(10, eal->ea.value.data, eal->ea.value.length); - - return eal; -} - -/**************************************************************************** Read a list of EA names and data from an incoming data buffer. Create an ea_list with them. ****************************************************************************/ diff --git a/source3/wscript_build b/source3/wscript_build index f52197a..569c632 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -67,7 +67,8 @@ LIB_SRC = ''' lib/fncall.c libads/krb5_errs.c lib/system_smbd.c lib/audit.c lib/tevent_wait.c - lib/idmap_cache.c''' + lib/idmap_cache.c + lib/util_ea.c''' LIB_UTIL_SRC = ''' lib/system.c -- 1.8.3 From 8f19d86cdab1c3aa603723a4f3b7ae72b7b1aae6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 15:54:05 -0700 Subject: [PATCH 26/57] s3:libsmb: Add in the core of the libsmb client SMB2 functions. These create a synchronous cli_smb2_XXX() style interface designed to plug directly into the libsmb/cliXXXX.c code. https://bugzilla.samba.org/show_bug.cgi?id=9974 Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher --- source3/include/client.h | 1 + source3/libsmb/cli_smb2_fnum.c | 2373 ++++++++++++++++++++++++++++++++++++++++ source3/libsmb/cli_smb2_fnum.h | 161 +++ source3/libsmb/clirap.c | 7 +- source3/libsmb/clirap.h | 4 + source3/libsmb/libsmb.h | 1 + source3/wscript_build | 3 +- 7 files changed, 2543 insertions(+), 7 deletions(-) create mode 100644 source3/libsmb/cli_smb2_fnum.c create mode 100644 source3/libsmb/cli_smb2_fnum.h diff --git a/source3/include/client.h b/source3/include/client.h index ba7b049..f6a4a81 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -108,6 +108,7 @@ struct cli_state { struct { struct smbXcli_session *session; struct smbXcli_tcon *tcon; + struct idr_context *open_handles; } smb2; }; diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c new file mode 100644 index 0000000..d0b744b --- /dev/null +++ b/source3/libsmb/cli_smb2_fnum.c @@ -0,0 +1,2373 @@ +/* + Unix SMB/CIFS implementation. + smb2 lib + Copyright (C) Jeremy Allison 2013 + Copyright (C) Volker Lendecke 2013 + Copyright (C) Stefan Metzmacher 2013 + + 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 . +*/ + +/* + This code is a thin wrapper around the existing + cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c, + but allows the handles to be mapped to uint16_t fnums, + which are easier for smbclient to use. +*/ + +#include "includes.h" +#include "client.h" +#include "async_smb.h" +#include "../libcli/smb/smbXcli_base.h" +#include "smb2cli.h" +#include "cli_smb2_fnum.h" +#include "trans2.h" +#include "clirap.h" +#include "../libcli/smb/smb2_create_blob.h" +#include "libsmb/proto.h" +#include "lib/util/tevent_ntstatus.h" +#include "../libcli/security/security.h" +#include "lib/util_ea.h" + +struct smb2_hnd { + uint64_t fid_persistent; + uint64_t fid_volatile; +}; + +/* + * Handle mapping code. + */ + +/*************************************************************** + Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd. + Ensures handle is owned by cli struct. +***************************************************************/ + +static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli, + const struct smb2_hnd *ph, /* In */ + uint16_t *pfnum) /* Out */ +{ + int ret; + struct idr_context *idp = cli->smb2.open_handles; + struct smb2_hnd *owned_h = talloc_memdup(cli, + ph, + sizeof(struct smb2_hnd)); + + if (owned_h == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (idp == NULL) { + /* Lazy init */ + cli->smb2.open_handles = idr_init(cli); + if (cli->smb2.open_handles == NULL) { + TALLOC_FREE(owned_h); + return NT_STATUS_NO_MEMORY; + } + idp = cli->smb2.open_handles; + } + + ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE); + if (ret == -1) { + TALLOC_FREE(owned_h); + return NT_STATUS_NO_MEMORY; + } + + *pfnum = (uint16_t)ret; + return NT_STATUS_OK; +} + +/*************************************************************** + Return the smb2_hnd pointer associated with the given fnum. +***************************************************************/ + +static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli, + uint16_t fnum, /* In */ + struct smb2_hnd **pph) /* Out */ +{ + struct idr_context *idp = cli->smb2.open_handles; + + if (idp == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + *pph = (struct smb2_hnd *)idr_find(idp, fnum); + if (*pph == NULL) { + return NT_STATUS_INVALID_HANDLE; + } + return NT_STATUS_OK; +} + +/*************************************************************** + Delete the fnum to smb2_hnd mapping. Zeros out handle on + successful return. +***************************************************************/ + +static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli, + struct smb2_hnd **pph, /* In */ + uint16_t fnum) /* In */ +{ + struct idr_context *idp = cli->smb2.open_handles; + struct smb2_hnd *ph; + + if (idp == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + ph = (struct smb2_hnd *)idr_find(idp, fnum); + if (ph != *pph) { + return NT_STATUS_INVALID_PARAMETER; + } + idr_remove(idp, fnum); + TALLOC_FREE(*pph); + return NT_STATUS_OK; +} + +/*************************************************************** + Oplock mapping code. +***************************************************************/ + +static uint8_t flags_to_smb2_oplock(uint32_t create_flags) +{ + if (create_flags & REQUEST_BATCH_OPLOCK) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (create_flags & REQUEST_OPLOCK) { + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } + + /* create_flags doesn't do a level2 request. */ + return SMB2_OPLOCK_LEVEL_NONE; +} + +/*************************************************************** + Small wrapper that allows SMB2 create to return a uint16_t fnum. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_create_fnum(struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint16_t *pfid, + struct smb2_create_returns *cr) +{ + NTSTATUS status; + struct smb2_hnd h; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (cli->backup_intent) { + create_options |= FILE_OPEN_FOR_BACKUP_INTENT; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + start in a '\' */ + if (*fname == '\\') { + fname++; + } + + status = smb2cli_create(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + fname, + flags_to_smb2_oplock(create_flags), + SMB2_IMPERSONATION_IMPERSONATION, + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + NULL, + &h.fid_persistent, + &h.fid_volatile, + cr); + + if (NT_STATUS_IS_OK(status)) { + status = map_smb2_handle_to_fnum(cli, &h, pfid); + } + + return status; +} + +/*************************************************************** + Small wrapper that allows SMB2 close to use a uint16_t fnum. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum) +{ + struct smb2_hnd *ph = NULL; + NTSTATUS status; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = smb2cli_close(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 0, + ph->fid_persistent, + ph->fid_volatile); + + /* Delete the fnum -> handle mapping. */ + if (NT_STATUS_IS_OK(status)) { + status = delete_smb2_handle_mapping(cli, &ph, fnum); + } + + return status; +} + +/*************************************************************** + Small wrapper that allows SMB2 to create a directory + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname) +{ + NTSTATUS status; + uint16_t fnum; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = cli_smb2_create_fnum(cli, + dname, + 0, /* create_flags */ + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */ + FILE_CREATE, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + &fnum, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return cli_smb2_close_fnum(cli, fnum); +} + +/*************************************************************** + Small wrapper that allows SMB2 to delete a directory + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname) +{ + NTSTATUS status; + uint16_t fnum; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = cli_smb2_create_fnum(cli, + dname, + 0, /* create_flags */ + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE, /* create_options */ + &fnum, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return cli_smb2_close_fnum(cli, fnum); +} + +/*************************************************************** + Small wrapper that allows SMB2 to unlink a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname) +{ + NTSTATUS status; + uint16_t fnum; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = cli_smb2_create_fnum(cli, + fname, + 0, /* create_flags */ + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_NORMAL, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DELETE_ON_CLOSE, /* create_options */ + &fnum, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return cli_smb2_close_fnum(cli, fnum); +} + +/*************************************************************** + Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply. +***************************************************************/ + +static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data, + uint32_t dir_data_length, + struct file_info *finfo, + uint32_t *next_offset) +{ + size_t namelen = 0; + size_t slen = 0; + size_t ret = 0; + + if (dir_data_length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + *next_offset = IVAL(dir_data, 0); + + if (*next_offset > dir_data_length) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + if (*next_offset != 0) { + /* Ensure we only read what in this record. */ + dir_data_length = *next_offset; + } + + if (dir_data_length < 105) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + finfo->atime_ts = interpret_long_date((const char *)dir_data + 16); + finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24); + finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32); + finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0); + finfo->mode = CVAL(dir_data + 56, 0); + namelen = IVAL(dir_data + 60,0); + if (namelen > (dir_data_length - 104)) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + slen = SVAL(dir_data + 68, 0); + if (slen > 24) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->short_name, + dir_data + 70, + slen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->name, + dir_data + 104, + namelen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; +} + +/******************************************************************* + Given a filename - get its directory name +********************************************************************/ + +static bool windows_parent_dirname(TALLOC_CTX *mem_ctx, + const char *dir, + char **parent, + const char **name) +{ + char *p; + ptrdiff_t len; + + p = strrchr_m(dir, '\\'); /* Find final '\\', if any */ + + if (p == NULL) { + if (!(*parent = talloc_strdup(mem_ctx, "\\"))) { + return false; + } + if (name) { + *name = dir; + } + return true; + } + + len = p-dir; + + if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) { + return false; + } + (*parent)[len] = '\0'; + + if (name) { + *name = p+1; + } + return true; +} + +/*************************************************************** + Wrapper that allows SMB2 to list a directory. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_list(struct cli_state *cli, + const char *pathname, + NTSTATUS (*fn)(const char *, + struct file_info *, + const char *, + void *), + void *state) +{ + NTSTATUS status; + uint16_t fnum = -1; + char *parent_dir = NULL; + const char *mask = NULL; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *subframe = NULL; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* Get the directory name. */ + if (!windows_parent_dirname(frame, + pathname, + &parent_dir, + &mask)) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = cli_smb2_create_fnum(cli, + parent_dir, + 0, /* create_flags */ + SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE,/* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + &fnum, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + do { + uint8_t *dir_data = NULL; + uint32_t dir_data_length = 0; + uint32_t next_offset = 0; + subframe = talloc_stackframe(); + + status = smb2cli_query_directory(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + SMB2_FIND_ID_BOTH_DIRECTORY_INFO, + 0, /* flags */ + 0, /* file_index */ + ph->fid_persistent, + ph->fid_volatile, + mask, + 0xffff, + subframe, + &dir_data, + &dir_data_length); + + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { + break; + } + goto fail; + } + + do { + struct file_info *finfo = talloc_zero(subframe, + struct file_info); + + if (finfo == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = parse_finfo_id_both_directory_info(dir_data, + dir_data_length, + finfo, + &next_offset); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = fn(cli->dfs_mountpoint, + finfo, + pathname, + state); + + if (!NT_STATUS_IS_OK(status)) { + break; + } + + TALLOC_FREE(finfo); + + /* Move to next entry. */ + if (next_offset) { + dir_data += next_offset; + dir_data_length -= next_offset; + } + } while (next_offset != 0); + + TALLOC_FREE(subframe); + + } while (NT_STATUS_IS_OK(status)); + + if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { + status = NT_STATUS_OK; + } + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + TALLOC_FREE(subframe); + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a path info (basic level). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, + const char *name, + SMB_STRUCT_STAT *sbuf, + uint32_t *attributes) +{ + NTSTATUS status; + struct smb2_create_returns cr; + uint16_t fnum = -1; + size_t namelen = strlen(name); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + end in a '\' */ + if (namelen > 0 && name[namelen-1] == '\\') { + char *modname = talloc_strdup(talloc_tos(), name); + modname[namelen-1] = '\0'; + name = modname; + } + + /* This is commonly used as a 'cd'. Try qpathinfo on + a directory handle first. */ + + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + &fnum, + &cr); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { + /* Maybe a file ? */ + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + FILE_READ_ATTRIBUTES, /* desired_access */ + 0, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + 0, /* create_options */ + &fnum, + &cr); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + cli_smb2_close_fnum(cli, fnum); + + ZERO_STRUCTP(sbuf); + + sbuf->st_ex_atime = nt_time_to_unix_timespec(&cr.last_access_time); + sbuf->st_ex_mtime = nt_time_to_unix_timespec(&cr.last_write_time); + sbuf->st_ex_ctime = nt_time_to_unix_timespec(&cr.change_time); + sbuf->st_ex_size = cr.end_of_file; + *attributes = cr.file_attributes; + + return NT_STATUS_OK; +} + +/*************************************************************** + Helper function for pathname operations. +***************************************************************/ + +static NTSTATUS get_fnum_from_path(struct cli_state *cli, + const char *name, + uint32_t desired_access, + uint16_t *pfnum) +{ + NTSTATUS status; + size_t namelen = strlen(name); + TALLOC_CTX *frame = talloc_stackframe(); + + /* SMB2 is pickier about pathnames. Ensure it doesn't + end in a '\' */ + if (namelen > 0 && name[namelen-1] == '\\') { + char *modname = talloc_strdup(frame, name); + if (modname == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + modname[namelen-1] = '\0'; + name = modname; + } + + /* Try to open a file handle first. */ + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + desired_access, + 0, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + 0, /* create_options */ + pfnum, + NULL); + + if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + desired_access, + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + pfnum, + NULL); + } + + fail: + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a path info (ALTNAME level). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, + const char *name, + fstring alt_name) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + uint16_t fnum = -1; + struct smb2_hnd *ph = NULL; + uint32_t altnamelen = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level SMB_FILE_ALTERNATE_NAME_INFORMATION (1021) == SMB2 21 */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (outbuf.length < 4) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + altnamelen = IVAL(outbuf.data, 0); + if (altnamelen > outbuf.length - 4) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + if (altnamelen > 0) { + size_t ret = 0; + char *short_name = NULL; + ret = pull_string_talloc(frame, + outbuf.data, + FLAGS2_UNICODE_STRINGS, + &short_name, + outbuf.data + 4, + altnamelen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + fstrcpy(alt_name, short_name); + } else { + alt_name[0] = '\0'; + } + + status = NT_STATUS_OK; + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + TALLOC_FREE(frame); + return status; +} + + +/*************************************************************** + Wrapper that allows SMB2 to query a fnum info (basic level). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli, + uint16_t fnum, + uint16_t *mode, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level 0x12 (SMB2_FILE_ALL_INFORMATION). */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (outbuf.length < 0x60) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + if (create_time) { + *create_time = interpret_long_date((const char *)outbuf.data + 0x0); + } + if (access_time) { + *access_time = interpret_long_date((const char *)outbuf.data + 0x8); + } + if (write_time) { + *write_time = interpret_long_date((const char *)outbuf.data + 0x10); + } + if (change_time) { + *change_time = interpret_long_date((const char *)outbuf.data + 0x18); + } + if (mode) { + uint32_t attr = IVAL(outbuf.data, 0x20); + *mode = (uint16_t)attr; + } + if (size) { + uint64_t file_size = BVAL(outbuf.data, 0x30); + *size = (off_t)file_size; + } + if (ino) { + uint64_t file_index = BVAL(outbuf.data, 0x40); + *ino = (SMB_INO_T)file_index; + } + + fail: + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query an fnum. + Implement on top of cli_smb2_qfileinfo_basic(). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_getattrE(struct cli_state *cli, + uint16_t fnum, + uint16_t *attr, + off_t *size, + time_t *change_time, + time_t *access_time, + time_t *write_time) +{ + struct timespec access_time_ts; + struct timespec write_time_ts; + struct timespec change_time_ts; + NTSTATUS status = cli_smb2_qfileinfo_basic(cli, + fnum, + attr, + size, + NULL, + &access_time_ts, + &write_time_ts, + &change_time_ts, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (change_time) { + *change_time = change_time_ts.tv_sec; + } + if (access_time) { + *access_time = access_time_ts.tv_sec; + } + if (write_time) { + *write_time = write_time_ts.tv_sec; + } + return NT_STATUS_OK; +} + +/*************************************************************** + Wrapper that allows SMB2 to get pathname attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_getatr(struct cli_state *cli, + const char *name, + uint16_t *attr, + off_t *size, + time_t *write_time) +{ + NTSTATUS status; + uint16_t fnum = -1; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = cli_smb2_getattrE(cli, + fnum, + attr, + size, + NULL, + NULL, + write_time); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a pathname info (basic level). + Implement on top of cli_smb2_qfileinfo_basic(). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, + const char *name, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, + uint16_t *mode, + SMB_INO_T *ino) +{ + NTSTATUS status; + struct smb2_hnd *ph = NULL; + uint16_t fnum = -1; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_smb2_qfileinfo_basic(cli, + fnum, + mode, + size, + create_time, + access_time, + write_time, + change_time, + ino); + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query pathname streams. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, + const char *name, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + NTSTATUS status; + struct smb2_hnd *ph = NULL; + uint16_t fnum = -1; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level 22 (SMB2_FILE_STREAM_INFORMATION). */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (!parse_streams_blob(mem_ctx, + outbuf.data, + outbuf.length, + pnum_streams, + pstreams)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set pathname attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setatr(struct cli_state *cli, + const char *name, + uint16_t attr, + time_t mtime) +{ + NTSTATUS status; + uint16_t fnum = -1; + struct smb2_hnd *ph = NULL; + uint8_t inbuf_store[40]; + DATA_BLOB inbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_WRITE_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ + + inbuf.data = inbuf_store; + inbuf.length = sizeof(inbuf_store); + data_blob_clear(&inbuf); + + SIVAL(inbuf.data,32,attr); + if (mtime != 0) { + put_long_date((char *)inbuf.data + 16,mtime); + } + /* Set all the other times to -1. */ + SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL); + SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL); + SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL); + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set file handle times. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time) +{ + NTSTATUS status; + struct smb2_hnd *ph = NULL; + uint8_t inbuf_store[40]; + DATA_BLOB inbuf = data_blob_null; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ + + inbuf.data = inbuf_store; + inbuf.length = sizeof(inbuf_store); + data_blob_clear(&inbuf); + + SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL); + if (change_time != 0) { + put_long_date((char *)inbuf.data + 24, change_time); + } + if (access_time != 0) { + put_long_date((char *)inbuf.data + 8, access_time); + } + if (write_time != 0) { + put_long_date((char *)inbuf.data + 16, write_time); + } + + return smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); +} + +/*************************************************************** + Wrapper that allows SMB2 to query disk attributes (size). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) +{ + NTSTATUS status; + uint16_t fnum = -1; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + uint32_t sectors_per_unit = 0; + uint32_t bytes_per_sector = 0; + uint64_t total_size = 0; + uint64_t size_free = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* First open the top level directory. */ + status = cli_smb2_create_fnum(cli, + "", + 0, /* create_flags */ + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + &fnum, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 3 (SMB_FS_SIZE_INFORMATION). */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 2, /* in_info_type */ + 3, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (outbuf.length != 24) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + total_size = BVAL(outbuf.data, 0); + size_free = BVAL(outbuf.data, 8); + sectors_per_unit = IVAL(outbuf.data, 16); + bytes_per_sector = IVAL(outbuf.data, 20); + + if (bsize) { + *bsize = (int)(sectors_per_unit * bytes_per_sector); + } + if (total) { + *total = (int)total_size; + } + if (avail) { + *avail = (int)size_free; + } + + status = NT_STATUS_OK; + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a security descriptor. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **ppsd) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + struct security_descriptor *lsd = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 3, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + sec_info, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + status = unmarshall_sec_desc(mem_ctx, + outbuf.data, + outbuf.length, + &lsd); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (ppsd != NULL) { + *ppsd = lsd; + } else { + TALLOC_FREE(lsd); + } + + fail: + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set a security descriptor. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = marshall_sec_desc(frame, + sd, + &inbuf.data, + &inbuf.length); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */ + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 3, /* in_info_type */ + 0, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + sec_info, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + + fail: + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to rename a file. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_rename(struct cli_state *cli, + const char *fname_src, + const char *fname_dst) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + uint16_t fnum = -1; + struct smb2_hnd *ph = NULL; + smb_ucs2_t *converted_str = NULL; + size_t converted_size_bytes = 0; + size_t namelen = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + fname_src, + DELETE_ACCESS, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + start in a '\' */ + if (*fname_dst == '\\') { + fname_dst++; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + end in a '\' */ + namelen = strlen(fname_dst); + if (namelen > 0 && fname_dst[namelen-1] == '\\') { + char *modname = talloc_strdup(frame, fname_dst); + modname[namelen-1] = '\0'; + fname_dst = modname; + } + + if (!push_ucs2_talloc(frame, + &converted_str, + fname_dst, + &converted_size_bytes)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* W2K8 insists the dest name is not null + terminated. Remove the last 2 zero bytes + and reduce the name length. */ + + if (converted_size_bytes < 2) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + converted_size_bytes -= 2; + + inbuf = data_blob_talloc_zero(frame, + 20 + converted_size_bytes); + if (inbuf.data == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + SIVAL(inbuf.data, 16, converted_size_bytes); + memcpy(inbuf.data + 20, converted_str, converted_size_bytes); + + /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1), + level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */ + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set an EA on a fnum. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, + uint16_t fnum, + const char *ea_name, + const char *ea_val, + size_t ea_len) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + size_t bloblen = 0; + char *ea_name_ascii = NULL; + size_t namelen = 0; + struct smb2_hnd *ph = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Marshall the SMB2 EA data. */ + if (ea_len > 0xFFFF) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (!push_ascii_talloc(frame, + &ea_name_ascii, + ea_name, + &namelen)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (namelen < 2 || namelen > 0xFF) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + bloblen = 8 + ea_len + namelen; + /* Round up to a 4 byte boundary. */ + bloblen = ((bloblen + 3)&~3); + + inbuf = data_blob_talloc_zero(frame, bloblen); + if (inbuf.data == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + /* namelen doesn't include the NULL byte. */ + SCVAL(inbuf.data, 5, namelen - 1); + SSVAL(inbuf.data, 6, ea_len); + memcpy(inbuf.data + 8, ea_name_ascii, namelen); + memcpy(inbuf.data + 8 + namelen, ea_val, ea_len); + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ + + status = smb2cli_set_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + ph->fid_persistent, + ph->fid_volatile); + + fail: + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set an EA on a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli, + const char *name, + const char *ea_name, + const char *ea_val, + size_t ea_len) +{ + NTSTATUS status; + uint16_t fnum = -1; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_WRITE_EA, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_set_ea_fnum(cli, + fnum, + ea_name, + ea_val, + ea_len); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + fail: + + if (fnum != -1) { + cli_smb2_close_fnum(cli, fnum); + } + + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to get an EA list on a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, + const char *name, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_array) +{ + NTSTATUS status; + uint16_t fnum = -1; + DATA_BLOB outbuf = data_blob_null; + struct smb2_hnd *ph = NULL; + struct ea_list *ea_list = NULL; + struct ea_list *eal = NULL; + size_t ea_count = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + *pnum_eas = 0; + *pea_array = NULL; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_EA, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ + + status = smb2cli_query_info(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + 1, /* in_info_type */ + SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + ph->fid_persistent, + ph->fid_volatile, + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + ea_list = read_nttrans_ea_list(ctx, + (const char *)outbuf.data, + outbuf.length); + if (ea_list == NULL) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + /* Convert to an array. */ + for (eal = ea_list; eal; eal = eal->next) { + ea_count++; + } + + if (ea_count) { + *pea_array = talloc_array(ctx, struct ea_struct, ea_count); + if (*pea_array == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + ea_count = 0; + for (eal = ea_list; eal; eal = eal->next) { + (*pea_array)[ea_count++] = ea_list->ea; + } + *pnum_eas = ea_count; + } + + fail: + + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_read_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint64_t start_offset; + uint32_t size; + uint32_t received; + uint8_t *buf; +}; + +static void cli_smb2_read_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq; + struct cli_smb2_read_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->start_offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->received = 0; + state->buf = NULL; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_read_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->size, + state->start_offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* minimum_count */ + 0); /* remaining_bytes */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_read_done, req); + return req; +} + +static void cli_smb2_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_read_state *state = tevent_req_data( + req, struct cli_smb2_read_state); + NTSTATUS status; + + status = smb2cli_read_recv(subreq, state, + &state->buf, &state->received); + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->received > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_read_recv(struct tevent_req *req, + ssize_t *received, + uint8_t **rcvbuf) +{ + NTSTATUS status; + struct cli_smb2_read_state *state = tevent_req_data( + req, struct cli_smb2_read_state); + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + /* + * As in cli_read_andx_recv() rcvbuf is talloced from the request, so + * better make sure that you copy it away before you talloc_free(req). + * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it! + */ + *received = (ssize_t)state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} + +struct cli_smb2_write_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint32_t flags; + const uint8_t *buf; + uint64_t offset; + uint32_t size; + uint32_t written; +}; + +static void cli_smb2_write_written(struct tevent_req *req); + +struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq = NULL; + struct cli_smb2_write_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */ + state->flags = (uint32_t)mode; + state->buf = buf; + state->offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->written = 0; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->size, + state->offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_write_written, req); + return req; +} + +static void cli_smb2_write_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_write_state *state = tevent_req_data( + req, struct cli_smb2_write_state); + NTSTATUS status; + uint32_t written; + + status = smb2cli_write_recv(subreq, &written); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->written = written; + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_write_recv(struct tevent_req *req, + size_t *pwritten) +{ + struct cli_smb2_write_state *state = tevent_req_data( + req, struct cli_smb2_write_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + if (pwritten != NULL) { + *pwritten = (size_t)state->written; + } + tevent_req_received(req); + return NT_STATUS_OK; +} + +/*************************************************************** + Wrapper that allows SMB2 async write using an fnum. + This is mostly cut-and-paste from Volker's code inside + source3/libsmb/clireadwrite.c, adapted for SMB2. + + Done this way so I can reuse all the logic inside cli_push() + for free :-). +***************************************************************/ + +struct cli_smb2_writeall_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint32_t flags; + const uint8_t *buf; + uint64_t offset; + uint32_t size; + uint32_t written; +}; + +static void cli_smb2_writeall_written(struct tevent_req *req); + +struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq = NULL; + struct cli_smb2_writeall_state *state = NULL; + uint32_t to_write; + uint32_t max_size; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */ + state->flags = (uint32_t)mode; + state->buf = buf; + state->offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->written = 0; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + to_write = state->size; + max_size = smb2cli_conn_max_write_size(state->cli->conn); + to_write = MIN(max_size, to_write); + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (ok) { + to_write = MIN(max_size, to_write); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + to_write, + state->offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf + state->written); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_writeall_written, req); + return req; +} + +static void cli_smb2_writeall_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_writeall_state *state = tevent_req_data( + req, struct cli_smb2_writeall_state); + NTSTATUS status; + uint32_t written, to_write; + uint32_t max_size; + bool ok; + + status = smb2cli_write_recv(subreq, &written); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->written += written; + + if (state->written > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + to_write = state->size - state->written; + + if (to_write == 0) { + tevent_req_done(req); + return; + } + + max_size = smb2cli_conn_max_write_size(state->cli->conn); + to_write = MIN(max_size, to_write); + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (ok) { + to_write = MIN(max_size, to_write); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + to_write, + state->offset + state->written, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf + state->written); + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_writeall_written, req); +} + +NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req, + size_t *pwritten) +{ + struct cli_smb2_writeall_state *state = tevent_req_data( + req, struct cli_smb2_writeall_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pwritten != NULL) { + *pwritten = (size_t)state->written; + } + return NT_STATUS_OK; +} diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h new file mode 100644 index 0000000..0068686 --- /dev/null +++ b/source3/libsmb/cli_smb2_fnum.h @@ -0,0 +1,161 @@ +/* + Unix SMB/CIFS implementation. + smb2 wrapper client routines + Copyright (C) Jeremy Allison 2013 + + 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 . +*/ + +#ifndef __SMB2CLI_FNUM_H__ +#define __SMB2CLI_FNUM_H__ + +struct smbXcli_conn; +struct smbXcli_session; +struct cli_state; +struct file_info; + +NTSTATUS cli_smb2_create_fnum(struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint16_t *pfid, + struct smb2_create_returns *cr); + +NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum); +NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dirname); +NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dirname); +NTSTATUS cli_smb2_unlink(struct cli_state *cli,const char *fname); +NTSTATUS cli_smb2_list(struct cli_state *cli, + const char *pathname, + NTSTATUS (*fn)(const char *, + struct file_info *, + const char *, + void *), + void *state); +NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, + const char *name, + SMB_STRUCT_STAT *sbuf, + uint32_t *attributes); +NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, + const char *name, + fstring alt_name); +NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli, + uint16_t fnum, + uint16_t *mode, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino); +NTSTATUS cli_smb2_getattrE(struct cli_state *cli, + uint16_t fnum, + uint16_t *attr, + off_t *size, + time_t *change_time, + time_t *access_time, + time_t *write_time); +NTSTATUS cli_smb2_getatr(struct cli_state *cli, + const char *name, + uint16_t *attr, + off_t *size, + time_t *write_time); +NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, + const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, + uint16_t *mode, + SMB_INO_T *ino); +NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, + const char *name, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams); +NTSTATUS cli_smb2_setatr(struct cli_state *cli, + const char *fname, + uint16_t attr, + time_t mtime); +NTSTATUS cli_smb2_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time); +NTSTATUS cli_smb2_dskattr(struct cli_state *cli, + int *bsize, + int *total, + int *avail); +NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **ppsd); +NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd); +NTSTATUS cli_smb2_rename(struct cli_state *cli, + const char *fname_src, + const char *fname_dst); +NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, + uint16_t fnum, + const char *ea_name, + const char *ea_val, + size_t ea_len); +NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, + const char *name, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_list); +NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli, + const char *name, + const char *ea_name, + const char *ea_val, + size_t ea_len); +struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, + size_t size); +NTSTATUS cli_smb2_read_recv(struct tevent_req *req, + ssize_t *received, + uint8_t **rcvbuf); +struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size); +NTSTATUS cli_smb2_write_recv(struct tevent_req *req, + size_t *pwritten); +struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size); +NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req, + size_t *pwritten); +#endif /* __SMB2CLI_FNUM_H__ */ diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 77e2fa3..12a340c 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -879,11 +879,6 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname, Get the stream info ****************************************************************************/ -static bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *data, - size_t data_len, - unsigned int *pnum_streams, - struct stream_struct **pstreams); - struct cli_qpathinfo_streams_state { uint32_t num_data; uint8_t *data; @@ -986,7 +981,7 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname, return status; } -static bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata, +bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata, size_t data_len, unsigned int *pnum_streams, struct stream_struct **pstreams) diff --git a/source3/libsmb/clirap.h b/source3/libsmb/clirap.h index 91628ea..e105182 100644 --- a/source3/libsmb/clirap.h +++ b/source3/libsmb/clirap.h @@ -94,6 +94,10 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname, TALLOC_CTX *mem_ctx, unsigned int *pnum_streams, struct stream_struct **pstreams); +bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata, + size_t data_len, + unsigned int *pnum_streams, + struct stream_struct **pstreams); NTSTATUS cli_qfilename(struct cli_state *cli, uint16_t fnum, TALLOC_CTX *mem_ctx, char **name); NTSTATUS cli_qfileinfo_basic(struct cli_state *cli, uint16_t fnum, diff --git a/source3/libsmb/libsmb.h b/source3/libsmb/libsmb.h index 061f317..6df06ae 100644 --- a/source3/libsmb/libsmb.h +++ b/source3/libsmb/libsmb.h @@ -26,5 +26,6 @@ #include "client.h" #include "libads/ads_status.h" #include "libsmb/proto.h" +#include "libsmb/cli_smb2_fnum.h" #endif /* _LIBSMB_LIBSMB_H */ diff --git a/source3/wscript_build b/source3/wscript_build index 569c632..e166b16 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -135,7 +135,8 @@ LIBSMB_SRC = '''libsmb/clientgen.c libsmb/cliconnect.c libsmb/clifile.c libsmb/cli_np_tstream.c libsmb/reparse_symlink.c libsmb/clisymlink.c - libsmb/smbsock_connect.c''' + libsmb/smbsock_connect.c + libsmb/cli_smb2_fnum.c''' LIBMSRPC_SRC = ''' rpc_client/cli_pipe.c -- 1.8.3 From de93438a1d7681c3286dc1833413f0a53923b180 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 15:59:13 -0700 Subject: [PATCH 27/57] s3:libsmb: Plumb cli_smb2_rename() inside cli_rename(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 4b4e1a0..bfc3984 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1097,11 +1097,19 @@ NTSTATUS cli_rename_recv(struct tevent_req *req) NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_rename(cli, + fname_src, + fname_dst); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From b627d648dc6cbc57de58e1b2ef79fb100b8858c5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:00:40 -0700 Subject: [PATCH 28/57] s3:libsmb: Plumb cli_smb2_unlink() inside cli_unlink(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index bfc3984..f2e4ac2 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1415,11 +1415,17 @@ NTSTATUS cli_unlink_recv(struct tevent_req *req) NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint16_t mayhave_attrs) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_unlink(cli, fname); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From a5bcc5fbc43e5a5cbd20f8a86e5131817a3e0482 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:01:49 -0700 Subject: [PATCH 29/57] s3:libsmb: Plumb cli_smb2_mkdir() inside cli_mkdir(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index f2e4ac2..9659fc1 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1525,11 +1525,17 @@ NTSTATUS cli_mkdir_recv(struct tevent_req *req) NTSTATUS cli_mkdir(struct cli_state *cli, const char *dname) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_mkdir(cli, dname); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From e02dedc7522a407f328f6c695376d715e74013da Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:03:00 -0700 Subject: [PATCH 30/57] s3:libsmb: Plumb cli_smb2_rmdir() inside cli_rmdir(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 9659fc1..d63734d 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1635,11 +1635,17 @@ NTSTATUS cli_rmdir_recv(struct tevent_req *req) NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_rmdir(cli, dname); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From d828477e6063fa0f9c36c3a286378893e7f61dae Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:06:19 -0700 Subject: [PATCH 31/57] s3:libsmb: Plumb cli_smb2_create_fnum() inside cli_ntcreate(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index d63734d..3c799a6 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -1914,11 +1914,26 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, uint8_t SecurityFlags, uint16_t *pfid) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_create_fnum(cli, + fname, + CreatFlags, + DesiredAccess, + FileAttributes, + ShareAccess, + CreateDisposition, + CreateOptions, + pfid, + NULL); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From c3c38330eac8e7605a997b26abad9d1001d25138 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:10:34 -0700 Subject: [PATCH 32/57] s3:libsmb: Plumb cli_smb2_close_fnum() inside cli_close(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 3c799a6..571e1cc 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -2536,11 +2536,17 @@ NTSTATUS cli_close_recv(struct tevent_req *req) NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_close_fnum(cli, fnum); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 58259f2ceaff268a32d5930c2e8109e3169990d2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:11:59 -0700 Subject: [PATCH 33/57] s3:libsmb: Plumb cli_smb2_getattrE() inside cli_getattrE(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 571e1cc..e3fb62d 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -3379,11 +3379,23 @@ NTSTATUS cli_getattrE(struct cli_state *cli, time_t *access_time, time_t *write_time) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_getattrE(cli, + fnum, + attr, + size, + change_time, + access_time, + write_time); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 1d9f056d2a3f25a38c899a3e371b7d533cb6fa79 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:14:47 -0700 Subject: [PATCH 34/57] s3:libsmb: Plumb cli_smb2_setattrE() inside cli_setattrE(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index e3fb62d..d0e9bb6 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -3647,11 +3647,21 @@ NTSTATUS cli_setattrE(struct cli_state *cli, time_t access_time, time_t write_time) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_setattrE(cli, + fnum, + change_time, + access_time, + write_time); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 392225c3a834b3852fac68cd5e276971f1d17f4d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:16:03 -0700 Subject: [PATCH 35/57] s3:libsmb: Plumb cli_smb2_setatr() inside cli_setatr(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index d0e9bb6..4ac7313 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -3786,11 +3786,20 @@ NTSTATUS cli_setatr(struct cli_state *cli, uint16_t attr, time_t mtime) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_setatr(cli, + fname, + attr, + mtime); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 65c530a863921a6cc7642793860b511cd4aa00e6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 14 Aug 2013 14:37:11 -0700 Subject: [PATCH 36/57] s3:libsmb: Plumb cli_smb2_getatr() inside cli_getatr(). Signed-off-by: Jeremy Allison --- source3/libsmb/clifile.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 4ac7313..f674118 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -3538,11 +3538,21 @@ NTSTATUS cli_getatr(struct cli_state *cli, off_t *size, time_t *write_time) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_getatr(cli, + fname, + attr, + size, + write_time); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 6f538bcca10e80a28a4f93547983f165cf391d68 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:17:12 -0700 Subject: [PATCH 37/57] s3:libsmb: Plumb cli_smb2_dskattr() inside cli_dskattr(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index f674118..77796d8 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -4034,11 +4034,17 @@ NTSTATUS cli_dskattr_recv(struct tevent_req *req, int *bsize, int *total, int *a NTSTATUS cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_OK; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_dskattr(cli, bsize, total, avail); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 6df2e029c3c8203ca906220ac534519cfe8b5e5e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:19:06 -0700 Subject: [PATCH 38/57] s3:libsmb: Fix cli_set_ea_path() to use frame instead of talloc_tos(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 77796d8..5cd1ff2 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -4322,9 +4322,10 @@ NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path, NTSTATUS status; TALLOC_CTX *frame = talloc_stackframe(); - param = talloc_array(talloc_tos(), uint8_t, 6); + param = talloc_array(frame, uint8_t, 6); if (!param) { - return NT_STATUS_NO_MEMORY; + status = NT_STATUS_NO_MEMORY; + goto fail; } SSVAL(param,0,SMB_INFO_SET_EA); SSVAL(param,2,0); @@ -4337,7 +4338,10 @@ NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path, status = cli_set_ea(cli, TRANSACT2_SETPATHINFO, param, param_len, ea_name, ea_val, ea_len); - talloc_free(frame); + + fail: + + TALLOC_FREE(frame); return status; } -- 1.8.3 From 71f3e1501c584a272fafcf44fe232113aa4fb6fb Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:21:48 -0700 Subject: [PATCH 39/57] s3:libsmb: Plumb cli_smb2_set_ea_path() inside cli_set_ea_path(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 5cd1ff2..2cfe7cd 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -4320,7 +4320,17 @@ NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path, unsigned int param_len = 0; uint8_t *param; NTSTATUS status; - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_ea_path(cli, + path, + ea_name, + ea_val, + ea_len); + } + + frame = talloc_stackframe(); param = talloc_array(frame, uint8_t, 6); if (!param) { -- 1.8.3 From 092bed292d2ca60c5a026cd02542982588c485ce Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:32:55 -0700 Subject: [PATCH 40/57] s3:libsmb: Plumb cli_smb2_set_ea_fnum() inside cli_set_ea_fnum(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 2cfe7cd..d186112 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -4365,6 +4365,14 @@ NTSTATUS cli_set_ea_fnum(struct cli_state *cli, uint16_t fnum, { uint8_t param[6]; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_ea_fnum(cli, + fnum, + ea_name, + ea_val, + ea_len); + } + memset(param, 0, 6); SSVAL(param,0,fnum); SSVAL(param,2,SMB_INFO_SET_EA); -- 1.8.3 From 55770a7ffcc40b73c43bb19b5b56a2b29541cbd3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:42:02 -0700 Subject: [PATCH 41/57] s3:libsmb: Plumb cli_smb2_get_ea_list_path() inside cli_get_ea_list_path(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clifile.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index d186112..424354b 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -4554,11 +4554,21 @@ NTSTATUS cli_get_ea_list_path(struct cli_state *cli, const char *path, size_t *pnum_eas, struct ea_struct **pea_list) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev = NULL; struct tevent_req *req = NULL; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_ea_list_path(cli, + path, + ctx, + pnum_eas, + pea_list); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 1754cdddd22ad760a48143cf8beba2f53dba5da3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:43:33 -0700 Subject: [PATCH 42/57] s3:libsmb: Plumb cli_smb2_list() inside cli_list(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clilist.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c index b0d3a4e..ed970cd 100644 --- a/source3/libsmb/clilist.c +++ b/source3/libsmb/clilist.c @@ -933,7 +933,7 @@ NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute, NTSTATUS (*fn)(const char *, struct file_info *, const char *, void *), void *state) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; @@ -941,6 +941,12 @@ NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute, size_t i, num_finfo; uint16_t info_level; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_list(cli, mask, fn, state); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From c2ae51be9c06af4dbad31e542d953e34163cfaeb Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:45:05 -0700 Subject: [PATCH 43/57] s3:libsmb: Plumb cli_smb2_qpathinfo2() inside cli_qpathinfo2(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clirap.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 12a340c..41608dd 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -845,11 +845,25 @@ NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname, off_t *size, uint16 *mode, SMB_INO_T *ino) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo2(cli, + fname, + create_time, + access_time, + write_time, + change_time, + size, + mode, + ino); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From d2eb90a132bba0b5094966226641c2926fc7baac Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:46:05 -0700 Subject: [PATCH 44/57] s3:libsmb: Plumb cli_smb2_qpathinfo_streams() inside cli_qpathinfo_streams(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clirap.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 41608dd..bb4f334 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -965,11 +965,21 @@ NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname, unsigned int *pnum_streams, struct stream_struct **pstreams) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_streams(cli, + fname, + mem_ctx, + pnum_streams, + pstreams); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 2bc5541cd7eb427f1132ca19a871b3af0e7afbcc Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:47:21 -0700 Subject: [PATCH 45/57] s3:libsmb: Plumb cli_smb2_qfileinfo_basic() inside cli_qfileinfo_basic(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clirap.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index bb4f334..06dea82 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -1149,6 +1149,18 @@ NTSTATUS cli_qfileinfo_basic(struct cli_state *cli, uint16_t fnum, uint32_t num_rdata; NTSTATUS status; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qfileinfo_basic(cli, + fnum, + mode, + size, + create_time, + access_time, + write_time, + change_time, + ino); + } + /* if its a win95 server then fail this - win95 totally screws it up */ if (cli->win95) { -- 1.8.3 From a54e1f7750dc316fc275fbe13214b690db3d3d70 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 Aug 2013 10:52:02 -0700 Subject: [PATCH 46/57] s3:libsmb: Plumb cli_smb2_qpathinfo_basic() inside cli_qpathinfo_basic(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clirap.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 06dea82..7a480d6 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -1275,11 +1275,20 @@ NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req, NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name, SMB_STRUCT_STAT *sbuf, uint32 *attributes) { - TALLOC_CTX *frame = talloc_stackframe(); + TALLOC_CTX *frame = NULL; struct tevent_context *ev; struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_basic(cli, + name, + sbuf, + attributes); + } + + frame = talloc_stackframe(); + if (smbXcli_conn_has_async_calls(cli->conn)) { /* * Can't use sync call while an async call is in flight -- 1.8.3 From 389f59815cb381a3f4f305456425f301de260eb7 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 7 Aug 2013 16:50:03 -0700 Subject: [PATCH 47/57] s3:libsmb: Plumb cli_smb2_qpathinfo_alt_name() inside cli_qpathinfo_alt_name(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clirap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c index 7a480d6..d6d2fae 100644 --- a/source3/libsmb/clirap.c +++ b/source3/libsmb/clirap.c @@ -1326,6 +1326,12 @@ NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstrin size_t converted_size = 0; NTSTATUS status; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_alt_name(cli, + fname, + alt_name); + } + status = cli_qpathinfo(talloc_tos(), cli, fname, SMB_QUERY_FILE_ALT_NAME_INFO, 4, CLI_BUFFER_SIZE, &rdata, &num_rdata); -- 1.8.3 From fc373562770558b451449c7aeff57cd746f0ded2 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 Aug 2013 11:00:08 -0700 Subject: [PATCH 48/57] s3:libsmb: Plumb cli_smb2_query_security_descriptor() inside cli_query_security_descriptor(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clisecdesc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c index 24da39d..a82feab 100644 --- a/source3/libsmb/clisecdesc.c +++ b/source3/libsmb/clisecdesc.c @@ -20,6 +20,7 @@ #include "includes.h" #include "libsmb/libsmb.h" #include "../libcli/security/secdesc.h" +#include "../libcli/smb/smbXcli_base.h" NTSTATUS cli_query_security_descriptor(struct cli_state *cli, uint16_t fnum, @@ -33,6 +34,14 @@ NTSTATUS cli_query_security_descriptor(struct cli_state *cli, NTSTATUS status; struct security_descriptor *lsd; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_query_security_descriptor(cli, + fnum, + sec_info, + mem_ctx, + sd); + } + SIVAL(param, 0, fnum); SIVAL(param, 4, sec_info); -- 1.8.3 From 6e795f9d6e50a5aa1e7f03aa774d3b51aad71997 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 8 Aug 2013 11:00:49 -0700 Subject: [PATCH 49/57] s3:libsmb: Plumb cli_smb2_set_security_descriptor() inside cli_set_security_descriptor(). Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/libsmb/clisecdesc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c index a82feab..986610f 100644 --- a/source3/libsmb/clisecdesc.c +++ b/source3/libsmb/clisecdesc.c @@ -104,6 +104,13 @@ NTSTATUS cli_set_security_descriptor(struct cli_state *cli, size_t len; NTSTATUS status; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_security_descriptor(cli, + fnum, + sec_info, + sd); + } + status = marshall_sec_desc(talloc_tos(), sd, &data, &len); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("marshall_sec_desc failed: %s\n", -- 1.8.3 From a9563e8e7a6b67ab313f9b7eb86d2f2793251ce0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 16:33:30 +0200 Subject: [PATCH 50/57] s3:libsmb: add SMB2 support to cli_push* Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 66 ++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 10dc79b..821bcb1 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -1120,7 +1120,11 @@ struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, state->priv = priv; state->next_offset = start_offset; - state->chunk_size = cli_write_max_bufsize(cli, mode, 14); + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + state->chunk_size = smb2cli_conn_max_write_size(cli->conn); + } else { + state->chunk_size = cli_write_max_bufsize(cli, mode, 14); + } if (state->chunk_size > page_size) { state->chunk_size &= ~(page_size - 1); } @@ -1257,21 +1261,47 @@ static void cli_push_chunk_ship(struct cli_push_chunk *chunk) ofs = chunk->ofs + chunk->tmp_size; size = chunk->total_size - chunk->tmp_size; - ok = smb1cli_conn_req_possible(state->cli->conn); - if (!ok) { - return; - } + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; - chunk->subreq = cli_write_andx_send(chunk, - state->ev, - state->cli, - state->fnum, - state->mode, - buf, - ofs, - size); - if (tevent_req_nomem(chunk->subreq, req)) { - return; + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + return; + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + chunk->subreq = cli_smb2_write_send(chunk, + state->ev, + state->cli, + state->fnum, + state->mode, + buf, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } else { + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + return; + } + + chunk->subreq = cli_write_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + state->mode, + buf, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } } tevent_req_set_callback(chunk->subreq, cli_push_chunk_done, @@ -1296,7 +1326,11 @@ static void cli_push_chunk_done(struct tevent_req *subreq) chunk->subreq = NULL; - status = cli_write_andx_recv(subreq, &written); + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_write_recv(subreq, &written); + } else { + status = cli_write_andx_recv(subreq, &written); + } TALLOC_FREE(subreq); if (tevent_req_nterror(req, status)) { return; -- 1.8.3 From 83d2c6c690d679118e4fa60db7cc87369b5f2b2c Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 18:20:08 +0200 Subject: [PATCH 51/57] s3:libsmb: add SMB2 support to cli_pull* Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 60 +++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 821bcb1..d28c226 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -360,7 +360,11 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, return tevent_req_post(req, ev); } - state->chunk_size = cli_read_max_bufsize(cli); + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + state->chunk_size = smb2cli_conn_max_read_size(cli->conn); + } else { + state->chunk_size = cli_read_max_bufsize(cli); + } if (state->chunk_size > page_size) { state->chunk_size &= ~(page_size - 1); } @@ -519,19 +523,43 @@ static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk) ofs = chunk->ofs + chunk->tmp_size; size = chunk->total_size - chunk->tmp_size; - ok = smb1cli_conn_req_possible(state->cli->conn); - if (!ok) { - return; - } + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; - chunk->subreq = cli_read_andx_send(chunk, - state->ev, - state->cli, - state->fnum, - ofs, - size); - if (tevent_req_nomem(chunk->subreq, req)) { - return; + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + return; + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + chunk->subreq = cli_smb2_read_send(chunk, + state->ev, + state->cli, + state->fnum, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } else { + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + return; + } + + chunk->subreq = cli_read_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } } tevent_req_set_callback(chunk->subreq, cli_pull_chunk_done, @@ -557,7 +585,11 @@ static void cli_pull_chunk_done(struct tevent_req *subreq) chunk->subreq = NULL; - status = cli_read_andx_recv(subreq, &received, &buf); + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_read_recv(subreq, &received, &buf); + } else { + status = cli_read_andx_recv(subreq, &received, &buf); + } if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { received = 0; status = NT_STATUS_OK; -- 1.8.3 From 1fba3c247afc7c4e66b1379087776cdd6c823273 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 13 Aug 2013 18:38:57 +0200 Subject: [PATCH 52/57] s3:libsmb: add support for SMB2 in cli_writeall() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/clireadwrite.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index d28c226..adcd98b 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -1066,7 +1066,13 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, if (ev == NULL) { goto fail; } - req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size); + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode, + buf, offset, size); + } else { + req = cli_writeall_send(frame, ev, cli, fnum, mode, + buf, offset, size); + } if (req == NULL) { goto fail; } @@ -1074,7 +1080,11 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, status = map_nt_error_from_unix(errno); goto fail; } - status = cli_writeall_recv(req, pwritten); + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_writeall_recv(req, pwritten); + } else { + status = cli_writeall_recv(req, pwritten); + } fail: TALLOC_FREE(frame); return status; -- 1.8.3 From b5fe3c859d2bfe78cb61de79f3c4b9e72d9974c5 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 11:38:22 +0200 Subject: [PATCH 53/57] s3:libsmb: make cli_tdis_send/recv static Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/cliconnect.c | 4 ++-- source3/libsmb/proto.h | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index f963edf..a8769e5 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -2752,7 +2752,7 @@ struct cli_tdis_state { static void cli_tdis_done(struct tevent_req *subreq); -struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli) { @@ -2791,7 +2791,7 @@ static void cli_tdis_done(struct tevent_req *subreq) tevent_req_done(req); } -NTSTATUS cli_tdis_recv(struct tevent_req *req) +static NTSTATUS cli_tdis_recv(struct tevent_req *req) { return tevent_req_simple_recv_ntstatus(req); } diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h index 48496c8..648bdda 100644 --- a/source3/libsmb/proto.h +++ b/source3/libsmb/proto.h @@ -75,10 +75,6 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, const char *dev, const char *pass, int passlen); NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share, const char *dev, const char *pass, int passlen); -struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli); -NTSTATUS cli_tdis_recv(struct tevent_req *req); NTSTATUS cli_tdis(struct cli_state *cli); NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss, uint16_t port, int name_type, const char *myname, -- 1.8.3 From 27cbebb1f967f225da2e1f9ba9012de1360776ef Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 11:44:10 +0200 Subject: [PATCH 54/57] s3:libsmb: only set tcon to invalid in smb2cli_tdis* This matches the behavior of cli_tdis*. Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/smb2cli_tcon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/smb2cli_tcon.c b/source3/libsmb/smb2cli_tcon.c index b3136fa..2467ce5 100644 --- a/source3/libsmb/smb2cli_tcon.c +++ b/source3/libsmb/smb2cli_tcon.c @@ -243,7 +243,8 @@ static void smb2cli_tdis_done(struct tevent_req *subreq) if (tevent_req_nterror(req, status)) { return; } - TALLOC_FREE(state->cli->smb2.tcon); + smb2cli_tcon_set_values(state->cli->smb2.tcon, NULL, + UINT32_MAX, 0, 0, 0, 0); tevent_req_done(req); } -- 1.8.3 From a6c63ac13004ee0538b982506c46117e7f68a411 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 11:44:58 +0200 Subject: [PATCH 55/57] s3:libsmb: call smb2cli_tdis() from cli_tdis() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/cliconnect.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index a8769e5..26317d8 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -2802,6 +2802,10 @@ NTSTATUS cli_tdis(struct cli_state *cli) struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return smb2cli_tdis(cli); + } + if (smbXcli_conn_has_async_calls(cli->conn)) { return NT_STATUS_INVALID_PARAMETER; } -- 1.8.3 From 58414c1fb1d7ebbefb3dcea969461c8a6242d692 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 11:48:40 +0200 Subject: [PATCH 56/57] s3:libsmb: make cli_ulogoff_send/recv static Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/cliconnect.c | 4 ++-- source3/libsmb/proto.h | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index 26317d8..b91c3eb 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -2240,7 +2240,7 @@ struct cli_ulogoff_state { static void cli_ulogoff_done(struct tevent_req *subreq); -struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx, +static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli) { @@ -2283,7 +2283,7 @@ static void cli_ulogoff_done(struct tevent_req *subreq) tevent_req_done(req); } -NTSTATUS cli_ulogoff_recv(struct tevent_req *req) +static NTSTATUS cli_ulogoff_recv(struct tevent_req *req) { return tevent_req_simple_recv_ntstatus(req); } diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h index 648bdda..a1389ff 100644 --- a/source3/libsmb/proto.h +++ b/source3/libsmb/proto.h @@ -54,10 +54,6 @@ struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli); NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req); -struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct cli_state *cli); -NTSTATUS cli_ulogoff_recv(struct tevent_req *req); NTSTATUS cli_ulogoff(struct cli_state *cli); struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx, struct tevent_context *ev, -- 1.8.3 From ee9242a14d970406152bec5f36798d3655597f8d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 14 Aug 2013 15:18:47 -0700 Subject: [PATCH 57/57] s3:libsmb: call smb2cli_logoff() from cli_ulogoff() Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/libsmb/cliconnect.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index b91c3eb..7bf4187 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -2294,6 +2294,18 @@ NTSTATUS cli_ulogoff(struct cli_state *cli) struct tevent_req *req; NTSTATUS status = NT_STATUS_NO_MEMORY; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_logoff(cli->conn, + cli->timeout, + cli->smb2.session); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + smb2cli_session_set_id_and_flags(cli->smb2.session, + UINT64_MAX, 0); + return NT_STATUS_OK; + } + if (smbXcli_conn_has_async_calls(cli->conn)) { return NT_STATUS_INVALID_PARAMETER; } -- 1.8.3