s3/libsmb: adjust smb2 code for new idl structs & generated ndr push/pull funcs.
[asn/samba.git] / source3 / libsmb / cli_smb2_fnum.c
index 70fa798ed65c26f298e32a6f410869eb2aff5e1c..3537932c0d08481ae89ae65223ac240ea5f33eef 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"
+#include "librpc/gen_ndr/ndr_quota.h"
 
 struct smb2_hnd {
        uint64_t fid_persistent;
@@ -178,6 +180,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);
@@ -195,14 +201,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)) {
@@ -225,7 +268,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);
        }
@@ -407,8 +450,12 @@ NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
 {
        struct cli_smb2_close_fnum_state *state = tevent_req_data(
                req, struct cli_smb2_close_fnum_state);
-       NTSTATUS status = tevent_req_simple_recv_ntstatus(req);
-       state->cli->raw_status = status;
+       NTSTATUS status = NT_STATUS_OK;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+       }
+       tevent_req_received(req);
        return status;
 }
 
@@ -443,6 +490,133 @@ NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
        return status;
 }
 
+struct cli_smb2_delete_on_close_state {
+       struct cli_state *cli;
+       uint16_t fnum;
+       struct smb2_hnd *ph;
+       uint8_t data[1];
+       DATA_BLOB inbuf;
+};
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       uint16_t fnum,
+                                       bool flag)
+{
+       struct tevent_req *req = NULL;
+       struct cli_smb2_delete_on_close_state *state = NULL;
+       struct tevent_req *subreq = NULL;
+       uint8_t in_info_type;
+       uint8_t in_file_info_class;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_delete_on_close_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+       state->fnum = fnum;
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+        * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
+        */
+       in_info_type = 1;
+       in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
+       /* Setup data array. */
+       SCVAL(&state->data[0], 0, flag ? 1 : 0);
+       state->inbuf.data = &state->data[0];
+       state->inbuf.length = 1;
+
+       subreq = smb2cli_set_info_send(state, ev,
+                                      cli->conn,
+                                      cli->timeout,
+                                      cli->smb2.session,
+                                      cli->smb2.tcon,
+                                      in_info_type,
+                                      in_file_info_class,
+                                      &state->inbuf, /* in_input_buffer */
+                                      0, /* in_additional_info */
+                                      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_delete_on_close_done,
+                               req);
+       return req;
+}
+
+static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = smb2cli_set_info_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
+{
+       struct cli_smb2_delete_on_close_state *state =
+               tevent_req_data(req,
+               struct cli_smb2_delete_on_close_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               tevent_req_received(req);
+               return status;
+       }
+
+       state->cli->raw_status = NT_STATUS_OK;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
+{
+       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_delete_on_close_send(frame, ev, cli, fnum, flag);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_smb2_delete_on_close_recv(req);
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
 /***************************************************************
  Small wrapper that allows SMB2 to create a directory
  Synchronous only.
@@ -513,6 +687,27 @@ NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname)
                        &fnum,
                        NULL);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               status = cli_smb2_create_fnum(cli,
+                       dname,
+                       0,                      /* create_flags */
+                       DELETE_ACCESS,          /* 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|
+                               FILE_DELETE_ON_CLOSE|
+                               FILE_OPEN_REPARSE_POINT, /* create_options */
+                       &fnum,
+                       NULL);
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -551,6 +746,26 @@ NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname)
                        &fnum,
                        NULL);
 
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               status = cli_smb2_create_fnum(cli,
+                       fname,
+                       0,                      /* create_flags */
+                       DELETE_ACCESS,          /* desired_access */
+                       FILE_ATTRIBUTE_NORMAL, /* file attributes */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+                       FILE_OPEN,              /* create_disposition */
+                       FILE_DELETE_ON_CLOSE|
+                               FILE_OPEN_REPARSE_POINT, /* create_options */
+                       &fnum,
+                       NULL);
+       }
+
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -589,6 +804,7 @@ static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
                return NT_STATUS_INFO_LENGTH_MISMATCH;
        }
 
+       finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
        finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
        finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
        finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
@@ -688,6 +904,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
        TALLOC_CTX *frame = talloc_stackframe();
        TALLOC_CTX *subframe = NULL;
        bool mask_has_wild;
+       uint32_t max_trans = smb2cli_conn_max_trans_size(cli->conn);
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -751,7 +968,7 @@ NTSTATUS cli_smb2_list(struct cli_state *cli,
                                        ph->fid_persistent,
                                        ph->fid_volatile,
                                        mask,
-                                       0xffff,
+                                       max_trans,
                                        subframe,
                                        &dir_data,
                                        &dir_data_length);
@@ -844,6 +1061,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;
@@ -915,7 +1135,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);
 
@@ -925,7 +1145,48 @@ 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;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 to check if a path is a directory.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_chkpath(struct cli_state *cli,
+                               const char *name)
+{
+       NTSTATUS status;
+       uint16_t fnum = 0xffff;
+
+       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;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* Ensure this is a directory. */
+       status = cli_smb2_create_fnum(cli,
+                       name,
+                       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)) {
+               return status;
+       }
+
+       return cli_smb2_close_fnum(cli, fnum);
 }
 
 /***************************************************************
@@ -940,6 +1201,7 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli,
        NTSTATUS status;
        size_t namelen = strlen(name);
        TALLOC_CTX *frame = talloc_stackframe();
+       uint32_t create_options = 0;
 
        /* SMB2 is pickier about pathnames. Ensure it doesn't
           end in a '\' */
@@ -961,11 +1223,32 @@ static NTSTATUS get_fnum_from_path(struct cli_state *cli,
                        0, /* file attributes */
                        FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
                        FILE_OPEN,              /* create_disposition */
-                       0,      /* create_options */
+                       create_options,
+                       pfnum,
+                       NULL);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               /*
+                * Naive option to match our SMB1 code. Assume the
+                * symlink path that tripped us up was the last
+                * component and try again. Eventually we will have to
+                * deal with the returned path unprocessed component. JRA.
+                */
+               create_options |= FILE_OPEN_REPARSE_POINT;
+               status = cli_smb2_create_fnum(cli,
+                       name,
+                       0,                      /* create_flags */
+                       desired_access,
+                       0, /* file attributes */
+                       FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
+                       FILE_OPEN,              /* create_disposition */
+                       create_options,
                        pfnum,
                        NULL);
+       }
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
+               create_options |= FILE_DIRECTORY_FILE;
                status = cli_smb2_create_fnum(cli,
                        name,
                        0,                      /* create_flags */
@@ -1091,6 +1374,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;
 }
@@ -1190,6 +1476,8 @@ NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli,
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1221,6 +1509,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;
        }
@@ -1298,6 +1588,8 @@ NTSTATUS cli_smb2_getatr(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1368,6 +1660,8 @@ NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
@@ -1456,25 +1750,27 @@ NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to set pathname attributes.
+ Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
+ a pathname.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
                        const char *name,
-                       uint16_t attr,
-                       time_t mtime)
+                       uint8_t in_info_type,
+                       uint8_t in_file_info_class,
+                       const DATA_BLOB *p_in_data)
 {
        NTSTATUS status;
        uint16_t fnum = 0xffff;
        struct smb2_hnd *ph = NULL;
-       uint8_t inbuf_store[40];
-       DATA_BLOB inbuf = data_blob_null;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -1506,29 +1802,13 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli,
                goto fail;
        }
 
-       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
-          level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
-
-       inbuf.data = inbuf_store;
-       inbuf.length = sizeof(inbuf_store);
-       data_blob_clear(&inbuf);
-
-       SSVAL(inbuf.data, 32, attr);
-       if (mtime != 0) {
-               put_long_date((char *)inbuf.data + 16,mtime);
-       }
-       /* Set all the other times to -1. */
-       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
-       SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
-       SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
-
        status = smb2cli_set_info(cli->conn,
                                cli->timeout,
                                cli->smb2.session,
                                cli->smb2.tcon,
-                               1, /* in_info_type */
-                               SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
-                               &inbuf, /* in_input_buffer */
+                               in_info_type,
+                               in_file_info_class,
+                               p_in_data, /* in_input_buffer */
                                0, /* in_additional_info */
                                ph->fid_persistent,
                                ph->fid_volatile);
@@ -1538,44 +1818,26 @@ NTSTATUS cli_smb2_setatr(struct cli_state *cli,
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
 
+
 /***************************************************************
- Wrapper that allows SMB2 to set file handle times.
+ Wrapper that allows SMB2 to set pathname attributes.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
-                       uint16_t fnum,
-                       time_t change_time,
-                       time_t access_time,
-                       time_t write_time)
+NTSTATUS cli_smb2_setatr(struct cli_state *cli,
+                       const char *name,
+                       uint16_t attr,
+                       time_t mtime)
 {
-       NTSTATUS status;
-       struct smb2_hnd *ph = NULL;
        uint8_t inbuf_store[40];
        DATA_BLOB inbuf = data_blob_null;
 
-       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;
-       }
-
-       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       status = map_fnum_to_smb2_handle(cli,
-                                       fnum,
-                                       &ph);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
        /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
           level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
 
@@ -1583,7 +1845,89 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
        inbuf.length = sizeof(inbuf_store);
        data_blob_clear(&inbuf);
 
-       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+       /*
+        * SMB1 uses attr == 0 to clear all attributes
+        * on a file (end up with FILE_ATTRIBUTE_NORMAL),
+        * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
+        * request attribute change.
+        *
+        * SMB2 uses exactly the reverse. Unfortunately as the
+        * cli_setatr() ABI is exposed inside libsmbclient,
+        * we must make the SMB2 cli_smb2_setatr() call
+        * export the same ABI as the SMB1 cli_setatr()
+        * which calls it. This means reversing the sense
+        * of the requested attr argument if it's zero
+        * or FILE_ATTRIBUTE_NORMAL.
+        *
+        * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
+        */
+
+       if (attr == 0) {
+               attr = FILE_ATTRIBUTE_NORMAL;
+       } else if (attr == FILE_ATTRIBUTE_NORMAL) {
+               attr = 0;
+       }
+
+       SSVAL(inbuf.data, 32, attr);
+       if (mtime != 0) {
+               put_long_date((char *)inbuf.data + 16,mtime);
+       }
+       /* Set all the other times to -1. */
+       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
+       SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
+       SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
+
+       return cli_smb2_setpathinfo(cli,
+                               name,
+                               1, /* in_info_type */
+                               /* in_file_info_class */
+                               SMB_FILE_BASIC_INFORMATION - 1000,
+                               &inbuf);
+}
+
+
+/***************************************************************
+ Wrapper that allows SMB2 to set file handle times.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
+                       uint16_t fnum,
+                       time_t change_time,
+                       time_t access_time,
+                       time_t write_time)
+{
+       NTSTATUS status;
+       struct smb2_hnd *ph = NULL;
+       uint8_t inbuf_store[40];
+       DATA_BLOB inbuf = data_blob_null;
+
+       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;
+       }
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+          level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
+
+       inbuf.data = inbuf_store;
+       inbuf.length = sizeof(inbuf_store);
+       data_blob_clear(&inbuf);
+
+       SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
        if (change_time != 0) {
                put_long_date((char *)inbuf.data + 24, change_time);
        }
@@ -1594,7 +1938,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,
@@ -1604,6 +1948,8 @@ NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
                                0, /* in_additional_info */
                                ph->fid_persistent,
                                ph->fid_volatile);
+
+       return cli->raw_status;
 }
 
 /***************************************************************
@@ -1710,25 +2056,28 @@ 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 a security descriptor.
+ Wrapper that allows SMB2 to query file system sizes.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
-                                       uint16_t fnum,
-                                       uint32_t sec_info,
-                                       TALLOC_CTX *mem_ctx,
-                                       struct security_descriptor **ppsd)
+NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
+                               uint64_t *total_allocation_units,
+                               uint64_t *caller_allocation_units,
+                               uint64_t *actual_allocation_units,
+                               uint64_t *sectors_per_allocation_unit,
+                               uint64_t *bytes_per_sector)
 {
        NTSTATUS status;
+       uint16_t fnum = 0xffff;
        DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
-       struct security_descriptor *lsd = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -1744,68 +2093,82 @@ NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
                goto fail;
        }
 
-       status = map_fnum_to_smb2_handle(cli,
-                                       fnum,
-                                       &ph);
+       /* 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;
        }
 
-       /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
+       status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+          level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
 
        status = smb2cli_query_info(cli->conn,
                                cli->timeout,
                                cli->smb2.session,
                                cli->smb2.tcon,
-                               3, /* in_info_type */
-                               0, /* in_file_info_class */
+                               SMB2_GETINFO_FS, /* in_info_type */
+                               /* in_file_info_class */
+                               SMB_FS_FULL_SIZE_INFORMATION - 1000,
                                0xFFFF, /* in_max_output_length */
                                NULL, /* in_input_buffer */
-                               sec_info, /* in_additional_info */
+                               0, /* in_additional_info */
                                0, /* in_flags */
                                ph->fid_persistent,
                                ph->fid_volatile,
                                frame,
                                &outbuf);
-
        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
-       /* Parse the reply. */
-       status = unmarshall_sec_desc(mem_ctx,
-                               outbuf.data,
-                               outbuf.length,
-                               &lsd);
-
-       if (!NT_STATUS_IS_OK(status)) {
+       if (outbuf.length < 32) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                goto fail;
        }
 
-       if (ppsd != NULL) {
-               *ppsd = lsd;
-       } else {
-               TALLOC_FREE(lsd);
+       *total_allocation_units = BIG_UINT(outbuf.data, 0);
+       *caller_allocation_units = BIG_UINT(outbuf.data, 8);
+       *actual_allocation_units = BIG_UINT(outbuf.data, 16);
+       *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
+       *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
+
+fail:
+
+       if (fnum != 0xffff) {
+               cli_smb2_close_fnum(cli, fnum);
        }
 
-  fail:
+       cli->raw_status = status;
 
        TALLOC_FREE(frame);
        return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to set a security descriptor.
+ Wrapper that allows SMB2 to query file system attributes.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
-                                       uint16_t fnum,
-                                       uint32_t sec_info,
-                                       const struct security_descriptor *sd)
+NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
 {
        NTSTATUS status;
-       DATA_BLOB inbuf = data_blob_null;
+       uint16_t fnum = 0xffff;
+       DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
 
@@ -1822,57 +2185,76 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
                goto fail;
        }
 
-       status = map_fnum_to_smb2_handle(cli,
-                                       fnum,
-                                       &ph);
+       /* 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 = marshall_sec_desc(frame,
-                               sd,
-                               &inbuf.data,
-                               &inbuf.length);
+       status = map_fnum_to_smb2_handle(cli, fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
 
-        if (!NT_STATUS_IS_OK(status)) {
+       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;
-        }
+       }
 
-       /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
+       if (outbuf.length < 12) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto fail;
+       }
 
-       status = smb2cli_set_info(cli->conn,
-                               cli->timeout,
-                               cli->smb2.session,
-                               cli->smb2.tcon,
-                               3, /* in_info_type */
-                               0, /* in_file_info_class */
-                               &inbuf, /* in_input_buffer */
-                               sec_info, /* in_additional_info */
-                               ph->fid_persistent,
-                               ph->fid_volatile);
+       *fs_attr = IVAL(outbuf.data, 0);
 
-  fail:
+fail:
+
+       if (fnum != 0xffff) {
+               cli_smb2_close_fnum(cli, fnum);
+       }
+
+       cli->raw_status = status;
 
        TALLOC_FREE(frame);
        return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to rename a file.
+ Wrapper that allows SMB2 to query file system volume info.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_rename(struct cli_state *cli,
-                       const char *fname_src,
-                       const char *fname_dst)
+NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
+                                TALLOC_CTX *mem_ctx,
+                                char **_volume_name,
+                                uint32_t *pserial_number,
+                                time_t *pdate)
 {
        NTSTATUS status;
-       DATA_BLOB inbuf = data_blob_null;
        uint16_t fnum = 0xffff;
+       DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
-       smb_ucs2_t *converted_str = NULL;
-       size_t converted_size_bytes = 0;
-       size_t namelen = 0;
+       uint32_t nlen;
+       char *volume_name = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -1888,106 +2270,119 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli,
                goto fail;
        }
 
-       status = get_fnum_from_path(cli,
-                               fname_src,
-                               DELETE_ACCESS,
-                               &fnum);
+       /* 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);
+       status = map_fnum_to_smb2_handle(cli, fnum, &ph);
        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
-       /* SMB2 is pickier about pathnames. Ensure it doesn't
-          start in a '\' */
-       if (*fname_dst == '\\') {
-               fname_dst++;
-       }
+       /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
+          level 1 (SMB_FS_VOLUME_INFORMATION). */
 
-       /* SMB2 is pickier about pathnames. Ensure it doesn't
-          end in a '\' */
-       namelen = strlen(fname_dst);
-       if (namelen > 0 && fname_dst[namelen-1] == '\\') {
-               char *modname = talloc_strdup(frame, fname_dst);
-               modname[namelen-1] = '\0';
-               fname_dst = modname;
+       status = smb2cli_query_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               SMB2_GETINFO_FS, /* in_info_type */
+                               /* in_file_info_class */
+                               SMB_FS_VOLUME_INFORMATION - 1000,
+                               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 (!push_ucs2_talloc(frame,
-                               &converted_str,
-                               fname_dst,
-                               &converted_size_bytes)) {
-               status = NT_STATUS_INVALID_PARAMETER;
+       if (outbuf.length < 24) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                goto fail;
        }
 
-       /* W2K8 insists the dest name is not null
-          terminated. Remove the last 2 zero bytes
-          and reduce the name length. */
-
-       if (converted_size_bytes < 2) {
-               status = NT_STATUS_INVALID_PARAMETER;
+       if (pdate) {
+               struct timespec ts;
+               ts = interpret_long_date((char *)outbuf.data);
+               *pdate = ts.tv_sec;
+       }
+       if (pserial_number) {
+               *pserial_number = IVAL(outbuf.data,8);
+       }
+       nlen = IVAL(outbuf.data,12);
+       if (nlen + 18 < 18) {
+               /* Integer wrap. */
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                goto fail;
        }
-       converted_size_bytes -= 2;
-
-       inbuf = data_blob_talloc_zero(frame,
-                               20 + converted_size_bytes);
-       if (inbuf.data == NULL) {
-               status = NT_STATUS_NO_MEMORY;
+       /*
+        * The next check is safe as we know outbuf.length >= 24
+        * from above.
+        */
+       if (nlen > (outbuf.length - 18)) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                goto fail;
        }
 
-       SIVAL(inbuf.data, 16, converted_size_bytes);
-       memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
-
-       /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
-          level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+       clistr_pull_talloc(mem_ctx,
+                       (const char *)outbuf.data,
+                       0,
+                       &volume_name,
+                       outbuf.data + 18,
+                       nlen,
+                       STR_UNICODE);
+       if (volume_name == NULL) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
 
-       status = smb2cli_set_info(cli->conn,
-                               cli->timeout,
-                               cli->smb2.session,
-                               cli->smb2.tcon,
-                               1, /* in_info_type */
-                               SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
-                               &inbuf, /* in_input_buffer */
-                               0, /* in_additional_info */
-                               ph->fid_persistent,
-                               ph->fid_volatile);
+       *_volume_name = volume_name;
 
-  fail:
+fail:
 
        if (fnum != 0xffff) {
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
 
+
 /***************************************************************
- Wrapper that allows SMB2 to set an EA on a fnum.
+ Wrapper that allows SMB2 to query a security descriptor.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
-                       uint16_t fnum,
-                       const char *ea_name,
-                       const char *ea_val,
-                       size_t ea_len)
+NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
+                                       uint16_t fnum,
+                                       uint32_t sec_info,
+                                       TALLOC_CTX *mem_ctx,
+                                       struct security_descriptor **ppsd)
 {
        NTSTATUS status;
-       DATA_BLOB inbuf = data_blob_null;
-       size_t bloblen = 0;
-       char *ea_name_ascii = NULL;
-       size_t namelen = 0;
+       DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
+       struct security_descriptor *lsd = NULL;
        TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -2010,73 +2405,65 @@ NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
                goto fail;
        }
 
-       /* Marshall the SMB2 EA data. */
-       if (ea_len > 0xFFFF) {
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
+       /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
 
-       if (!push_ascii_talloc(frame,
-                               &ea_name_ascii,
-                               ea_name,
-                               &namelen)) {
-               status = NT_STATUS_INVALID_PARAMETER;
-               goto fail;
-       }
+       status = smb2cli_query_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               3, /* in_info_type */
+                               0, /* in_file_info_class */
+                               0xFFFF, /* in_max_output_length */
+                               NULL, /* in_input_buffer */
+                               sec_info, /* in_additional_info */
+                               0, /* in_flags */
+                               ph->fid_persistent,
+                               ph->fid_volatile,
+                               frame,
+                               &outbuf);
 
-       if (namelen < 2 || namelen > 0xFF) {
-               status = NT_STATUS_INVALID_PARAMETER;
+       if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
-       bloblen = 8 + ea_len + namelen;
-       /* Round up to a 4 byte boundary. */
-       bloblen = ((bloblen + 3)&~3);
+       /* Parse the reply. */
+       status = unmarshall_sec_desc(mem_ctx,
+                               outbuf.data,
+                               outbuf.length,
+                               &lsd);
 
-       inbuf = data_blob_talloc_zero(frame, bloblen);
-       if (inbuf.data == NULL) {
-               status = NT_STATUS_NO_MEMORY;
+       if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
-       /* namelen doesn't include the NULL byte. */
-       SCVAL(inbuf.data, 5, namelen - 1);
-       SSVAL(inbuf.data, 6, ea_len);
-       memcpy(inbuf.data + 8, ea_name_ascii, namelen);
-       memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
-
-       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
-          level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
 
-       status = smb2cli_set_info(cli->conn,
-                               cli->timeout,
-                               cli->smb2.session,
-                               cli->smb2.tcon,
-                               1, /* in_info_type */
-                               SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
-                               &inbuf, /* in_input_buffer */
-                               0, /* in_additional_info */
-                               ph->fid_persistent,
-                               ph->fid_volatile);
+       if (ppsd != NULL) {
+               *ppsd = lsd;
+       } else {
+               TALLOC_FREE(lsd);
+       }
 
   fail:
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to set an EA on a pathname.
+ Wrapper that allows SMB2 to set a security descriptor.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
-                       const char *name,
-                       const char *ea_name,
-                       const char *ea_val,
-                       size_t ea_len)
+NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
+                                       uint16_t fnum,
+                                       uint32_t sec_info,
+                                       const struct security_descriptor *sd)
 {
        NTSTATUS status;
-       uint16_t fnum = 0xffff;
+       DATA_BLOB inbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -2091,56 +2478,62 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
                goto fail;
        }
 
-       status = get_fnum_from_path(cli,
-                               name,
-                               FILE_WRITE_EA,
-                               &fnum);
-
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &ph);
        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }
 
-       status = cli_set_ea_fnum(cli,
-                               fnum,
-                               ea_name,
-                               ea_val,
-                               ea_len);
-       if (!NT_STATUS_IS_OK(status)) {
+       status = marshall_sec_desc(frame,
+                               sd,
+                               &inbuf.data,
+                               &inbuf.length);
+
+        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
-       }
+        }
+
+       /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
+
+       status = smb2cli_set_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               3, /* in_info_type */
+                               0, /* in_file_info_class */
+                               &inbuf, /* in_input_buffer */
+                               sec_info, /* in_additional_info */
+                               ph->fid_persistent,
+                               ph->fid_volatile);
 
   fail:
 
-       if (fnum != 0xffff) {
-               cli_smb2_close_fnum(cli, fnum);
-       }
+       cli->raw_status = status;
 
+       TALLOC_FREE(frame);
        return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 to get an EA list on a pathname.
+ Wrapper that allows SMB2 to rename a file.
  Synchronous only.
 ***************************************************************/
 
-NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
-                               const char *name,
-                               TALLOC_CTX *ctx,
-                               size_t *pnum_eas,
-                               struct ea_struct **pea_array)
+NTSTATUS cli_smb2_rename(struct cli_state *cli,
+                        const char *fname_src,
+                        const char *fname_dst,
+                        bool replace)
 {
        NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
        uint16_t fnum = 0xffff;
-       DATA_BLOB outbuf = data_blob_null;
        struct smb2_hnd *ph = NULL;
-       struct ea_list *ea_list = NULL;
-       struct ea_list *eal = NULL;
-       size_t ea_count = 0;
+       smb_ucs2_t *converted_str = NULL;
+       size_t converted_size_bytes = 0;
+       size_t namelen = 0;
        TALLOC_CTX *frame = talloc_stackframe();
 
-       *pnum_eas = 0;
-       *pea_array = NULL;
-
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -2155,8 +2548,8 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
        }
 
        status = get_fnum_from_path(cli,
-                               name,
-                               FILE_READ_EA,
+                               fname_src,
+                               DELETE_ACCESS,
                                &fnum);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -2170,738 +2563,1851 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
                goto fail;
        }
 
-       /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
-          level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+       /* SMB2 is pickier about pathnames. Ensure it doesn't
+          start in a '\' */
+       if (*fname_dst == '\\') {
+               fname_dst++;
+       }
 
-       status = smb2cli_query_info(cli->conn,
-                               cli->timeout,
-                               cli->smb2.session,
-                               cli->smb2.tcon,
-                               1, /* in_info_type */
-                               SMB_FILE_FULL_EA_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);
+       /* SMB2 is pickier about pathnames. Ensure it doesn't
+          end in a '\' */
+       namelen = strlen(fname_dst);
+       if (namelen > 0 && fname_dst[namelen-1] == '\\') {
+               char *modname = talloc_strdup(frame, fname_dst);
+               modname[namelen-1] = '\0';
+               fname_dst = modname;
+       }
 
-       if (!NT_STATUS_IS_OK(status)) {
+       if (!push_ucs2_talloc(frame,
+                               &converted_str,
+                               fname_dst,
+                               &converted_size_bytes)) {
+               status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
 
-       /* Parse the reply. */
-       ea_list = read_nttrans_ea_list(ctx,
-                               (const char *)outbuf.data,
-                               outbuf.length);
-       if (ea_list == NULL) {
-               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+       /* W2K8 insists the dest name is not null
+          terminated. Remove the last 2 zero bytes
+          and reduce the name length. */
+
+       if (converted_size_bytes < 2) {
+               status = NT_STATUS_INVALID_PARAMETER;
                goto fail;
        }
+       converted_size_bytes -= 2;
 
-       /* Convert to an array. */
-       for (eal = ea_list; eal; eal = eal->next) {
-               ea_count++;
+       inbuf = data_blob_talloc_zero(frame,
+                               20 + converted_size_bytes);
+       if (inbuf.data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
 
-       if (ea_count) {
-               *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
-               if (*pea_array == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto fail;
-               }
-               ea_count = 0;
-               for (eal = ea_list; eal; eal = eal->next) {
-                       (*pea_array)[ea_count++] = eal->ea;
-               }
-               *pnum_eas = ea_count;
+       if (replace) {
+               SCVAL(inbuf.data, 0, 1);
        }
 
+       SIVAL(inbuf.data, 16, converted_size_bytes);
+       memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
+
+       /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
+          level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
+
+       status = smb2cli_set_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               1, /* in_info_type */
+                               SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
+                               &inbuf, /* in_input_buffer */
+                               0, /* in_additional_info */
+                               ph->fid_persistent,
+                               ph->fid_volatile);
+
   fail:
 
        if (fnum != 0xffff) {
                cli_smb2_close_fnum(cli, fnum);
        }
 
+       cli->raw_status = status;
+
        TALLOC_FREE(frame);
        return status;
 }
 
-struct cli_smb2_read_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       struct smb2_hnd *ph;
-       uint64_t start_offset;
-       uint32_t size;
-       uint32_t received;
-       uint8_t *buf;
-};
-
-static void cli_smb2_read_done(struct tevent_req *subreq);
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a fnum.
+ Synchronous only.
+***************************************************************/
 
-struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
-                               struct tevent_context *ev,
-                               struct cli_state *cli,
-                               uint16_t fnum,
-                               off_t offset,
-                               size_t size)
+NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
+                       uint16_t fnum,
+                       const char *ea_name,
+                       const char *ea_val,
+                       size_t ea_len)
 {
        NTSTATUS status;
-       struct tevent_req *req, *subreq;
-       struct cli_smb2_read_state *state;
-
-       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
-       if (req == NULL) {
-               return NULL;
+       DATA_BLOB inbuf = data_blob_null;
+       size_t bloblen = 0;
+       char *ea_name_ascii = NULL;
+       size_t namelen = 0;
+       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;
        }
-       state->ev = ev;
-       state->cli = cli;
-       state->start_offset = (uint64_t)offset;
-       state->size = (uint32_t)size;
-       state->received = 0;
-       state->buf = NULL;
 
        status = map_fnum_to_smb2_handle(cli,
                                        fnum,
-                                       &state->ph);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
+                                       &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
 
-       subreq = smb2cli_read_send(state,
-                               state->ev,
-                               state->cli->conn,
-                               state->cli->timeout,
-                               state->cli->smb2.session,
-                               state->cli->smb2.tcon,
-                               state->size,
-                               state->start_offset,
-                               state->ph->fid_persistent,
-                               state->ph->fid_volatile,
-                               0, /* minimum_count */
-                               0); /* remaining_bytes */
-
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       /* Marshall the SMB2 EA data. */
+       if (ea_len > 0xFFFF) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
        }
-       tevent_req_set_callback(subreq, cli_smb2_read_done, req);
-       return req;
-}
 
-static void cli_smb2_read_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_smb2_read_state *state = tevent_req_data(
-               req, struct cli_smb2_read_state);
-       NTSTATUS status;
+       if (!push_ascii_talloc(frame,
+                               &ea_name_ascii,
+                               ea_name,
+                               &namelen)) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
 
-       status = smb2cli_read_recv(subreq, state,
-                                  &state->buf, &state->received);
-       if (tevent_req_nterror(req, status)) {
-               return;
+       if (namelen < 2 || namelen > 0xFF) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
        }
 
-       if (state->received > state->size) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-               return;
+       bloblen = 8 + ea_len + namelen;
+       /* Round up to a 4 byte boundary. */
+       bloblen = ((bloblen + 3)&~3);
+
+       inbuf = data_blob_talloc_zero(frame, bloblen);
+       if (inbuf.data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
        }
+       /* namelen doesn't include the NULL byte. */
+       SCVAL(inbuf.data, 5, namelen - 1);
+       SSVAL(inbuf.data, 6, ea_len);
+       memcpy(inbuf.data + 8, ea_name_ascii, namelen);
+       memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
 
-       tevent_req_done(req);
-}
+       /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
+          level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
 
-NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
-                               ssize_t *received,
-                               uint8_t **rcvbuf)
-{
-       NTSTATUS status;
-       struct cli_smb2_read_state *state = tevent_req_data(
-                               req, struct cli_smb2_read_state);
+       status = smb2cli_set_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               1, /* in_info_type */
+                               SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
+                               &inbuf, /* in_input_buffer */
+                               0, /* in_additional_info */
+                               ph->fid_persistent,
+                               ph->fid_volatile);
 
-       if (tevent_req_is_nterror(req, &status)) {
-               state->cli->raw_status = status;
-               return status;
-       }
-       /*
-        * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
-        * better make sure that you copy it away before you talloc_free(req).
-        * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
-        */
-       *received = (ssize_t)state->received;
-       *rcvbuf = state->buf;
-       state->cli->raw_status = NT_STATUS_OK;
-       return NT_STATUS_OK;
-}
+  fail:
 
-struct cli_smb2_write_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       struct smb2_hnd *ph;
-       uint32_t flags;
-       const uint8_t *buf;
-       uint64_t offset;
-       uint32_t size;
-       uint32_t written;
-};
+       cli->raw_status = status;
 
-static void cli_smb2_write_written(struct tevent_req *req);
+       TALLOC_FREE(frame);
+       return status;
+}
 
-struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
-                                       struct tevent_context *ev,
-                                       struct cli_state *cli,
-                                       uint16_t fnum,
-                                       uint16_t mode,
-                                       const uint8_t *buf,
-                                       off_t offset,
-                                       size_t size)
+/***************************************************************
+ Wrapper that allows SMB2 to set an EA on a pathname.
+ Synchronous only.
+***************************************************************/
+
+NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
+                       const char *name,
+                       const char *ea_name,
+                       const char *ea_val,
+                       size_t ea_len)
 {
        NTSTATUS status;
-       struct tevent_req *req, *subreq = NULL;
-       struct cli_smb2_write_state *state = NULL;
+       uint16_t fnum = 0xffff;
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
-       if (req == NULL) {
-               return NULL;
+       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;
        }
-       state->ev = ev;
-       state->cli = cli;
-       /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
-       state->flags = (uint32_t)mode;
-       state->buf = buf;
-       state->offset = (uint64_t)offset;
-       state->size = (uint32_t)size;
-       state->written = 0;
 
-       status = map_fnum_to_smb2_handle(cli,
-                                       fnum,
-                                       &state->ph);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
        }
 
-       subreq = smb2cli_write_send(state,
-                               state->ev,
-                               state->cli->conn,
-                               state->cli->timeout,
-                               state->cli->smb2.session,
-                               state->cli->smb2.tcon,
-                               state->size,
-                               state->offset,
-                               state->ph->fid_persistent,
-                               state->ph->fid_volatile,
-                               0, /* remaining_bytes */
-                               state->flags, /* flags */
-                               state->buf);
+       status = get_fnum_from_path(cli,
+                               name,
+                               FILE_WRITE_EA,
+                               &fnum);
 
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
-       tevent_req_set_callback(subreq, cli_smb2_write_written, req);
-       return req;
-}
-
-static void cli_smb2_write_written(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_smb2_write_state *state = tevent_req_data(
-               req, struct cli_smb2_write_state);
-        NTSTATUS status;
-       uint32_t written;
 
-       status = smb2cli_write_recv(subreq, &written);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
-               return;
+       status = cli_set_ea_fnum(cli,
+                               fnum,
+                               ea_name,
+                               ea_val,
+                               ea_len);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
 
-       state->written = written;
-
-       tevent_req_done(req);
-}
-
-NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
-                            size_t *pwritten)
-{
-       struct cli_smb2_write_state *state = tevent_req_data(
-               req, struct cli_smb2_write_state);
-       NTSTATUS status;
+  fail:
 
-       if (tevent_req_is_nterror(req, &status)) {
-               state->cli->raw_status = status;
-               tevent_req_received(req);
-               return status;
+       if (fnum != 0xffff) {
+               cli_smb2_close_fnum(cli, fnum);
        }
 
-       if (pwritten != NULL) {
-               *pwritten = (size_t)state->written;
-       }
-       state->cli->raw_status = NT_STATUS_OK;
-       tevent_req_received(req);
-       return NT_STATUS_OK;
+       cli->raw_status = status;
+
+       return status;
 }
 
 /***************************************************************
- Wrapper that allows SMB2 async write using an fnum.
- This is mostly cut-and-paste from Volker's code inside
- source3/libsmb/clireadwrite.c, adapted for SMB2.
-
- Done this way so I can reuse all the logic inside cli_push()
- for free :-).
+ Wrapper that allows SMB2 to get an EA list on a pathname.
+ Synchronous only.
 ***************************************************************/
 
-struct cli_smb2_writeall_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       struct smb2_hnd *ph;
-       uint32_t flags;
-       const uint8_t *buf;
-       uint64_t offset;
-       uint32_t size;
-       uint32_t written;
-};
-
-static void cli_smb2_writeall_written(struct tevent_req *req);
-
-struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
-                                       struct tevent_context *ev,
-                                       struct cli_state *cli,
-                                       uint16_t fnum,
-                                       uint16_t mode,
-                                       const uint8_t *buf,
-                                       off_t offset,
-                                       size_t size)
+NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
+                               const char *name,
+                               TALLOC_CTX *ctx,
+                               size_t *pnum_eas,
+                               struct ea_struct **pea_array)
 {
        NTSTATUS status;
-       struct tevent_req *req, *subreq = NULL;
-       struct cli_smb2_writeall_state *state = NULL;
-       uint32_t to_write;
-       uint32_t max_size;
-       bool ok;
+       uint16_t fnum = 0xffff;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       struct ea_list *ea_list = NULL;
+       struct ea_list *eal = NULL;
+       size_t ea_count = 0;
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
-       if (req == NULL) {
-               return NULL;
+       *pnum_eas = 0;
+       *pea_array = NULL;
+
+       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 = get_fnum_from_path(cli,
+                               name,
+                               FILE_READ_EA,
+                               &fnum);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
-       state->ev = ev;
-       state->cli = cli;
-       /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
-       state->flags = (uint32_t)mode;
-       state->buf = buf;
-       state->offset = (uint64_t)offset;
-       state->size = (uint32_t)size;
-       state->written = 0;
 
        status = map_fnum_to_smb2_handle(cli,
                                        fnum,
-                                       &state->ph);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
+                                       &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
 
-       to_write = state->size;
-       max_size = smb2cli_conn_max_write_size(state->cli->conn);
-       to_write = MIN(max_size, to_write);
-       ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
-       if (ok) {
-               to_write = MIN(max_size, to_write);
+       /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
+          level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
+
+       status = smb2cli_query_info(cli->conn,
+                               cli->timeout,
+                               cli->smb2.session,
+                               cli->smb2.tcon,
+                               1, /* in_info_type */
+                               SMB_FILE_FULL_EA_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 fail;
        }
 
-       subreq = smb2cli_write_send(state,
-                               state->ev,
-                               state->cli->conn,
-                               state->cli->timeout,
-                               state->cli->smb2.session,
-                               state->cli->smb2.tcon,
-                               to_write,
-                               state->offset,
-                               state->ph->fid_persistent,
-                               state->ph->fid_volatile,
-                               0, /* remaining_bytes */
-                               state->flags, /* flags */
-                               state->buf + state->written);
+       /* Parse the reply. */
+       ea_list = read_nttrans_ea_list(ctx,
+                               (const char *)outbuf.data,
+                               outbuf.length);
+       if (ea_list == NULL) {
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               goto fail;
+       }
 
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+       /* Convert to an array. */
+       for (eal = ea_list; eal; eal = eal->next) {
+               ea_count++;
        }
-       tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
-       return req;
+
+       if (ea_count) {
+               *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
+               if (*pea_array == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
+               ea_count = 0;
+               for (eal = ea_list; eal; eal = eal->next) {
+                       (*pea_array)[ea_count++] = eal->ea;
+               }
+               *pnum_eas = ea_count;
+       }
+
+  fail:
+
+       if (fnum != 0xffff) {
+               cli_smb2_close_fnum(cli, fnum);
+       }
+
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
 }
 
-static void cli_smb2_writeall_written(struct tevent_req *subreq)
+/***************************************************************
+ 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)
 {
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct cli_smb2_writeall_state *state = tevent_req_data(
-               req, struct cli_smb2_writeall_state);
-        NTSTATUS status;
-       uint32_t written, to_write;
-       uint32_t max_size;
-       bool ok;
+       NTSTATUS status;
+       DATA_BLOB inbuf = data_blob_null;
+       DATA_BLOB info_blob = 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;
+       struct smb2_query_quota_info query = {0};
+       struct file_get_quota_info info = {0};
+       enum ndr_err_code err;
+       struct ndr_push *ndr_push = NULL;
 
-       status = smb2cli_write_recv(subreq, &written);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
-               return;
+       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;
        }
 
-       state->written += written;
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
 
-       if (state->written > state->size) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
-               return;
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
        }
 
-       to_write = state->size - state->written;
+       sid_len = ndr_size_dom_sid(&pqt->sid, 0);
 
-       if (to_write == 0) {
-               tevent_req_done(req);
-               return;
+       query.return_single = 1;
+       if (sid_len < 0) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
        }
 
-       max_size = smb2cli_conn_max_write_size(state->cli->conn);
-       to_write = MIN(max_size, to_write);
-       ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
-       if (ok) {
-               to_write = MIN(max_size, to_write);
+       info.next_entry_offset = 0;
+       info.sid_length = sid_len;
+       info.sid = pqt->sid;
+
+       err = ndr_push_struct_blob(
+                       &info_blob,
+                       frame,
+                       &info,
+                       (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
        }
 
-       subreq = smb2cli_write_send(state,
-                               state->ev,
-                               state->cli->conn,
-                               state->cli->timeout,
-                               state->cli->smb2.session,
-                               state->cli->smb2.tcon,
-                               to_write,
-                               state->offset + state->written,
-                               state->ph->fid_persistent,
-                               state->ph->fid_volatile,
-                               0, /* remaining_bytes */
-                               state->flags, /* flags */
-                               state->buf + state->written);
+       query.sid_list_length = info_blob.length;
+       ndr_push = ndr_push_init_ctx(frame);
+       if (!ndr_push) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
 
-       if (tevent_req_nomem(subreq, req)) {
-               return;
+       err = ndr_push_smb2_query_quota_info(ndr_push,
+                                            NDR_SCALARS | NDR_BUFFERS,
+                                            &query);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
        }
-       tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
-}
 
-NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
-                               size_t *pwritten)
-{
-       struct cli_smb2_writeall_state *state = tevent_req_data(
-               req, struct cli_smb2_writeall_state);
-       NTSTATUS status;
+       err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
+                                  info_blob.length);
 
-       if (tevent_req_is_nterror(req, &status)) {
-               state->cli->raw_status = status;
-               return status;
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto fail;
        }
-       if (pwritten != NULL) {
-               *pwritten = (size_t)state->written;
+       inbuf.data = ndr_push->data;
+       inbuf.length = ndr_push->offset;
+
+       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;
        }
-       state->cli->raw_status = NT_STATUS_OK;
-       return NT_STATUS_OK;
-}
 
-struct cli_smb2_splice_state {
-       struct tevent_context *ev;
-       struct cli_state *cli;
-       struct smb2_hnd *src_ph;
-       struct smb2_hnd *dst_ph;
-       int (*splice_cb)(off_t n, void *priv);
-       void *priv;
-       off_t written;
-       off_t size;
-       off_t src_offset;
-       off_t dst_offset;
-       bool resized;
-       struct req_resume_key_rsp resume_rsp;
-       struct srv_copychunk_copy cc_copy;
-};
+       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"));
+       }
 
-static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
-                                     struct tevent_req *req);
+fail:
+       cli->raw_status = status;
 
-static void cli_splice_copychunk_done(struct tevent_req *subreq)
+       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)
 {
-       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;
+       DATA_BLOB inbuf = data_blob_null;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct smb2_query_quota_info info = {0};
+       enum ndr_err_code err;
 
-       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;
+       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;
        }
 
-       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 (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
        }
 
-       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;
-               }
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
 
-               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;
-               }
+
+       info.restart_scan = first ? 1 : 0;
+
+       err = ndr_push_struct_blob(
+                       &inbuf,
+                       frame,
+                       &info,
+                       (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
+
+       if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               goto cleanup;
        }
 
-       cli_splice_copychunk_send(state, req);
+       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);
+
+       /*
+        * safeguard against panic from calling parse_user_quota_list with
+        * NULL buffer
+        */
+       if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
+               status = NT_STATUS_NO_MORE_ENTRIES;
+       }
+
+       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;
 }
 
-static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
-                                     struct tevent_req *req)
+/***************************************************************
+ 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)
 {
-       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;
+       NTSTATUS status;
+       DATA_BLOB outbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       if (state->size - state->written == 0) {
-               tevent_req_done(req);
-               return;
+       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;
        }
 
-       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++;
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
        }
 
-       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;
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
        }
 
-       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;
+       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;
        }
-       tevent_req_set_callback(subreq,
-                               cli_splice_copychunk_done,
-                               req);
+
+       status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
+
+cleanup:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
 }
 
-static void cli_splice_key_done(struct tevent_req *subreq)
+/***************************************************************
+ 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)
 {
-       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 inbuf = data_blob_null;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       DATA_BLOB out_input_buffer = data_blob_null;
-       DATA_BLOB out_output_buffer = data_blob_null;
+       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;
+       }
 
-       status = smb2cli_ioctl_recv(subreq, state,
-                                   &out_input_buffer,
-                                   &out_output_buffer);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
-               return;
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto cleanup;
        }
 
-       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;
+       status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
        }
 
-       memcpy(&state->cc_copy.source_key,
-              &state->resume_rsp.resume_key,
-              sizeof state->resume_rsp.resume_key);
+       status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
+       }
 
-       cli_splice_copychunk_send(state, req);
+       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;
 }
 
-struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+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;
+}
+
+struct cli_smb2_read_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct smb2_hnd *ph;
+       uint64_t start_offset;
+       uint32_t size;
+       uint32_t received;
+       uint8_t *buf;
+};
+
+static void cli_smb2_read_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct cli_state *cli,
+                               uint16_t fnum,
+                               off_t offset,
+                               size_t size)
+{
+       NTSTATUS status;
+       struct tevent_req *req, *subreq;
+       struct cli_smb2_read_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->start_offset = (uint64_t)offset;
+       state->size = (uint32_t)size;
+       state->received = 0;
+       state->buf = NULL;
+
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = smb2cli_read_send(state,
+                               state->ev,
+                               state->cli->conn,
+                               state->cli->timeout,
+                               state->cli->smb2.session,
+                               state->cli->smb2.tcon,
+                               state->size,
+                               state->start_offset,
+                               state->ph->fid_persistent,
+                               state->ph->fid_volatile,
+                               0, /* minimum_count */
+                               0); /* remaining_bytes */
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_smb2_read_done, req);
+       return req;
+}
+
+static void cli_smb2_read_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_read_state *state = tevent_req_data(
+               req, struct cli_smb2_read_state);
+       NTSTATUS status;
+
+       status = smb2cli_read_recv(subreq, state,
+                                  &state->buf, &state->received);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       if (state->received > state->size) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
+                               ssize_t *received,
+                               uint8_t **rcvbuf)
+{
+       NTSTATUS status;
+       struct cli_smb2_read_state *state = tevent_req_data(
+                               req, struct cli_smb2_read_state);
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               return status;
+       }
+       /*
+        * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
+        * better make sure that you copy it away before you talloc_free(req).
+        * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
+        */
+       *received = (ssize_t)state->received;
+       *rcvbuf = state->buf;
+       state->cli->raw_status = NT_STATUS_OK;
+       return NT_STATUS_OK;
+}
+
+struct cli_smb2_write_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct smb2_hnd *ph;
+       uint32_t flags;
+       const uint8_t *buf;
+       uint64_t offset;
+       uint32_t size;
+       uint32_t written;
+};
+
+static void cli_smb2_write_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       uint16_t fnum,
+                                       uint16_t mode,
+                                       const uint8_t *buf,
+                                       off_t offset,
+                                       size_t size)
+{
+       NTSTATUS status;
+       struct tevent_req *req, *subreq = NULL;
+       struct cli_smb2_write_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+       state->flags = (uint32_t)mode;
+       state->buf = buf;
+       state->offset = (uint64_t)offset;
+       state->size = (uint32_t)size;
+       state->written = 0;
+
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = smb2cli_write_send(state,
+                               state->ev,
+                               state->cli->conn,
+                               state->cli->timeout,
+                               state->cli->smb2.session,
+                               state->cli->smb2.tcon,
+                               state->size,
+                               state->offset,
+                               state->ph->fid_persistent,
+                               state->ph->fid_volatile,
+                               0, /* remaining_bytes */
+                               state->flags, /* flags */
+                               state->buf);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_smb2_write_written, req);
+       return req;
+}
+
+static void cli_smb2_write_written(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_write_state *state = tevent_req_data(
+               req, struct cli_smb2_write_state);
+        NTSTATUS status;
+       uint32_t written;
+
+       status = smb2cli_write_recv(subreq, &written);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       state->written = written;
+
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
+                            size_t *pwritten)
+{
+       struct cli_smb2_write_state *state = tevent_req_data(
+               req, struct cli_smb2_write_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               tevent_req_received(req);
+               return status;
+       }
+
+       if (pwritten != NULL) {
+               *pwritten = (size_t)state->written;
+       }
+       state->cli->raw_status = NT_STATUS_OK;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+/***************************************************************
+ Wrapper that allows SMB2 async write using an fnum.
+ This is mostly cut-and-paste from Volker's code inside
+ source3/libsmb/clireadwrite.c, adapted for SMB2.
+
+ Done this way so I can reuse all the logic inside cli_push()
+ for free :-).
+***************************************************************/
+
+struct cli_smb2_writeall_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct smb2_hnd *ph;
+       uint32_t flags;
+       const uint8_t *buf;
+       uint64_t offset;
+       uint32_t size;
+       uint32_t written;
+};
+
+static void cli_smb2_writeall_written(struct tevent_req *req);
+
+struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       uint16_t fnum,
+                                       uint16_t mode,
+                                       const uint8_t *buf,
+                                       off_t offset,
+                                       size_t size)
+{
+       NTSTATUS status;
+       struct tevent_req *req, *subreq = NULL;
+       struct cli_smb2_writeall_state *state = NULL;
+       uint32_t to_write;
+       uint32_t max_size;
+       bool ok;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
+       state->flags = (uint32_t)mode;
+       state->buf = buf;
+       state->offset = (uint64_t)offset;
+       state->size = (uint32_t)size;
+       state->written = 0;
+
+       status = map_fnum_to_smb2_handle(cli,
+                                       fnum,
+                                       &state->ph);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       to_write = state->size;
+       max_size = smb2cli_conn_max_write_size(state->cli->conn);
+       to_write = MIN(max_size, to_write);
+       ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+       if (ok) {
+               to_write = MIN(max_size, to_write);
+       }
+
+       subreq = smb2cli_write_send(state,
+                               state->ev,
+                               state->cli->conn,
+                               state->cli->timeout,
+                               state->cli->smb2.session,
+                               state->cli->smb2.tcon,
+                               to_write,
+                               state->offset,
+                               state->ph->fid_persistent,
+                               state->ph->fid_volatile,
+                               0, /* remaining_bytes */
+                               state->flags, /* flags */
+                               state->buf + state->written);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+       return req;
+}
+
+static void cli_smb2_writeall_written(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_writeall_state *state = tevent_req_data(
+               req, struct cli_smb2_writeall_state);
+        NTSTATUS status;
+       uint32_t written, to_write;
+       uint32_t max_size;
+       bool ok;
+
+       status = smb2cli_write_recv(subreq, &written);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       state->written += written;
+
+       if (state->written > state->size) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       to_write = state->size - state->written;
+
+       if (to_write == 0) {
+               tevent_req_done(req);
+               return;
+       }
+
+       max_size = smb2cli_conn_max_write_size(state->cli->conn);
+       to_write = MIN(max_size, to_write);
+       ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
+       if (ok) {
+               to_write = MIN(max_size, to_write);
+       }
+
+       subreq = smb2cli_write_send(state,
+                               state->ev,
+                               state->cli->conn,
+                               state->cli->timeout,
+                               state->cli->smb2.session,
+                               state->cli->smb2.tcon,
+                               to_write,
+                               state->offset + state->written,
+                               state->ph->fid_persistent,
+                               state->ph->fid_volatile,
+                               0, /* remaining_bytes */
+                               state->flags, /* flags */
+                               state->buf + state->written);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
+}
+
+NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
+                               size_t *pwritten)
+{
+       struct cli_smb2_writeall_state *state = tevent_req_data(
+               req, struct cli_smb2_writeall_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               return status;
+       }
+       if (pwritten != NULL) {
+               *pwritten = (size_t)state->written;
+       }
+       state->cli->raw_status = NT_STATUS_OK;
+       return NT_STATUS_OK;
+}
+
+struct cli_smb2_splice_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct smb2_hnd *src_ph;
+       struct smb2_hnd *dst_ph;
+       int (*splice_cb)(off_t n, void *priv);
+       void *priv;
+       off_t written;
+       off_t size;
+       off_t src_offset;
+       off_t dst_offset;
+       bool resized;
+       struct req_resume_key_rsp resume_rsp;
+       struct srv_copychunk_copy cc_copy;
+};
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+                                     struct tevent_req *req);
+
+static void cli_splice_copychunk_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_splice_state *state =
+               tevent_req_data(req,
+               struct cli_smb2_splice_state);
+       struct smbXcli_conn *conn = state->cli->conn;
+       DATA_BLOB out_input_buffer = data_blob_null;
+       DATA_BLOB out_output_buffer = data_blob_null;
+       struct srv_copychunk_rsp cc_copy_rsp;
+       enum ndr_err_code ndr_ret;
+       NTSTATUS status;
+
+       status = smb2cli_ioctl_recv(subreq, state,
+                                   &out_input_buffer,
+                                   &out_output_buffer);
+       TALLOC_FREE(subreq);
+       if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
+            state->resized) && tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
+       if (ndr_ret != NDR_ERR_SUCCESS) {
+               DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+               uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
+                            cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
+               if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
+                    max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
+                    tevent_req_nterror(req, status)) {
+                       return;
+               }
+
+               state->resized = true;
+               smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
+               smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
+       } else {
+               if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+                   (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
+                   (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
+                       tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+                       return;
+               }
+               state->src_offset += cc_copy_rsp.total_bytes_written;
+               state->dst_offset += cc_copy_rsp.total_bytes_written;
+               state->written += cc_copy_rsp.total_bytes_written;
+               if (!state->splice_cb(state->written, state->priv)) {
+                       tevent_req_nterror(req, NT_STATUS_CANCELLED);
+                       return;
+               }
+       }
+
+       cli_splice_copychunk_send(state, req);
+}
+
+static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
+                                     struct tevent_req *req)
+{
+       struct tevent_req *subreq;
+       enum ndr_err_code ndr_ret;
+       struct smbXcli_conn *conn = state->cli->conn;
+       struct srv_copychunk_copy *cc_copy = &state->cc_copy;
+       off_t src_offset = state->src_offset;
+       off_t dst_offset = state->dst_offset;
+       uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
+                              state->size - state->written);
+       DATA_BLOB in_input_buffer = data_blob_null;
+       DATA_BLOB in_output_buffer = data_blob_null;
+
+       if (state->size - state->written == 0) {
+               tevent_req_done(req);
+               return;
+       }
+
+       cc_copy->chunk_count = 0;
+       while (req_len) {
+               cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
+               cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
+               cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
+                                                                  smb2cli_conn_cc_chunk_len(conn));
+               if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
+                       tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+                       return;
+               }
+               req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
+               if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
+                   (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
+                       tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
+                       return;
+               }
+               src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+               dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
+               cc_copy->chunk_count++;
+       }
+
+       ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
+                                      (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
+       if (ndr_ret != NDR_ERR_SUCCESS) {
+               DEBUG(0, ("failed to marshall copy chunk req\n"));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
+                              state->cli->timeout,
+                              state->cli->smb2.session,
+                              state->cli->smb2.tcon,
+                              state->dst_ph->fid_persistent, /* in_fid_persistent */
+                              state->dst_ph->fid_volatile, /* in_fid_volatile */
+                              FSCTL_SRV_COPYCHUNK_WRITE,
+                              0, /* in_max_input_length */
+                              &in_input_buffer,
+                              12, /* in_max_output_length */
+                              &in_output_buffer,
+                              SMB2_IOCTL_FLAG_IS_FSCTL);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq,
+                               cli_splice_copychunk_done,
+                               req);
+}
+
+static void cli_splice_key_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_splice_state *state =
+               tevent_req_data(req,
+               struct cli_smb2_splice_state);
+       enum ndr_err_code ndr_ret;
+       NTSTATUS status;
+
+       DATA_BLOB out_input_buffer = data_blob_null;
+       DATA_BLOB out_output_buffer = data_blob_null;
+
+       status = smb2cli_ioctl_recv(subreq, state,
+                                   &out_input_buffer,
+                                   &out_output_buffer);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+
+       ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
+                       state, &state->resume_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
+       if (ndr_ret != NDR_ERR_SUCCESS) {
+               DEBUG(0, ("failed to unmarshall resume key rsp\n"));
+               tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+               return;
+       }
+
+       memcpy(&state->cc_copy.source_key,
+              &state->resume_rsp.resume_key,
+              sizeof state->resume_rsp.resume_key);
+
+       cli_splice_copychunk_send(state, req);
+}
+
+struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               struct cli_state *cli,
+                               uint16_t src_fnum, uint16_t dst_fnum,
+                               off_t size, off_t src_offset, off_t dst_offset,
+                               int (*splice_cb)(off_t n, void *priv),
+                               void *priv)
+{
+       struct tevent_req *req;
+       struct tevent_req *subreq;
+       struct cli_smb2_splice_state *state;
+       NTSTATUS status;
+       DATA_BLOB in_input_buffer = data_blob_null;
+       DATA_BLOB in_output_buffer = data_blob_null;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->cli = cli;
+       state->ev = ev;
+       state->splice_cb = splice_cb;
+       state->priv = priv;
+       state->size = size;
+       state->written = 0;
+       state->src_offset = src_offset;
+       state->dst_offset = dst_offset;
+       state->cc_copy.chunks = talloc_array(state,
+                                            struct srv_copychunk,
+                                            smb2cli_conn_cc_max_chunks(cli->conn));
+       if (state->cc_copy.chunks == NULL) {
+               return NULL;
+       }
+
+       status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
+       if (tevent_req_nterror(req, status))
+               return tevent_req_post(req, ev);
+
+       status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
+       if (tevent_req_nterror(req, status))
+               return tevent_req_post(req, ev);
+
+       subreq = smb2cli_ioctl_send(state, ev, cli->conn,
+                              cli->timeout,
+                              cli->smb2.session,
+                              cli->smb2.tcon,
+                              state->src_ph->fid_persistent, /* in_fid_persistent */
+                              state->src_ph->fid_volatile, /* in_fid_volatile */
+                              FSCTL_SRV_REQUEST_RESUME_KEY,
+                              0, /* in_max_input_length */
+                              &in_input_buffer,
+                              32, /* in_max_output_length */
+                              &in_output_buffer,
+                              SMB2_IOCTL_FLAG_IS_FSCTL);
+       if (tevent_req_nomem(subreq, req)) {
+               return NULL;
+       }
+       tevent_req_set_callback(subreq,
+                               cli_splice_key_done,
+                               req);
+
+       return req;
+}
+
+NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
+{
+       struct cli_smb2_splice_state *state = tevent_req_data(
+               req, struct cli_smb2_splice_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               state->cli->raw_status = status;
+               tevent_req_received(req);
+               return status;
+       }
+       if (written != NULL) {
+               *written = state->written;
+       }
+       state->cli->raw_status = NT_STATUS_OK;
+       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;
+}
+
+NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
+                        uint32_t buffer_size, uint32_t completion_filter,
+                        bool recursive, TALLOC_CTX *mem_ctx,
+                        struct notify_change **pchanges,
+                        uint32_t *pnum_changes)
+{
+       NTSTATUS status;
+       struct smb2_hnd *ph = NULL;
+       TALLOC_CTX *frame = talloc_stackframe();
+       uint8_t *base;
+       uint32_t len, ofs;
+       struct notify_change *changes = NULL;
+       size_t num_changes = 0;
+
+       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;
+       }
+
+       status = smb2cli_notify(cli->conn, cli->timeout,
+                               cli->smb2.session, cli->smb2.tcon,
+                               buffer_size,
+                               ph->fid_persistent, ph->fid_volatile,
+                               completion_filter, recursive,
+                               frame, &base, &len);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
+               len = 0;
+               status = NT_STATUS_OK;
+       }
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       ofs = 0;
+
+       while (len - ofs >= 12) {
+               struct notify_change *tmp;
+               struct notify_change *c;
+               uint32_t next_ofs = IVAL(base, ofs);
+               uint32_t file_name_length = IVAL(base, ofs+8);
+               size_t namelen;
+               bool ok;
+
+               tmp = talloc_realloc(frame, changes, struct notify_change,
+                                    num_changes + 1);
+               if (tmp == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
+               changes = tmp;
+               c = &changes[num_changes];
+               num_changes += 1;
+
+               if (smb_buffer_oob(len, ofs, next_ofs) ||
+                   smb_buffer_oob(len, ofs+12, file_name_length)) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto fail;
+               }
+
+               c->action = IVAL(base, ofs+4);
+
+               ok = convert_string_talloc(changes, CH_UTF16LE, CH_UNIX,
+                                          base + ofs + 12, file_name_length,
+                                          &c->name, &namelen);
+               if (!ok) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       goto fail;
+               }
+
+               if (next_ofs == 0) {
+                       break;
+               }
+               ofs += next_ofs;
+       }
+
+       *pchanges = talloc_move(mem_ctx, &changes);
+       *pnum_changes = num_changes;
+       status = NT_STATUS_OK;
+
+fail:
+       cli->raw_status = status;
+
+       TALLOC_FREE(frame);
+       return status;
+}
+
+struct cli_smb2_set_reparse_point_fnum_state {
+       struct cli_state *cli;
+       uint16_t fnum;
+       struct smb2_hnd *ph;
+       DATA_BLOB input_buffer;
+};
+
+static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_smb2_set_reparse_point_fnum_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)
+                               uint16_t fnum,
+                               DATA_BLOB in_buf)
 {
-       struct tevent_req *req;
-       struct tevent_req *subreq;
-       struct cli_smb2_splice_state *state;
+       struct tevent_req *req, *subreq;
+       struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
        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);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_smb2_set_reparse_point_fnum_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;
+
+       if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
        }
 
-       status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
-       if (tevent_req_nterror(req, status))
+       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);
+       }
 
-       status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
-       if (tevent_req_nterror(req, status))
+       state->input_buffer = data_blob_talloc(state,
+                                               in_buf.data,
+                                               in_buf.length);
+       if (state->input_buffer.data == NULL) {
+               tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
                return tevent_req_post(req, ev);
+       }
+
+       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_SET_REPARSE_POINT,
+                       0, /* in_max_input_length */
+                       &state->input_buffer ,
+                       0,
+                       NULL,
+                       SMB2_IOCTL_FLAG_IS_FSCTL);
 
-       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;
+               return tevent_req_post(req, ev);
        }
        tevent_req_set_callback(subreq,
-                               cli_splice_key_done,
+                               cli_smb2_set_reparse_point_fnum_done,
                                req);
 
        return req;
 }
 
-NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
+static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq)
 {
-       struct cli_smb2_splice_state *state = tevent_req_data(
-               req, struct cli_smb2_splice_state);
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_smb2_set_reparse_point_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_set_reparse_point_fnum_state);
        NTSTATUS status;
 
-       if (tevent_req_is_nterror(req, &status)) {
-               state->cli->raw_status = status;
-               tevent_req_received(req);
-               return status;
-       }
-       if (written != NULL) {
-               *written = state->written;
+       status = smb2cli_ioctl_recv(subreq, state,
+                               NULL,
+                               NULL);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
        }
-       state->cli->raw_status = NT_STATUS_OK;
-       tevent_req_received(req);
-       return NT_STATUS_OK;
+       tevent_req_done(req);
 }
 
-/***************************************************************
- SMB2 enum shadow copy data.
-***************************************************************/
+NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req)
+{
+        return tevent_req_simple_recv_ntstatus(req);
+}
 
-struct cli_smb2_shadow_copy_data_fnum_state {
+struct cli_smb2_get_reparse_point_fnum_state {
        struct cli_state *cli;
        uint16_t fnum;
        struct smb2_hnd *ph;
-       DATA_BLOB out_input_buffer;
-       DATA_BLOB out_output_buffer;
+       DATA_BLOB output_buffer;
 };
 
-static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
+static void cli_smb2_get_reparse_point_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 *cli_smb2_get_reparse_point_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;
+       struct cli_smb2_set_reparse_point_fnum_state *state = NULL;
        NTSTATUS status;
 
        req = tevent_req_create(mem_ctx, &state,
-                               struct cli_smb2_shadow_copy_data_fnum_state);
+                               struct cli_smb2_get_reparse_point_fnum_state);
        if (req == NULL) {
                return NULL;
        }
@@ -2919,186 +4425,64 @@ static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
                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,
+                       FSCTL_GET_REPARSE_POINT,
                        0, /* in_max_input_length */
-                       NULL, /* in_input_buffer */
-                       get_names ?
-                               CLI_BUFFER_SIZE : 16, /* in_max_output_length */
-                       NULL, /* in_output_buffer */
+                       NULL,
+                       64*1024,
+                       NULL,
                        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,
+                               cli_smb2_get_reparse_point_fnum_done,
                                req);
 
        return req;
 }
 
-static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
+static void cli_smb2_get_reparse_point_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);
+       struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_get_reparse_point_fnum_state);
        NTSTATUS status;
 
        status = smb2cli_ioctl_recv(subreq, state,
-                               &state->out_input_buffer,
-                               &state->out_output_buffer);
+                               NULL,
+                               &state->output_buffer);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
+               state->cli->raw_status = status;
                return;
        }
        tevent_req_done(req);
 }
 
-static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
+NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req,
                                TALLOC_CTX *mem_ctx,
-                               bool get_names,
-                               char ***pnames,
-                               int *pnum_names)
+                               DATA_BLOB *output)
 {
-       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;
-       }
+       struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data(
+               req, struct cli_smb2_get_reparse_point_fnum_state);
 
-       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;
+       if (tevent_req_is_nterror(req, &state->cli->raw_status)) {
+               tevent_req_received(req);
+               return state->cli->raw_status;
        }
-
-       names = talloc_array(mem_ctx, char *, num_names_returned);
-       if (names == NULL) {
+       *output = data_blob_dup_talloc(mem_ctx, state->output_buffer);
+       if (output->data == NULL) {
+               tevent_req_received(req);
                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;
+       tevent_req_received(req);
        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:
-       TALLOC_FREE(frame);
-       return status;
-}