summaryrefslogtreecommitdiff
path: root/source3/libsmb/cli_smb2_fnum.c
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2013-08-07 15:54:05 -0700
committerStefan Metzmacher <metze@samba.org>2013-08-15 09:07:06 +0200
commit28591dfd5d2163d9181d45d64a4a750e335b7c56 (patch)
tree6abefaa642d2790d57047343800ab2db6022bbdb /source3/libsmb/cli_smb2_fnum.c
parentc80349e0fad7c182b0bddefed99a78d95323faca (diff)
downloadsamba-28591dfd5d2163d9181d45d64a4a750e335b7c56.tar.gz
samba-28591dfd5d2163d9181d45d64a4a750e335b7c56.tar.bz2
samba-28591dfd5d2163d9181d45d64a4a750e335b7c56.zip
s3:libsmb: Add in the core of the libsmb client SMB2 functions.
These create a synchronous cli_smb2_XXX() style interface designed to plug directly into the libsmb/cliXXXX.c code. https://bugzilla.samba.org/show_bug.cgi?id=9974 Signed-off-by: Jeremy Allison <jra@samba.org> Signed-off-by: Stefan Metzmacher <metze@samba.org>
Diffstat (limited to 'source3/libsmb/cli_smb2_fnum.c')
-rw-r--r--source3/libsmb/cli_smb2_fnum.c2373
1 files changed, 2373 insertions, 0 deletions
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
new file mode 100644
index 0000000000..d0b744b626
--- /dev/null
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -0,0 +1,2373 @@
+/*
+ Unix SMB/CIFS implementation.
+ smb2 lib
+ Copyright (C) Jeremy Allison 2013
+ Copyright (C) Volker Lendecke 2013
+ Copyright (C) Stefan Metzmacher 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ This code is a thin wrapper around the existing
+ cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
+ but allows the handles to be mapped to uint16_t fnums,
+ which are easier for smbclient to use.
+*/
+
+#include "includes.h"
+#include "client.h"
+#include "async_smb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "smb2cli.h"
+#include "cli_smb2_fnum.h"
+#include "trans2.h"
+#include "clirap.h"
+#include "../libcli/smb/smb2_create_blob.h"
+#include "libsmb/proto.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "../libcli/security/security.h"
+#include "lib/util_ea.h"
+
+struct smb2_hnd {
+ uint64_t fid_persistent;
+ uint64_t fid_volatile;
+};
+
+/*
+ * Handle mapping code.
+ */
+
+/***************************************************************
+ Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
+ Ensures handle is owned by cli struct.
+***************************************************************/
+
+static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
+ const struct smb2_hnd *ph, /* In */
+ uint16_t *pfnum) /* Out */
+{
+ int ret;
+ struct idr_context *idp = cli->smb2.open_handles;
+ struct smb2_hnd *owned_h = talloc_memdup(cli,
+ ph,
+ sizeof(struct smb2_hnd));
+
+ if (owned_h == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (idp == NULL) {
+ /* Lazy init */
+ cli->smb2.open_handles = idr_init(cli);
+ if (cli->smb2.open_handles == NULL) {
+ TALLOC_FREE(owned_h);
+ return NT_STATUS_NO_MEMORY;
+ }
+ idp = cli->smb2.open_handles;
+ }
+
+ ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
+ if (ret == -1) {
+ TALLOC_FREE(owned_h);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ *pfnum = (uint16_t)ret;
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Return the smb2_hnd pointer associated with the given fnum.
+***************************************************************/
+
+static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
+ uint16_t fnum, /* In */
+ struct smb2_hnd **pph) /* Out */
+{
+ struct idr_context *idp = cli->smb2.open_handles;
+
+ if (idp == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ *pph = (struct smb2_hnd *)idr_find(idp, fnum);
+ if (*pph == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Delete the fnum to smb2_hnd mapping. Zeros out handle on
+ successful return.
+***************************************************************/
+
+static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
+ struct smb2_hnd **pph, /* In */
+ uint16_t fnum) /* In */
+{
+ struct idr_context *idp = cli->smb2.open_handles;
+ struct smb2_hnd *ph;
+
+ if (idp == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ph = (struct smb2_hnd *)idr_find(idp, fnum);
+ if (ph != *pph) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ idr_remove(idp, fnum);
+ TALLOC_FREE(*pph);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Oplock mapping code.
+***************************************************************/
+
+static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
+{
+ if (create_flags & REQUEST_BATCH_OPLOCK) {
+ return SMB2_OPLOCK_LEVEL_BATCH;
+ } else if (create_flags & REQUEST_OPLOCK) {
+ return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ }
+
+ /* create_flags doesn't do a level2 request. */
+ return SMB2_OPLOCK_LEVEL_NONE;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 create to return a uint16_t fnum.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_create_fnum(struct cli_state *cli,
+ const char *fname,
+ uint32_t create_flags,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ uint16_t *pfid,
+ struct smb2_create_returns *cr)
+{
+ NTSTATUS status;
+ struct smb2_hnd h;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (cli->backup_intent) {
+ create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname == '\\') {
+ fname++;
+ }
+
+ status = smb2cli_create(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname,
+ flags_to_smb2_oplock(create_flags),
+ SMB2_IMPERSONATION_IMPERSONATION,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ NULL,
+ &h.fid_persistent,
+ &h.fid_volatile,
+ cr);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = map_smb2_handle_to_fnum(cli, &h, pfid);
+ }
+
+ return status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 close to use a uint16_t fnum.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
+{
+ struct smb2_hnd *ph = NULL;
+ NTSTATUS status;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smb2cli_close(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 0,
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ /* Delete the fnum -> handle mapping. */
+ if (NT_STATUS_IS_OK(status)) {
+ status = delete_smb2_handle_mapping(cli, &ph, fnum);
+ }
+
+ return status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to create a directory
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname)
+{
+ NTSTATUS status;
+ uint16_t fnum;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = cli_smb2_create_fnum(cli,
+ dname,
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
+ FILE_CREATE, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return cli_smb2_close_fnum(cli, fnum);
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to delete a directory
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname)
+{
+ NTSTATUS status;
+ uint16_t fnum;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = cli_smb2_create_fnum(cli,
+ dname,
+ 0, /* create_flags */
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return cli_smb2_close_fnum(cli, fnum);
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 to unlink a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname)
+{
+ NTSTATUS status;
+ uint16_t fnum;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = cli_smb2_create_fnum(cli,
+ fname,
+ 0, /* create_flags */
+ DELETE_ACCESS, /* desired_access */
+ FILE_ATTRIBUTE_NORMAL, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DELETE_ON_CLOSE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return cli_smb2_close_fnum(cli, fnum);
+}
+
+/***************************************************************
+ Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
+***************************************************************/
+
+static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
+ uint32_t dir_data_length,
+ struct file_info *finfo,
+ uint32_t *next_offset)
+{
+ size_t namelen = 0;
+ size_t slen = 0;
+ size_t ret = 0;
+
+ if (dir_data_length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ *next_offset = IVAL(dir_data, 0);
+
+ if (*next_offset > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ if (*next_offset != 0) {
+ /* Ensure we only read what in this record. */
+ dir_data_length = *next_offset;
+ }
+
+ if (dir_data_length < 105) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
+ finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
+ finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
+ finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
+ finfo->mode = CVAL(dir_data + 56, 0);
+ namelen = IVAL(dir_data + 60,0);
+ if (namelen > (dir_data_length - 104)) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ slen = SVAL(dir_data + 68, 0);
+ if (slen > 24) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->short_name,
+ dir_data + 70,
+ slen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->name,
+ dir_data + 104,
+ namelen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Given a filename - get its directory name
+********************************************************************/
+
+static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
+ const char *dir,
+ char **parent,
+ const char **name)
+{
+ char *p;
+ ptrdiff_t len;
+
+ p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
+
+ if (p == NULL) {
+ if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
+ return false;
+ }
+ if (name) {
+ *name = dir;
+ }
+ return true;
+ }
+
+ len = p-dir;
+
+ if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
+ return false;
+ }
+ (*parent)[len] = '\0';
+
+ if (name) {
+ *name = p+1;
+ }
+ return true;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to list a directory.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_list(struct cli_state *cli,
+ const char *pathname,
+ NTSTATUS (*fn)(const char *,
+ struct file_info *,
+ const char *,
+ void *),
+ void *state)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+ char *parent_dir = NULL;
+ const char *mask = NULL;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ TALLOC_CTX *subframe = NULL;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* Get the directory name. */
+ if (!windows_parent_dirname(frame,
+ pathname,
+ &parent_dir,
+ &mask)) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = cli_smb2_create_fnum(cli,
+ parent_dir,
+ 0, /* create_flags */
+ SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE,/* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ do {
+ uint8_t *dir_data = NULL;
+ uint32_t dir_data_length = 0;
+ uint32_t next_offset = 0;
+ subframe = talloc_stackframe();
+
+ status = smb2cli_query_directory(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
+ 0, /* flags */
+ 0, /* file_index */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ mask,
+ 0xffff,
+ subframe,
+ &dir_data,
+ &dir_data_length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ break;
+ }
+ goto fail;
+ }
+
+ do {
+ struct file_info *finfo = talloc_zero(subframe,
+ struct file_info);
+
+ if (finfo == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = parse_finfo_id_both_directory_info(dir_data,
+ dir_data_length,
+ finfo,
+ &next_offset);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = fn(cli->dfs_mountpoint,
+ finfo,
+ pathname,
+ state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ TALLOC_FREE(finfo);
+
+ /* Move to next entry. */
+ if (next_offset) {
+ dir_data += next_offset;
+ dir_data_length -= next_offset;
+ }
+ } while (next_offset != 0);
+
+ TALLOC_FREE(subframe);
+
+ } while (NT_STATUS_IS_OK(status));
+
+ if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+ status = NT_STATUS_OK;
+ }
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+ TALLOC_FREE(subframe);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a path info (basic level).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
+ const char *name,
+ SMB_STRUCT_STAT *sbuf,
+ uint32_t *attributes)
+{
+ NTSTATUS status;
+ struct smb2_create_returns cr;
+ uint16_t fnum = -1;
+ size_t namelen = strlen(name);
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ end in a '\' */
+ if (namelen > 0 && name[namelen-1] == '\\') {
+ char *modname = talloc_strdup(talloc_tos(), name);
+ modname[namelen-1] = '\0';
+ name = modname;
+ }
+
+ /* This is commonly used as a 'cd'. Try qpathinfo on
+ a directory handle first. */
+
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ &cr);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
+ /* Maybe a file ? */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ 0, /* create_options */
+ &fnum,
+ &cr);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ cli_smb2_close_fnum(cli, fnum);
+
+ ZERO_STRUCTP(sbuf);
+
+ sbuf->st_ex_atime = nt_time_to_unix_timespec(&cr.last_access_time);
+ sbuf->st_ex_mtime = nt_time_to_unix_timespec(&cr.last_write_time);
+ sbuf->st_ex_ctime = nt_time_to_unix_timespec(&cr.change_time);
+ sbuf->st_ex_size = cr.end_of_file;
+ *attributes = cr.file_attributes;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Helper function for pathname operations.
+***************************************************************/
+
+static NTSTATUS get_fnum_from_path(struct cli_state *cli,
+ const char *name,
+ uint32_t desired_access,
+ uint16_t *pfnum)
+{
+ NTSTATUS status;
+ size_t namelen = strlen(name);
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ end in a '\' */
+ if (namelen > 0 && name[namelen-1] == '\\') {
+ char *modname = talloc_strdup(frame, name);
+ if (modname == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ modname[namelen-1] = '\0';
+ name = modname;
+ }
+
+ /* Try to open a file handle first. */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ desired_access,
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ 0, /* create_options */
+ pfnum,
+ NULL);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ desired_access,
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ pfnum,
+ NULL);
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a path info (ALTNAME level).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
+ const char *name,
+ fstring alt_name)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ uint16_t fnum = -1;
+ struct smb2_hnd *ph = NULL;
+ uint32_t altnamelen = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+ level SMB_FILE_ALTERNATE_NAME_INFORMATION (1021) == SMB2 21 */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ if (outbuf.length < 4) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ altnamelen = IVAL(outbuf.data, 0);
+ if (altnamelen > outbuf.length - 4) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ if (altnamelen > 0) {
+ size_t ret = 0;
+ char *short_name = NULL;
+ ret = pull_string_talloc(frame,
+ outbuf.data,
+ FLAGS2_UNICODE_STRINGS,
+ &short_name,
+ outbuf.data + 4,
+ altnamelen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ fstrcpy(alt_name, short_name);
+ } else {
+ alt_name[0] = '\0';
+ }
+
+ status = NT_STATUS_OK;
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a fnum info (basic level).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t *mode,
+ off_t *size,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ SMB_INO_T *ino)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+ level 0x12 (SMB2_FILE_ALL_INFORMATION). */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ if (outbuf.length < 0x60) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ if (create_time) {
+ *create_time = interpret_long_date((const char *)outbuf.data + 0x0);
+ }
+ if (access_time) {
+ *access_time = interpret_long_date((const char *)outbuf.data + 0x8);
+ }
+ if (write_time) {
+ *write_time = interpret_long_date((const char *)outbuf.data + 0x10);
+ }
+ if (change_time) {
+ *change_time = interpret_long_date((const char *)outbuf.data + 0x18);
+ }
+ if (mode) {
+ uint32_t attr = IVAL(outbuf.data, 0x20);
+ *mode = (uint16_t)attr;
+ }
+ if (size) {
+ uint64_t file_size = BVAL(outbuf.data, 0x30);
+ *size = (off_t)file_size;
+ }
+ if (ino) {
+ uint64_t file_index = BVAL(outbuf.data, 0x40);
+ *ino = (SMB_INO_T)file_index;
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query an fnum.
+ Implement on top of cli_smb2_qfileinfo_basic().
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_getattrE(struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t *attr,
+ off_t *size,
+ time_t *change_time,
+ time_t *access_time,
+ time_t *write_time)
+{
+ struct timespec access_time_ts;
+ struct timespec write_time_ts;
+ struct timespec change_time_ts;
+ NTSTATUS status = cli_smb2_qfileinfo_basic(cli,
+ fnum,
+ attr,
+ size,
+ NULL,
+ &access_time_ts,
+ &write_time_ts,
+ &change_time_ts,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (change_time) {
+ *change_time = change_time_ts.tv_sec;
+ }
+ if (access_time) {
+ *access_time = access_time_ts.tv_sec;
+ }
+ if (write_time) {
+ *write_time = write_time_ts.tv_sec;
+ }
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_getatr(struct cli_state *cli,
+ const char *name,
+ uint16_t *attr,
+ off_t *size,
+ time_t *write_time)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ status = cli_smb2_getattrE(cli,
+ fnum,
+ attr,
+ size,
+ NULL,
+ NULL,
+ write_time);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a pathname info (basic level).
+ Implement on top of cli_smb2_qfileinfo_basic().
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
+ const char *name,
+ struct timespec *create_time,
+ struct timespec *access_time,
+ struct timespec *write_time,
+ struct timespec *change_time,
+ off_t *size,
+ uint16_t *mode,
+ SMB_INO_T *ino)
+{
+ NTSTATUS status;
+ struct smb2_hnd *ph = NULL;
+ uint16_t fnum = -1;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = cli_smb2_qfileinfo_basic(cli,
+ fnum,
+ mode,
+ size,
+ create_time,
+ access_time,
+ write_time,
+ change_time,
+ ino);
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query pathname streams.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
+ const char *name,
+ TALLOC_CTX *mem_ctx,
+ unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ NTSTATUS status;
+ struct smb2_hnd *ph = NULL;
+ uint16_t fnum = -1;
+ DATA_BLOB outbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+ level 22 (SMB2_FILE_STREAM_INFORMATION). */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ if (!parse_streams_blob(mem_ctx,
+ outbuf.data,
+ outbuf.length,
+ pnum_streams,
+ pstreams)) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set pathname attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+ const char *name,
+ uint16_t attr,
+ time_t mtime)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+ struct smb2_hnd *ph = NULL;
+ uint8_t inbuf_store[40];
+ DATA_BLOB inbuf = data_blob_null;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_WRITE_ATTRIBUTES,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+ inbuf.data = inbuf_store;
+ inbuf.length = sizeof(inbuf_store);
+ data_blob_clear(&inbuf);
+
+ SIVAL(inbuf.data,32,attr);
+ if (mtime != 0) {
+ put_long_date((char *)inbuf.data + 16,mtime);
+ }
+ /* Set all the other times to -1. */
+ SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+ SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
+ SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set file handle times.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
+ uint16_t fnum,
+ time_t change_time,
+ time_t access_time,
+ time_t write_time)
+{
+ NTSTATUS status;
+ struct smb2_hnd *ph = NULL;
+ uint8_t inbuf_store[40];
+ DATA_BLOB inbuf = data_blob_null;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+ inbuf.data = inbuf_store;
+ inbuf.length = sizeof(inbuf_store);
+ data_blob_clear(&inbuf);
+
+ SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+ if (change_time != 0) {
+ put_long_date((char *)inbuf.data + 24, change_time);
+ }
+ if (access_time != 0) {
+ put_long_date((char *)inbuf.data + 8, access_time);
+ }
+ if (write_time != 0) {
+ put_long_date((char *)inbuf.data + 16, write_time);
+ }
+
+ return smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query disk attributes (size).
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ uint32_t sectors_per_unit = 0;
+ uint32_t bytes_per_sector = 0;
+ uint64_t total_size = 0;
+ uint64_t size_free = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* First open the top level directory. */
+ status = cli_smb2_create_fnum(cli,
+ "",
+ 0, /* create_flags */
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_DIRECTORY_FILE, /* create_options */
+ &fnum,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+ level 3 (SMB_FS_SIZE_INFORMATION). */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 2, /* in_info_type */
+ 3, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ if (outbuf.length != 24) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ total_size = BVAL(outbuf.data, 0);
+ size_free = BVAL(outbuf.data, 8);
+ sectors_per_unit = IVAL(outbuf.data, 16);
+ bytes_per_sector = IVAL(outbuf.data, 20);
+
+ if (bsize) {
+ *bsize = (int)(sectors_per_unit * bytes_per_sector);
+ }
+ if (total) {
+ *total = (int)total_size;
+ }
+ if (avail) {
+ *avail = (int)size_free;
+ }
+
+ status = NT_STATUS_OK;
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query a security descriptor.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **ppsd)
+{
+ NTSTATUS status;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ struct security_descriptor *lsd = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 3, /* in_info_type */
+ 0, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ sec_info, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ status = unmarshall_sec_desc(mem_ctx,
+ outbuf.data,
+ outbuf.length,
+ &lsd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (ppsd != NULL) {
+ *ppsd = lsd;
+ } else {
+ TALLOC_FREE(lsd);
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set a security descriptor.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t sec_info,
+ const struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = marshall_sec_desc(frame,
+ sd,
+ &inbuf.data,
+ &inbuf.length);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 3, /* in_info_type */
+ 0, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ sec_info, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to rename a file.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_rename(struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ uint16_t fnum = -1;
+ struct smb2_hnd *ph = NULL;
+ smb_ucs2_t *converted_str = NULL;
+ size_t converted_size_bytes = 0;
+ size_t namelen = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ fname_src,
+ DELETE_ACCESS,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname_dst == '\\') {
+ fname_dst++;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ end in a '\' */
+ namelen = strlen(fname_dst);
+ if (namelen > 0 && fname_dst[namelen-1] == '\\') {
+ char *modname = talloc_strdup(frame, fname_dst);
+ modname[namelen-1] = '\0';
+ fname_dst = modname;
+ }
+
+ if (!push_ucs2_talloc(frame,
+ &converted_str,
+ fname_dst,
+ &converted_size_bytes)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ /* W2K8 insists the dest name is not null
+ terminated. Remove the last 2 zero bytes
+ and reduce the name length. */
+
+ if (converted_size_bytes < 2) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ converted_size_bytes -= 2;
+
+ inbuf = data_blob_talloc_zero(frame,
+ 20 + converted_size_bytes);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ SIVAL(inbuf.data, 16, converted_size_bytes);
+ memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
+
+ /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
+ level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a fnum.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
+ uint16_t fnum,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len)
+{
+ NTSTATUS status;
+ DATA_BLOB inbuf = data_blob_null;
+ size_t bloblen = 0;
+ char *ea_name_ascii = NULL;
+ size_t namelen = 0;
+ struct smb2_hnd *ph = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Marshall the SMB2 EA data. */
+ if (ea_len > 0xFFFF) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (!push_ascii_talloc(frame,
+ &ea_name_ascii,
+ ea_name,
+ &namelen)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (namelen < 2 || namelen > 0xFF) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ bloblen = 8 + ea_len + namelen;
+ /* Round up to a 4 byte boundary. */
+ bloblen = ((bloblen + 3)&~3);
+
+ inbuf = data_blob_talloc_zero(frame, bloblen);
+ if (inbuf.data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ /* namelen doesn't include the NULL byte. */
+ SCVAL(inbuf.data, 5, namelen - 1);
+ SSVAL(inbuf.data, 6, ea_len);
+ memcpy(inbuf.data + 8, ea_name_ascii, namelen);
+ memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
+
+ /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+ level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+
+ status = smb2cli_set_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0, /* in_additional_info */
+ ph->fid_persistent,
+ ph->fid_volatile);
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
+ const char *name,
+ const char *ea_name,
+ const char *ea_val,
+ size_t ea_len)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_WRITE_EA,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = cli_set_ea_fnum(cli,
+ fnum,
+ ea_name,
+ ea_val,
+ ea_len);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ fail:
+
+ if (fnum != -1) {
+ cli_smb2_close_fnum(cli, fnum);
+ }
+
+ return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get an EA list on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
+ const char *name,
+ TALLOC_CTX *ctx,
+ size_t *pnum_eas,
+ struct ea_struct **pea_array)
+{
+ NTSTATUS status;
+ uint16_t fnum = -1;
+ DATA_BLOB outbuf = data_blob_null;
+ struct smb2_hnd *ph = NULL;
+ struct ea_list *ea_list = NULL;
+ struct ea_list *eal = NULL;
+ size_t ea_count = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ *pnum_eas = 0;
+ *pea_array = NULL;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ status = get_fnum_from_path(cli,
+ name,
+ FILE_READ_EA,
+ &fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+ level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+
+ status = smb2cli_query_info(cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ 1, /* in_info_type */
+ SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
+ 0xFFFF, /* in_max_output_length */
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0, /* in_flags */
+ ph->fid_persistent,
+ ph->fid_volatile,
+ frame,
+ &outbuf);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ /* Parse the reply. */
+ ea_list = read_nttrans_ea_list(ctx,
+ (const char *)outbuf.data,
+ outbuf.length);
+ if (ea_list == NULL) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto fail;
+ }
+
+ /* Convert to an array. */
+ for (eal = ea_list; eal; eal = eal->next) {
+ ea_count++;
+ }
+
+ if (ea_count) {
+ *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
+ if (*pea_array == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ ea_count = 0;
+ for (eal = ea_list; eal; eal = eal->next) {
+ (*pea_array)[ea_count++] = ea_list->ea;
+ }
+ *pnum_eas = ea_count;
+ }
+
+ fail:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct cli_smb2_read_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint64_t start_offset;
+ uint32_t size;
+ uint32_t received;
+ uint8_t *buf;
+};
+
+static void cli_smb2_read_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_read_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->start_offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->received = 0;
+ state->buf = NULL;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_read_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->size,
+ state->start_offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* minimum_count */
+ 0); /* remaining_bytes */
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_read_done, req);
+ return req;
+}
+
+static void cli_smb2_read_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_read_state *state = tevent_req_data(
+ req, struct cli_smb2_read_state);
+ NTSTATUS status;
+
+ status = smb2cli_read_recv(subreq, state,
+ &state->buf, &state->received);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ if (state->received > state->size) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
+ ssize_t *received,
+ uint8_t **rcvbuf)
+{
+ NTSTATUS status;
+ struct cli_smb2_read_state *state = tevent_req_data(
+ req, struct cli_smb2_read_state);
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ /*
+ * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
+ * better make sure that you copy it away before you talloc_free(req).
+ * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
+ */
+ *received = (ssize_t)state->received;
+ *rcvbuf = state->buf;
+ return NT_STATUS_OK;
+}
+
+struct cli_smb2_write_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint32_t flags;
+ const uint8_t *buf;
+ uint64_t offset;
+ uint32_t size;
+ uint32_t written;
+};
+
+static void cli_smb2_write_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq = NULL;
+ struct cli_smb2_write_state *state = NULL;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+ state->flags = (uint32_t)mode;
+ state->buf = buf;
+ state->offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->written = 0;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->size,
+ state->offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_write_written, req);
+ return req;
+}
+
+static void cli_smb2_write_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_write_state *state = tevent_req_data(
+ req, struct cli_smb2_write_state);
+ NTSTATUS status;
+ uint32_t written;
+
+ status = smb2cli_write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->written = written;
+
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
+ size_t *pwritten)
+{
+ struct cli_smb2_write_state *state = tevent_req_data(
+ req, struct cli_smb2_write_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+
+ if (pwritten != NULL) {
+ *pwritten = (size_t)state->written;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 async write using an fnum.
+ This is mostly cut-and-paste from Volker's code inside
+ source3/libsmb/clireadwrite.c, adapted for SMB2.
+
+ Done this way so I can reuse all the logic inside cli_push()
+ for free :-).
+***************************************************************/
+
+struct cli_smb2_writeall_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *ph;
+ uint32_t flags;
+ const uint8_t *buf;
+ uint64_t offset;
+ uint32_t size;
+ uint32_t written;
+};
+
+static void cli_smb2_writeall_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint16_t mode,
+ const uint8_t *buf,
+ off_t offset,
+ size_t size)
+{
+ NTSTATUS status;
+ struct tevent_req *req, *subreq = NULL;
+ struct cli_smb2_writeall_state *state = NULL;
+ uint32_t to_write;
+ uint32_t max_size;
+ bool ok;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+ state->flags = (uint32_t)mode;
+ state->buf = buf;
+ state->offset = (uint64_t)offset;
+ state->size = (uint32_t)size;
+ state->written = 0;
+
+ status = map_fnum_to_smb2_handle(cli,
+ fnum,
+ &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ to_write = state->size;
+ max_size = smb2cli_conn_max_write_size(state->cli->conn);
+ to_write = MIN(max_size, to_write);
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (ok) {
+ to_write = MIN(max_size, to_write);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ to_write,
+ state->offset,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf + state->written);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+ return req;
+}
+
+static void cli_smb2_writeall_written(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_writeall_state *state = tevent_req_data(
+ req, struct cli_smb2_writeall_state);
+ NTSTATUS status;
+ uint32_t written, to_write;
+ uint32_t max_size;
+ bool ok;
+
+ status = smb2cli_write_recv(subreq, &written);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->written += written;
+
+ if (state->written > state->size) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ to_write = state->size - state->written;
+
+ if (to_write == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ max_size = smb2cli_conn_max_write_size(state->cli->conn);
+ to_write = MIN(max_size, to_write);
+ ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+ if (ok) {
+ to_write = MIN(max_size, to_write);
+ }
+
+ subreq = smb2cli_write_send(state,
+ state->ev,
+ state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ to_write,
+ state->offset + state->written,
+ state->ph->fid_persistent,
+ state->ph->fid_volatile,
+ 0, /* remaining_bytes */
+ state->flags, /* flags */
+ state->buf + state->written);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+}
+
+NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+ size_t *pwritten)
+{
+ struct cli_smb2_writeall_state *state = tevent_req_data(
+ req, struct cli_smb2_writeall_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ if (pwritten != NULL) {
+ *pwritten = (size_t)state->written;
+ }
+ return NT_STATUS_OK;
+}