s3:libsmb: pass impersonation_level to cli_smb2_create_fnum_send()
[garming/samba-autobuild/.git] / source3 / libsmb / clifile.c
index 2907a6f5866c404fbaf57469d105f241e99aa54c..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;
@@ -197,7 +70,7 @@ struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(path) &&
+       if (clistr_is_previous_version_path(path, NULL, NULL, NULL) &&
                        !INFO_LEVEL_IS_UNIX(level)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
@@ -278,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 {
@@ -291,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;
@@ -309,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,
@@ -334,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)
@@ -347,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();
@@ -372,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;
@@ -1119,29 +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);
+
+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_rename_state {
+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);
@@ -1155,7 +1146,7 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(fname_src)) {
+       if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
 
@@ -1178,11 +1169,11 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
        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);
@@ -1201,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;
@@ -1209,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();
@@ -1230,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;
@@ -1290,7 +1282,7 @@ 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)) {
+       if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
 
@@ -1495,7 +1487,7 @@ struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(fname)) {
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
 
@@ -1610,7 +1602,7 @@ struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(dname)) {
+       if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
 
@@ -1725,7 +1717,7 @@ struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(dname)) {
+       if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
 
@@ -1812,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,
@@ -1833,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);
 
@@ -1865,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);
@@ -1943,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) {
@@ -1977,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);
 
@@ -1986,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, 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);
        }
@@ -2081,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) {
@@ -2095,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 {
@@ -2235,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);
@@ -2275,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);
@@ -2292,7 +2320,7 @@ struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx,
        SCVAL(param, 52, SecurityFlags);
 
        subreq = cli_trans_send(state, ev, cli,
-                               0, /* additional_flags2 */
+                               additional_flags2, /* additional_flags2 */
                                SMBnttrans,
                                NULL, -1, /* name, fid */
                                NT_TRANSACT_CREATE, 0,
@@ -2429,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);
@@ -2496,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,
-                                   0, 15, state->vwv, 1, &state->bytes);
+                       additional_flags2, 15, state->vwv, 1, &state->bytes);
        if (subreq == NULL) {
                TALLOC_FREE(req);
                return NULL;
@@ -2739,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;
        }
@@ -2768,28 +2801,66 @@ struct tevent_req *cli_close_create(TALLOC_CTX *mem_ctx,
                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;
 }
 
@@ -2797,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);
@@ -2819,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)) {
@@ -2930,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
@@ -3727,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);
@@ -3748,8 +3823,13 @@ struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBgetatr, additional_flags, 0,
-                             0, NULL, talloc_get_size(bytes), bytes);
+       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,
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4001,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);
@@ -4034,8 +4115,13 @@ struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBsetatr, additional_flags, 0,
-                             8, state->vwv, talloc_get_size(bytes), bytes);
+       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,
+                       additional_flags2,
+                       8, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4113,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);
@@ -4130,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);
@@ -4149,8 +4236,13 @@ struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBcheckpath, additional_flags, 0,
-                             0, NULL, talloc_get_size(bytes), bytes);
+       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,
+                       additional_flags2,
+                       0, NULL, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4179,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
@@ -4433,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);
@@ -4454,8 +4553,13 @@ struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       subreq = cli_smb_send(state, ev, cli, SMBctemp, additional_flags, 0,
-                             3, state->vwv, talloc_get_size(bytes), bytes);
+       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,
+                       additional_flags2,
+                       3, state->vwv, talloc_get_size(bytes), bytes);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4632,6 +4736,15 @@ 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,
@@ -5380,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,
@@ -5393,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;
 
@@ -5402,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);
@@ -5411,7 +5552,7 @@ 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. */
@@ -5433,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(
@@ -5454,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;
@@ -5511,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)
@@ -5533,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
@@ -5602,7 +5778,7 @@ struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
-       if (clistr_is_previous_version_path(fname) &&
+       if (clistr_is_previous_version_path(fname, NULL, NULL, NULL) &&
                        !INFO_LEVEL_IS_UNIX(level)) {
                additional_flags2 = FLAGS2_REPARSE_PATH;
        }
@@ -5959,7 +6135,8 @@ struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx,
 
        subreq = cli_trans_send(
                state, ev, cli, 0, SMBnttrans, NULL, 0, NT_TRANSACT_IOCTL, 0,
-               state->setup, ARRAY_SIZE(state->setup), 0,
+               state->setup, ARRAY_SIZE(state->setup),
+               ARRAY_SIZE(state->setup),
                NULL, 0, 0,
                NULL, 0, ret_size);
        if (tevent_req_nomem(subreq, req)) {
@@ -5993,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),
@@ -6032,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;
 }
@@ -6041,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