1
0
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:
Ross Lagerwall 2015-05-27 23:13:15 +01:00 committed by Jeremy Allison
parent 2ffa939bbe
commit f73bcf4934
13 changed files with 624 additions and 6 deletions

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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);

View 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);

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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;
}

View File

@ -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);

View File

@ -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 ...
*/

View 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)
{

View File

@ -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,

View File

@ -414,6 +414,7 @@ bld.SAMBA3_LIBRARY('libsmb',
SPNEGO_PARSE
LIBTSOCKET
KRBCLIENT
NDR_IOCTL
cli_smb_common
util_cmdline
tevent''',