#include "client.h"
#include "async_smb.h"
#include "../libcli/smb/smbXcli_base.h"
-#include "smb2cli.h"
#include "cli_smb2_fnum.h"
#include "trans2.h"
#include "clirap.h"
#include "lib/util/tevent_ntstatus.h"
#include "../libcli/security/security.h"
#include "lib/util_ea.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
struct smb2_hnd {
uint64_t fid_persistent;
/***************************************************************
Small wrapper that allows SMB2 create to return a uint16_t fnum.
- Synchronous only.
***************************************************************/
+struct cli_smb2_create_fnum_state {
+ struct cli_state *cli;
+ struct smb_create_returns cr;
+ uint16_t fnum;
+ struct tevent_req *subreq;
+};
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ uint32_t create_flags,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_create_fnum_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_create_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ 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);
+ }
+
+ if (cli->backup_intent) {
+ create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ }
+
+ /* SMB2 is pickier about pathnames. Ensure it doesn't
+ start in a '\' */
+ if (*fname == '\\') {
+ fname++;
+ }
+
+ subreq = smb2cli_create_send(state, ev,
+ cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ fname,
+ flags_to_smb2_oplock(create_flags),
+ SMB2_IMPERSONATION_IMPERSONATION,
+ desired_access,
+ file_attributes,
+ share_access,
+ create_disposition,
+ create_options,
+ NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
+
+ state->subreq = subreq;
+ tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
+
+ return req;
+}
+
+static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ struct smb2_hnd h;
+ NTSTATUS status;
+
+ status = smb2cli_create_recv(subreq, &h.fid_persistent,
+ &h.fid_volatile, &state->cr, NULL, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
+{
+ struct cli_smb2_create_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_create_fnum_state);
+ return tevent_req_cancel(state->subreq);
+}
+
+NTSTATUS cli_smb2_create_fnum_recv(struct tevent_req *req, uint16_t *pfnum,
+ struct smb_create_returns *cr)
+{
+ 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)) {
+ return status;
+ }
+ if (pfnum != NULL) {
+ *pfnum = state->fnum;
+ }
+ if (cr != NULL) {
+ *cr = state->cr;
+ }
+ return NT_STATUS_OK;
+}
+
NTSTATUS cli_smb2_create_fnum(struct cli_state *cli,
const char *fname,
uint32_t create_flags,
uint16_t *pfid,
struct smb_create_returns *cr)
{
- NTSTATUS status;
- struct smb2_hnd h;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ 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
*/
- return NT_STATUS_INVALID_PARAMETER;
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_create_fnum_send(frame, ev, cli, fname, create_flags,
+ desired_access, file_attributes,
+ share_access, create_disposition,
+ create_options);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_create_fnum_recv(req, pfid, cr);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/***************************************************************
+ Small wrapper that allows SMB2 close to use a uint16_t fnum.
+***************************************************************/
+
+struct cli_smb2_close_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+};
+
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_close_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_close_fnum_state);
+ if (req == NULL) {
+ return NULL;
}
+ state->cli = cli;
+ state->fnum = fnum;
if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- return NT_STATUS_INVALID_PARAMETER;
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
}
- if (cli->backup_intent) {
- create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
}
- /* SMB2 is pickier about pathnames. Ensure it doesn't
- start in a '\' */
- if (*fname == '\\') {
- fname++;
+ subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
+ cli->smb2.session, cli->smb2.tcon,
+ 0, state->ph->fid_persistent,
+ state->ph->fid_volatile);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
}
+ tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
+ return req;
+}
- status = smb2cli_create(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
- fname,
- flags_to_smb2_oplock(create_flags),
- SMB2_IMPERSONATION_IMPERSONATION,
- desired_access,
- file_attributes,
- share_access,
- create_disposition,
- create_options,
- NULL,
- &h.fid_persistent,
- &h.fid_volatile,
- cr);
+static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_close_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_close_fnum_state);
+ NTSTATUS status;
- if (NT_STATUS_IS_OK(status)) {
- status = map_smb2_handle_to_fnum(cli, &h, pfid);
+ status = smb2cli_close_recv(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
}
- return status;
+ /* Delete the fnum -> handle mapping. */
+ status = delete_smb2_handle_mapping(state->cli, &state->ph,
+ state->fnum);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
}
-/***************************************************************
- Small wrapper that allows SMB2 close to use a uint16_t fnum.
- Synchronous only.
-***************************************************************/
+NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
{
- struct smb2_hnd *ph = NULL;
- NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ 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
*/
- return NT_STATUS_INVALID_PARAMETER;
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
}
-
- if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
- return NT_STATUS_INVALID_PARAMETER;
+ 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)) {
- return status;
+ req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
+ if (req == NULL) {
+ goto fail;
}
-
- status = smb2cli_close(cli->conn,
- cli->timeout,
- cli->smb2.session,
- cli->smb2.tcon,
- 0,
- ph->fid_persistent,
- ph->fid_volatile);
-
- /* Delete the fnum -> handle mapping. */
- if (NT_STATUS_IS_OK(status)) {
- status = delete_smb2_handle_mapping(cli, &ph, fnum);
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
}
-
+ status = cli_smb2_close_fnum_recv(req);
+ fail:
+ TALLOC_FREE(frame);
return status;
}
bool processed_file = false;
TALLOC_CTX *frame = talloc_stackframe();
TALLOC_CTX *subframe = NULL;
+ bool mask_has_wild;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
goto fail;
}
+ mask_has_wild = ms_has_wild(mask);
+
status = cli_smb2_create_fnum(cli,
parent_dir,
0, /* create_flags */
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;
+ }
+
} while (NT_STATUS_IS_OK(status));
if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
ZERO_STRUCTP(sbuf);
- sbuf->st_ex_atime = nt_time_to_unix_timespec(&cr.last_access_time);
- sbuf->st_ex_mtime = nt_time_to_unix_timespec(&cr.last_write_time);
- sbuf->st_ex_ctime = nt_time_to_unix_timespec(&cr.change_time);
+ sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
+ sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
+ sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
sbuf->st_ex_size = cr.end_of_file;
*attributes = cr.file_attributes;
}
ea_count = 0;
for (eal = ea_list; eal; eal = eal->next) {
- (*pea_array)[ea_count++] = ea_list->ea;
+ (*pea_array)[ea_count++] = eal->ea;
}
*pnum_eas = ea_count;
}
}
return NT_STATUS_OK;
}
+
+struct cli_smb2_splice_state {
+ struct tevent_context *ev;
+ struct cli_state *cli;
+ struct smb2_hnd *src_ph;
+ struct smb2_hnd *dst_ph;
+ int (*splice_cb)(off_t n, void *priv);
+ void *priv;
+ off_t written;
+ off_t size;
+ off_t src_offset;
+ off_t dst_offset;
+ bool resized;
+ struct req_resume_key_rsp resume_rsp;
+ struct srv_copychunk_copy cc_copy;
+};
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req);
+
+static void cli_splice_copychunk_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ struct smbXcli_conn *conn = state->cli->conn;
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+ struct srv_copychunk_rsp cc_copy_rsp;
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
+ state->resized) && tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
+ cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
+ if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
+ max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
+ tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ state->resized = true;
+ smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
+ smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
+ } else {
+ if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+ (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ state->src_offset += cc_copy_rsp.total_bytes_written;
+ state->dst_offset += cc_copy_rsp.total_bytes_written;
+ state->written += cc_copy_rsp.total_bytes_written;
+ if (!state->splice_cb(state->written, state->priv)) {
+ tevent_req_nterror(req, NT_STATUS_CANCELLED);
+ return;
+ }
+ }
+
+ cli_splice_copychunk_send(state, req);
+}
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+ struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ enum ndr_err_code ndr_ret;
+ struct smbXcli_conn *conn = state->cli->conn;
+ struct srv_copychunk_copy *cc_copy = &state->cc_copy;
+ off_t src_offset = state->src_offset;
+ off_t dst_offset = state->dst_offset;
+ uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
+ state->size - state->written);
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ if (state->size - state->written == 0) {
+ tevent_req_done(req);
+ return;
+ }
+
+ cc_copy->chunk_count = 0;
+ while (req_len) {
+ cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
+ cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
+ cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
+ smb2cli_conn_cc_chunk_len(conn));
+ if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+ req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
+ if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
+ (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
+ tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+ return;
+ }
+ src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+ cc_copy->chunk_count++;
+ }
+
+ ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
+ (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to marshall copy chunk req\n"));
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->dst_ph->fid_persistent, /* in_fid_persistent */
+ state->dst_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_COPYCHUNK_WRITE,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 12, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_copychunk_done,
+ req);
+}
+
+static void cli_splice_key_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_splice_state *state =
+ tevent_req_data(req,
+ struct cli_smb2_splice_state);
+ enum ndr_err_code ndr_ret;
+ NTSTATUS status;
+
+ DATA_BLOB out_input_buffer = data_blob_null;
+ DATA_BLOB out_output_buffer = data_blob_null;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &out_input_buffer,
+ &out_output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
+ state, &state->resume_rsp,
+ (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
+ if (ndr_ret != NDR_ERR_SUCCESS) {
+ DEBUG(0, ("failed to unmarshall resume key rsp\n"));
+ tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+ return;
+ }
+
+ memcpy(&state->cc_copy.source_key,
+ &state->resume_rsp.resume_key,
+ sizeof state->resume_rsp.resume_key);
+
+ cli_splice_copychunk_send(state, req);
+}
+
+struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t src_fnum, uint16_t dst_fnum,
+ off_t size, off_t src_offset, off_t dst_offset,
+ int (*splice_cb)(off_t n, void *priv),
+ void *priv)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct cli_smb2_splice_state *state;
+ NTSTATUS status;
+ DATA_BLOB in_input_buffer = data_blob_null;
+ DATA_BLOB in_output_buffer = data_blob_null;
+
+ req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->cli = cli;
+ state->ev = ev;
+ state->splice_cb = splice_cb;
+ state->priv = priv;
+ state->size = size;
+ state->written = 0;
+ state->src_offset = src_offset;
+ state->dst_offset = dst_offset;
+ state->cc_copy.chunks = talloc_array(state,
+ struct srv_copychunk,
+ smb2cli_conn_cc_max_chunks(cli->conn));
+ if (state->cc_copy.chunks == NULL) {
+ return NULL;
+ }
+
+ status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
+ if (tevent_req_nterror(req, status))
+ return tevent_req_post(req, ev);
+
+ subreq = smb2cli_ioctl_send(state, ev, cli->conn,
+ cli->timeout,
+ cli->smb2.session,
+ cli->smb2.tcon,
+ state->src_ph->fid_persistent, /* in_fid_persistent */
+ state->src_ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_SRV_REQUEST_RESUME_KEY,
+ 0, /* in_max_input_length */
+ &in_input_buffer,
+ 32, /* in_max_output_length */
+ &in_output_buffer,
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+ if (tevent_req_nomem(subreq, req)) {
+ return NULL;
+ }
+ tevent_req_set_callback(subreq,
+ cli_splice_key_done,
+ req);
+
+ return req;
+}
+
+NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
+{
+ struct cli_smb2_splice_state *state = tevent_req_data(
+ req, struct cli_smb2_splice_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ tevent_req_received(req);
+ return status;
+ }
+ if (written != NULL) {
+ *written = state->written;
+ }
+ tevent_req_received(req);
+ return NT_STATUS_OK;
+}