libsmb: Fix CID 1034605 Incorrect pointer comparison
[sfrench/samba-autobuild/.git] / source3 / libsmb / cli_smb2_fnum.c
index 950398a5cb9582f58640c35c3780dc312fb70881..816ad1374a30928953a04f5484f8e0436e4d809a 100644 (file)
@@ -30,7 +30,6 @@
 #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"
@@ -39,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;
@@ -151,9 +151,128 @@ static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
 
 /***************************************************************
  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,
@@ -165,95 +284,143 @@ NTSTATUS cli_smb2_create_fnum(struct cli_state *cli,
                        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;
 }
 
@@ -501,6 +668,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
        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)) {
                /*
@@ -524,6 +692,8 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
                goto fail;
         }
 
+       mask_has_wild = ms_has_wild(mask);
+
        status = cli_smb2_create_fnum(cli,
                        parent_dir,
                        0,                      /* create_flags */
@@ -625,6 +795,17 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
 
                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)) {
@@ -719,9 +900,9 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
 
        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;
 
@@ -2013,7 +2194,7 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
                }
                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;
        }
@@ -2398,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;
+}