#include "librpc/gen_ndr/ndr_ioctl.h"
#include "ntioctl.h"
#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_smb3posix.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/util/idtree.h"
struct smb2_hnd {
uint64_t fid_persistent;
Oplock mapping code.
***************************************************************/
-static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
+static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
{
- if (create_flags & REQUEST_BATCH_OPLOCK) {
+ if (create_flags.batch_oplock) {
return SMB2_OPLOCK_LEVEL_BATCH;
- } else if (create_flags & REQUEST_OPLOCK) {
+ } else if (create_flags.exclusive_oplock) {
return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
}
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,
- uint32_t create_flags,
+ const char *fname_in,
+ struct cli_smb2_create_flags create_flags,
uint32_t impersonation_level,
uint32_t desired_access,
uint32_t file_attributes,
{
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);
}
create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
}
- /* 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 (cli->smb2.client_smb311_posix) {
+ uint8_t modebuf[4] = {
+ 0,
+ };
- if (tevent_req_nomem(new_fname, req)) {
+ status =
+ smb2_create_blob_add(state,
+ &state->in_cblobs,
+ SMB2_CREATE_TAG_POSIX,
+ (DATA_BLOB){
+ .data = modebuf,
+ .length = sizeof(modebuf),
+ });
+ if (tevent_req_nterror(req, status)) {
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);
-
+ /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
+ 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;
}
NTSTATUS cli_smb2_create_fnum(
struct cli_state *cli,
const char *fname,
- uint32_t create_flags,
+ struct cli_smb2_create_flags create_flags,
uint32_t impersonation_level,
uint32_t desired_access,
uint32_t file_attributes,
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;
struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
- uint16_t fnum)
+ uint16_t fnum,
+ uint16_t flags)
{
struct tevent_req *req, *subreq;
struct cli_smb2_close_fnum_state *state;
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);
}
- subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
- cli->smb2.session, cli->smb2.tcon,
- 0, state->ph->fid_persistent,
+ subreq = smb2cli_close_send(state,
+ ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ flags,
+ state->ph->fid_persistent,
state->ph->fid_volatile);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
if (ev == NULL) {
goto fail;
}
- req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
+ req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
if (req == NULL) {
goto fail;
}
}
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).
ev, /* ev */
cli, /* cli */
dname, /* fname */
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
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;
}
- subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
if (tevent_req_nomem(subreq, req)) {
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,
state->cli,
state->dname,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
DELETE_ACCESS, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
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)) {
state->ev,
state->cli,
state->dname,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
DELETE_ACCESS, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
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;
* Close the fd even if the set_disp failed
*/
- subreq = cli_smb2_close_fnum_send(
- state, state->ev, state->cli, state->fnum);
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
if (tevent_req_nomem(subreq, req)) {
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 */
state->cli, /* cli_struct */
state->fname, /* filename */
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
DELETE_ACCESS, /* desired_access */
FILE_ATTRIBUTE_NORMAL, /* file attributes */
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
state->ev, /* tevent_context */
state->cli, /* cli_struct */
state->fname, /* filename */
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
DELETE_ACCESS, /* desired_access */
FILE_ATTRIBUTE_NORMAL, /* file attributes */
return;
}
- subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
if (tevent_req_nomem(subreq, req)) {
return;
}
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;
}
- subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
+ subreq =
+ cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
if (tevent_req_nomem(subreq, req)) {
return;
}
return tevent_req_simple_recv_ntstatus(req);
}
+/***************************************************************
+ 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)
+{
+ struct smb3_file_posix_information info = {};
+ size_t consumed;
+ enum ndr_err_code ndr_err;
+ size_t namelen = 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;
+ }
+
+ /*
+ * Skip NextEntryOffset and FileIndex
+ */
+ if (dir_data_length < 8) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ dir_data += 8;
+ dir_data_length -= 8;
+
+ ndr_err = ndr_pull_struct_blob_noalloc(
+ dir_data,
+ dir_data_length,
+ &info,
+ (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
+ &consumed);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+ if (consumed > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ dir_data += consumed;
+ dir_data_length -= consumed;
+
+ finfo->btime_ts = interpret_long_date(info.creation_time);
+ finfo->atime_ts = interpret_long_date(info.last_access_time);
+ finfo->mtime_ts = interpret_long_date(info.last_write_time);
+ finfo->ctime_ts = interpret_long_date(info.change_time);
+ finfo->allocated_size = info.allocation_size;
+ finfo->size = info.end_of_file;
+ finfo->attr = info.file_attributes;
+ finfo->ino = info.inode;
+ finfo->st_ex_dev = info.device;
+ finfo->st_ex_nlink = info.cc.nlinks;
+ finfo->reparse_tag = info.cc.reparse_tag;
+ finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
+ sid_copy(&finfo->owner_sid, &info.cc.owner);
+ sid_copy(&finfo->group_sid, &info.cc.group);
+
+ if (dir_data_length < 4) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ namelen = PULL_LE_U32(dir_data, 0);
+
+ dir_data += 4;
+ dir_data_length -= 4;
+
+ if (namelen > dir_data_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ ret = pull_string_talloc(finfo,
+ dir_data,
+ FLAGS2_UNICODE_STRINGS,
+ &finfo->name,
+ dir_data,
+ 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 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->btime_ts = interpret_long_date(BVAL(dir_data, 8));
+ finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
+ finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
+ finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
finfo->attr = IVAL(dir_data + 56, 0);
if (namelen > (dir_data_length - 104)) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
}
+ finfo->reparse_tag = IVAL(dir_data + 64, 0);
slen = CVAL(dir_data + 68, 0);
if (slen > 24) {
return NT_STATUS_INFO_LENGTH_MISMATCH;
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)(const char *,
- struct file_info *,
- const char *,
- void *),
- void *state)
-{
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)
+{
+ 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) &&
+ info_level == SMB2_FIND_POSIX_INFORMATION)
+ {
+ 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 (tevent_req_nterror(req, 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 */
+ (struct cli_smb2_create_flags){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.
- * On SMB1 server does this, so on
- * SMB2 we need to emulate in the
- * client.
- *
- * https://bugzilla.samba.org/show_bug.cgi?id=10260
- */
- processed_file = true;
-
- status = fn(cli->dfs_mountpoint,
- finfo,
- pathname,
- state);
-
- if (!NT_STATUS_IS_OK(status)) {
- break;
- }
- }
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
+}
- TALLOC_FREE(finfo);
+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);
+}
- /* 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;
+/*
+ * 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 directory 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;
+
+ 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;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
}
- } while (NT_STATUS_IS_OK(status));
+ 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);
+ }
- if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
- status = NT_STATUS_OK;
+ 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] == '\\') {
status = cli_smb2_create_fnum(cli,
name,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
/* Maybe a file ? */
status = cli_smb2_create_fnum(cli,
name,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
0, /* file attributes */
NULL);
}
+ if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+ /* Maybe a reparse point ? */
+ status = cli_smb2_create_fnum(cli,
+ name,
+ (struct cli_smb2_create_flags){0},
+ 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;
};
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)
+struct get_fnum_from_path_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *name;
+ uint32_t desired_access;
+ uint16_t fnum;
+};
+
+static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
+static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
+static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
+
+static struct tevent_req *get_fnum_from_path_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *name,
+ uint32_t desired_access)
{
- NTSTATUS status;
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct get_fnum_from_path_state *state = NULL;
size_t namelen = strlen(name);
- TALLOC_CTX *frame = talloc_stackframe();
- uint32_t create_options = 0;
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- end in a '\' */
+ req = tevent_req_create(
+ mem_ctx, &state, struct get_fnum_from_path_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->cli = cli;
+ state->name = name;
+ state->desired_access = desired_access;
+
+ /*
+ * 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;
+ state->name = talloc_strndup(state, name, namelen-1);
+ if (tevent_req_nomem(state->name, req)) {
+ return tevent_req_post(req, ev);
}
- modname[namelen-1] = '\0';
- name = modname;
}
- /* Try to open a file handle first. */
- status = cli_smb2_create_fnum(cli,
- name,
- 0, /* create_flags */
- SMB2_IMPERSONATION_IMPERSONATION,
- desired_access,
- 0, /* file attributes */
- FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
- FILE_OPEN, /* create_disposition */
- create_options,
- NULL,
- pfnum,
- NULL,
- NULL,
- NULL);
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ ev, /* ev */
+ cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
+ desired_access, /* desired_access */
+ 0, /* file_attributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE|
+ FILE_SHARE_DELETE, /* share_access */
+ FILE_OPEN, /* create_disposition */
+ 0, /* create_options */
+ NULL); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
+ return req;
+}
- if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct get_fnum_from_path_state *state = tevent_req_data(
+ req, struct get_fnum_from_path_state);
+ NTSTATUS status;
+
+ status = cli_smb2_create_fnum_recv(
+ subreq, &state->fnum, NULL, NULL, NULL, NULL);
+ TALLOC_FREE(subreq);
+
+ 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
* component and try again. Eventually we will have to
* deal with the returned path unprocessed component. JRA.
*/
- create_options |= FILE_OPEN_REPARSE_POINT;
- status = cli_smb2_create_fnum(cli,
- name,
- 0, /* create_flags */
- SMB2_IMPERSONATION_IMPERSONATION,
- desired_access,
- 0, /* file attributes */
- FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
- FILE_OPEN, /* create_disposition */
- create_options,
- NULL,
- pfnum,
- NULL,
- NULL,
- NULL);
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ state->ev, /* ev */
+ state->cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
+ state->desired_access, /* 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); /* in_cblobs */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(
+ subreq, get_fnum_from_path_opened_reparse, req);
+ return;
}
if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
- create_options |= FILE_DIRECTORY_FILE;
- status = cli_smb2_create_fnum(cli,
- name,
- 0, /* create_flags */
- SMB2_IMPERSONATION_IMPERSONATION,
- desired_access,
- FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
- FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
- FILE_OPEN, /* create_disposition */
- create_options, /* create_options */
- NULL,
- pfnum,
- NULL,
- NULL,
- NULL);
+ subreq = cli_smb2_create_fnum_send(
+ state, /* mem_ctx, */
+ state->ev, /* ev */
+ state->cli, /* cli */
+ state->name, /* fname */
+ (struct cli_smb2_create_flags){0}, /* create_flags */
+ SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
+ state->desired_access, /* desired_access */
+ 0, /* 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_set_callback(
+ subreq, get_fnum_from_path_opened_dir, req);
+ return;
}
- fail:
-
- TALLOC_FREE(frame);
- return status;
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
}
-/***************************************************************
- 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)
+static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
{
- NTSTATUS status;
- DATA_BLOB outbuf = data_blob_null;
- uint16_t fnum = 0xffff;
- 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 = cli_smb2_query_info_fnum(
- cli,
- fnum,
- 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 */
- 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';
- }
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ 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, NULL);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
- status = NT_STATUS_OK;
+static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
+{
+ /* Abstraction violation, but these two are just the same... */
+ get_fnum_from_path_opened_reparse(subreq);
+}
- fail:
+static NTSTATUS get_fnum_from_path_recv(
+ struct tevent_req *req, uint16_t *pfnum)
+{
+ struct get_fnum_from_path_state *state = tevent_req_data(
+ req, struct get_fnum_from_path_state);
+ NTSTATUS status = NT_STATUS_OK;
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
+ if (!tevent_req_is_nterror(req, &status)) {
+ *pfnum = state->fnum;
}
-
- cli->raw_status = status;
-
- TALLOC_FREE(frame);
+ tevent_req_received(req);
return status;
}
-/***************************************************************
- Wrapper that allows SMB2 to get pathname attributes.
- Synchronous only.
-***************************************************************/
-
-NTSTATUS cli_smb2_getatr(struct cli_state *cli,
- const char *name,
- uint32_t *pattr,
- off_t *size,
- time_t *write_time)
+static NTSTATUS get_fnum_from_path(struct cli_state *cli,
+ const char *name,
+ uint32_t desired_access,
+ uint16_t *pfnum)
{
- NTSTATUS status;
- uint16_t fnum = 0xffff;
- struct smb2_hnd *ph = NULL;
- struct timespec write_time_ts;
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)) {
- /*
- * 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)) {
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
goto fail;
}
-
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
+ req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
+ if (req == NULL) {
goto fail;
}
- status = cli_qfileinfo_basic(
- cli,
- fnum,
- pattr,
- size,
- NULL, /* create_time */
- NULL, /* access_time */
- &write_time_ts,
- NULL, /* change_time */
- NULL); /* ino */
- if (!NT_STATUS_IS_OK(status)) {
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
goto fail;
}
- if (write_time != NULL) {
- *write_time = write_time_ts.tv_sec;
- }
-
- fail:
-
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
- }
-
- cli->raw_status = status;
-
+ status = get_fnum_from_path_recv(req, pfnum);
+ fail:
TALLOC_FREE(frame);
return status;
}
-/***************************************************************
- Wrapper that allows SMB2 to query a pathname info (basic level).
- Implement on top of cli_qfileinfo_basic().
- Synchronous only.
-***************************************************************/
+struct cli_smb2_qpathinfo_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname;
+ uint16_t fnum;
+ uint16_t level;
+ uint32_t min_rdata;
+ uint32_t max_rdata;
-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,
- uint32_t *pattr,
- SMB_INO_T *ino)
-{
NTSTATUS status;
- struct smb2_hnd *ph = NULL;
- uint16_t fnum = 0xffff;
- 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);
+ DATA_BLOB out;
+};
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
- }
+static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
+static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
+static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint16_t level,
+ uint32_t min_rdata,
+ uint32_t max_rdata)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_qpathinfo_state *state = NULL;
- status = map_fnum_to_smb2_handle(cli,
- fnum,
- &ph);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct cli_smb2_qpathinfo_state);
+ if (req == NULL) {
+ return NULL;
}
-
- status = cli_qfileinfo_basic(
- cli,
- fnum,
- pattr,
- size,
- create_time,
- access_time,
- write_time,
- change_time,
- ino);
-
- fail:
-
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
+ state->ev = ev;
+ state->cli = cli;
+ state->level = level;
+ state->min_rdata = min_rdata;
+ state->max_rdata = max_rdata;
+
+ subreq = get_fnum_from_path_send(state,
+ ev,
+ cli,
+ fname,
+ FILE_READ_ATTRIBUTES);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
}
-
- cli->raw_status = status;
-
- TALLOC_FREE(frame);
- return status;
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
+ return req;
}
-/***************************************************************
- 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)
+static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
NTSTATUS status;
- uint16_t fnum = 0xffff;
- 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_recv(subreq, &state->fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- status = get_fnum_from_path(cli,
- name,
- FILE_READ_ATTRIBUTES,
- &fnum);
-
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ subreq = cli_smb2_query_info_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 1, /* in_info_type */
+ state->level,
+ state->max_rdata,
+ NULL, /* in_input_buffer */
+ 0, /* in_additional_info */
+ 0); /* in_flags */
+ if (tevent_req_nomem(subreq, req)) {
+ return;
}
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
+}
- /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
- level 22 (SMB2_FILE_STREAM_INFORMATION). */
+static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
- status = cli_smb2_query_info_fnum(
- cli,
- fnum,
- 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 */
- frame,
- &outbuf);
+ state->status =
+ cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
+ TALLOC_FREE(subreq);
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ if (NT_STATUS_IS_OK(state->status) &&
+ (state->out.length < state->min_rdata)) {
+ state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
}
- /* Parse the reply. */
- if (!parse_streams_blob(mem_ctx,
- outbuf.data,
- outbuf.length,
- pnum_streams,
- pstreams)) {
- status = NT_STATUS_INVALID_NETWORK_RESPONSE;
- goto fail;
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
}
+ tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
+}
- fail:
+static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+ NTSTATUS status;
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
+ status = cli_smb2_close_fnum_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
+ if (tevent_req_nterror(req, state->status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **rdata,
+ uint32_t *num_rdata)
+{
+ struct cli_smb2_qpathinfo_state *state =
+ tevent_req_data(req, struct cli_smb2_qpathinfo_state);
+ NTSTATUS status;
- cli->raw_status = status;
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
- TALLOC_FREE(frame);
- return status;
+ *rdata = talloc_move(mem_ctx, &state->out.data);
+ *num_rdata = state->out.length;
+ tevent_req_received(req);
+ return NT_STATUS_OK;
}
/***************************************************************
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,
{
uint8_t inbuf_store[40];
DATA_BLOB inbuf = data_blob_null;
+ NTSTATUS status;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
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). */
put_long_date((char *)inbuf.data + 16, write_time);
}
- cli->raw_status = cli_smb2_set_info_fnum(
- cli,
- fnum,
- 1, /* in_info_type */
- SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
- &inbuf, /* in_input_buffer */
- 0); /* in_additional_info */
-
- return cli->raw_status;
+ status = cli_smb2_set_info_fnum(cli,
+ fnum,
+ 1, /* in_info_type */
+ SMB_FILE_BASIC_INFORMATION -
+ 1000, /* in_file_info_class */
+ &inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+ cli->raw_status = status;
+ return status;
}
/***************************************************************
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,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
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 */
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
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 */
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
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 */
+ cli_smb2_create_fnum(cli, "",
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES, /* desired_access */
FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
if (pdate) {
struct timespec ts;
- ts = interpret_long_date((char *)outbuf.data);
+ ts = interpret_long_date(BVAL(outbuf.data, 0));
*pdate = ts.tv_sec;
}
if (pserial_number) {
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 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;
- }
-
- /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
-
- status = cli_smb2_query_info_fnum(
- cli,
- fnum,
- 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 */
- 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:
-
- cli->raw_status = status;
-
- 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;
- 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 = 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 = cli_smb2_set_info_fnum(
- cli,
- fnum,
- 3, /* in_info_type */
- 0, /* in_file_info_class */
- &inbuf, /* in_input_buffer */
- sec_info); /* in_additional_info */
-
- fail:
-
- cli->raw_status = status;
-
- TALLOC_FREE(frame);
- return status;
-}
-
-/***************************************************************
- Wrapper that allows SMB2 to query a security descriptor.
- Synchronous only.
-
-***************************************************************/
-
struct cli_smb2_mxac_state {
struct tevent_context *ev;
struct cli_state *cli;
.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,
state->ev,
state->cli,
state->fname,
- 0, /* create_flags */
+ (struct cli_smb2_create_flags){0},
SMB2_IMPERSONATION_IMPERSONATION,
FILE_READ_ATTRIBUTES,
0, /* file attributes */
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)) {
state->mxac = IVAL(mxac_blob->data.data, 4);
close:
- subreq = cli_smb2_close_fnum_send(
- state, state->ev, state->cli, state->fnum);
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
if (tevent_req_nomem(subreq, req)) {
return;
}
return status;
}
-/***************************************************************
- Wrapper that allows SMB2 to rename a file.
- Synchronous only.
-***************************************************************/
+struct cli_smb2_rename_fnum_state {
+ DATA_BLOB inbuf;
+};
+
+static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
-NTSTATUS cli_smb2_rename(struct cli_state *cli,
- const char *fname_src,
- const char *fname_dst,
- bool replace)
+static struct tevent_req *cli_smb2_rename_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ const char *fname_dst,
+ bool replace)
{
- NTSTATUS status;
- DATA_BLOB inbuf = data_blob_null;
- uint16_t fnum = 0xffff;
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_rename_fnum_state *state = NULL;
+ size_t namelen = strlen(fname_dst);
smb_ucs2_t *converted_str = NULL;
size_t converted_size_bytes = 0;
- size_t namelen = 0;
- TALLOC_CTX *frame = talloc_stackframe();
+ size_t inbuf_size;
+ bool ok;
- 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_rename_fnum_state);
+ if (req == NULL) {
+ return NULL;
}
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+ /*
+ * SMB2 is pickier about pathnames. Ensure it doesn't start in
+ * a '\'
+ */
+ if (*fname_dst == '\\') {
+ fname_dst++;
}
- status = get_fnum_from_path(cli,
- fname_src,
- DELETE_ACCESS,
- &fnum);
+ /*
+ * SMB2 is pickier about pathnames. Ensure it doesn't end in a
+ * '\'
+ */
+ if (namelen > 0 && fname_dst[namelen-1] == '\\') {
+ fname_dst = talloc_strndup(state, fname_dst, namelen-1);
+ if (tevent_req_nomem(fname_dst, req)) {
+ return tevent_req_post(req, ev);
+ }
+ }
+
+ ok = push_ucs2_talloc(
+ state, &converted_str, fname_dst, &converted_size_bytes);
+ if (!ok) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * 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) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ converted_size_bytes -= 2;
+
+ inbuf_size = 20 + converted_size_bytes;
+ if (inbuf_size < 20) {
+ /* Integer wrap check. */
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * The Windows 10 SMB2 server has a minimum length
+ * for a SMB2_FILE_RENAME_INFORMATION buffer of
+ * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
+ * if the length is less. This isn't an alignment
+ * issue as Windows client accepts happily 2-byte align
+ * for larger target name sizes. Also the Windows 10
+ * SMB1 server doesn't have this restriction.
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
+ */
+ inbuf_size = MAX(inbuf_size, 24);
+
+ state->inbuf = data_blob_talloc_zero(state, inbuf_size);
+ if (tevent_req_nomem(state->inbuf.data, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ if (replace) {
+ SCVAL(state->inbuf.data, 0, 1);
+ }
+
+ SIVAL(state->inbuf.data, 16, converted_size_bytes);
+ memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
+
+ TALLOC_FREE(converted_str);
+
+ /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
+ level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+
+ subreq = cli_smb2_set_info_fnum_send(
+ state, /* mem_ctx */
+ ev, /* ev */
+ cli, /* cli */
+ fnum, /* fnum */
+ 1, /* in_info_type */
+ SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
+ &state->inbuf, /* in_input_buffer */
+ 0); /* in_additional_info */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
+ return req;
+}
+
+static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to rename a file.
+***************************************************************/
+
+struct cli_smb2_rename_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ const char *fname_dst;
+ bool replace;
+ uint16_t fnum;
+
+ NTSTATUS rename_status;
+};
+
+static void cli_smb2_rename_opened(struct tevent_req *subreq);
+static void cli_smb2_rename_renamed(struct tevent_req *subreq);
+static void cli_smb2_rename_closed(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_rename_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname_src,
+ const char *fname_dst,
+ bool replace)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct cli_smb2_rename_state *state = NULL;
+ NTSTATUS status;
- if (!NT_STATUS_IS_OK(status)) {
- goto fail;
+ req = tevent_req_create(
+ mem_ctx, &state, struct cli_smb2_rename_state);
+ if (req == NULL) {
+ return NULL;
}
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- start in a '\' */
- if (*fname_dst == '\\') {
- fname_dst++;
+ /*
+ * 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);
}
- /* 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;
- }
+ state->ev = ev;
+ state->cli = cli;
+ state->fname_dst = fname_dst;
+ state->replace = replace;
- if (!push_ucs2_talloc(frame,
- &converted_str,
- fname_dst,
- &converted_size_bytes)) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+ subreq = get_fnum_from_path_send(
+ state, ev, cli, fname_src, DELETE_ACCESS);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
}
+ tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
+ return req;
+}
- /* W2K8 insists the dest name is not null
- terminated. Remove the last 2 zero bytes
- and reduce the name length. */
+static void cli_smb2_rename_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
+ NTSTATUS status;
- if (converted_size_bytes < 2) {
- status = NT_STATUS_INVALID_PARAMETER;
- goto fail;
+ status = get_fnum_from_path_recv(subreq, &state->fnum);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- 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;
+ subreq = cli_smb2_rename_fnum_send(
+ state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ state->fname_dst,
+ state->replace);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
}
+ tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
+}
- if (replace) {
- SCVAL(inbuf.data, 0, 1);
- }
+static void cli_smb2_rename_renamed(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
- SIVAL(inbuf.data, 16, converted_size_bytes);
- memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
+ state->rename_status = cli_smb2_rename_fnum_recv(subreq);
+ TALLOC_FREE(subreq);
- /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
- level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+ subreq = cli_smb2_close_fnum_send(state,
+ state->ev,
+ state->cli,
+ state->fnum,
+ 0);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
+}
- status = cli_smb2_set_info_fnum(
- cli,
- fnum,
- 1, /* in_info_type */
- SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
- &inbuf, /* in_input_buffer */
- 0); /* in_additional_info */
+static void cli_smb2_rename_closed(struct tevent_req *subreq)
+{
+ NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
+ tevent_req_simple_finish_ntstatus(subreq, status);
+}
- fail:
+NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
+{
+ struct cli_smb2_rename_state *state = tevent_req_data(
+ req, struct cli_smb2_rename_state);
+ NTSTATUS status = NT_STATUS_OK;
- if (fnum != 0xffff) {
- cli_smb2_close_fnum(cli, fnum);
+ if (!tevent_req_is_nterror(req, &status)) {
+ status = state->rename_status;
}
-
- cli->raw_status = status;
-
- TALLOC_FREE(frame);
+ tevent_req_received(req);
return status;
}
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;
}