[PATCH} Implement SMB3/SMB3 in smbclient and smbcacls (and generic client library).

Jeremy Allison jra at samba.org
Fri Aug 9 16:07:27 MDT 2013


So here's the complete patch, broken up into
(hopefully) reviewable-sized micro-patches.

It implements full (except for POSIX SMB1 calls
obviously :-) SMB2/SMB3 support for smbclient
and smbcacls.

Test using

smbclient //server/share -mSMB3

and you'll get an SMB3 connection.

I'm hoping to get this reviewed and
in as OEMs are starting to see customer
environments where SMB1 is turned off,
so having our client tools work there
is obviously very important.

Cheers,

Jeremy.
-------------- next part --------------
From 20da853d83971086065b6cf068acb0998778122e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 13:48:55 -0700
Subject: [PATCH 01/37] 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 <jra at samba.org>
---
 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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 <http://www.gnu.org/licenses/>.
+*/
+
+#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 f7988a6..e3605e3 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 c6d683737fd44f8f8ce8ca42b26c2e9eb8fdd885 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 14:23:02 -0700
Subject: [PATCH 02/37] Change interpret_protocol() to use the same protocol
 list in enum_protocol[] used by the server code.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 lib/param/param_table.c    |  2 +-
 source3/include/includes.h |  4 ++--
 source3/include/proto.h    |  1 +
 source3/lib/util.c         | 25 +++++++++----------------
 source3/param/loadparm.c   |  2 +-
 5 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 1b1497c..3d6ddbb 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -27,7 +27,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-static const struct enum_list enum_protocol[] = {
+const struct enum_list enum_protocol[] = {
 	{PROTOCOL_SMB2_10, "SMB2"}, /* for now keep PROTOCOL_SMB2_10 */
 	{PROTOCOL_SMB3_00, "SMB3"}, /* for now keep PROTOCOL_SMB3_00 */
 	{PROTOCOL_SMB3_00, "SMB3_00"},
diff --git a/source3/include/includes.h b/source3/include/includes.h
index 1b22a57..cbb5dd9 100644
--- a/source3/include/includes.h
+++ b/source3/include/includes.h
@@ -399,13 +399,13 @@ typedef char fstring[FSTRING_LEN];
 /* samba_setXXid functions. */
 #include "../lib/util/setid.h"
 
+#include "lib/param/loadparm.h"
+
 /***** prototypes *****/
 #ifndef NO_PROTO_H
 #include "proto.h"
 #endif
 
-#include "lib/param/loadparm.h"
-
 /* String routines */
 
 #include "srvstr.h"
diff --git a/source3/include/proto.h b/source3/include/proto.h
index a9270fc..1efb2ef 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -1360,6 +1360,7 @@ int lp_name_cache_timeout(void);
 int lp_client_signing(void);
 int lp_server_signing(void);
 int lp_client_ldap_sasl_wrapping(void);
+int lp_enum(const char *s,const struct enum_list *);
 char *lp_parm_talloc_string(TALLOC_CTX *ctx, int snum, const char *type, const char *option, const char *def);
 const char *lp_parm_const_string(int snum, const char *type, const char *option, const char *def);
 struct loadparm_service;
diff --git a/source3/lib/util.c b/source3/lib/util.c
index 93aab3c..8138ae5 100644
--- a/source3/lib/util.c
+++ b/source3/lib/util.c
@@ -565,22 +565,15 @@ char *get_mydnsdomname(TALLOC_CTX *ctx)
 
 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);
+	extern const struct enum_list enum_protocol[];
+	int ret = lp_enum(str, enum_protocol);
+
+	if (ret == -1) {
+		DEBUG(0,("Unrecognised protocol level %s\n",str));
+		ret = def;
+	}
+
+	return ret;
 }
 
 
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index fa2f9b6..acb2f77 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -1328,7 +1328,7 @@ static bool lp_bool(const char *s)
 /*******************************************************************
 convenience routine to return enum parameters.
 ********************************************************************/
-static int lp_enum(const char *s,const struct enum_list *_enum)
+int lp_enum(const char *s,const struct enum_list *_enum)
 {
 	int i;
 
-- 
1.8.3


From f6a9b1d698ac6376def6dbe8cc40d5a3dcc88c40 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 14:41:24 -0700
Subject: [PATCH 03/37] Fix smb2cli_write_recv() and smb2cli_write() to return
 the bytes written.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smb2cli_write.c      | 28 +++++++++++++++++++++++-----
 libcli/smb/smbXcli_base.h       |  6 ++++--
 source3/libsmb/cli_np_tstream.c |  7 +++++--
 source3/torture/test_smb2.c     | 14 +++++++-------
 4 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/libcli/smb/smb2cli_write.c b/libcli/smb/smb2cli_write.c
index 8e65460..c42443f 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,30 @@ 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)) {
+		return status;
+	}
+	if (written) {
+		*written = state->written;
+	}
+	return NT_STATUS_OK;
 }
 
 NTSTATUS smb2cli_write(struct smbXcli_conn *conn,
@@ -126,7 +143,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 +173,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 a1fe1ab50ba1eb3e2467ab492b8dbd1bd4e320cc Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 15:01:50 -0700
Subject: [PATCH 04/37] 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 <jra at samba.org>
---
 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 640014d38b55180deb6738ef4a3584280cd75bf4 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 15:17:01 -0700
Subject: [PATCH 05/37] Correctly set up cli_maxprotocol, cli_minprotocol in
 our parameter block. Allow cli_maxprotocol to be changed.

Set to PROTOCOL_NT1, PROTOCOL_CORE by default.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/include/proto.h  | 3 +++
 source3/param/loadparm.c | 7 +++++++
 2 files changed, 10 insertions(+)

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 1efb2ef..7713454 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -1190,6 +1190,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);
@@ -1486,6 +1488,7 @@ char* lp_perfcount_module(TALLOC_CTX *ctx);
 void widelinks_warning(int snum);
 const char *lp_ncalrpc_dir(void);
 void _lp_set_server_role(int server_role);
+void lp_set_cli_maxprotocol(int max_protocol);
 
 /* The following definitions come from param/loadparm_ctx.c  */
 
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index acb2f77..078bea6 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;
@@ -5538,3 +5540,8 @@ int lp_security(void)
 	return lp_find_security(lp__server_role(),
 				lp__security());
 }
+
+void lp_set_cli_maxprotocol(int max_protocol)
+{
+	Globals.cli_maxprotocol = max_protocol;
+}
-- 
1.8.3


From 1bef944961bd08cacff2df22706886cdad5d3832 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 15:54:05 -0700
Subject: [PATCH 06/37] Add in the core of the libsmb client SMB2 functions.

These create a synchronous smb2cli_XXX() style interface
designed to plug directly into the libsmb/cliXXXX.c code.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/include/client.h      |    1 +
 source3/libsmb/clirap.c       |    7 +-
 source3/libsmb/clirap.h       |    4 +
 source3/libsmb/smb2cli_fnum.c | 2235 +++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/smb2cli_fnum.h |  146 +++
 source3/wscript_build         |    3 +-
 6 files changed, 2389 insertions(+), 7 deletions(-)
 create mode 100644 source3/libsmb/smb2cli_fnum.c
 create mode 100644 source3/libsmb/smb2cli_fnum.h

diff --git a/source3/include/client.h b/source3/include/client.h
index 52e2212..09f9660 100644
--- a/source3/include/client.h
+++ b/source3/include/client.h
@@ -109,6 +109,7 @@ struct cli_state {
 	struct {
 		struct smbXcli_session *session;
 		struct smbXcli_tcon *tcon;
+		struct idr_context *open_handles;
 	} smb2;
 };
 
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/smb2cli_fnum.c b/source3/libsmb/smb2cli_fnum.c
new file mode 100644
index 0000000..ddf1ed3
--- /dev/null
+++ b/source3/libsmb/smb2cli_fnum.c
@@ -0,0 +1,2235 @@
+/*
+   Unix SMB/CIFS implementation.
+   smb2 lib
+   Copyright (C) Jeremy Allison 2013
+   Copyright (C) Volker Lendecke 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 <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This code is a thin wrapper around the existing
+ smb2cli_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 "smb2cli_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_PARAMETER;
+	}
+	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 smb2cli_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 smb2cli_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 smb2cli_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 = smb2cli_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 smb2cli_close_fnum(cli, fnum);
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to delete a directory
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 = smb2cli_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 smb2cli_close_fnum(cli, fnum);
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to unlink a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 = smb2cli_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 smb2cli_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 smb2cli_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 = smb2cli_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) {
+		smb2cli_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 smb2cli_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 = smb2cli_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 = smb2cli_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;
+	}
+
+	smb2cli_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 = smb2cli_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 = smb2cli_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 smb2cli_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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+	TALLOC_FREE(frame);
+	return status;
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a fnum info (basic level).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 smb2cli_qfileinfo_basic().
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 = smb2cli_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 query a pathname info (basic level).
+ Implement on top of smb2cli_qfileinfo_basic().
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 = smb2cli_qfileinfo_basic(cli,
+					fnum,
+					mode,
+					size,
+					create_time,
+					access_time,
+					write_time,
+					change_time,
+					ino);
+
+  fail:
+
+	if (fnum != -1) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	TALLOC_FREE(frame);
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query pathname streams.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	TALLOC_FREE(frame);
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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[36];
+	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);
+	}
+
+	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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	TALLOC_FREE(frame);
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set file handle times.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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[36];
+	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);
+
+	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 smb2cli_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 = smb2cli_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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	TALLOC_FREE(frame);
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a security descriptor.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 smb2cli_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 smb2cli_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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	TALLOC_FREE(frame);
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a fnum.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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 smb2cli_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) {
+		smb2cli_close_fnum(cli, fnum);
+	}
+
+	return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get an EA list on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS smb2cli_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;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 async read 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_pull()
+ for free :-).
+***************************************************************/
+
+struct smb2cli_readall_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 smb2cli_readall_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *smb2cli_readall_fnum_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 smb2cli_readall_state *state;
+
+	req = tevent_req_create(mem_ctx, &state, struct smb2cli_readall_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 (!NT_STATUS_IS_OK(status)) {
+		return NULL;
+	}
+
+	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, smb2cli_readall_fnum_done, req);
+	return req;
+}
+
+static void smb2cli_readall_fnum_done(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smb2cli_readall_state *state = tevent_req_data(
+		req, struct smb2cli_readall_state);
+	uint32_t received = 0;
+	uint8_t *buf = NULL;
+	NTSTATUS status;
+
+	status = smb2cli_read_recv(subreq, state, &buf, &received);
+	if (tevent_req_nterror(req, status)) {
+		return;
+	}
+
+	if (received == 0) {
+		/* EOF */
+		tevent_req_done(req);
+		return;
+	}
+
+	if (received > state->size) {
+		tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+		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. smb2cli_read 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 = smb2cli_read_send(state,
+				state->ev,
+				state->cli->conn,
+				state->cli->timeout,
+				state->cli->smb2.session,
+				state->cli->smb2.tcon,
+				state->size - state->received,
+				state->start_offset + state->received,
+				state->ph->fid_persistent,
+				state->ph->fid_volatile,
+				0, /* minimum_count */
+				0); /* remaining_bytes */
+
+	if (tevent_req_nomem(subreq, req)) {
+		return;
+	}
+	tevent_req_set_callback(subreq, smb2cli_readall_fnum_done, req);
+}
+
+NTSTATUS smb2cli_readall_fnum_recv(struct tevent_req *req,
+				ssize_t *received,
+				uint8_t **rcvbuf)
+{
+        NTSTATUS status;
+	struct smb2cli_readall_state *state = tevent_req_data(
+				req, struct smb2cli_readall_state);
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*received = (ssize_t)state->received;
+	*rcvbuf = state->buf;
+	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 smb2cli_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 smb2cli_writeall_written(struct tevent_req *req);
+
+struct tevent_req *smb2cli_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 smb2cli_writeall_state *state = NULL;
+
+	req = tevent_req_create(mem_ctx, &state, struct smb2cli_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 (!NT_STATUS_IS_OK(status)) {
+		return NULL;
+	}
+
+	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, smb2cli_writeall_written, req);
+	return req;
+}
+
+static void smb2cli_writeall_written(struct tevent_req *subreq)
+{
+	struct tevent_req *req = tevent_req_callback_data(
+		subreq, struct tevent_req);
+	struct smb2cli_writeall_state *state = tevent_req_data(
+		req, struct smb2cli_writeall_state);
+        NTSTATUS status;
+	uint32_t written, to_write;
+
+	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;
+	}
+
+	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, smb2cli_writeall_written, req);
+}
+
+NTSTATUS smb2cli_writeall_recv(struct tevent_req *req,
+				size_t *pwritten)
+{
+	struct smb2cli_writeall_state *state = tevent_req_data(
+		req, struct smb2cli_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/smb2cli_fnum.h b/source3/libsmb/smb2cli_fnum.h
new file mode 100644
index 0000000..8933afd
--- /dev/null
+++ b/source3/libsmb/smb2cli_fnum.h
@@ -0,0 +1,146 @@
+/*
+   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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SMB2CLI_FNUM_H__
+#define __SMB2CLI_FNUM_H__
+
+struct smbXcli_conn;
+struct smbXcli_session;
+struct cli_state;
+struct file_info;
+
+NTSTATUS smb2cli_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 smb2cli_close_fnum(struct cli_state *cli, uint16_t fnum);
+NTSTATUS smb2cli_mkdir(struct cli_state *cli, const char *dirname);
+NTSTATUS smb2cli_rmdir(struct cli_state *cli, const char *dirname);
+NTSTATUS smb2cli_unlink(struct cli_state *cli,const char *fname);
+NTSTATUS smb2cli_list(struct cli_state *cli,
+			const char *pathname,
+			NTSTATUS (*fn)(const char *,
+				struct file_info *,
+				const char *,
+				void *),
+			void *state);
+NTSTATUS smb2cli_qpathinfo_basic(struct cli_state *cli,
+			const char *name,
+			SMB_STRUCT_STAT *sbuf,
+			uint32_t *attributes);
+NTSTATUS smb2cli_qpathinfo_alt_name(struct cli_state *cli,
+			const char *name,
+			fstring alt_name);
+NTSTATUS smb2cli_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 smb2cli_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 smb2cli_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 smb2cli_qpathinfo_streams(struct cli_state *cli,
+			const char *name,
+			TALLOC_CTX *mem_ctx,
+			unsigned int *pnum_streams,
+			struct stream_struct **pstreams);
+NTSTATUS smb2cli_setatr(struct cli_state *cli,
+			const char *fname,
+			uint16_t attr,
+			time_t mtime);
+NTSTATUS smb2cli_setattrE(struct cli_state *cli,
+                        uint16_t fnum,
+                        time_t change_time,
+                        time_t access_time,
+                        time_t write_time);
+NTSTATUS smb2cli_dskattr(struct cli_state *cli,
+			int *bsize,
+			int *total,
+			int *avail);
+NTSTATUS smb2cli_query_security_descriptor(struct cli_state *cli,
+			uint16_t fnum,
+			uint32_t sec_info,
+			TALLOC_CTX *mem_ctx,
+			struct security_descriptor **ppsd);
+NTSTATUS smb2cli_set_security_descriptor(struct cli_state *cli,
+			uint16_t fnum,
+			uint32_t sec_info,
+			const struct security_descriptor *sd);
+NTSTATUS smb2cli_rename(struct cli_state *cli,
+			const char *fname_src,
+			const char *fname_dst);
+NTSTATUS smb2cli_set_ea_fnum(struct cli_state *cli,
+			uint16_t fnum,
+			const char *ea_name,
+			const char *ea_val,
+			size_t ea_len);
+NTSTATUS smb2cli_get_ea_list_path(struct cli_state *cli,
+			const char *name,
+			TALLOC_CTX *ctx,
+			size_t *pnum_eas,
+			struct ea_struct **pea_list);
+NTSTATUS smb2cli_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 *smb2cli_readall_fnum_send(TALLOC_CTX *mem_ctx,
+			struct tevent_context *ev,
+			struct cli_state *cli,
+			uint16_t fnum,
+			off_t offset,
+			size_t size);
+NTSTATUS smb2cli_readall_fnum_recv(struct tevent_req *req,
+			ssize_t *received,
+			uint8_t **rcvbuf);
+struct tevent_req *smb2cli_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 smb2cli_writeall_recv(struct tevent_req *req,
+			size_t *pwritten);
+#endif /* __SMB2CLI_FNUM_H__ */
diff --git a/source3/wscript_build b/source3/wscript_build
index e3605e3..0e0be74 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/smb2cli_fnum.c'''
 
 LIBMSRPC_SRC = '''
                rpc_client/cli_pipe.c
-- 
1.8.3


From 070a3781338d1bdc2df53b7789754ca2ea7c64e2 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 15:59:13 -0700
Subject: [PATCH 07/37] Plumb smb2cli_rename() inside cli_rename().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 4b4e1a0..ee19feb 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -21,6 +21,8 @@
 #include "includes.h"
 #include "system/filesys.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/smb2cli.h"
+#include "libsmb/smb2cli_fnum.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "libsmb/clirap.h"
@@ -1097,11 +1099,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 smb2cli_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 b91a2aa338da41189b84bd708c646d8bd23bf351 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:00:40 -0700
Subject: [PATCH 08/37] Plumb smb2cli_unlink() inside cli_unlink().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 ee19feb..9a06bf3 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1417,11 +1417,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 smb2cli_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 8fccd610be1ab812d287867b554cd5acc13f1b24 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:01:49 -0700
Subject: [PATCH 09/37] Plumb smb2cli_mkdir() inside cli_mkdir().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 9a06bf3..75d0c42 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1527,11 +1527,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 smb2cli_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 bb9d21fb37aa47697dc77056097d39a85fc4bb9f Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:03:00 -0700
Subject: [PATCH 10/37] Plumb smb2cli_rmdir() inside cli_rmdir().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 75d0c42..ba32b9e 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1637,11 +1637,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 smb2cli_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 3caf3183d7074ad3f900726b86a44c8572a7ebe6 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:06:19 -0700
Subject: [PATCH 11/37] Plumb smb2cli_create_fnum() inside cli_ntcreate().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 ba32b9e..42698dc 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1916,11 +1916,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 smb2cli_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 fa2d06451fd4a47dc5537a6a6034d23a4aa63083 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:10:34 -0700
Subject: [PATCH 12/37] Plumb smb2cli_close_fnum() inside cli_close().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 42698dc..bdf4677 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -2538,11 +2538,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 smb2cli_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 eb90104d0287b80fb6200e37c940999cc8bcc71b Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:11:59 -0700
Subject: [PATCH 13/37] Plumb smb2cli_getattrE() inside cli_getattrE().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 bdf4677..f3e89fc 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -3381,11 +3381,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 smb2cli_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 02788945fbbcb394377a54c256ccbd46f4de7195 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:14:47 -0700
Subject: [PATCH 14/37] Plumb smb2cli_setattrE() inside cli_setattrE().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 f3e89fc..5cb6b9c 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -3649,11 +3649,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 smb2cli_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 77eab36f9e8e32b050931a88bbac1284f1f672d3 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:16:03 -0700
Subject: [PATCH 15/37] Plumb smb2cli_setatr() inside cli_setatr().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 5cb6b9c..5b19597 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -3788,11 +3788,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 smb2cli_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 68c4737d0d72bc481bcefda340836d457c2db7b3 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:17:12 -0700
Subject: [PATCH 16/37] Plumb smb2cli_dskattr() inside cli_dskattr().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 5b19597..5ffa571 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -4026,11 +4026,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 smb2cli_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 44d2c91e791cf29f3c4f4b20182124f0442bdc04 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:19:06 -0700
Subject: [PATCH 17/37] Fix cli_set_ea_path() to use frame instead of
 talloc_tos().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 5ffa571..0a5cfdc 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -4314,9 +4314,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);
@@ -4329,7 +4330,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 1f686e21e995b998d7cc99b2edd7b622605daf61 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:21:48 -0700
Subject: [PATCH 18/37] Plumb smb2cli_set_ea_path() inside cli_set_ea_path().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 0a5cfdc..e95778c 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -4312,7 +4312,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 smb2cli_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 9d63158d35c2dca5cd661288197b6a3641908c4a Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:32:55 -0700
Subject: [PATCH 19/37] Plumb smb2cli_set_ea_fnum() inside cli_set_ea_fnum().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clifile.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index e95778c..0eb3995 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -4357,6 +4357,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 smb2cli_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 9c31eb4edf57a2841274aebed7d6969d20d31ea9 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:42:02 -0700
Subject: [PATCH 20/37] Plumb smb2cli_get_ea_list_path() inside
 cli_get_ea_list_path().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 0eb3995..e47f49e 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -4546,11 +4546,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 smb2cli_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 66121c00ce788b1f34c3faad5d955f6ca704b94d Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:43:33 -0700
Subject: [PATCH 21/37] Plumb smb2cli_list() inside cli_list().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clilist.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c
index b0d3a4e..51e6f9f 100644
--- a/source3/libsmb/clilist.c
+++ b/source3/libsmb/clilist.c
@@ -19,6 +19,8 @@
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/smb2cli.h"
+#include "libsmb/smb2cli_fnum.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "trans2.h"
@@ -933,7 +935,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 +943,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 smb2cli_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 70fe6acfff4b98591bf879ad269832c74b8b016e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:45:05 -0700
Subject: [PATCH 22/37] Plumb smb2cli_qpathinfo2() inside cli_qpathinfo2().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clirap.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
index 12a340c..06e3258 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -26,6 +26,8 @@
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/smb2cli.h"
+#include "libsmb/smb2cli_fnum.h"
 #include "libsmb/clirap.h"
 #include "trans2.h"
 #include "../libcli/smb/smbXcli_base.h"
@@ -845,11 +847,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 smb2cli_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 ff5d714f5c094b6d8dd3cf0e94af7f9a58c6a6b1 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:46:05 -0700
Subject: [PATCH 23/37] Plumb smb2cli_qpathinfo_streams() inside
 cli_qpathinfo_streams().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 06e3258..02b015a 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -967,11 +967,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 smb2cli_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 eb8e03420117a85d22cf379631649efeab7e014c Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:47:21 -0700
Subject: [PATCH 24/37] Plumb smb2cli_qfileinfo_basic() inside
 cli_qfileinfo_basic().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clirap.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
index 02b015a..d621dca 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -1151,6 +1151,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 smb2cli_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 799cf995e103b7911b170a0564914ed23b0b1821 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 10:52:02 -0700
Subject: [PATCH 25/37] Plumb smb2cli_qpathinfo_basic() inside
 cli_qpathinfo_basic().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 d621dca..8fcedd8 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -1277,11 +1277,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 smb2cli_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 f65087103d1f06f200130ef7ca37d6e37e6d9f45 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 7 Aug 2013 16:50:03 -0700
Subject: [PATCH 26/37] Plumb smb2cli_qpathinfo_alt_name() inside
 cli_qpathinfo_alt_name().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clirap.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
index 8fcedd8..f1255ef 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -1328,6 +1328,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 smb2cli_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 9d76847e697a43bdb2e2bd67fb4640c7685bad6f Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:00:08 -0700
Subject: [PATCH 27/37] Plumb smb2cli_query_security_descriptor() inside
 cli_query_security_descriptor().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clisecdesc.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c
index 24da39d..14834cd 100644
--- a/source3/libsmb/clisecdesc.c
+++ b/source3/libsmb/clisecdesc.c
@@ -19,7 +19,10 @@
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/smb2cli.h"
+#include "libsmb/smb2cli_fnum.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 +36,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 smb2cli_query_security_descriptor(cli,
+							fnum,
+							sec_info,
+							mem_ctx,
+							sd);
+	}
+
 	SIVAL(param, 0, fnum);
 	SIVAL(param, 4, sec_info);
 
-- 
1.8.3


From 15f1d6415f518b4a6eeb495ec88271db4a376bb5 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:00:49 -0700
Subject: [PATCH 28/37] Plumb smb2cli_set_security_descriptor() inside
 cli_set_security_descriptor().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clisecdesc.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c
index 14834cd..e46928e 100644
--- a/source3/libsmb/clisecdesc.c
+++ b/source3/libsmb/clisecdesc.c
@@ -106,6 +106,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 smb2cli_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 2e78411833ee349a7b5dd56a5794ed54122e77a6 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:16:58 -0700
Subject: [PATCH 29/37] Add an SMB2 version of the underlying engine behind
 async cli_pull.

Adds smb2cli_readall_fnum_send() and smb2cli_readall_fnum_recv().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clireadwrite.c | 45 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index 47e7f1b..d947b01 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -19,6 +19,8 @@
 
 #include "includes.h"
 #include "libsmb/libsmb.h"
+#include "libsmb/smb2cli.h"
+#include "libsmb/smb2cli_fnum.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "async_smb.h"
 #include "trans2.h"
@@ -533,10 +535,17 @@ struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx,
 		size_left = size - state->requested;
 		request_thistime = MIN(size_left, state->chunk_size);
 
-		subreq->req = cli_readall_send(
-			state->reqs, ev, cli, fnum,
-			state->start_offset + state->requested,
-			request_thistime);
+		if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+			subreq->req = smb2cli_readall_fnum_send(
+				state->reqs, ev, cli, fnum,
+				state->start_offset + state->requested,
+				request_thistime);
+		} else {
+			subreq->req = cli_readall_send(
+				state->reqs, ev, cli, fnum,
+				state->start_offset + state->requested,
+				request_thistime);
+		}
 
 		if (subreq->req == NULL) {
 			goto failed;
@@ -578,8 +587,14 @@ static void cli_pull_read_done(struct tevent_req *subreq)
 		return;
 	}
 
-	status = cli_readall_recv(subreq, &pull_subreq->received,
-				  &pull_subreq->buf);
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		status = smb2cli_readall_fnum_recv(subreq, &pull_subreq->received,
+					&pull_subreq->buf);
+	} else {
+		status = cli_readall_recv(subreq, &pull_subreq->received,
+					&pull_subreq->buf);
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		tevent_req_nterror(state->req, status);
 		return;
@@ -636,11 +651,19 @@ static void cli_pull_read_done(struct tevent_req *subreq)
 					 + 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 (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+				new_req = smb2cli_readall_fnum_send(
+					state->reqs, state->ev, state->cli,
+					state->fnum,
+					state->start_offset + state->requested,
+					request_thistime);
+			} else {
+				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;
-- 
1.8.3


From 9887f8d6a62ad976790cd32e237af062e4c600dc Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:22:56 -0700
Subject: [PATCH 30/37] Add an SMB2 version of the underlying engine behind
 async cli_push.

Adds smb2cli_writeall_send() and smb2cli_writeall_recv().

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clireadwrite.c | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index d947b01..ab86301 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -1101,7 +1101,11 @@ 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 = smb2cli_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;
 	}
@@ -1109,7 +1113,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 = smb2cli_writeall_recv(req, pwritten);
+	} else {
+		status = cli_writeall_recv(req, pwritten);
+	}
  fail:
 	TALLOC_FREE(frame);
 	return status;
@@ -1179,12 +1187,23 @@ static bool cli_push_write_setup(struct tevent_req *req,
 		return true;
 	}
 
-	subreq = cli_writeall_send(substate,
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		subreq = smb2cli_writeall_send(substate,
+				state->ev,
+				state->cli,
+				state->fnum,
+				state->mode,
+				substate->buf,
+				substate->ofs,
+				substate->size);
+	} else {
+		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;
@@ -1284,7 +1303,11 @@ static void cli_push_written(struct tevent_req *subreq)
 	state->reqs[idx] = NULL;
 	state->pending -= 1;
 
-	status = cli_writeall_recv(subreq, NULL);
+	if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
+		status = smb2cli_writeall_recv(subreq, NULL);
+	} else {
+		status = cli_writeall_recv(subreq, NULL);
+	}
 	TALLOC_FREE(subreq);
 	TALLOC_FREE(substate);
 	if (tevent_req_nterror(req, status)) {
-- 
1.8.3


From d62bedbc62243cff88b1f5f8c33b1f9ad9869288 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:35:44 -0700
Subject: [PATCH 31/37] Modify cli_start_connection_connected() to use
 lp_cli_minprotocol()/lp_cli_maxprotocol() instead of hard coding
 PROTOCOL_CORE, PROTOCOL_NT1.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 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 7179c4f..fa9d99f 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -3123,7 +3123,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 64ebe855274eb2cecfa6499c5dce82858f2a822d Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Thu, 8 Aug 2013 11:43:16 -0700
Subject: [PATCH 32/37] Add -m<MAX PROTOCOL> option to smbcacls.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/utils/smbcacls.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c
index d3d60bc..4423d35 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_cli_maxprotocol(interpret_protocol(poptGetOptArg(pc), PROTOCOL_NT1));
+			break;
 		}
 	}
 
-- 
1.8.3


From 89e8d12622f0cbbb87710464c96132fdc01f00be Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 9 Aug 2013 09:04:44 -0700
Subject: [PATCH 33/37] Fix smb2cli_req_create() to set the credit charge
 correctly on large-MTU read requests.

If we don't do this right, W2K12 returns INVALID_PARAMETER on SMB2_READ.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smbXcli_base.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index 5a5828a..98851bc 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -2639,6 +2639,18 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
 		 */
 		SBVAL(state->smb2.hdr, SMB2_HDR_MESSAGE_ID, UINT64_MAX);
 		break;
+	case SMB2_OP_READ:
+		/*
+		 * For reads we set the credit charge
+		 * based on what we asked for, not what
+		 * we send. If we don't set this correctly
+		 * W2K12 responds with INVALID_PARAMETER for
+		 * a large MTU read.
+		 */
+		if (conn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) {
+			uint32_t len = IVAL(fixed, 4);
+			state->smb2.credit_charge =  (MAX(len, 1) - 1)/ 65536 + 1;
+		}
 	}
 
 	if (timeout_msec > 0) {
-- 
1.8.3


From a66457b4d758911a3c6e19a2e098e97b04a8fce3 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 9 Aug 2013 09:06:34 -0700
Subject: [PATCH 34/37] Add utility function smb2cli_conn_available_credits()
 returns current available credits.

Needed to calculate max buffer size for an MTU SMB2_READ.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smbXcli_base.c | 5 +++++
 libcli/smb/smbXcli_base.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index 98851bc..f81841b 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -2461,6 +2461,11 @@ void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
 	conn->smb2.max_credits = max_credits;
 }
 
+uint16_t smb2cli_conn_available_credits(struct smbXcli_conn *conn)
+{
+	return conn->smb2.cur_credits;
+}
+
 static void smb2cli_req_cancel_done(struct tevent_req *subreq);
 
 static bool smb2cli_req_cancel(struct tevent_req *req)
diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h
index a497e13..96f4d9f 100644
--- a/libcli/smb/smbXcli_base.h
+++ b/libcli/smb/smbXcli_base.h
@@ -205,6 +205,7 @@ uint32_t smb2cli_conn_max_read_size(struct smbXcli_conn *conn);
 uint32_t smb2cli_conn_max_write_size(struct smbXcli_conn *conn);
 void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
 				  uint16_t max_credits);
+uint16_t smb2cli_conn_available_credits(struct smbXcli_conn *conn);
 
 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
 				      struct tevent_context *ev,
-- 
1.8.3


From 4b2f3a6614297435626c45c870b400b2c18a4efb Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 9 Aug 2013 10:36:57 -0700
Subject: [PATCH 35/37] Fix cli_read_max_bufsize() to cope with available
 credits in the large MTU case.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/clireadwrite.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
index ab86301..bc893ea 100644
--- a/source3/libsmb/clireadwrite.c
+++ b/source3/libsmb/clireadwrite.c
@@ -36,6 +36,19 @@ static size_t cli_read_max_bufsize(struct cli_state *cli)
 	uint32_t data_offset;
 	uint32_t useable_space = 0;
 
+	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+		if (smb2cli_conn_server_capabilities(cli->conn) & SMB2_CAP_LARGE_MTU) {
+			size_t bufsize = smb2cli_conn_max_read_size(cli->conn);
+			uint16_t cur_credits = smb2cli_conn_available_credits(cli->conn);
+			/*
+			 * Modify to fit within the available credits.
+			 */
+			return MIN(bufsize, (cur_credits * 65536));
+		} else {
+			return 65536;
+		}
+	}
+
 	data_offset = HDR_VWV;
 	data_offset += wct * sizeof(uint16_t);
 	data_offset += sizeof(uint16_t); /* byte count */
-- 
1.8.3


From a354ae1c395b2e32fd7e487c4c59dbcd1102be2e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 9 Aug 2013 11:15:48 -0700
Subject: [PATCH 36/37] Ensure we ask for lp_smb2_max_credits() on successful
 negprot. Should this be a separate client parameter ?

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/libsmb/cliconnect.c | 6 ++++++
 source3/libsmb/clidfs.c     | 6 ++++++
 2 files changed, 12 insertions(+)

diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c
index fa9d99f..0afb5e4 100644
--- a/source3/libsmb/cliconnect.c
+++ b/source3/libsmb/cliconnect.c
@@ -3156,6 +3156,12 @@ static NTSTATUS cli_start_connection_recv(struct tevent_req *req,
 		return status;
 	}
 	*output_cli = state->cli;
+
+	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,
+					lp_smb2_max_credits());
+	}
 	return NT_STATUS_OK;
 }
 
diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c
index 95f8817..3a30dda 100644
--- a/source3/libsmb/clidfs.c
+++ b/source3/libsmb/clidfs.c
@@ -160,6 +160,12 @@ 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,
+					lp_smb2_max_credits());
+	}
+
 	username = get_cmdline_auth_info_username(auth_info);
 	password = get_cmdline_auth_info_password(auth_info);
 
-- 
1.8.3


From 067accf75bfe0e1ce4b07a2827adf7ee3a8caa7b Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 9 Aug 2013 13:03:57 -0700
Subject: [PATCH 37/37] Correctly size smbXcli_conn_max_requests() based off
 the available credits and the server max iosize.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smbXcli_base.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c
index f81841b..004ddaa 100644
--- a/libcli/smb/smbXcli_base.c
+++ b/libcli/smb/smbXcli_base.c
@@ -447,10 +447,22 @@ const char *smbXcli_conn_remote_name(struct smbXcli_conn *conn)
 uint16_t smbXcli_conn_max_requests(struct smbXcli_conn *conn)
 {
 	if (conn->protocol >= PROTOCOL_SMB2_02) {
-		/*
-		 * TODO...
-		 */
-		return 1;
+		uint16_t avail_creds = smb2cli_conn_available_credits(conn);
+		if (conn->smb2.server.capabilities & SMB2_CAP_LARGE_MTU) {
+			/*
+			 * Each available credit is 65536 bytes. Take
+			 * the larger of the max_read_size and max_write
+			 * size and work out how many we can issue given
+			 * the available credits.
+			 */
+			uint32_t max_size = MAX(conn->smb2.server.max_read_size,
+					conn->smb2.server.max_write_size);
+
+			/* Convert max_size into credit units. */
+			max_size = ((max_size - 1)/65536) + 1;
+			avail_creds = avail_creds / max_size;
+		}
+		return MAX(avail_creds, 1);
 	}
 
 	return conn->smb1.server.max_mux;
-- 
1.8.3



More information about the samba-technical mailing list