s3:libsmb: pass impersonation_level to cli_smb2_create_fnum_send()
[garming/samba-autobuild/.git] / source3 / libsmb / clifile.c
index 364376b7e90a1d3baecc91c8e9b109c175083f4c..dc3751aa821351e0d9d9df8a6af3aa9da9e6ce41 100644 (file)
 #include "libcli/security/secdesc.h"
 #include "../libcli/smb/smbXcli_base.h"
 
-/***********************************************************
- Common function for pushing stings, used by smb_bytes_push_str()
- and trans_bytes_push_str(). Only difference is the align_odd
- parameter setting.
-***********************************************************/
-
-static uint8_t *internal_bytes_push_str(uint8_t *buf, bool ucs2,
-                               const char *str, size_t str_len,
-                               bool align_odd,
-                               size_t *pconverted_size)
-{
-       size_t buflen;
-       char *converted;
-       size_t converted_size;
-
-       if (buf == NULL) {
-               return NULL;
-       }
-
-       buflen = talloc_get_size(buf);
-
-       if (ucs2 &&
-           ((align_odd && (buflen % 2 == 0)) ||
-            (!align_odd && (buflen % 2 == 1)))) {
-               /*
-                * We're pushing into an SMB buffer, align odd
-                */
-               buf = talloc_realloc(NULL, buf, uint8_t, buflen + 1);
-               if (buf == NULL) {
-                       return NULL;
-               }
-               buf[buflen] = '\0';
-               buflen += 1;
-       }
-
-       if (!convert_string_talloc(talloc_tos(), CH_UNIX,
-                                  ucs2 ? CH_UTF16LE : CH_DOS,
-                                  str, str_len, &converted,
-                                  &converted_size)) {
-               return NULL;
-       }
-
-       buf = talloc_realloc(NULL, buf, uint8_t,
-                                  buflen + converted_size);
-       if (buf == NULL) {
-               TALLOC_FREE(converted);
-               return NULL;
-       }
-
-       memcpy(buf + buflen, converted, converted_size);
-
-       TALLOC_FREE(converted);
-
-       if (pconverted_size) {
-               *pconverted_size = converted_size;
-       }
-
-       return buf;
-}
-
-/***********************************************************
- Push a string into an SMB buffer, with odd byte alignment
- if it's a UCS2 string.
-***********************************************************/
-
-uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2,
-                           const char *str, size_t str_len,
-                           size_t *pconverted_size)
-{
-       return internal_bytes_push_str(buf, ucs2, str, str_len,
-                       true, pconverted_size);
-}
-
-uint8_t *smb_bytes_push_bytes(uint8_t *buf, uint8_t prefix,
-                             const uint8_t *bytes, size_t num_bytes)
-{
-       size_t buflen;
-
-       if (buf == NULL) {
-               return NULL;
-       }
-       buflen = talloc_get_size(buf);
-
-       buf = talloc_realloc(NULL, buf, uint8_t,
-                                  buflen + 1 + num_bytes);
-       if (buf == NULL) {
-               return NULL;
-       }
-       buf[buflen] = prefix;
-       memcpy(&buf[buflen+1], bytes, num_bytes);
-       return buf;
-}
-
-/***********************************************************
- Same as smb_bytes_push_str(), but without the odd byte
- align for ucs2 (we're pushing into a param or data block).
- static for now, although this will probably change when
- other modules use async trans calls.
-***********************************************************/
-
-uint8_t *trans2_bytes_push_str(uint8_t *buf, bool ucs2,
-                              const char *str, size_t str_len,
-                              size_t *pconverted_size)
-{
-       return internal_bytes_push_str(buf, ucs2, str, str_len,
-                       false, pconverted_size);
-}
-
-uint8_t *trans2_bytes_push_bytes(uint8_t *buf,
-                                const uint8_t *bytes, size_t num_bytes)
-{
-       size_t buflen;
-
-       if (buf == NULL) {
-               return NULL;
-       }
-       buflen = talloc_get_size(buf);
-
-       buf = talloc_realloc(NULL, buf, uint8_t,
-                            buflen + num_bytes);
-       if (buf == NULL) {
-               return NULL;
-       }
-       memcpy(&buf[buflen], bytes, num_bytes);
-       return buf;
-}
-
 struct cli_setpathinfo_state {
        uint16_t setup;
        uint8_t *param;
@@ -173,6 +46,7 @@ struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx,
 {
        struct tevent_req *req, *subreq;
        struct cli_setpathinfo_state *state;
+       uint16_t additional_flags2 = 0;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct cli_setpathinfo_state);
@@ -196,10 +70,16 @@ struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(path, NULL, NULL, NULL) &&
+                       !INFO_LEVEL_IS_UNIX(level)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_trans_send(
                state,                  /* mem ctx. */
                ev,                     /* event ctx. */
                cli,                    /* cli_state. */
+               additional_flags2,      /* additional_flags2 */
                SMBtrans2,              /* cmd. */
                NULL,                   /* pipe name. */
                -1,                     /* fid. */
@@ -271,7 +151,7 @@ NTSTATUS cli_setpathinfo(struct cli_state *cli,
 
 /****************************************************************************
  Hard/Symlink a file (UNIX extensions).
- Creates new name (sym)linked to oldname.
+ Creates new name (sym)linked to link_target.
 ****************************************************************************/
 
 struct cli_posix_link_internal_state {
@@ -284,7 +164,7 @@ static struct tevent_req *cli_posix_link_internal_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        uint16_t level,
-                                       const char *oldname,
+                                       const char *link_target,
                                        const char *newname)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
@@ -302,7 +182,8 @@ static struct tevent_req *cli_posix_link_internal_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
        state->data = trans2_bytes_push_str(
-               state->data, smbXcli_conn_use_unicode(cli->conn), oldname, strlen(oldname)+1, NULL);
+               state->data, smbXcli_conn_use_unicode(cli->conn),
+               link_target, strlen(link_target)+1, NULL);
 
        subreq = cli_setpathinfo_send(
                state, ev, cli, level, newname,
@@ -327,11 +208,11 @@ static void cli_posix_link_internal_done(struct tevent_req *subreq)
 struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
-                                       const char *oldname,
+                                       const char *link_target,
                                        const char *newname)
 {
        return cli_posix_link_internal_send(
-               mem_ctx, ev, cli, SMB_SET_FILE_UNIX_LINK, oldname, newname);
+               mem_ctx, ev, cli, SMB_SET_FILE_UNIX_LINK, link_target, newname);
 }
 
 NTSTATUS cli_posix_symlink_recv(struct tevent_req *req)
@@ -340,7 +221,7 @@ NTSTATUS cli_posix_symlink_recv(struct tevent_req *req)
 }
 
 NTSTATUS cli_posix_symlink(struct cli_state *cli,
-                       const char *oldname,
+                       const char *link_target,
                        const char *newname)
 {
        TALLOC_CTX *frame = talloc_stackframe();
@@ -365,7 +246,7 @@ NTSTATUS cli_posix_symlink(struct cli_state *cli,
        req = cli_posix_symlink_send(frame,
                                ev,
                                cli,
-                               oldname,
+                               link_target,
                                newname);
        if (req == NULL) {
                status = NT_STATUS_NO_MEMORY;
@@ -587,25 +468,25 @@ NTSTATUS cli_posix_hardlink(struct cli_state *cli,
 }
 
 /****************************************************************************
- Do a POSIX getfacl (UNIX extensions).
+ Do a POSIX getacl - pathname based ACL get (UNIX extensions).
 ****************************************************************************/
 
-struct getfacl_state {
+struct getacl_state {
        uint32_t num_data;
        uint8_t *data;
 };
 
-static void cli_posix_getfacl_done(struct tevent_req *subreq);
+static void cli_posix_getacl_done(struct tevent_req *subreq);
 
-struct tevent_req *cli_posix_getfacl_send(TALLOC_CTX *mem_ctx,
+struct tevent_req *cli_posix_getacl_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *fname)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
-       struct getfacl_state *state = NULL;
+       struct getacl_state *state = NULL;
 
-       req = tevent_req_create(mem_ctx, &state, struct getfacl_state);
+       req = tevent_req_create(mem_ctx, &state, struct getacl_state);
        if (req == NULL) {
                return NULL;
        }
@@ -614,16 +495,16 @@ struct tevent_req *cli_posix_getfacl_send(TALLOC_CTX *mem_ctx,
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_posix_getfacl_done, req);
+       tevent_req_set_callback(subreq, cli_posix_getacl_done, req);
        return req;
 }
 
-static void cli_posix_getfacl_done(struct tevent_req *subreq)
+static void cli_posix_getacl_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       struct getfacl_state *state = tevent_req_data(
-               req, struct getfacl_state);
+       struct getacl_state *state = tevent_req_data(
+               req, struct getacl_state);
        NTSTATUS status;
 
        status = cli_qpathinfo_recv(subreq, state, &state->data,
@@ -635,12 +516,12 @@ static void cli_posix_getfacl_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
-NTSTATUS cli_posix_getfacl_recv(struct tevent_req *req,
+NTSTATUS cli_posix_getacl_recv(struct tevent_req *req,
                                TALLOC_CTX *mem_ctx,
                                size_t *prb_size,
                                char **retbuf)
 {
-       struct getfacl_state *state = tevent_req_data(req, struct getfacl_state);
+       struct getacl_state *state = tevent_req_data(req, struct getacl_state);
        NTSTATUS status;
 
        if (tevent_req_is_nterror(req, &status)) {
@@ -651,7 +532,7 @@ NTSTATUS cli_posix_getfacl_recv(struct tevent_req *req,
        return NT_STATUS_OK;
 }
 
-NTSTATUS cli_posix_getfacl(struct cli_state *cli,
+NTSTATUS cli_posix_getacl(struct cli_state *cli,
                        const char *fname,
                        TALLOC_CTX *mem_ctx,
                        size_t *prb_size,
@@ -676,7 +557,7 @@ NTSTATUS cli_posix_getfacl(struct cli_state *cli,
                goto fail;
        }
 
-       req = cli_posix_getfacl_send(frame,
+       req = cli_posix_getacl_send(frame,
                                ev,
                                cli,
                                fname);
@@ -689,7 +570,107 @@ NTSTATUS cli_posix_getfacl(struct cli_state *cli,
                goto fail;
        }
 
-       status = cli_posix_getfacl_recv(req, mem_ctx, prb_size, retbuf);
+       status = cli_posix_getacl_recv(req, mem_ctx, prb_size, retbuf);
+
+ fail:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+/****************************************************************************
+ Do a POSIX setacl - pathname based ACL set (UNIX extensions).
+****************************************************************************/
+
+struct setacl_state {
+       uint8_t *data;
+};
+
+static void cli_posix_setacl_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_posix_setacl_send(TALLOC_CTX *mem_ctx,
+                                       struct tevent_context *ev,
+                                       struct cli_state *cli,
+                                       const char *fname,
+                                       const void *data,
+                                       size_t num_data)
+{
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct setacl_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state, struct setacl_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->data = talloc_memdup(state, data, num_data);
+       if (tevent_req_nomem(state->data, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = cli_setpathinfo_send(state,
+                               ev,
+                               cli,
+                               SMB_SET_POSIX_ACL,
+                               fname,
+                               state->data,
+                               num_data);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_setacl_done, req);
+       return req;
+}
+
+static void cli_posix_setacl_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_setpathinfo_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+NTSTATUS cli_posix_setacl_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+NTSTATUS cli_posix_setacl(struct cli_state *cli,
+                       const char *fname,
+                       const void *acl_buf,
+                       size_t acl_buf_size)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct tevent_context *ev = NULL;
+       struct tevent_req *req = NULL;
+       NTSTATUS status = NT_STATUS_OK;
+
+       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) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       req = cli_posix_setacl_send(frame,
+                               ev,
+                               cli,
+                               fname,
+                               acl_buf,
+                               acl_buf_size);
+       if (req == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+
+       status = cli_posix_setacl_recv(req);
 
  fail:
        TALLOC_FREE(frame);
@@ -1012,28 +993,146 @@ NTSTATUS cli_posix_chown(struct cli_state *cli,
  Rename a file.
 ****************************************************************************/
 
-static void cli_rename_done(struct tevent_req *subreq);
+static struct tevent_req *cli_cifs_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace);
 
-struct cli_rename_state {
+static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace);
+
+struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev,
+                                  struct cli_state *cli,
+                                  const char *fname_src,
+                                  const char *fname_dst,
+                                  bool replace)
+{
+       if (replace && smbXcli_conn_support_passthrough(cli->conn)) {
+               return cli_smb1_rename_send(mem_ctx, ev, cli, fname_src,
+                                           fname_dst, replace);
+       } else {
+               return cli_cifs_rename_send(mem_ctx, ev, cli, fname_src,
+                                           fname_dst, replace);
+       }
+}
+
+struct cli_smb1_rename_state {
+       uint8_t *data;
+};
+
+static void cli_smb1_rename_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace)
+{
+       NTSTATUS status;
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_smb1_rename_state *state = NULL;
+       smb_ucs2_t *converted_str = NULL;
+       size_t converted_size_bytes = 0;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb1_rename_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!push_ucs2_talloc(talloc_tos(), &converted_str, fname_dst,
+                             &converted_size_bytes)) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               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;
+               goto fail;
+       }
+       converted_size_bytes -= 2;
+
+       state->data =
+           talloc_zero_array(state, uint8_t, 12 + converted_size_bytes);
+       if (state->data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       if (replace) {
+               SCVAL(state->data, 0, 1);
+       }
+
+       SIVAL(state->data, 8, converted_size_bytes);
+       memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+       TALLOC_FREE(converted_str);
+
+       subreq = cli_setpathinfo_send(
+           state, ev, cli, SMB_FILE_RENAME_INFORMATION, fname_src, state->data,
+           talloc_get_size(state->data));
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+       tevent_req_set_callback(subreq, cli_smb1_rename_done, req);
+       return req;
+
+fail:
+       TALLOC_FREE(converted_str);
+       tevent_req_nterror(req, status);
+       return tevent_req_post(req, ev);
+}
+
+static void cli_smb1_rename_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_setpathinfo_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_cifs_rename_done(struct tevent_req *subreq);
+
+struct cli_cifs_rename_state {
        uint16_t vwv[1];
 };
 
-struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
-                               struct tevent_context *ev,
-                               struct cli_state *cli,
-                               const char *fname_src,
-                               const char *fname_dst)
+static struct tevent_req *cli_cifs_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
-       struct cli_rename_state *state = NULL;
+       struct cli_cifs_rename_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_rename_state);
+       req = tevent_req_create(mem_ctx, &state, struct cli_cifs_rename_state);
        if (req == NULL) {
                return NULL;
        }
 
+       if (replace) {
+               /*
+                * CIFS doesn't support replace
+                */
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
        SSVAL(state->vwv+0, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY);
 
        bytes = talloc_array(state, uint8_t, 1);
@@ -1047,6 +1146,10 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        bytes = talloc_realloc(state, bytes, uint8_t,
                        talloc_get_size(bytes)+1);
        if (tevent_req_nomem(bytes, req)) {
@@ -1061,15 +1164,16 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
        }
 
        subreq = cli_smb_send(state, ev, cli, SMBmv, additional_flags,
-                             1, state->vwv, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       1, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_rename_done, req);
+       tevent_req_set_callback(subreq, cli_cifs_rename_done, req);
        return req;
 }
 
-static void cli_rename_done(struct tevent_req *subreq)
+static void cli_cifs_rename_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                                subreq, struct tevent_req);
@@ -1088,7 +1192,10 @@ NTSTATUS cli_rename_recv(struct tevent_req *req)
        return tevent_req_simple_recv_ntstatus(req);
 }
 
-NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+NTSTATUS cli_rename(struct cli_state *cli,
+                   const char *fname_src,
+                   const char *fname_dst,
+                   bool replace)
 {
        TALLOC_CTX *frame = NULL;
        struct tevent_context *ev;
@@ -1096,9 +1203,7 @@ NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fn
        NTSTATUS status = NT_STATUS_OK;
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_rename(cli,
-                                       fname_src,
-                                       fname_dst);
+               return cli_smb2_rename(cli, fname_src, fname_dst, replace);
        }
 
        frame = talloc_stackframe();
@@ -1117,7 +1222,7 @@ NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fn
                goto fail;
        }
 
-       req = cli_rename_send(frame, ev, cli, fname_src, fname_dst);
+       req = cli_rename_send(frame, ev, cli, fname_src, fname_dst, replace);
        if (req == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto fail;
@@ -1154,6 +1259,7 @@ static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_ntrename_internal_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -1176,6 +1282,10 @@ static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        bytes = talloc_realloc(state, bytes, uint8_t,
                        talloc_get_size(bytes)+1);
        if (tevent_req_nomem(bytes, req)) {
@@ -1190,7 +1300,8 @@ static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx,
        }
 
        subreq = cli_smb_send(state, ev, cli, SMBntrename, additional_flags,
-                             4, state->vwv, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       4, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1354,6 +1465,7 @@ struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_unlink_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_unlink_state);
@@ -1375,7 +1487,12 @@ struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBunlink, additional_flags,
+                               additional_flags2,
                                1, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -1465,6 +1582,7 @@ struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_mkdir_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_mkdir_state);
@@ -1484,8 +1602,13 @@ struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBmkdir, additional_flags,
-                             0, NULL, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1574,6 +1697,7 @@ struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_rmdir_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_rmdir_state);
@@ -1593,8 +1717,13 @@ struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBrmdir, additional_flags,
-                             0, NULL, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1675,12 +1804,8 @@ struct doc_state {
        uint8_t data[1];
 };
 
-static void cli_nt_delete_on_close_done(struct tevent_req *subreq)
-{
-       NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
-                                        NULL, 0, NULL, NULL, 0, NULL);
-       tevent_req_simple_finish_ntstatus(subreq, status);
-}
+static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq);
+static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq);
 
 struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
@@ -1696,6 +1821,18 @@ struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_smb2_delete_on_close_send(state, ev, cli,
+                                                      fnum, flag);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq,
+                                       cli_nt_delete_on_close_smb2_done,
+                                       req);
+               return req;
+       }
+
        /* Setup setup word. */
        SSVAL(&state->setup, 0, TRANSACT2_SETFILEINFO);
 
@@ -1709,6 +1846,7 @@ struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
        subreq = cli_trans_send(state,                  /* mem ctx. */
                                ev,                     /* event ctx. */
                                cli,                    /* cli_state. */
+                               0,                      /* additional_flags2 */
                                SMBtrans2,              /* cmd. */
                                NULL,                   /* pipe name. */
                                -1,                     /* fid. */
@@ -1727,10 +1865,25 @@ struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx,
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_nt_delete_on_close_done, req);
+       tevent_req_set_callback(subreq,
+                               cli_nt_delete_on_close_smb1_done,
+                               req);
        return req;
 }
 
+static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL,
+                                        NULL, 0, NULL, NULL, 0, NULL);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
+static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_smb2_delete_on_close_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
+}
+
 NTSTATUS cli_nt_delete_on_close_recv(struct tevent_req *req)
 {
        return tevent_req_simple_recv_ntstatus(req);
@@ -1805,6 +1958,7 @@ static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
        uint16_t *vwv;
        uint8_t *bytes;
        size_t converted_len;
+       uint16_t additional_flags2 = 0;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate1_state);
        if (req == NULL) {
@@ -1839,6 +1993,10 @@ static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
                                   fname, strlen(fname)+1,
                                   &converted_len);
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        /* sigh. this copes with broken netapp filer behaviour */
        bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, NULL);
 
@@ -1848,8 +2006,9 @@ static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
 
        SSVAL(vwv+2, 1, converted_len);
 
-       subreq = cli_smb_send(state, ev, cli, SMBntcreateX, 0, 24, vwv,
-                             talloc_get_size(bytes), bytes);
+       subreq = cli_smb_send(state, ev, cli, SMBntcreateX, 0,
+                       additional_flags2, 24, vwv,
+                       talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -1943,6 +2102,7 @@ struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
 {
        struct tevent_req *req, *subreq;
        struct cli_ntcreate_state *state;
+       uint32_t impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate_state);
        if (req == NULL) {
@@ -1957,7 +2117,8 @@ struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
                }
 
                subreq = cli_smb2_create_fnum_send(
-                       state, ev, cli, fname, create_flags, desired_access,
+                       state, ev, cli, fname, create_flags,
+                       impersonation_level, desired_access,
                        file_attributes, share_access, create_disposition,
                        create_options);
        } else {
@@ -2097,6 +2258,7 @@ struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
        size_t secdesc_len;
        NTSTATUS status;
        size_t converted_len;
+       uint16_t additional_flags2 = 0;
 
        req = tevent_req_create(mem_ctx,
                                &state, struct cli_nttrans_create_state);
@@ -2137,6 +2299,10 @@ struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        SIVAL(param, 0, CreatFlags);
        SIVAL(param, 4, 0x0);   /* RootDirectoryFid */
        SIVAL(param, 8, DesiredAccess);
@@ -2153,7 +2319,9 @@ struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
        SIVAL(param, 48, 0x02); /* ImpersonationLevel */
        SCVAL(param, 52, SecurityFlags);
 
-       subreq = cli_trans_send(state, ev, cli, SMBnttrans,
+       subreq = cli_trans_send(state, ev, cli,
+                               additional_flags2, /* additional_flags2 */
+                               SMBnttrans,
                                NULL, -1, /* name, fid */
                                NT_TRANSACT_CREATE, 0,
                                NULL, 0, 0, /* setup */
@@ -2289,6 +2457,7 @@ struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx,
        unsigned openfn;
        unsigned accessmode;
        uint8_t additional_flags;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_openx_state);
@@ -2356,11 +2525,15 @@ struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        state->bytes.iov_base = (void *)bytes;
        state->bytes.iov_len = talloc_get_size(bytes);
 
        subreq = cli_smb_req_create(state, ev, cli, SMBopenX, additional_flags,
-                                   15, state->vwv, 1, &state->bytes);
+                       additional_flags2, 15, state->vwv, 1, &state->bytes);
        if (subreq == NULL) {
                TALLOC_FREE(req);
                return NULL;
@@ -2599,22 +2772,22 @@ NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags,
  Close a file.
 ****************************************************************************/
 
-struct cli_close_state {
+struct cli_smb1_close_state {
        uint16_t vwv[3];
 };
 
-static void cli_close_done(struct tevent_req *subreq);
+static void cli_smb1_close_done(struct tevent_req *subreq);
 
-struct tevent_req *cli_close_create(TALLOC_CTX *mem_ctx,
+struct tevent_req *cli_smb1_close_create(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
                                struct cli_state *cli,
                                uint16_t fnum,
                                struct tevent_req **psubreq)
 {
        struct tevent_req *req, *subreq;
-       struct cli_close_state *state;
+       struct cli_smb1_close_state *state;
 
-       req = tevent_req_create(mem_ctx, &state, struct cli_close_state);
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb1_close_state);
        if (req == NULL) {
                return NULL;
        }
@@ -2622,34 +2795,72 @@ struct tevent_req *cli_close_create(TALLOC_CTX *mem_ctx,
        SSVAL(state->vwv+0, 0, fnum);
        SIVALS(state->vwv+1, 0, -1);
 
-       subreq = cli_smb_req_create(state, ev, cli, SMBclose, 0, 3, state->vwv,
-                                   0, NULL);
+       subreq = cli_smb_req_create(state, ev, cli, SMBclose, 0, 0,
+                               3, state->vwv, 0, NULL);
        if (subreq == NULL) {
                TALLOC_FREE(req);
                return NULL;
        }
-       tevent_req_set_callback(subreq, cli_close_done, req);
+       tevent_req_set_callback(subreq, cli_smb1_close_done, req);
        *psubreq = subreq;
        return req;
 }
 
+static void cli_smb1_close_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+struct cli_close_state {
+       int dummy;
+};
+
+static void cli_close_done(struct tevent_req *subreq);
+
 struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx,
                                struct tevent_context *ev,
                                struct cli_state *cli,
                                uint16_t fnum)
 {
        struct tevent_req *req, *subreq;
+       struct cli_close_state *state;
        NTSTATUS status;
 
-       req = cli_close_create(mem_ctx, ev, cli, fnum, &subreq);
+       req = tevent_req_create(mem_ctx, &state, struct cli_close_state);
        if (req == NULL) {
                return NULL;
        }
 
-       status = smb1cli_req_chain_submit(&subreq, 1);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               subreq = cli_smb2_close_fnum_send(state,
+                                               ev,
+                                               cli,
+                                               fnum);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+       } else {
+               struct tevent_req *ch_req = NULL;
+               subreq = cli_smb1_close_create(state, ev, cli, fnum, &ch_req);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               status = smb1cli_req_chain_submit(&ch_req, 1);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
        }
+
+       tevent_req_set_callback(subreq, cli_close_done, req);
        return req;
 }
 
@@ -2657,11 +2868,12 @@ static void cli_close_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_OK;
+       bool err = tevent_req_is_nterror(subreq, &status);
 
-       status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
        TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
+       if (err) {
+               tevent_req_nterror(req, status);
                return;
        }
        tevent_req_done(req);
@@ -2679,10 +2891,6 @@ NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum)
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_OK;
 
-       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_close_fnum(cli, fnum);
-       }
-
        frame = talloc_stackframe();
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -2760,6 +2968,7 @@ struct tevent_req *cli_ftruncate_send(TALLOC_CTX *mem_ctx,
        subreq = cli_trans_send(state,                  /* mem ctx. */
                                ev,                     /* event ctx. */
                                cli,                    /* cli_state. */
+                               0,                      /* additional_flags2 */
                                SMBtrans2,              /* cmd. */
                                NULL,                   /* pipe name. */
                                -1,                     /* fid. */
@@ -2789,11 +2998,17 @@ NTSTATUS cli_ftruncate_recv(struct tevent_req *req)
 
 NTSTATUS cli_ftruncate(struct cli_state *cli, uint16_t fnum, uint64_t size)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
+       TALLOC_CTX *frame = NULL;
        struct tevent_context *ev = NULL;
        struct tevent_req *req = NULL;
        NTSTATUS status = NT_STATUS_OK;
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_ftruncate(cli, fnum, size);
+       }
+
+       frame = talloc_stackframe();
+
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -2932,7 +3147,7 @@ struct tevent_req *cli_unlock_send(TALLOC_CTX *mem_ctx,
        SIVAL(state->data, 2, offset);
        SIVAL(state->data, 6, len);
 
-       subreq = cli_smb_send(state, ev, cli, SMBlockingX, additional_flags,
+       subreq = cli_smb_send(state, ev, cli, SMBlockingX, additional_flags, 0,
                                8, state->vwv, 10, state->data);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -3096,7 +3311,7 @@ struct tevent_req *cli_unlock64_send(TALLOC_CTX *mem_ctx,
        SOFF_T_R(state->data, 4, offset);
        SOFF_T_R(state->data, 12, len);
 
-       subreq = cli_smb_send(state, ev, cli, SMBlockingX, additional_flags,
+       subreq = cli_smb_send(state, ev, cli, SMBlockingX, additional_flags, 0,
                                8, state->vwv, 20, state->data);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -3244,6 +3459,7 @@ static struct tevent_req *cli_posix_lock_internal_send(TALLOC_CTX *mem_ctx,
        subreq = cli_trans_send(state,                  /* mem ctx. */
                                ev,                     /* event ctx. */
                                cli,                    /* cli_state. */
+                               0,                      /* additional_flags2 */
                                SMBtrans2,              /* cmd. */
                                NULL,                   /* pipe name. */
                                -1,                     /* fid. */
@@ -3436,7 +3652,7 @@ struct tevent_req *cli_getattrE_send(TALLOC_CTX *mem_ctx,
        state->zone_offset = smb1cli_conn_server_time_zone(cli->conn);
        SSVAL(state->vwv+0,0,fnum);
 
-       subreq = cli_smb_send(state, ev, cli, SMBgetattrE, additional_flags,
+       subreq = cli_smb_send(state, ev, cli, SMBgetattrE, additional_flags, 0,
                              1, state->vwv, 0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -3585,6 +3801,7 @@ struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_getatr_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_getatr_state);
@@ -3606,8 +3823,13 @@ struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBgetatr, additional_flags,
-                             0, NULL, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -3753,7 +3975,7 @@ struct tevent_req *cli_setattrE_send(TALLOC_CTX *mem_ctx,
        push_dos_date2((uint8_t *)&state->vwv[5], 0, write_time,
                       smb1cli_conn_server_time_zone(cli->conn));
 
-       subreq = cli_smb_send(state, ev, cli, SMBsetattrE, additional_flags,
+       subreq = cli_smb_send(state, ev, cli, SMBsetattrE, additional_flags, 0,
                              7, state->vwv, 0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -3859,6 +4081,7 @@ struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_setatr_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_setatr_state);
@@ -3892,8 +4115,13 @@ struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBsetatr, additional_flags,
-                             8, state->vwv, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       8, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -3971,7 +4199,7 @@ NTSTATUS cli_setatr(struct cli_state *cli,
 }
 
 /****************************************************************************
- Check for existance of a dir.
+ Check for existence of a dir.
 ****************************************************************************/
 
 static void cli_chkpath_done(struct tevent_req *subreq);
@@ -3988,6 +4216,7 @@ struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct cli_chkpath_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_chkpath_state);
@@ -4007,8 +4236,13 @@ struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBcheckpath, additional_flags,
-                             0, NULL, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4037,12 +4271,18 @@ NTSTATUS cli_chkpath_recv(struct tevent_req *req)
 
 NTSTATUS cli_chkpath(struct cli_state *cli, const char *path)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
+       TALLOC_CTX *frame = NULL;
        struct tevent_context *ev = NULL;
        struct tevent_req *req = NULL;
        char *path2 = NULL;
        NTSTATUS status = NT_STATUS_OK;
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_chkpath(cli, path);
+       }
+
+       frame = talloc_stackframe();
+
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -4113,7 +4353,7 @@ struct tevent_req *cli_dskattr_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBdskattr, additional_flags,
+       subreq = cli_smb_send(state, ev, cli, SMBdskattr, additional_flags, 0,
                              0, NULL, 0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -4291,6 +4531,7 @@ struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx,
        struct tevent_req *req = NULL, *subreq = NULL;
        struct ctemp_state *state = NULL;
        uint8_t additional_flags = 0;
+       uint16_t additional_flags2 = 0;
        uint8_t *bytes = NULL;
 
        req = tevent_req_create(mem_ctx, &state, struct ctemp_state);
@@ -4312,8 +4553,13 @@ struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(path, NULL, NULL, NULL)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_smb_send(state, ev, cli, SMBctemp, additional_flags,
-                             3, state->vwv, talloc_get_size(bytes), bytes);
+                       additional_flags2,
+                       3, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4490,10 +4736,19 @@ static NTSTATUS cli_set_ea(struct cli_state *cli, uint16_t setup_val,
                memcpy(p+4+ea_namelen+1, ea_val, ea_len);
        }
 
+       /*
+        * FIXME - if we want to do previous version path
+        * processing on an EA set call we need to turn this
+        * into calls to cli_trans_send()/cli_trans_recv()
+        * with a temporary event context, as cli_trans_send()
+        * have access to the additional_flags2 needed to
+        * send @GMT- paths. JRA.
+        */
+
        status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, -1, 0, 0,
                           setup, 1, 0,
                           param, param_len, 2,
-                          data,  data_len, CLI_BUFFER_SIZE,
+                          data,  data_len, 0,
                           NULL,
                           NULL, 0, NULL, /* rsetup */
                           NULL, 0, NULL, /* rparam */
@@ -4917,6 +5172,7 @@ static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx,
        subreq = cli_trans_send(state,                  /* mem ctx. */
                                ev,                     /* event ctx. */
                                cli,                    /* cli_state. */
+                               0,                      /* additional_flags2 */
                                SMBtrans2,              /* cmd. */
                                NULL,                   /* pipe name. */
                                -1,                     /* fid. */
@@ -5237,12 +5493,15 @@ NTSTATUS cli_posix_rmdir(struct cli_state *cli, const char *fname)
 ****************************************************************************/
 
 struct cli_notify_state {
+       struct tevent_req *subreq;
        uint8_t setup[8];
        uint32_t num_changes;
        struct notify_change *changes;
 };
 
 static void cli_notify_done(struct tevent_req *subreq);
+static void cli_notify_done_smb2(struct tevent_req *subreq);
+static bool cli_notify_cancel(struct tevent_req *req);
 
 struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
                                   struct tevent_context *ev,
@@ -5250,7 +5509,7 @@ struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
                                   uint32_t buffer_size,
                                   uint32_t completion_filter, bool recursive)
 {
-       struct tevent_req *req, *subreq;
+       struct tevent_req *req;
        struct cli_notify_state *state;
        unsigned old_timeout;
 
@@ -5259,6 +5518,31 @@ struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               /*
+                * Notifies should not time out
+                */
+               old_timeout = cli_set_timeout(cli, 0);
+
+               state->subreq = cli_smb2_notify_send(
+                       state,
+                       ev,
+                       cli,
+                       fnum,
+                       buffer_size,
+                       completion_filter,
+                       recursive);
+
+               cli_set_timeout(cli, old_timeout);
+
+               if (tevent_req_nomem(state->subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(
+                       state->subreq, cli_notify_done_smb2, req);
+               goto done;
+       }
+
        SIVAL(state->setup, 0, completion_filter);
        SSVAL(state->setup, 4, fnum);
        SSVAL(state->setup, 6, recursive);
@@ -5268,10 +5552,11 @@ struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
         */
        old_timeout = cli_set_timeout(cli, 0);
 
-       subreq = cli_trans_send(
+       state->subreq = cli_trans_send(
                state,                  /* mem ctx. */
                ev,                     /* event ctx. */
                cli,                    /* cli_state. */
+               0,                      /* additional_flags2 */
                SMBnttrans,             /* cmd. */
                NULL,                   /* pipe name. */
                -1,                     /* fid. */
@@ -5289,13 +5574,25 @@ struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx,
 
        cli_set_timeout(cli, old_timeout);
 
-       if (tevent_req_nomem(subreq, req)) {
+       if (tevent_req_nomem(state->subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, cli_notify_done, req);
+       tevent_req_set_callback(state->subreq, cli_notify_done, req);
+done:
+       tevent_req_set_cancel_fn(req, cli_notify_cancel);
        return req;
 }
 
+static bool cli_notify_cancel(struct tevent_req *req)
+{
+       struct cli_notify_state *state = tevent_req_data(
+               req, struct cli_notify_state);
+       bool ok;
+
+       ok = tevent_req_cancel(state->subreq);
+       return ok;
+}
+
 static void cli_notify_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
@@ -5310,6 +5607,7 @@ static void cli_notify_done(struct tevent_req *subreq)
        status = cli_trans_recv(subreq, talloc_tos(), &flags2, NULL, 0, NULL,
                                &params, 0, &num_params, NULL, 0, NULL);
        TALLOC_FREE(subreq);
+       state->subreq = NULL;
        if (tevent_req_nterror(req, status)) {
                DEBUG(10, ("cli_trans_recv returned %s\n", nt_errstr(status)));
                return;
@@ -5367,6 +5665,26 @@ static void cli_notify_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+static void cli_notify_done_smb2(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_notify_state *state = tevent_req_data(
+               req, struct cli_notify_state);
+       NTSTATUS status;
+
+       status = cli_smb2_notify_recv(
+               subreq,
+               state,
+               &state->changes,
+               &state->num_changes);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
 NTSTATUS cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                         uint32_t *pnum_changes,
                         struct notify_change **pchanges)
@@ -5389,11 +5707,13 @@ NTSTATUS cli_notify(struct cli_state *cli, uint16_t fnum, uint32_t buffer_size,
                    TALLOC_CTX *mem_ctx, uint32_t *pnum_changes,
                    struct notify_change **pchanges)
 {
-       TALLOC_CTX *frame = talloc_stackframe();
+       TALLOC_CTX *frame;
        struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
+       frame = talloc_stackframe();
+
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight
@@ -5438,6 +5758,7 @@ struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx,
 {
        struct tevent_req *req, *subreq;
        struct cli_qpathinfo_state *state;
+       uint16_t additional_flags2 = 0;
 
        req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo_state);
        if (req == NULL) {
@@ -5457,10 +5778,16 @@ struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL) &&
+                       !INFO_LEVEL_IS_UNIX(level)) {
+               additional_flags2 = FLAGS2_REPARSE_PATH;
+       }
+
        subreq = cli_trans_send(
                state,                  /* mem ctx. */
                ev,                     /* event ctx. */
                cli,                    /* cli_state. */
+               additional_flags2,      /* additional_flags2 */
                SMBtrans2,              /* cmd. */
                NULL,                   /* pipe name. */
                -1,                     /* fid. */
@@ -5591,6 +5918,7 @@ struct tevent_req *cli_qfileinfo_send(TALLOC_CTX *mem_ctx,
                state,                  /* mem ctx. */
                ev,                     /* event ctx. */
                cli,                    /* cli_state. */
+               0,                      /* additional_flags2 */
                SMBtrans2,              /* cmd. */
                NULL,                   /* pipe name. */
                -1,                     /* fid. */
@@ -5714,7 +6042,7 @@ struct tevent_req *cli_flush_send(TALLOC_CTX *mem_ctx,
        }
        SSVAL(state->vwv + 0, 0, fnum);
 
-       subreq = cli_smb_send(state, ev, cli, SMBflush, 0, 1, state->vwv,
+       subreq = cli_smb_send(state, ev, cli, SMBflush, 0, 0, 1, state->vwv,
                              0, NULL);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
@@ -5806,8 +6134,9 @@ struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx,
        SCVAL(state->setup + 3, 1, 0); /* compfilter, isFlags (WSSP) */
 
        subreq = cli_trans_send(
-               state, ev, cli, SMBnttrans, NULL, 0, NT_TRANSACT_IOCTL, 0,
-               state->setup, ARRAY_SIZE(state->setup), 0,
+               state, ev, cli, 0, SMBnttrans, NULL, 0, NT_TRANSACT_IOCTL, 0,
+               state->setup, ARRAY_SIZE(state->setup),
+               ARRAY_SIZE(state->setup),
                NULL, 0, 0,
                NULL, 0, ret_size);
        if (tevent_req_nomem(subreq, req)) {
@@ -5841,36 +6170,61 @@ NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
 {
        struct cli_shadow_copy_data_state *state = tevent_req_data(
                req, struct cli_shadow_copy_data_state);
-       char **names;
-       int i, num_names;
+       char **names = NULL;
+       uint32_t i, num_names;
        uint32_t dlength;
+       uint8_t *endp = NULL;
        NTSTATUS status;
 
        if (tevent_req_is_nterror(req, &status)) {
                return status;
        }
+
+       if (state->num_data < 16) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
        num_names = IVAL(state->data, 4);
        dlength = IVAL(state->data, 8);
 
+       if (num_names > 0x7FFFFFFF) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
        if (!state->get_names) {
-               *pnum_names = num_names;
+               *pnum_names = (int)num_names;
                return NT_STATUS_OK;
        }
 
-       if (dlength+12 > state->num_data) {
+       if (dlength + 12 < 12) {
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
+       if (dlength + 12 > state->num_data) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+       if (state->num_data + (2 * sizeof(SHADOW_COPY_LABEL)) <
+                       state->num_data) {
+               return NT_STATUS_INVALID_NETWORK_RESPONSE;
+       }
+
        names = talloc_array(mem_ctx, char *, num_names);
        if (names == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
 
+       endp = state->data + state->num_data;
+
        for (i=0; i<num_names; i++) {
                bool ret;
                uint8_t *src;
                size_t converted_size;
 
                src = state->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),
@@ -5880,7 +6234,7 @@ NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                        return NT_STATUS_INVALID_NETWORK_RESPONSE;
                }
        }
-       *pnum_names = num_names;
+       *pnum_names = (int)num_names;
        *pnames = names;
        return NT_STATUS_OK;
 }
@@ -5889,11 +6243,22 @@ NTSTATUS cli_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();
+       TALLOC_CTX *frame = NULL;
        struct tevent_context *ev;
        struct tevent_req *req;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
+        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_shadow_copy_data(mem_ctx,
+                                       cli,
+                                       fnum,
+                                       get_names,
+                                       pnames,
+                                       pnum_names);
+       }
+
+       frame = talloc_stackframe();
+
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
                 * Can't use sync call while an async call is in flight