#include "ntioctl.h"
#include "librpc/gen_ndr/ndr_quota.h"
#include "lib/util/string_wrappers.h"
+#include "lib/util/idtree.h"
struct smb2_hnd {
uint64_t fid_persistent;
return SMB2_OPLOCK_LEVEL_NONE;
}
+/***************************************************************
+ If we're on a DFS share, ensure we convert to a full DFS path
+ if this hasn't already been done.
+***************************************************************/
+
+static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ char *path)
+{
+ bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
+ smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
+ bool is_already_dfs_path = false;
+
+ if (!is_dfs) {
+ return path;
+ }
+ is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
+ if (is_already_dfs_path) {
+ return path;
+ }
+ if (path[0] == '\0') {
+ return talloc_asprintf(ctx,
+ "%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share);
+ }
+ while (*path == '\\') {
+ path++;
+ }
+ return talloc_asprintf(ctx,
+ "%s\\%s\\%s",
+ smbXcli_conn_remote_name(cli->conn),
+ cli->share,
+ path);
+}
+
/***************************************************************
Small wrapper that allows SMB2 create to return a uint16_t fnum.
***************************************************************/
struct smb2_create_blobs in_cblobs;
struct smb2_create_blobs out_cblobs;
struct smb_create_returns cr;
+ struct symlink_reparse_struct *symlink;
uint16_t fnum;
struct tevent_req *subreq;
};
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
- const char *fname,
+ const char *fname_in,
uint32_t create_flags,
uint32_t impersonation_level,
uint32_t desired_access,
{
struct tevent_req *req, *subreq;
struct cli_smb2_create_fnum_state *state;
+ char *fname = NULL;
size_t fname_len = 0;
- const char *startp = NULL;
- const char *endp = NULL;
- time_t tstamp = (time_t)0;
+ bool have_twrp;
+ NTTIME ntt;
NTSTATUS status;
req = tevent_req_create(mem_ctx, &state,
}
state->cli = cli;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ fname = talloc_strdup(state, fname_in);
+ if (tevent_req_nomem(fname, req)) {
return tevent_req_post(req, ev);
}
}
/* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
- fname_len = strlen(fname);
- if (clistr_is_previous_version_path(fname, &startp, &endp, &tstamp)) {
- size_t len_before_gmt = startp - fname;
- size_t len_after_gmt = fname + fname_len - endp;
- DATA_BLOB twrp_blob;
- NTTIME ntt;
-
- char *new_fname = talloc_array(state, char,
- len_before_gmt + len_after_gmt + 1);
-
- if (tevent_req_nomem(new_fname, req)) {
- return tevent_req_post(req, ev);
- }
-
- memcpy(new_fname, fname, len_before_gmt);
- memcpy(new_fname + len_before_gmt, endp, len_after_gmt + 1);
- fname = new_fname;
- fname_len = len_before_gmt + len_after_gmt;
-
- unix_to_nt_time(&ntt, tstamp);
- twrp_blob = data_blob_const((const void *)&ntt, 8);
-
+ have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
+ if (have_twrp) {
status = smb2_create_blob_add(
state,
&state->in_cblobs,
SMB2_CREATE_TAG_TWRP,
- twrp_blob);
- if (!NT_STATUS_IS_OK(status)) {
- tevent_req_nterror(req, status);
+ (DATA_BLOB) {
+ .data = (uint8_t *)&ntt,
+ .length = sizeof(ntt),
+ });
+ if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
}
}
}
+ fname = smb2_dfs_share_path(state, cli, fname);
+ if (tevent_req_nomem(fname, req)) {
+ return tevent_req_post(req, ev);
+ }
+ fname_len = strlen(fname);
+
/* SMB2 is pickier about pathnames. Ensure it doesn't
start in a '\' */
if (*fname == '\\') {
/* Or end in a '\' */
if (fname_len > 0 && fname[fname_len-1] == '\\') {
- char *new_fname = talloc_strdup(state, fname);
- if (tevent_req_nomem(new_fname, req)) {
- return tevent_req_post(req, ev);
- }
- new_fname[fname_len-1] = '\0';
- fname = new_fname;
+ fname[fname_len-1] = '\0';
}
subreq = smb2cli_create_send(state, ev,
&h.fid_persistent,
&h.fid_volatile, &state->cr,
state,
- &state->out_cblobs);
+ &state->out_cblobs,
+ &state->symlink);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
uint16_t *pfnum,
struct smb_create_returns *cr,
TALLOC_CTX *mem_ctx,
- struct smb2_create_blobs *out_cblobs)
+ struct smb2_create_blobs *out_cblobs,
+ struct symlink_reparse_struct **symlink)
{
struct cli_smb2_create_fnum_state *state = tevent_req_data(
req, struct cli_smb2_create_fnum_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
+ (symlink != NULL)) {
+ *symlink = talloc_move(mem_ctx, &state->symlink);
+ }
state->cli->raw_status = status;
return status;
}
if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- status = cli_smb2_create_fnum_recv(req, pfid, cr, mem_ctx, out_cblobs);
+ status = cli_smb2_create_fnum_recv(
+ req, pfid, cr, mem_ctx, out_cblobs, NULL);
fail:
TALLOC_FREE(frame);
return status;
state->cli = cli;
state->fnum = fnum;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
state->cli = cli;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
/*
* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
* level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
struct cli_smb2_mkdir_state *state = tevent_req_data(
req, struct cli_smb2_mkdir_state);
NTSTATUS status;
- uint16_t fnum;
+ uint16_t fnum = 0xffff;
- status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
state->dname = dname;
state->in_cblobs = in_cblobs;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
subreq = cli_smb2_create_fnum_send(
state,
state->ev,
NTSTATUS status;
status = cli_smb2_create_fnum_recv(
- subreq, &state->fnum, NULL, NULL, NULL);
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
NTSTATUS status;
status = cli_smb2_create_fnum_recv(
- subreq, &state->fnum, NULL, NULL, NULL);
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
state->fname = fname;
state->in_cblobs = in_cblobs;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
subreq = cli_smb2_create_fnum_send(
state, /* mem_ctx */
state->ev, /* tevent_context */
subreq, struct tevent_req);
struct cli_smb2_unlink_state *state = tevent_req_data(
req, struct cli_smb2_unlink_state);
- uint16_t fnum;
+ uint16_t fnum = 0xffff;
NTSTATUS status;
- status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
- if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
/*
* Naive option to match our SMB1 code. Assume the
* symlink path that tripped us up was the last
subreq, struct tevent_req);
struct cli_smb2_unlink_state *state = tevent_req_data(
req, struct cli_smb2_unlink_state);
- uint16_t fnum;
+ uint16_t fnum = 0xffff;
NTSTATUS status;
- status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
+ status = cli_smb2_create_fnum_recv(
+ subreq, &fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
return tevent_req_simple_recv_ntstatus(req);
}
+static ssize_t sid_parse_wire(TALLOC_CTX *mem_ctx, const uint8_t *data,
+ struct dom_sid *sid, size_t num_rdata)
+{
+ size_t sid_size;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB in = data_blob_const(data, num_rdata);
+
+ ndr_err = ndr_pull_struct_blob(&in,
+ mem_ctx,
+ sid,
+ (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return 0;
+ }
+
+ sid_size = ndr_size_dom_sid(sid, 0);
+ if (sid_size > num_rdata) {
+ return 0;
+ }
+
+ return sid_size;
+}
+
+/***************************************************************
+ Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
+***************************************************************/
+
+static NTSTATUS parse_finfo_posix_info(const 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, slen2 = 0;
+ size_t ret = 0;
+ uint32_t _next_offset = 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 < 92) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
+ 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->allocated_size = PULL_LE_U64(dir_data, 40);
+ finfo->size = PULL_LE_U64(dir_data, 48);
+ finfo->mode = PULL_LE_U32(dir_data, 56);
+ finfo->ino = PULL_LE_U64(dir_data, 60);
+ finfo->st_ex_dev = PULL_LE_U32(dir_data, 68);
+ finfo->st_ex_nlink = PULL_LE_U32(dir_data, 76);
+ finfo->reparse_tag = PULL_LE_U32(dir_data, 80);
+ finfo->st_ex_mode = wire_perms_to_unix(PULL_LE_U32(dir_data, 84));
+
+ slen = sid_parse_wire(finfo, dir_data+88, &finfo->owner_sid,
+ dir_data_length-88);
+ if (slen == 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ slen2 = sid_parse_wire(finfo, dir_data+88+slen, &finfo->group_sid,
+ dir_data_length-88-slen);
+ if (slen2 == 0) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ slen += slen2;
+
+ namelen = PULL_LE_U32(dir_data, 88+slen);
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->name,
+ dir_data+92+slen,
+ namelen,
+ STR_UNICODE);
+ if (ret == (size_t)-1) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (finfo->name == NULL) {
+ /* Bad conversion. */
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ *next_offset = _next_offset;
+ return NT_STATUS_OK;
+}
+
/***************************************************************
Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
***************************************************************/
return true;
}
-/***************************************************************
- Wrapper that allows SMB2 to list a directory.
- Synchronous only.
-***************************************************************/
+struct cli_smb2_list_dir_data {
+ uint8_t *data;
+ uint32_t length;
+};
+
+struct cli_smb2_list_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *mask;
+
+ uint16_t fnum;
-NTSTATUS cli_smb2_list(struct cli_state *cli,
- const char *pathname,
- uint32_t attribute,
- NTSTATUS (*fn)(struct file_info *finfo,
- const char *mask,
- void *private_data),
- void *private_data)
-{
NTSTATUS status;
- uint16_t fnum = 0xffff;
- char *parent_dir = NULL;
- const char *mask = NULL;
- struct smb2_hnd *ph = NULL;
- bool processed_file = false;
- TALLOC_CTX *frame = talloc_stackframe();
- TALLOC_CTX *subframe = NULL;
- bool mask_has_wild;
- uint32_t max_trans;
- uint32_t max_avail_len;
+ struct cli_smb2_list_dir_data *response;
+ uint32_t offset;
+ unsigned int info_level;
+};
+
+static void cli_smb2_list_opened(struct tevent_req *subreq);
+static void cli_smb2_list_done(struct tevent_req *subreq);
+static void cli_smb2_list_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_list_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *pathname,
+ unsigned int info_level,
+ bool posix)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_list_state *state = NULL;
+ char *parent = NULL;
bool ok;
+ struct smb2_create_blobs *in_cblobs = 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;
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
+ if (req == NULL) {
+ return NULL;
}
+ state->ev = ev;
+ state->cli = cli;
+ state->status = NT_STATUS_OK;
+ state->info_level = info_level;
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+ ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
+ if (!ok) {
+ tevent_req_oom(req);
+ return tevent_req_post(req, ev);
}
- /* Get the directory name. */
- if (!windows_parent_dirname(frame,
- pathname,
- &parent_dir,
- &mask)) {
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
+ if (smbXcli_conn_have_posix(cli->conn) && posix) {
+ NTSTATUS status;
- mask_has_wild = ms_has_wild(mask);
+ /* The mode MUST be 0 when opening an existing file/dir, and
+ * will be ignored by the server.
+ */
+ uint8_t linear_mode[4] = { 0 };
+ DATA_BLOB blob = { .data=linear_mode,
+ .length=sizeof(linear_mode) };
- status = cli_smb2_create_fnum(cli,
- parent_dir,
- 0, /* create_flags */
- SMB2_IMPERSONATION_IMPERSONATION,
- 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 */
- NULL,
- &fnum,
- NULL,
- NULL,
- NULL);
+ in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
+ if (in_cblobs == NULL) {
+ return NULL;
+ }
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ status = smb2_create_blob_add(in_cblobs, in_cblobs,
+ SMB2_CREATE_TAG_POSIX, blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ parent, /* fname */
+ 0, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
+ 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 */
+ in_cblobs); /* in_cblobs */
+ TALLOC_FREE(in_cblobs);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
+ return req;
+}
+
+static void cli_smb2_list_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
/*
- * ideally, use the max transaction size, but don't send a request
- * bigger than we have credits available for
+ * Make our caller get back to us via cli_smb2_list_recv(),
+ * triggering the smb2_query_directory_send()
*/
- max_trans = smb2cli_conn_max_trans_size(cli->conn);
- ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
- if (ok) {
- max_trans = MIN(max_trans, max_avail_len);
- }
-
- 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,
- max_trans,
- subframe,
- &dir_data,
- &dir_data_length);
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+}
- if (!NT_STATUS_IS_OK(status)) {
- if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
- break;
- }
- goto fail;
- }
+static void cli_smb2_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ struct cli_smb2_list_dir_data *response = NULL;
- do {
- struct file_info *finfo = talloc_zero(subframe,
- struct file_info);
+ response = talloc(state, struct cli_smb2_list_dir_data);
+ if (tevent_req_nomem(response, req)) {
+ return;
+ }
- if (finfo == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
+ state->status = smb2cli_query_directory_recv(
+ subreq, response, &response->data, &response->length);
+ TALLOC_FREE(subreq);
- status = parse_finfo_id_both_directory_info(dir_data,
- dir_data_length,
- finfo,
- &next_offset);
+ if (NT_STATUS_IS_OK(state->status)) {
+ state->response = response;
+ state->offset = 0;
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
- }
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+ return;
+ }
- /* Protect against server attack. */
- status = is_bad_finfo_name(cli, finfo);
- if (!NT_STATUS_IS_OK(status)) {
- smbXcli_conn_disconnect(cli->conn, status);
- goto fail;
- }
+ TALLOC_FREE(response);
- if (dir_check_ftype(finfo->attr, attribute)) {
- /*
- * Only process if attributes match.
- * SMB1 servers do the filtering, so
- * with SMB2 we need to emulate it in
- * the client.
- *
- * https://bugzilla.samba.org/show_bug.cgi?id=10260
- */
- processed_file = true;
-
- status = fn(
- finfo,
- pathname,
- private_data);
-
- if (!NT_STATUS_IS_OK(status)) {
- break;
- }
- }
+ subreq = cli_smb2_close_fnum_send(
+ state, state->ev, state->cli, state->fnum);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
+}
+
+static void cli_smb2_list_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
- TALLOC_FREE(finfo);
+/*
+ * Return the next finfo directory.
+ *
+ * This parses the blob returned from QUERY_DIRECTORY step by step. If
+ * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
+ * NT_STATUS_RETRY, which will then trigger the caller again when the
+ * QUERY_DIRECTORY has returned with another buffer. This way we
+ * guarantee that no asynchronous request is open after this call
+ * returns an entry, so that other synchronous requests can be issued
+ * on the same connection while the directoy listing proceeds.
+ */
+NTSTATUS cli_smb2_list_recv(
+ struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ struct file_info **pfinfo)
+{
+ struct cli_smb2_list_state *state = tevent_req_data(
+ req, struct cli_smb2_list_state);
+ struct cli_smb2_list_dir_data *response = NULL;
+ struct file_info *finfo = NULL;
+ NTSTATUS status;
+ uint32_t next_offset = 0;
+ bool in_progress;
- /* Move to next entry. */
- if (next_offset) {
- dir_data += next_offset;
- dir_data_length -= next_offset;
- }
- } while (next_offset != 0);
-
- TALLOC_FREE(subframe);
-
- if (!mask_has_wild) {
- /*
- * MacOSX 10 doesn't set STATUS_NO_MORE_FILES
- * when handed a non-wildcard path. Do it
- * for the server (with a non-wildcard path
- * there should only ever be one file returned.
- */
- status = STATUS_NO_MORE_FILES;
- break;
+ in_progress = tevent_req_is_in_progress(req);
+
+ if (!in_progress) {
+ if (!tevent_req_is_nterror(req, &status)) {
+ status = NT_STATUS_NO_MORE_FILES;
+ }
+ goto fail;
+ }
+
+ response = state->response;
+ if (response == NULL) {
+ struct tevent_req *subreq = NULL;
+ struct cli_state *cli = state->cli;
+ struct smb2_hnd *ph = NULL;
+ uint32_t max_trans, max_avail_len;
+ bool ok;
+
+ if (!NT_STATUS_IS_OK(state->status)) {
+ status = state->status;
+ goto fail;
}
- } while (NT_STATUS_IS_OK(status));
+ status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
- if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
- status = NT_STATUS_OK;
+ max_trans = smb2cli_conn_max_trans_size(cli->conn);
+ ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
+ if (ok) {
+ max_trans = MIN(max_trans, max_avail_len);
+ }
+
+ subreq = smb2cli_query_directory_send(
+ state, /* mem_ctx */
+ state->ev, /* ev */
+ cli->conn, /* conn */
+ cli->timeout, /* timeout_msec */
+ cli->smb2.session, /* session */
+ cli->smb2.tcon, /* tcon */
+ state->info_level, /* level */
+ 0, /* flags */
+ 0, /* file_index */
+ ph->fid_persistent, /* fid_persistent */
+ ph->fid_volatile, /* fid_volatile */
+ state->mask, /* mask */
+ max_trans); /* outbuf_len */
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_done, req);
+ return NT_STATUS_RETRY;
}
- if (NT_STATUS_IS_OK(status) && !processed_file) {
- /*
- * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
- * if no files match. Emulate this in the client.
- */
- status = NT_STATUS_NO_SUCH_FILE;
+ SMB_ASSERT(response->length > state->offset);
+
+ finfo = talloc_zero(mem_ctx, struct file_info);
+ if (finfo == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
}
- fail:
+ if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
+ status = parse_finfo_posix_info(
+ response->data + state->offset,
+ response->length - state->offset,
+ finfo,
+ &next_offset);
+ } else {
+ status = parse_finfo_id_both_directory_info(
+ response->data + state->offset,
+ response->length - state->offset,
+ finfo,
+ &next_offset);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
+ status = is_bad_finfo_name(state->cli, finfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
}
- cli->raw_status = status;
+ /*
+ * parse_finfo_id_both_directory_info() checks for overflow,
+ * no need to check again here.
+ */
+ state->offset += next_offset;
- TALLOC_FREE(subframe);
- TALLOC_FREE(frame);
+ if (next_offset == 0) {
+ TALLOC_FREE(state->response);
+ }
+
+ tevent_req_defer_callback(req, state->ev);
+ tevent_req_notify_callback(req);
+
+ *pfinfo = finfo;
+ return NT_STATUS_OK;
+
+fail:
+ TALLOC_FREE(finfo);
+ tevent_req_received(req);
return status;
}
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] == '\\') {
NULL);
}
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /* Maybe a reparse point ? */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ 0, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION,
+ FILE_READ_ATTRIBUTES, /* desired_access */
+ 0, /* file attributes */
+ FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ FILE_OPEN_REPARSE_POINT, /* create_options */
+ NULL,
+ &fnum,
+ &cr,
+ NULL,
+ NULL);
+ }
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return status;
}
-struct cli_smb2_chkpath_state {
- struct tevent_context *ev;
- struct cli_state *cli;
-};
-
-static void cli_smb2_chkpath_opened(struct tevent_req *subreq);
-static void cli_smb2_chkpath_closed(struct tevent_req *subreq);
-
-struct tevent_req *cli_smb2_chkpath_send(
- TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- const char *name)
-{
- struct tevent_req *req = NULL, *subreq = NULL;
- struct cli_smb2_chkpath_state *state = NULL;
-
- req = tevent_req_create(
- mem_ctx, &state, struct cli_smb2_chkpath_state);
- if (req == NULL) {
- return NULL;
- }
- state->ev = ev;
- state->cli = cli;
-
- /* Ensure this is a directory. */
- subreq = cli_smb2_create_fnum_send(
- state, /* mem_ctx */
- ev, /* ev */
- cli, /* cli */
- name, /* fname */
- 0, /* create_flags */
- SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
- 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 */
- NULL); /* in_cblobs */
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq, cli_smb2_chkpath_opened, req);
- return req;
-}
-
-static void cli_smb2_chkpath_opened(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_smb2_chkpath_state *state = tevent_req_data(
- req, struct cli_smb2_chkpath_state);
- NTSTATUS status;
- uint16_t fnum;
-
- status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL);
- TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
- return;
- }
-
- subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
- if (tevent_req_nomem(subreq, req)) {
- return;
- }
- tevent_req_set_callback(subreq, cli_smb2_chkpath_closed, req);
-}
-
-static void cli_smb2_chkpath_closed(struct tevent_req *subreq)
-{
- NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
- tevent_req_simple_finish_ntstatus(subreq, status);
-}
-
-NTSTATUS cli_smb2_chkpath_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
struct cli_smb2_query_info_fnum_state {
DATA_BLOB outbuf;
};
NTSTATUS status;
status = cli_smb2_create_fnum_recv(
- subreq, &state->fnum, NULL, NULL, NULL);
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
struct get_fnum_from_path_state *state = tevent_req_data(
req, struct get_fnum_from_path_state);
NTSTATUS status = cli_smb2_create_fnum_recv(
- subreq, &state->fnum, NULL, NULL, NULL);
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
tevent_req_simple_finish_ntstatus(subreq, status);
}
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,
{
NTSTATUS status;
uint16_t fnum = 0xffff;
- struct smb2_hnd *ph = NULL;
struct timespec write_time_ts;
TALLOC_CTX *frame = talloc_stackframe();
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,
goto fail;
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
- }
status = cli_qfileinfo_basic(
cli,
fnum,
SMB_INO_T *ino)
{
NTSTATUS status;
- struct smb2_hnd *ph = NULL;
uint16_t fnum = 0xffff;
TALLOC_CTX *frame = talloc_stackframe();
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,
goto fail;
}
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
- }
-
status = cli_qfileinfo_basic(
cli,
fnum,
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,
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,
return NT_STATUS_INVALID_PARAMETER;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- return NT_STATUS_INVALID_PARAMETER;
- }
-
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
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,
path,
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 */
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 */
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 */
.fname = fname,
};
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
status = smb2_create_blob_add(state,
&state->in_cblobs,
SMB2_CREATE_TAG_MXAC,
NTSTATUS status;
status = cli_smb2_create_fnum_recv(
- subreq, &state->fnum, NULL, state, &out_cblobs);
+ subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
{
struct tevent_req *req = NULL, *subreq = NULL;
struct cli_smb2_rename_state *state = NULL;
+ NTSTATUS status;
req = tevent_req_create(
mem_ctx, &state, struct cli_smb2_rename_state);
if (req == NULL) {
return NULL;
}
+
+ /*
+ * Strip a MSDFS path from fname_dst if we were given one.
+ */
+ status = cli_dfs_target_check(state,
+ cli,
+ fname_dst,
+ &fname_dst);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
state->ev = ev;
state->cli = cli;
state->fname_dst = fname_dst;
return status;
}
-NTSTATUS cli_smb2_rename(struct cli_state *cli,
- const char *fname_src,
- const char *fname_dst,
- bool replace)
-{
- TALLOC_CTX *frame = talloc_stackframe();
- struct tevent_context *ev = NULL;
- struct tevent_req *req = NULL;
- NTSTATUS status = NT_STATUS_NO_MEMORY;
-
- if (smbXcli_conn_has_async_calls(cli->conn)) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
- }
- ev = samba_tevent_context_init(frame);
- if (ev == NULL) {
- goto fail;
- }
- req = cli_smb2_rename_send(
- frame, ev, cli, fname_src, fname_dst, replace);
- if (req == NULL) {
- goto fail;
- }
- if (!tevent_req_poll_ntstatus(req, ev, &status)) {
- goto fail;
- }
- status = cli_smb2_rename_recv(req);
- fail:
- TALLOC_FREE(frame);
- return status;
-}
-
/***************************************************************
Wrapper that allows SMB2 to set an EA on a fnum.
Synchronous only.
goto fail;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
- }
-
/* Marshall the SMB2 EA data. */
if (ea_len > 0xFFFF) {
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,
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,
goto fail;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
- }
-
sid_len = ndr_size_dom_sid(&pqt->sid, 0);
query.return_single = 1;
goto cleanup;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto cleanup;
- }
-
info.restart_scan = first ? 1 : 0;
err = ndr_push_struct_blob(
goto cleanup;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto cleanup;
- }
-
status = cli_smb2_query_info_fnum(
cli,
quota_fnum,
goto cleanup;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto cleanup;
- }
-
status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup;
goto cleanup;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto cleanup;
- }
-
status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup;
return NULL;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
state->cli = cli;
state->fnum = fnum;
goto fail;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
- }
-
SBVAL(buf, 0, newsize);
/* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
return NULL;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
status = map_fnum_to_smb2_handle(cli, fnum, &ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
return status;
}
-struct cli_smb2_set_reparse_point_fnum_state {
- struct cli_state *cli;
- uint16_t fnum;
- struct smb2_hnd *ph;
- DATA_BLOB input_buffer;
-};
-
-static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq);
-
-struct tevent_req *cli_smb2_set_reparse_point_fnum_send(
- TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- uint16_t fnum,
- DATA_BLOB in_buf)
-{
- struct tevent_req *req, *subreq;
- struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
- NTSTATUS status;
-
- req = tevent_req_create(mem_ctx, &state,
- struct cli_smb2_set_reparse_point_fnum_state);
- if (req == NULL) {
- return NULL;
- }
-
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
- state->cli = cli;
- state->fnum = fnum;
-
- status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
- if (tevent_req_nterror(req, status)) {
- return tevent_req_post(req, ev);
- }
-
- state->input_buffer = data_blob_talloc(state,
- in_buf.data,
- in_buf.length);
- if (state->input_buffer.data == NULL) {
- tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
- return tevent_req_post(req, ev);
- }
-
- subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
- state->cli->timeout,
- state->cli->smb2.session,
- state->cli->smb2.tcon,
- state->ph->fid_persistent, /* in_fid_persistent */
- state->ph->fid_volatile, /* in_fid_volatile */
- FSCTL_SET_REPARSE_POINT,
- 0, /* in_max_input_length */
- &state->input_buffer ,
- 0,
- NULL,
- SMB2_IOCTL_FLAG_IS_FSCTL);
-
- if (tevent_req_nomem(subreq, req)) {
- return tevent_req_post(req, ev);
- }
- tevent_req_set_callback(subreq,
- cli_smb2_set_reparse_point_fnum_done,
- req);
-
- return req;
-}
-
-static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq)
-{
- struct tevent_req *req = tevent_req_callback_data(
- subreq, struct tevent_req);
- struct cli_smb2_set_reparse_point_fnum_state *state = tevent_req_data(
- req, struct cli_smb2_set_reparse_point_fnum_state);
- NTSTATUS status;
-
- status = smb2cli_ioctl_recv(subreq, state,
- NULL,
- NULL);
- TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
- return;
- }
- tevent_req_done(req);
-}
-
-NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req)
-{
- return tevent_req_simple_recv_ntstatus(req);
-}
-
-struct cli_smb2_get_reparse_point_fnum_state {
- struct cli_state *cli;
- uint16_t fnum;
- struct smb2_hnd *ph;
- DATA_BLOB output_buffer;
+struct cli_smb2_fsctl_state {
+ DATA_BLOB out;
};
-static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq);
+static void cli_smb2_fsctl_done(struct tevent_req *subreq);
-struct tevent_req *cli_smb2_get_reparse_point_fnum_send(
- TALLOC_CTX *mem_ctx,
- struct tevent_context *ev,
- struct cli_state *cli,
- uint16_t fnum)
+struct tevent_req *cli_smb2_fsctl_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ uint32_t ctl_code,
+ const DATA_BLOB *in,
+ uint32_t max_out)
{
- struct tevent_req *req, *subreq;
- struct cli_smb2_get_reparse_point_fnum_state *state = NULL;
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_fsctl_state *state = NULL;
+ struct smb2_hnd *ph = NULL;
NTSTATUS status;
- req = tevent_req_create(mem_ctx, &state,
- struct cli_smb2_get_reparse_point_fnum_state);
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
if (req == NULL) {
return NULL;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return tevent_req_post(req, ev);
- }
-
- state->cli = cli;
- state->fnum = fnum;
-
- status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ status = map_fnum_to_smb2_handle(cli, fnum, &ph);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
- subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
- state->cli->timeout,
- state->cli->smb2.session,
- state->cli->smb2.tcon,
- state->ph->fid_persistent, /* in_fid_persistent */
- state->ph->fid_volatile, /* in_fid_volatile */
- FSCTL_GET_REPARSE_POINT,
- 0, /* in_max_input_length */
- NULL,
- 64*1024,
- NULL,
- SMB2_IOCTL_FLAG_IS_FSCTL);
+ subreq = smb2cli_ioctl_send(
+ state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ ph->fid_persistent,
+ ph->fid_volatile,
+ ctl_code,
+ 0, /* in_max_input_length */
+ in,
+ max_out,
+ NULL,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- tevent_req_set_callback(subreq,
- cli_smb2_get_reparse_point_fnum_done,
- req);
-
+ tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
return req;
}
-static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq)
+static void cli_smb2_fsctl_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
- struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
- req, struct cli_smb2_get_reparse_point_fnum_state);
- struct cli_state *cli = state->cli;
+ struct cli_smb2_fsctl_state *state = tevent_req_data(
+ req, struct cli_smb2_fsctl_state);
NTSTATUS status;
- status = smb2cli_ioctl_recv(subreq, state,
- NULL,
- &state->output_buffer);
- TALLOC_FREE(subreq);
- if (tevent_req_nterror(req, status)) {
- cli->raw_status = status;
- return;
- }
- tevent_req_done(req);
+ status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
+ tevent_req_simple_finish_ntstatus(subreq, status);
}
-NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req,
- TALLOC_CTX *mem_ctx,
- DATA_BLOB *output)
+NTSTATUS cli_smb2_fsctl_recv(
+ struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
{
- struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
- req, struct cli_smb2_get_reparse_point_fnum_state);
+ struct cli_smb2_fsctl_state *state = tevent_req_data(
+ req, struct cli_smb2_fsctl_state);
+ NTSTATUS status = NT_STATUS_OK;
- if (tevent_req_is_nterror(req, &state->cli->raw_status)) {
- NTSTATUS status = state->cli->raw_status;
+ if (tevent_req_is_nterror(req, &status)) {
tevent_req_received(req);
return status;
}
- *output = data_blob_dup_talloc(mem_ctx, state->output_buffer);
- if (output->data == NULL) {
- tevent_req_received(req);
- return NT_STATUS_NO_MEMORY;
+
+ if (state->out.length == 0) {
+ *out = (DATA_BLOB) { .data = NULL, };
+ } else {
+ /*
+ * Can't use talloc_move() here, the outblobs from
+ * smb2cli_ioctl_recv() are not standalone talloc
+ * objects but just peek into the larger buffers
+ * received, hanging off "state".
+ */
+ *out = data_blob_talloc(
+ mem_ctx, state->out.data, state->out.length);
+ if (out->data == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ }
}
+
tevent_req_received(req);
return NT_STATUS_OK;
}