mirror of
https://github.com/samba-team/samba.git
synced 2025-01-10 01:18:15 +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 "smbXcli_base.h"
|
||||
#include "librpc/ndr/libndr.h"
|
||||
#include "librpc/gen_ndr/ioctl.h"
|
||||
|
||||
struct smb2cli_ioctl_state {
|
||||
uint8_t fixed[0x38];
|
||||
@ -32,6 +33,7 @@ struct smb2cli_ioctl_state {
|
||||
struct iovec *recv_iov;
|
||||
DATA_BLOB out_input_buffer;
|
||||
DATA_BLOB out_output_buffer;
|
||||
uint32_t ctl_code;
|
||||
};
|
||||
|
||||
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) {
|
||||
return NULL;
|
||||
}
|
||||
state->ctl_code = in_ctl_code;
|
||||
state->max_input_length = in_max_input_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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct tevent_req *req =
|
||||
@ -195,12 +224,16 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
||||
.status = NT_STATUS_FILE_CLOSED,
|
||||
.body_size = 0x09,
|
||||
},
|
||||
{
|
||||
.status = NT_STATUS_INVALID_PARAMETER,
|
||||
.body_size = 0x31
|
||||
},
|
||||
};
|
||||
|
||||
status = smb2cli_req_recv(subreq, state, &iov,
|
||||
expected, ARRAY_SIZE(expected));
|
||||
TALLOC_FREE(subreq);
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
if (iov == NULL && tevent_req_nterror(req, status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -214,6 +247,11 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
||||
output_buffer_offset = IVAL(fixed, 0x20);
|
||||
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)) {
|
||||
uint32_t ofs;
|
||||
|
||||
@ -294,6 +332,10 @@ static void smb2cli_ioctl_done(struct tevent_req *subreq)
|
||||
state->out_output_buffer.length = output_buffer_length;
|
||||
}
|
||||
|
||||
if (tevent_req_nterror(req, status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tevent_req_done(req);
|
||||
}
|
||||
|
||||
@ -305,9 +347,10 @@ NTSTATUS smb2cli_ioctl_recv(struct tevent_req *req,
|
||||
struct smb2cli_ioctl_state *state =
|
||||
tevent_req_data(req,
|
||||
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);
|
||||
return status;
|
||||
}
|
||||
@ -321,7 +364,7 @@ NTSTATUS smb2cli_ioctl_recv(struct tevent_req *req,
|
||||
}
|
||||
|
||||
tevent_req_received(req);
|
||||
return NT_STATUS_OK;
|
||||
return status;
|
||||
}
|
||||
|
||||
NTSTATUS smb2cli_ioctl(struct smbXcli_conn *conn,
|
||||
|
@ -130,6 +130,9 @@ struct smbXcli_conn {
|
||||
uint16_t cur_credits;
|
||||
uint16_t max_credits;
|
||||
|
||||
uint32_t cc_chunk_len;
|
||||
uint32_t cc_max_chunks;
|
||||
|
||||
uint8_t io_priority;
|
||||
|
||||
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.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);
|
||||
return conn;
|
||||
|
||||
@ -2595,6 +2605,28 @@ void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
|
||||
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 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);
|
||||
void smb2cli_conn_set_io_priority(struct smbXcli_conn *conn,
|
||||
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_context *ev,
|
||||
|
@ -238,13 +238,12 @@ struct SMBC_internal_data {
|
||||
} printing;
|
||||
#endif
|
||||
|
||||
#if 0 /* None available yet */
|
||||
/* SMB high-level functions */
|
||||
struct
|
||||
{
|
||||
smbc_splice_fn splice_fn;
|
||||
} smb;
|
||||
|
||||
#endif
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
@ -365,6 +364,14 @@ SMBC_write_ctx(SMBCCTX *context,
|
||||
const void *buf,
|
||||
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
|
||||
SMBC_close_ctx(SMBCCTX *context,
|
||||
SMBCFILE *file);
|
||||
|
@ -872,6 +872,15 @@ typedef ssize_t (*smbc_write_fn)(SMBCCTX *c,
|
||||
smbc_write_fn smbc_getFunctionWrite(SMBCCTX *c);
|
||||
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,
|
||||
const char *fname);
|
||||
smbc_unlink_fn smbc_getFunctionUnlink(SMBCCTX *c);
|
||||
|
@ -38,6 +38,7 @@
|
||||
#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;
|
||||
@ -2578,3 +2579,270 @@ NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
|
||||
}
|
||||
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);
|
||||
NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
|
||||
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__ */
|
||||
|
@ -1442,3 +1442,119 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode,
|
||||
TALLOC_FREE(frame);
|
||||
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_setFunctionCreat(context, SMBC_creat_ctx);
|
||||
smbc_setFunctionRead(context, SMBC_read_ctx);
|
||||
smbc_setFunctionSplice(context, SMBC_splice_ctx);
|
||||
smbc_setFunctionWrite(context, SMBC_write_ctx);
|
||||
smbc_setFunctionClose(context, SMBC_close_ctx);
|
||||
smbc_setFunctionUnlink(context, SMBC_unlink_ctx);
|
||||
|
@ -316,6 +316,115 @@ SMBC_read_ctx(SMBCCTX *context,
|
||||
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 ...
|
||||
*/
|
||||
|
@ -695,6 +695,18 @@ smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn 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_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),
|
||||
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 */
|
||||
|
||||
NTSTATUS cli_query_security_descriptor(struct cli_state *cli,
|
||||
|
@ -414,6 +414,7 @@ bld.SAMBA3_LIBRARY('libsmb',
|
||||
SPNEGO_PARSE
|
||||
LIBTSOCKET
|
||||
KRBCLIENT
|
||||
NDR_IOCTL
|
||||
cli_smb_common
|
||||
util_cmdline
|
||||
tevent''',
|
||||
|
Loading…
Reference in New Issue
Block a user