mirror of
https://github.com/samba-team/samba.git
synced 2025-06-19 23:17:05 +03:00
s3: libsmbclient: Add server-side copy support
Introduce a new operation, splice, which copies data from one SMBCFILE to another. Implement this operation using FSCTL_SRV_COPYCHUNK_WRITE for SMB2+ protocols and using read+write for older protocols. Since the operation may be long running, it takes a callback which gets called periodically to indicate progress to the application and given an opportunity to stop it. Signed-off-by: Ross Lagerwall <rosslagerwall@gmail.com> Reviewed-by: David Disseldorp <ddiss@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
This commit is contained in:
parent
2ffa939bbe
commit
f73bcf4934
@ -23,6 +23,7 @@
|
|||||||
#include "smb_common.h"
|
#include "smb_common.h"
|
||||||
#include "smbXcli_base.h"
|
#include "smbXcli_base.h"
|
||||||
#include "librpc/ndr/libndr.h"
|
#include "librpc/ndr/libndr.h"
|
||||||
|
#include "librpc/gen_ndr/ioctl.h"
|
||||||
|
|
||||||
struct smb2cli_ioctl_state {
|
struct smb2cli_ioctl_state {
|
||||||
uint8_t fixed[0x38];
|
uint8_t fixed[0x38];
|
||||||
@ -32,6 +33,7 @@ struct smb2cli_ioctl_state {
|
|||||||
struct iovec *recv_iov;
|
struct iovec *recv_iov;
|
||||||
DATA_BLOB out_input_buffer;
|
DATA_BLOB out_input_buffer;
|
||||||
DATA_BLOB out_output_buffer;
|
DATA_BLOB out_output_buffer;
|
||||||
|
uint32_t ctl_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void smb2cli_ioctl_done(struct tevent_req *subreq);
|
static void smb2cli_ioctl_done(struct tevent_req *subreq);
|
||||||
@ -69,6 +71,7 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx,
|
|||||||
if (req == NULL) {
|
if (req == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
state->ctl_code = in_ctl_code;
|
||||||
state->max_input_length = in_max_input_length;
|
state->max_input_length = in_max_input_length;
|
||||||
state->max_output_length = in_max_output_length;
|
state->max_output_length = in_max_output_length;
|
||||||
|
|
||||||
@ -158,6 +161,32 @@ struct tevent_req *smb2cli_ioctl_send(TALLOC_CTX *mem_ctx,
|
|||||||
return req;
|
return req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 3.3.4.4 Sending an Error Response
|
||||||
|
* An error code other than one of the following indicates a failure:
|
||||||
|
*/
|
||||||
|
static bool smb2cli_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
|
||||||
|
size_t data_size)
|
||||||
|
{
|
||||||
|
if (NT_STATUS_IS_OK(status)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* STATUS_INVALID_PARAMETER in a FSCTL_SRV_COPYCHUNK or
|
||||||
|
* FSCTL_SRV_COPYCHUNK_WRITE Response, when returning an
|
||||||
|
* SRV_COPYCHUNK_RESPONSE as described in section 2.2.32.1.
|
||||||
|
*/
|
||||||
|
if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) &&
|
||||||
|
(ctl_code == FSCTL_SRV_COPYCHUNK ||
|
||||||
|
ctl_code == FSCTL_SRV_COPYCHUNK_WRITE) &&
|
||||||
|
data_size == sizeof(struct srv_copychunk_rsp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
||||||
{
|
{
|
||||||
struct tevent_req *req =
|
struct tevent_req *req =
|
||||||
@ -195,12 +224,16 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
|||||||
.status = NT_STATUS_FILE_CLOSED,
|
.status = NT_STATUS_FILE_CLOSED,
|
||||||
.body_size = 0x09,
|
.body_size = 0x09,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.status = NT_STATUS_INVALID_PARAMETER,
|
||||||
|
.body_size = 0x31
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
status = smb2cli_req_recv(subreq, state, &iov,
|
status = smb2cli_req_recv(subreq, state, &iov,
|
||||||
expected, ARRAY_SIZE(expected));
|
expected, ARRAY_SIZE(expected));
|
||||||
TALLOC_FREE(subreq);
|
TALLOC_FREE(subreq);
|
||||||
if (tevent_req_nterror(req, status)) {
|
if (iov == NULL && tevent_req_nterror(req, status)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,6 +247,11 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
|||||||
output_buffer_offset = IVAL(fixed, 0x20);
|
output_buffer_offset = IVAL(fixed, 0x20);
|
||||||
output_buffer_length = IVAL(fixed, 0x24);
|
output_buffer_length = IVAL(fixed, 0x24);
|
||||||
|
|
||||||
|
if (smb2cli_ioctl_is_failure(state->ctl_code, status, output_buffer_length) &&
|
||||||
|
tevent_req_nterror(req, status)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((input_buffer_offset > 0) && (input_buffer_length > 0)) {
|
if ((input_buffer_offset > 0) && (input_buffer_length > 0)) {
|
||||||
uint32_t ofs;
|
uint32_t ofs;
|
||||||
|
|
||||||
@ -294,6 +332,10 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
|||||||
state->out_output_buffer.length = output_buffer_length;
|
state->out_output_buffer.length = output_buffer_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tevent_req_nterror(req, status)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
tevent_req_done(req);
|
tevent_req_done(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,9 +347,10 @@ NTSTATUS smb2cli_ioctl_recv(struct tevent_req *req,
|
|||||||
struct smb2cli_ioctl_state *state =
|
struct smb2cli_ioctl_state *state =
|
||||||
tevent_req_data(req,
|
tevent_req_data(req,
|
||||||
struct smb2cli_ioctl_state);
|
struct smb2cli_ioctl_state);
|
||||||
NTSTATUS status;
|
NTSTATUS status = NT_STATUS_OK;
|
||||||
|
|
||||||
if (tevent_req_is_nterror(req, &status)) {
|
if (tevent_req_is_nterror(req, &status) &&
|
||||||
|
smb2cli_ioctl_is_failure(state->ctl_code, status, state->out_output_buffer.length)) {
|
||||||
tevent_req_received(req);
|
tevent_req_received(req);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -321,7 +364,7 @@ NTSTATUS smb2cli_ioctl_recv(struct tevent_req *req,
|
|||||||
}
|
}
|
||||||
|
|
||||||
tevent_req_received(req);
|
tevent_req_received(req);
|
||||||
return NT_STATUS_OK;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
NTSTATUS smb2cli_ioctl(struct smbXcli_conn *conn,
|
NTSTATUS smb2cli_ioctl(struct smbXcli_conn *conn,
|
||||||
|
@ -130,6 +130,9 @@ struct smbXcli_conn {
|
|||||||
uint16_t cur_credits;
|
uint16_t cur_credits;
|
||||||
uint16_t max_credits;
|
uint16_t max_credits;
|
||||||
|
|
||||||
|
uint32_t cc_chunk_len;
|
||||||
|
uint32_t cc_max_chunks;
|
||||||
|
|
||||||
uint8_t io_priority;
|
uint8_t io_priority;
|
||||||
|
|
||||||
uint8_t preauth_sha512[64];
|
uint8_t preauth_sha512[64];
|
||||||
@ -409,6 +412,13 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx,
|
|||||||
conn->smb2.max_credits = 0;
|
conn->smb2.max_credits = 0;
|
||||||
conn->smb2.io_priority = 1;
|
conn->smb2.io_priority = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Samba and Windows servers accept a maximum of 16 MiB with a maximum
|
||||||
|
* chunk length of 1 MiB.
|
||||||
|
*/
|
||||||
|
conn->smb2.cc_chunk_len = 1024 * 1024;
|
||||||
|
conn->smb2.cc_max_chunks = 16;
|
||||||
|
|
||||||
talloc_set_destructor(conn, smbXcli_conn_destructor);
|
talloc_set_destructor(conn, smbXcli_conn_destructor);
|
||||||
return conn;
|
return conn;
|
||||||
|
|
||||||
@ -2595,6 +2605,28 @@ void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
|
|||||||
conn->smb2.io_priority = io_priority;
|
conn->smb2.io_priority = io_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t smb2cli_conn_cc_chunk_len(struct smbXcli_conn *conn)
|
||||||
|
{
|
||||||
|
return conn->smb2.cc_chunk_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smb2cli_conn_set_cc_chunk_len(struct smbXcli_conn *conn,
|
||||||
|
uint32_t chunk_len)
|
||||||
|
{
|
||||||
|
conn->smb2.cc_chunk_len = chunk_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t smb2cli_conn_cc_max_chunks(struct smbXcli_conn *conn)
|
||||||
|
{
|
||||||
|
return conn->smb2.cc_max_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smb2cli_conn_set_cc_max_chunks(struct smbXcli_conn *conn,
|
||||||
|
uint32_t max_chunks)
|
||||||
|
{
|
||||||
|
conn->smb2.cc_max_chunks = max_chunks;
|
||||||
|
}
|
||||||
|
|
||||||
static void smb2cli_req_cancel_done(struct tevent_req *subreq);
|
static void smb2cli_req_cancel_done(struct tevent_req *subreq);
|
||||||
|
|
||||||
static bool smb2cli_req_cancel(struct tevent_req *req)
|
static bool smb2cli_req_cancel(struct tevent_req *req)
|
||||||
|
@ -306,6 +306,12 @@ void smb2cli_conn_set_max_credits(struct smbXcli_conn *conn,
|
|||||||
uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn);
|
uint8_t smb2cli_conn_get_io_priority(struct smbXcli_conn *conn);
|
||||||
void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
|
void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
|
||||||
uint8_t io_priority);
|
uint8_t io_priority);
|
||||||
|
uint32_t smb2cli_conn_cc_chunk_len(struct smbXcli_conn *conn);
|
||||||
|
void smb2cli_conn_set_cc_chunk_len(struct smbXcli_conn *conn,
|
||||||
|
uint32_t chunk_len);
|
||||||
|
uint32_t smb2cli_conn_cc_max_chunks(struct smbXcli_conn *conn);
|
||||||
|
void smb2cli_conn_set_cc_max_chunks(struct smbXcli_conn *conn,
|
||||||
|
uint32_t max_chunks);
|
||||||
|
|
||||||
struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
|
struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
|
||||||
struct tevent_context *ev,
|
struct tevent_context *ev,
|
||||||
|
@ -238,13 +238,12 @@ struct SMBC_internal_data {
|
|||||||
} printing;
|
} printing;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if 0 /* None available yet */
|
|
||||||
/* SMB high-level functions */
|
/* SMB high-level functions */
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
smbc_splice_fn splice_fn;
|
||||||
} smb;
|
} smb;
|
||||||
|
|
||||||
#endif
|
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -365,6 +364,14 @@ SMBC_write_ctx(SMBCCTX *context,
|
|||||||
const void *buf,
|
const void *buf,
|
||||||
size_t count);
|
size_t count);
|
||||||
|
|
||||||
|
off_t
|
||||||
|
SMBC_splice_ctx(SMBCCTX *context,
|
||||||
|
SMBCFILE *srcfile,
|
||||||
|
SMBCFILE *dstfile,
|
||||||
|
off_t count,
|
||||||
|
int (*splice_cb)(off_t n, void *priv),
|
||||||
|
void *priv);
|
||||||
|
|
||||||
int
|
int
|
||||||
SMBC_close_ctx(SMBCCTX *context,
|
SMBC_close_ctx(SMBCCTX *context,
|
||||||
SMBCFILE *file);
|
SMBCFILE *file);
|
||||||
|
@ -872,6 +872,15 @@ typedef ssize_t (*smbc_write_fn)(SMBCCTX *c,
|
|||||||
smbc_write_fn smbc_getFunctionWrite(SMBCCTX *c);
|
smbc_write_fn smbc_getFunctionWrite(SMBCCTX *c);
|
||||||
void smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn);
|
void smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn);
|
||||||
|
|
||||||
|
typedef off_t (*smbc_splice_fn)(SMBCCTX *c,
|
||||||
|
SMBCFILE *srcfile,
|
||||||
|
SMBCFILE *dstfile,
|
||||||
|
off_t count,
|
||||||
|
int (*splice_cb)(off_t n, void *priv),
|
||||||
|
void *priv);
|
||||||
|
smbc_splice_fn smbc_getFunctionSplice(SMBCCTX *c);
|
||||||
|
void smbc_setFunctionSplice(SMBCCTX *c, smbc_splice_fn fn);
|
||||||
|
|
||||||
typedef int (*smbc_unlink_fn)(SMBCCTX *c,
|
typedef int (*smbc_unlink_fn)(SMBCCTX *c,
|
||||||
const char *fname);
|
const char *fname);
|
||||||
smbc_unlink_fn smbc_getFunctionUnlink(SMBCCTX *c);
|
smbc_unlink_fn smbc_getFunctionUnlink(SMBCCTX *c);
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "lib/util/tevent_ntstatus.h"
|
#include "lib/util/tevent_ntstatus.h"
|
||||||
#include "../libcli/security/security.h"
|
#include "../libcli/security/security.h"
|
||||||
#include "lib/util_ea.h"
|
#include "lib/util_ea.h"
|
||||||
|
#include "librpc/gen_ndr/ndr_ioctl.h"
|
||||||
|
|
||||||
struct smb2_hnd {
|
struct smb2_hnd {
|
||||||
uint64_t fid_persistent;
|
uint64_t fid_persistent;
|
||||||
@ -2578,3 +2579,270 @@ NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
|
|||||||
}
|
}
|
||||||
return NT_STATUS_OK;
|
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;
|
||||||
|
}
|
||||||
|
@ -176,4 +176,11 @@ struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
|
|||||||
size_t size);
|
size_t size);
|
||||||
NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
|
NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
|
||||||
size_t *pwritten);
|
size_t *pwritten);
|
||||||
|
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);
|
||||||
|
NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written);
|
||||||
#endif /* __SMB2CLI_FNUM_H__ */
|
#endif /* __SMB2CLI_FNUM_H__ */
|
||||||
|
@ -1442,3 +1442,119 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
|
|||||||
TALLOC_FREE(frame);
|
TALLOC_FREE(frame);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SPLICE_BLOCK_SIZE 1024 * 1024
|
||||||
|
|
||||||
|
static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame,
|
||||||
|
struct cli_state *srccli,
|
||||||
|
struct cli_state *dstcli,
|
||||||
|
uint16_t src_fnum, uint16_t dst_fnum,
|
||||||
|
off_t initial_size,
|
||||||
|
off_t src_offset, off_t dst_offset,
|
||||||
|
off_t *written,
|
||||||
|
int (*splice_cb)(off_t n, void *priv),
|
||||||
|
void *priv)
|
||||||
|
{
|
||||||
|
NTSTATUS status;
|
||||||
|
uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE);
|
||||||
|
size_t nread;
|
||||||
|
off_t remaining = initial_size;
|
||||||
|
|
||||||
|
while (remaining) {
|
||||||
|
status = cli_read(srccli, src_fnum,
|
||||||
|
(char *)buf, src_offset, SPLICE_BLOCK_SIZE,
|
||||||
|
&nread);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = cli_writeall(dstcli, dst_fnum, 0,
|
||||||
|
buf, dst_offset, nread, NULL);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((src_offset > INT64_MAX - nread) ||
|
||||||
|
(dst_offset > INT64_MAX - nread)) {
|
||||||
|
return NT_STATUS_FILE_TOO_LARGE;
|
||||||
|
}
|
||||||
|
src_offset += nread;
|
||||||
|
dst_offset += nread;
|
||||||
|
if (remaining < nread) {
|
||||||
|
return NT_STATUS_INTERNAL_ERROR;
|
||||||
|
}
|
||||||
|
remaining -= nread;
|
||||||
|
if (!splice_cb(initial_size - remaining, priv)) {
|
||||||
|
return NT_STATUS_CANCELLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NT_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
|
||||||
|
uint16_t src_fnum, uint16_t dst_fnum,
|
||||||
|
off_t size,
|
||||||
|
off_t src_offset, off_t dst_offset,
|
||||||
|
off_t *written,
|
||||||
|
int (*splice_cb)(off_t n, void *priv), void *priv)
|
||||||
|
{
|
||||||
|
TALLOC_CTX *frame = talloc_stackframe();
|
||||||
|
struct tevent_context *ev;
|
||||||
|
struct tevent_req *req;
|
||||||
|
NTSTATUS status = NT_STATUS_NO_MEMORY;
|
||||||
|
bool retry_fallback = false;
|
||||||
|
|
||||||
|
if (smbXcli_conn_has_async_calls(srccli->conn) ||
|
||||||
|
smbXcli_conn_has_async_calls(dstcli->conn))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Can't use sync call while an async call is in flight
|
||||||
|
*/
|
||||||
|
status = NT_STATUS_INVALID_PARAMETER;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
ev = samba_tevent_context_init(frame);
|
||||||
|
if (ev == NULL) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (srccli == dstcli &&
|
||||||
|
smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 &&
|
||||||
|
!retry_fallback)
|
||||||
|
{
|
||||||
|
req = cli_smb2_splice_send(frame, ev,
|
||||||
|
srccli, src_fnum, dst_fnum,
|
||||||
|
size, src_offset, dst_offset,
|
||||||
|
splice_cb, priv);
|
||||||
|
} else {
|
||||||
|
status = cli_splice_fallback(frame,
|
||||||
|
srccli, dstcli,
|
||||||
|
src_fnum, dst_fnum,
|
||||||
|
size,
|
||||||
|
src_offset, dst_offset,
|
||||||
|
written,
|
||||||
|
splice_cb, priv);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (req == NULL) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (!tevent_req_poll(req, ev)) {
|
||||||
|
status = map_nt_error_from_unix(errno);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
status = cli_smb2_splice_recv(req, written);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Older versions of Samba don't support
|
||||||
|
* FSCTL_SRV_COPYCHUNK_WRITE so use the fallback.
|
||||||
|
*/
|
||||||
|
retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST);
|
||||||
|
} while (retry_fallback);
|
||||||
|
|
||||||
|
out:
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
@ -194,6 +194,7 @@ smbc_new_context(void)
|
|||||||
smbc_setFunctionOpen(context, SMBC_open_ctx);
|
smbc_setFunctionOpen(context, SMBC_open_ctx);
|
||||||
smbc_setFunctionCreat(context, SMBC_creat_ctx);
|
smbc_setFunctionCreat(context, SMBC_creat_ctx);
|
||||||
smbc_setFunctionRead(context, SMBC_read_ctx);
|
smbc_setFunctionRead(context, SMBC_read_ctx);
|
||||||
|
smbc_setFunctionSplice(context, SMBC_splice_ctx);
|
||||||
smbc_setFunctionWrite(context, SMBC_write_ctx);
|
smbc_setFunctionWrite(context, SMBC_write_ctx);
|
||||||
smbc_setFunctionClose(context, SMBC_close_ctx);
|
smbc_setFunctionClose(context, SMBC_close_ctx);
|
||||||
smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
|
smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
|
||||||
|
@ -316,6 +316,115 @@ SMBC_read_ctx(SMBCCTX *context,
|
|||||||
return ret; /* Success, ret bytes of data ... */
|
return ret; /* Success, ret bytes of data ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
off_t
|
||||||
|
SMBC_splice_ctx(SMBCCTX *context,
|
||||||
|
SMBCFILE *srcfile,
|
||||||
|
SMBCFILE *dstfile,
|
||||||
|
off_t count,
|
||||||
|
int (*splice_cb)(off_t n, void *priv),
|
||||||
|
void *priv)
|
||||||
|
{
|
||||||
|
off_t written;
|
||||||
|
char *server = NULL, *share = NULL, *user = NULL, *password = NULL;
|
||||||
|
char *path = NULL;
|
||||||
|
char *targetpath = NULL;
|
||||||
|
struct cli_state *srccli = NULL;
|
||||||
|
struct cli_state *dstcli = NULL;
|
||||||
|
uint16_t port = 0;
|
||||||
|
TALLOC_CTX *frame = talloc_stackframe();
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
if (!context || !context->internal->initialized) {
|
||||||
|
errno = EINVAL;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!srcfile ||
|
||||||
|
!SMBC_dlist_contains(context->internal->files, srcfile))
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dstfile ||
|
||||||
|
!SMBC_dlist_contains(context->internal->files, dstfile))
|
||||||
|
{
|
||||||
|
errno = EBADF;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SMBC_parse_path(frame,
|
||||||
|
context,
|
||||||
|
srcfile->fname,
|
||||||
|
NULL,
|
||||||
|
&server,
|
||||||
|
&port,
|
||||||
|
&share,
|
||||||
|
&path,
|
||||||
|
&user,
|
||||||
|
&password,
|
||||||
|
NULL)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = cli_resolve_path(frame, "", context->internal->auth_info,
|
||||||
|
srcfile->srv->cli, path,
|
||||||
|
&srccli, &targetpath);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
d_printf("Could not resolve %s\n", path);
|
||||||
|
errno = ENOENT;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SMBC_parse_path(frame,
|
||||||
|
context,
|
||||||
|
dstfile->fname,
|
||||||
|
NULL,
|
||||||
|
&server,
|
||||||
|
&port,
|
||||||
|
&share,
|
||||||
|
&path,
|
||||||
|
&user,
|
||||||
|
&password,
|
||||||
|
NULL)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = cli_resolve_path(frame, "", context->internal->auth_info,
|
||||||
|
dstfile->srv->cli, path,
|
||||||
|
&dstcli, &targetpath);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
d_printf("Could not resolve %s\n", path);
|
||||||
|
errno = ENOENT;
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = cli_splice(srccli, dstcli,
|
||||||
|
srcfile->cli_fd, dstfile->cli_fd,
|
||||||
|
count, srcfile->offset, dstfile->offset, &written,
|
||||||
|
splice_cb, priv);
|
||||||
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
|
errno = SMBC_errno(context, srccli);
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
srcfile->offset += written;
|
||||||
|
dstfile->offset += written;
|
||||||
|
|
||||||
|
TALLOC_FREE(frame);
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routine to write() a file ...
|
* Routine to write() a file ...
|
||||||
*/
|
*/
|
||||||
|
@ -695,6 +695,18 @@ smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn)
|
|||||||
c->write = fn;
|
c->write = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smbc_splice_fn
|
||||||
|
smbc_getFunctionSplice(SMBCCTX *c)
|
||||||
|
{
|
||||||
|
return c->internal->smb.splice_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
smbc_setFunctionSplice(SMBCCTX *c, smbc_splice_fn fn)
|
||||||
|
{
|
||||||
|
c->internal->smb.splice_fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
smbc_unlink_fn
|
smbc_unlink_fn
|
||||||
smbc_getFunctionUnlink(SMBCCTX *c)
|
smbc_getFunctionUnlink(SMBCCTX *c)
|
||||||
{
|
{
|
||||||
|
@ -803,6 +803,13 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
|
|||||||
size_t (*source)(uint8_t *buf, size_t n, void *priv),
|
size_t (*source)(uint8_t *buf, size_t n, void *priv),
|
||||||
void *priv);
|
void *priv);
|
||||||
|
|
||||||
|
NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli,
|
||||||
|
uint16_t src_fnum, uint16_t dst_fnum,
|
||||||
|
off_t size,
|
||||||
|
off_t src_offset, off_t dst_offset,
|
||||||
|
off_t *written,
|
||||||
|
int (*splice_cb)(off_t n, void *priv), void *priv);
|
||||||
|
|
||||||
/* The following definitions come from libsmb/clisecdesc.c */
|
/* The following definitions come from libsmb/clisecdesc.c */
|
||||||
|
|
||||||
NTSTATUS cli_query_security_descriptor(struct cli_state *cli,
|
NTSTATUS cli_query_security_descriptor(struct cli_state *cli,
|
||||||
|
@ -414,6 +414,7 @@ bld.SAMBA3_LIBRARY('libsmb',
|
|||||||
SPNEGO_PARSE
|
SPNEGO_PARSE
|
||||||
LIBTSOCKET
|
LIBTSOCKET
|
||||||
KRBCLIENT
|
KRBCLIENT
|
||||||
|
NDR_IOCTL
|
||||||
cli_smb_common
|
cli_smb_common
|
||||||
util_cmdline
|
util_cmdline
|
||||||
tevent''',
|
tevent''',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user