s3: libsmb: add replace support to SMB2 rename
[sfrench/samba-autobuild/.git] / source3 / libsmb / cli_smb2_fnum.c
index c5b1434eaaa844c84c6788976fbc6edb2e69c833..351fccfb6453d07ddd5420ee5c04628232d848a6 100644 (file)
 #include "libsmb/proto.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
 #include "lib/util_ea.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
+#include "ntioctl.h"
 
 struct smb2_hnd {
        uint64_t fid_persistent;
@@ -177,6 +179,10 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req, *subreq;
        struct cli_smb2_create_fnum_state *state;
        size_t fname_len = 0;
+       const char *startp = NULL;
+       const char *endp = NULL;
+       time_t tstamp = (time_t)0;
+       struct smb2_create_blobs *cblobs = NULL;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_smb2_create_fnum_state);
@@ -194,14 +200,51 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
                create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
        }
 
+       /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
+       fname_len = strlen(fname);
+       if (clistr_is_previous_version_path(fname, &startp, &endp, &tstamp)) {
+               size_t len_before_gmt = startp - fname;
+               size_t len_after_gmt = fname + fname_len - endp;
+               DATA_BLOB twrp_blob;
+               NTTIME ntt;
+               NTSTATUS status;
+
+               char *new_fname = talloc_array(state, char,
+                               len_before_gmt + len_after_gmt + 1);
+
+               if (tevent_req_nomem(new_fname, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               memcpy(new_fname, fname, len_before_gmt);
+               memcpy(new_fname + len_before_gmt, endp, len_after_gmt + 1);
+               fname = new_fname;
+               fname_len = len_before_gmt + len_after_gmt;
+
+               unix_to_nt_time(&ntt, tstamp);
+               twrp_blob = data_blob_const((const void *)&ntt, 8);
+
+               cblobs = talloc_zero(state, struct smb2_create_blobs);
+               if (tevent_req_nomem(cblobs, req)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               status = smb2_create_blob_add(state, cblobs,
+                               SMB2_CREATE_TAG_TWRP, twrp_blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       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++;
+               fname_len--;
        }
 
        /* Or end in a '\' */
-       fname_len = strlen(fname);
        if (fname_len > 0 && fname[fname_len-1] == '\\') {
                char *new_fname = talloc_strdup(state, fname);
                if (tevent_req_nomem(new_fname, req)) {
@@ -224,7 +267,7 @@ struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
                                     share_access,
                                     create_disposition,
                                     create_options,
-                                    NULL);
+                                    cblobs);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -843,6 +886,9 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
        if (fnum != 0xffff) {
                cli_smb2_close_fnum(cli, fnum);
        }
+
+       cli->raw_status = status;
+
        TALLOC_FREE(subframe);
        TALLOC_FREE(frame);
        return status;
@@ -914,7 +960,7 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
                return status;
        }
 
-       cli_smb2_close_fnum(cli, fnum);
+       status = cli_smb2_close_fnum(cli, fnum);
 
        ZERO_STRUCTP(sbuf);
 
@@ -924,7 +970,7 @@ NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
        sbuf->st_ex_size = cr.end_of_file;
        *attributes = cr.file_attributes;
 
-       return NT_STATUS_OK;
+       return status;
 }
 
 /***************************************************************
@@ -1090,6 +1136,9 @@ NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
        if (fnum != 0xffff) {
                cli_smb2_close_fnum(cli, fnum);
        }
+
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1189,6 +1238,8 @@ NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli,
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1220,6 +1271,8 @@ NTSTATUS cli_smb2_getattrE(struct cli_state *cli,
                                        &change_time_ts,
                                         NULL);
 
+       cli->raw_status = status;
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -1297,6 +1350,8 @@ NTSTATUS cli_smb2_getatr(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1367,6 +1422,8 @@ NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1455,6 +1512,8 @@ NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1537,6 +1596,8 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1593,7 +1654,7 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
                put_long_date((char *)inbuf.data + 16, write_time);
        }
 
-       return smb2cli_set_info(cli->conn,
+       cli->raw_status = smb2cli_set_info(cli->conn,
                                cli->timeout,
                                cli->smb2.session,
                                cli->smb2.tcon,
@@ -1603,6 +1664,8 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
                                0, /* in_additional_info */
                                ph->fid_persistent,
                                ph->fid_volatile);
+
+       return cli->raw_status;
 }
 
 /***************************************************************
@@ -1709,6 +1772,87 @@ NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to query file system attributes.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
+{
+       NTSTATUS status;
+       uint16_t fnum = 0xffff;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       /* First open the top level directory. */
+       status =
+           cli_smb2_create_fnum(cli, "", 0,               /* create_flags */
+                                FILE_READ_ATTRIBUTES,     /* desired_access */
+                                FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
+                                FILE_SHARE_READ | FILE_SHARE_WRITE |
+                                    FILE_SHARE_DELETE, /* share_access */
+                                FILE_OPEN,             /* create_disposition */
+                                FILE_DIRECTORY_FILE,   /* create_options */
+                                &fnum,
+                                NULL);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+                                   cli->smb2.tcon, 2, /* in_info_type */
+                                   5,                 /* in_file_info_class */
+                                   0xFFFF, /* in_max_output_length */
+                                   NULL,   /* in_input_buffer */
+                                   0,      /* in_additional_info */
+                                   0,      /* in_flags */
+                                   ph->fid_persistent, ph->fid_volatile, frame,
+                                   &outbuf);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       if (outbuf.length < 12) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto fail;
+       }
+
+       *fs_attr = IVAL(outbuf.data, 0);
+
+fail:
+
+       if (fnum != 0xffff) {
+               cli_smb2_close_fnum(cli, fnum);
+       }
+
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1789,6 +1933,8 @@ NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1852,6 +1998,8 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1862,8 +2010,9 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
 ***************************************************************/
 
 NTSTATUS cli_smb2_rename(struct cli_state *cli,
-                       const char *fname_src,
-                       const char *fname_dst)
+                        const char *fname_src,
+                        const char *fname_dst,
+                        bool replace)
 {
        NTSTATUS status;
        DATA_BLOB inbuf = data_blob_null;
@@ -1943,6 +2092,10 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli,
                goto fail;
        }
 
+       if (replace) {
+               SCVAL(inbuf.data, 0, 1);
+       }
+
        SIVAL(inbuf.data, 16, converted_size_bytes);
        memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
 
@@ -1966,6 +2119,8 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -2059,6 +2214,8 @@ NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -2114,6 +2271,8 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        return status;
 }
 
@@ -2224,6 +2383,323 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
+                                int quota_fnum,
+                                SMB_NTQUOTA_STRUCT *pqt)
+{
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       unsigned sid_len;
+       unsigned int offset;
+       uint8_t *buf;
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       sid_len = ndr_size_dom_sid(&pqt->sid, 0);
+
+       inbuf = data_blob_talloc_zero(frame, 24 + sid_len);
+       if (inbuf.data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       buf = inbuf.data;
+
+       SCVAL(buf, 0, 1);          /* ReturnSingle */
+       SCVAL(buf, 1, 0);          /* RestartScan */
+       SSVAL(buf, 2, 0);          /* Reserved */
+       if (8 + sid_len < 8) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       SIVAL(buf, 4, 8 + sid_len); /* SidListLength */
+       SIVAL(buf, 8, 0);          /* StartSidLength */
+       SIVAL(buf, 12, 0);        /* StartSidOffset */
+       SIVAL(buf, 16, 0);        /* NextEntryOffset */
+       SIVAL(buf, 20, sid_len);    /* SidLength */
+       sid_linearize(buf + 24, sid_len, &pqt->sid);
+
+       status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+                                   cli->smb2.tcon, 4, /* in_info_type */
+                                   0,                 /* in_file_info_class */
+                                   0xFFFF, /* in_max_output_length */
+                                   &inbuf, /* in_input_buffer */
+                                   0,      /* in_additional_info */
+                                   0,      /* in_flags */
+                                   ph->fid_persistent, ph->fid_volatile, frame,
+                                   &outbuf);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
+                                    pqt)) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
+       }
+
+fail:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to list user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
+                                      TALLOC_CTX *mem_ctx,
+                                      int quota_fnum,
+                                      SMB_NTQUOTA_LIST **pqt_list,
+                                      bool first)
+{
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       uint8_t *buf;
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       inbuf = data_blob_talloc_zero(frame, 16);
+       if (inbuf.data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto cleanup;
+       }
+
+       buf = inbuf.data;
+
+       SCVAL(buf, 0, 0);            /* ReturnSingle */
+       SCVAL(buf, 1, first ? 1 : 0); /* RestartScan */
+       SSVAL(buf, 2, 0);            /* Reserved */
+       SIVAL(buf, 4, 0);            /* SidListLength */
+       SIVAL(buf, 8, 0);            /* StartSidLength */
+       SIVAL(buf, 12, 0);          /* StartSidOffset */
+
+       status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
+                                   cli->smb2.tcon, 4, /* in_info_type */
+                                   0,                 /* in_file_info_class */
+                                   0xFFFF, /* in_max_output_length */
+                                   &inbuf, /* in_input_buffer */
+                                   0,      /* in_additional_info */
+                                   0,      /* in_flags */
+                                   ph->fid_persistent, ph->fid_volatile, frame,
+                                   &outbuf);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
+                                      pqt_list);
+
+cleanup:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to get file system quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
+                                   int quota_fnum,
+                                   SMB_NTQUOTA_STRUCT *pqt)
+{
+       NTSTATUS status;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = smb2cli_query_info(
+           cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+           2,                               /* in_info_type */
+           SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+           0xFFFF,                          /* in_max_output_length */
+           NULL,                            /* in_input_buffer */
+           0,                               /* in_additional_info */
+           0,                               /* in_flags */
+           ph->fid_persistent, ph->fid_volatile, frame, &outbuf);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
+
+cleanup:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to set user quota.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
+                                int quota_fnum,
+                                SMB_NTQUOTA_LIST *qtl)
+{
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session,
+                                 cli->smb2.tcon, 4, /* in_info_type */
+                                 0,                 /* in_file_info_class */
+                                 &inbuf,           /* in_input_buffer */
+                                 0,                 /* in_additional_info */
+                                 ph->fid_persistent, ph->fid_volatile);
+cleanup:
+
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+
+       return status;
+}
+
+NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
+                                   int quota_fnum,
+                                   SMB_NTQUOTA_STRUCT *pqt)
+{
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
+
+       status = smb2cli_set_info(
+           cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
+           2,                               /* in_info_type */
+           SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
+           &inbuf,                          /* in_input_buffer */
+           0,                               /* in_additional_info */
+           ph->fid_persistent, ph->fid_volatile);
+cleanup:
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -2873,3 +3349,298 @@ NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
        tevent_req_received(req);
        return NT_STATUS_OK;
 }
+
+/***************************************************************
+ SMB2 enum shadow copy data.
+***************************************************************/
+
+struct cli_smb2_shadow_copy_data_fnum_state {
+       struct cli_state *cli;
+       uint16_t fnum;
+       struct smb2_hnd *ph;
+       DATA_BLOB out_input_buffer;
+       DATA_BLOB out_output_buffer;
+};
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
+                                       TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       uint16_t fnum,
+                                       bool get_names)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_smb2_shadow_copy_data_fnum_state *state;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_shadow_copy_data_fnum_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->cli = cli;
+       state->fnum = fnum;
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * TODO. Under SMB2 we should send a zero max_output_length
+        * ioctl to get the required size, then send another ioctl
+        * to get the data, but the current SMB1 implementation just
+        * does one roundtrip with a 64K buffer size. Do the same
+        * for now. JRA.
+        */
+
+       subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+                       state->cli->timeout,
+                       state->cli->smb2.session,
+                       state->cli->smb2.tcon,
+                       state->ph->fid_persistent, /* in_fid_persistent */
+                       state->ph->fid_volatile, /* in_fid_volatile */
+                       FSCTL_GET_SHADOW_COPY_DATA,
+                       0, /* in_max_input_length */
+                       NULL, /* in_input_buffer */
+                       get_names ?
+                               CLI_BUFFER_SIZE : 16, /* in_max_output_length */
+                       NULL, /* in_output_buffer */
+                       SMB2_IOCTL_FLAG_IS_FSCTL);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               cli_smb2_shadow_copy_data_fnum_done,
+                               req);
+
+       return req;
+}
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_shadow_copy_data_fnum_state);
+       NTSTATUS status;
+
+       status = smb2cli_ioctl_recv(subreq, state,
+                               &state->out_input_buffer,
+                               &state->out_output_buffer);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               bool get_names,
+                               char ***pnames,
+                               int *pnum_names)
+{
+       struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_shadow_copy_data_fnum_state);
+       char **names = NULL;
+       uint32_t num_names = 0;
+       uint32_t num_names_returned = 0;
+       uint32_t dlength = 0;
+       uint32_t i;
+       uint8_t *endp = NULL;
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+
+       if (state->out_output_buffer.length < 16) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       num_names = IVAL(state->out_output_buffer.data, 0);
+       num_names_returned = IVAL(state->out_output_buffer.data, 4);
+       dlength = IVAL(state->out_output_buffer.data, 8);
+
+       if (num_names > 0x7FFFFFFF) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       if (get_names == false) {
+               *pnum_names = (int)num_names;
+               return NT_STATUS_OK;
+       }
+       if (num_names != num_names_returned) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+       if (dlength + 12 < 12) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+       /*
+        * NB. The below is an allowable return if there are
+        * more snapshots than the buffer size we told the
+        * server we can receive. We currently don't support
+        * this.
+        */
+       if (dlength + 12 > state->out_output_buffer.length) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+       if (state->out_output_buffer.length +
+                       (2 * sizeof(SHADOW_COPY_LABEL)) <
+                               state->out_output_buffer.length) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
+       names = talloc_array(mem_ctx, char *, num_names_returned);
+       if (names == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       endp = state->out_output_buffer.data +
+                       state->out_output_buffer.length;
+
+       for (i=0; i<num_names_returned; i++) {
+               bool ret;
+               uint8_t *src;
+               size_t converted_size;
+
+               src = state->out_output_buffer.data + 12 +
+                       (i * 2 * sizeof(SHADOW_COPY_LABEL));
+
+               if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
+                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+               }
+               ret = convert_string_talloc(
+                       names, CH_UTF16LE, CH_UNIX,
+                       src, 2 * sizeof(SHADOW_COPY_LABEL),
+                       &names[i], &converted_size);
+               if (!ret) {
+                       TALLOC_FREE(names);
+                       return NT_STATUS_INVALID_NETWORK_RESPONSE;
+               }
+       }
+       *pnum_names = num_names;
+       *pnames = names;
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+                               struct cli_state *cli,
+                               uint16_t fnum,
+                               bool get_names,
+                               char ***pnames,
+                               int *pnum_names)
+{
+       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
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = samba_tevent_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_smb2_shadow_copy_data_fnum_send(frame,
+                                       ev,
+                                       cli,
+                                       fnum,
+                                       get_names);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_smb2_shadow_copy_data_fnum_recv(req,
+                                               mem_ctx,
+                                               get_names,
+                                               pnames,
+                                               pnum_names);
+ fail:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to truncate a file.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
+                       uint16_t fnum,
+                       uint64_t newsize)
+{
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+
+       if (smbXcli_conn_has_async_calls(cli->conn)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       inbuf = data_blob_talloc_zero(frame, 8);
+       if (inbuf.data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       SBVAL(inbuf.data, 0, newsize);
+
+       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+          level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
+
+       status = smb2cli_set_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               1, /* in_info_type */
+                                       /* in_file_info_class */
+                               SMB_FILE_END_OF_FILE_INFORMATION - 1000,
+                               &inbuf, /* in_input_buffer */
+                               0, /* in_additional_info */
+                               ph->fid_persistent,
+                               ph->fid_volatile);
+
+  fail:
+
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}