libsmb: Use tevent_req_simple_finish_ntstatus
[samba.git] / source3 / libsmb / clifile.c
index fca7c9150fd9ca667f20dd39ec8e047a327233d3..421e26f60863e37e1c412e866504f20f09f397cc 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;
@@ -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);
 
-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);
@@ -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;
@@ -1319,16 +1311,9 @@ static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx,
 
 static void cli_ntrename_internal_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);
+       NTSTATUS status = cli_smb_recv(
+               subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 static NTSTATUS cli_ntrename_internal_recv(struct tevent_req *req)
@@ -1536,7 +1521,7 @@ NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint16_t mayhave_a
        NTSTATUS status = NT_STATUS_OK;
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_unlink(cli, fname);
+               return cli_smb2_unlink(cli, fname, NULL);
        }
 
        frame = talloc_stackframe();
@@ -1766,7 +1751,7 @@ NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname)
        NTSTATUS status = NT_STATUS_OK;
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               return cli_smb2_rmdir(cli, dname);
+               return cli_smb2_rmdir(cli, dname, NULL);
        }
 
        frame = talloc_stackframe();
@@ -1812,12 +1797,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 +1814,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 +1858,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);
@@ -1936,6 +1944,7 @@ static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
                                             uint32_t ShareAccess,
                                             uint32_t CreateDisposition,
                                             uint32_t CreateOptions,
+                                            uint32_t ImpersonationLevel,
                                             uint8_t SecurityFlags)
 {
        struct tevent_req *req, *subreq;
@@ -1970,7 +1979,7 @@ static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx,
        SIVAL(vwv+17, 1, CreateDisposition);
        SIVAL(vwv+19, 1, CreateOptions |
                (cli->backup_intent ? FILE_OPEN_FOR_BACKUP_INTENT : 0));
-       SIVAL(vwv+21, 1, 0x02); /* ImpersonationLevel */
+       SIVAL(vwv+21, 1, ImpersonationLevel);
        SCVAL(vwv+23, 1, SecurityFlags);
 
        bytes = talloc_array(state, uint8_t, 0);
@@ -2063,14 +2072,13 @@ static NTSTATUS cli_ntcreate1_recv(struct tevent_req *req,
 }
 
 struct cli_ntcreate_state {
-       NTSTATUS (*recv)(struct tevent_req *req, uint16_t *fnum,
-                        struct smb_create_returns *cr);
        struct smb_create_returns cr;
        uint16_t fnum;
        struct tevent_req *subreq;
 };
 
-static void cli_ntcreate_done(struct tevent_req *subreq);
+static void cli_ntcreate_done_nt1(struct tevent_req *subreq);
+static void cli_ntcreate_done_smb2(struct tevent_req *subreq);
 static bool cli_ntcreate_cancel(struct tevent_req *req);
 
 struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
@@ -2083,6 +2091,7 @@ struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
                                     uint32_t share_access,
                                     uint32_t create_disposition,
                                     uint32_t create_options,
+                                    uint32_t impersonation_level,
                                     uint8_t security_flags)
 {
        struct tevent_req *req, *subreq;
@@ -2094,27 +2103,37 @@ struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
        }
 
        if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-               state->recv = cli_smb2_create_fnum_recv;
-
                if (cli->use_oplocks) {
                        create_flags |= REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK;
                }
 
                subreq = cli_smb2_create_fnum_send(
-                       state, ev, cli, fname, create_flags, desired_access,
-                       file_attributes, share_access, create_disposition,
-                       create_options);
+                       state,
+                       ev,
+                       cli,
+                       fname,
+                       create_flags,
+                       impersonation_level,
+                       desired_access,
+                       file_attributes,
+                       share_access,
+                       create_disposition,
+                       create_options,
+                       NULL);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_ntcreate_done_smb2, req);
        } else {
-               state->recv = cli_ntcreate1_recv;
                subreq = cli_ntcreate1_send(
                        state, ev, cli, fname, create_flags, desired_access,
                        file_attributes, share_access, create_disposition,
-                       create_options, security_flags);
-       }
-       if (tevent_req_nomem(subreq, req)) {
-               return tevent_req_post(req, ev);
+                       create_options, impersonation_level, security_flags);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, cli_ntcreate_done_nt1, req);
        }
-       tevent_req_set_callback(subreq, cli_ntcreate_done, req);
 
        state->subreq = subreq;
        tevent_req_set_cancel_fn(req, cli_ntcreate_cancel);
@@ -2122,7 +2141,7 @@ struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
-static void cli_ntcreate_done(struct tevent_req *subreq)
+static void cli_ntcreate_done_nt1(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
                subreq, struct tevent_req);
@@ -2130,7 +2149,28 @@ static void cli_ntcreate_done(struct tevent_req *subreq)
                req, struct cli_ntcreate_state);
        NTSTATUS status;
 
-       status = state->recv(subreq, &state->fnum, &state->cr);
+       status = cli_ntcreate1_recv(subreq, &state->fnum, &state->cr);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void cli_ntcreate_done_smb2(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_ntcreate_state *state = tevent_req_data(
+               req, struct cli_ntcreate_state);
+       NTSTATUS status;
+
+       status = cli_smb2_create_fnum_recv(
+               subreq,
+               &state->fnum,
+               &state->cr,
+               NULL,
+               NULL);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
@@ -2179,6 +2219,7 @@ NTSTATUS cli_ntcreate(struct cli_state *cli,
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev;
        struct tevent_req *req;
+       uint32_t ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION;
        NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
@@ -2197,7 +2238,7 @@ NTSTATUS cli_ntcreate(struct cli_state *cli,
        req = cli_ntcreate_send(frame, ev, cli, fname, CreatFlags,
                                DesiredAccess, FileAttributes, ShareAccess,
                                CreateDisposition, CreateOptions,
-                               SecurityFlags);
+                               ImpersonationLevel, SecurityFlags);
        if (req == NULL) {
                goto fail;
        }
@@ -2755,22 +2796,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;
        }
@@ -2784,28 +2825,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;
 }
 
@@ -2813,11 +2892,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);
@@ -2835,10 +2915,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)) {
@@ -2946,11 +3022,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
@@ -4141,7 +4223,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);
@@ -4213,12 +4295,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
@@ -5033,45 +5121,27 @@ static uint32_t open_flags_to_wire(int flags)
  Open a file - POSIX semantics. Returns fnum. Doesn't request oplock.
 ****************************************************************************/
 
-struct posix_open_state {
+struct cli_posix_open_internal_state {
        uint16_t setup;
        uint8_t *param;
        uint8_t data[18];
        uint16_t fnum; /* Out */
 };
 
-static void cli_posix_open_internal_done(struct tevent_req *subreq)
-{
-       struct tevent_req *req = tevent_req_callback_data(
-                               subreq, struct tevent_req);
-       struct posix_open_state *state = tevent_req_data(req, struct posix_open_state);
-       NTSTATUS status;
-       uint8_t *data;
-       uint32_t num_data;
-
-       status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
-                               NULL, 0, NULL, &data, 12, &num_data);
-       TALLOC_FREE(subreq);
-       if (tevent_req_nterror(req, status)) {
-               return;
-       }
-       state->fnum = SVAL(data,2);
-       tevent_req_done(req);
-}
+static void cli_posix_open_internal_done(struct tevent_req *subreq);
 
 static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *fname,
-                                       int flags,
-                                       mode_t mode,
-                                       bool is_dir)
+                                       uint32_t wire_flags,
+                                       mode_t mode)
 {
        struct tevent_req *req = NULL, *subreq = NULL;
-       struct posix_open_state *state = NULL;
-       uint32_t wire_flags = open_flags_to_wire(flags);
+       struct cli_posix_open_internal_state *state = NULL;
 
-       req = tevent_req_create(mem_ctx, &state, struct posix_open_state);
+       req = tevent_req_create(
+               mem_ctx, &state, struct cli_posix_open_internal_state);
        if (req == NULL) {
                return NULL;
        }
@@ -5080,25 +5150,23 @@ static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx,
        SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO);
 
        /* Setup param array. */
-       state->param = talloc_array(state, uint8_t, 6);
+       state->param = talloc_zero_array(state, uint8_t, 6);
        if (tevent_req_nomem(state->param, req)) {
                return tevent_req_post(req, ev);
        }
-       memset(state->param, '\0', 6);
        SSVAL(state->param, 0, SMB_POSIX_PATH_OPEN);
 
-       state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn), fname,
-                                  strlen(fname)+1, NULL);
+       state->param = trans2_bytes_push_str(
+               state->param,
+               smbXcli_conn_use_unicode(cli->conn),
+               fname,
+               strlen(fname)+1,
+               NULL);
 
        if (tevent_req_nomem(state->param, req)) {
                return tevent_req_post(req, ev);
        }
 
-       /* Setup data words. */
-       if (is_dir) {
-               wire_flags |= SMB_O_DIRECTORY;
-       }
-
        SIVAL(state->data,0,0); /* No oplock. */
        SIVAL(state->data,4,wire_flags);
        SIVAL(state->data,8,unix_perms_to_wire(mode));
@@ -5131,6 +5199,57 @@ static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void cli_posix_open_internal_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_posix_open_internal_state *state = tevent_req_data(
+               req, struct cli_posix_open_internal_state);
+       NTSTATUS status;
+       uint8_t *data;
+       uint32_t num_data;
+
+       status = cli_trans_recv(
+               subreq,
+               state,
+               NULL,
+               NULL,
+               0,
+               NULL,
+               NULL,
+               0,
+               NULL,
+               &data,
+               12,
+               &num_data);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       state->fnum = SVAL(data,2);
+       tevent_req_done(req);
+}
+
+static NTSTATUS cli_posix_open_internal_recv(struct tevent_req *req,
+                                            uint16_t *pfnum)
+{
+       struct cli_posix_open_internal_state *state = tevent_req_data(
+               req, struct cli_posix_open_internal_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       *pfnum = state->fnum;
+       return NT_STATUS_OK;
+}
+
+struct cli_posix_open_state {
+       uint16_t fnum;
+};
+
+static void cli_posix_open_done(struct tevent_req *subreq);
+
 struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
@@ -5138,13 +5257,43 @@ struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx,
                                        int flags,
                                        mode_t mode)
 {
-       return cli_posix_open_internal_send(mem_ctx, ev,
-                               cli, fname, flags, mode, false);
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_posix_open_state *state = NULL;
+       uint32_t wire_flags;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_posix_open_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       wire_flags = open_flags_to_wire(flags);
+
+       subreq = cli_posix_open_internal_send(
+               mem_ctx, ev, cli, fname, wire_flags, mode);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_open_done, req);
+       return req;
+}
+
+static void cli_posix_open_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_posix_open_state *state = tevent_req_data(
+               req, struct cli_posix_open_state);
+       NTSTATUS status;
+
+       status = cli_posix_open_internal_recv(subreq, &state->fnum);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 NTSTATUS cli_posix_open_recv(struct tevent_req *req, uint16_t *pfnum)
 {
-       struct posix_open_state *state = tevent_req_data(req, struct posix_open_state);
+       struct cli_posix_open_state *state = tevent_req_data(
+               req, struct cli_posix_open_state);
        NTSTATUS status;
 
        if (tevent_req_is_nterror(req, &status)) {
@@ -5165,7 +5314,7 @@ NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname,
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev = NULL;
        struct tevent_req *req = NULL;
-       NTSTATUS status = NT_STATUS_OK;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -5174,43 +5323,73 @@ NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname,
                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_open_send(frame,
-                               ev,
-                               cli,
-                               fname,
-                               flags,
-                               mode);
+       req = cli_posix_open_send(
+               frame, ev, cli, fname, flags, mode);
        if (req == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
-
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-
        status = cli_posix_open_recv(req, pfnum);
-
  fail:
        TALLOC_FREE(frame);
        return status;
 }
 
+struct cli_posix_mkdir_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+};
+
+static void cli_posix_mkdir_done(struct tevent_req *subreq);
+
 struct tevent_req *cli_posix_mkdir_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *fname,
                                        mode_t mode)
 {
-       return cli_posix_open_internal_send(mem_ctx, ev,
-                               cli, fname, O_CREAT, mode, true);
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_posix_mkdir_state *state = NULL;
+       uint32_t wire_flags;
+
+       req = tevent_req_create(
+               mem_ctx, &state, struct cli_posix_mkdir_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+
+       wire_flags = SMB_O_CREAT | SMB_O_DIRECTORY;
+
+       subreq = cli_posix_open_internal_send(
+               mem_ctx, ev, cli, fname, wire_flags, mode);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_mkdir_done, req);
+       return req;
+}
+
+static void cli_posix_mkdir_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+       uint16_t fnum;
+
+       status = cli_posix_open_internal_recv(subreq, &fnum);
+       TALLOC_FREE(subreq);
+       if (tevent_req_nterror(req, status)) {
+               return;
+       }
+       tevent_req_done(req);
 }
 
 NTSTATUS cli_posix_mkdir_recv(struct tevent_req *req)
@@ -5223,7 +5402,7 @@ NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode)
        TALLOC_CTX *frame = talloc_stackframe();
        struct tevent_context *ev = NULL;
        struct tevent_req *req = NULL;
-       NTSTATUS status = NT_STATUS_OK;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
 
        if (smbXcli_conn_has_async_calls(cli->conn)) {
                /*
@@ -5235,26 +5414,17 @@ NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode)
 
        ev = samba_tevent_context_init(frame);
        if (ev == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
-
-       req = cli_posix_mkdir_send(frame,
-                               ev,
-                               cli,
-                               fname,
-                               mode);
+       req = cli_posix_mkdir_send(
+               frame, ev, cli, fname, mode);
        if (req == NULL) {
-               status = NT_STATUS_NO_MEMORY;
                goto fail;
        }
-
        if (!tevent_req_poll_ntstatus(req, ev, &status)) {
                goto fail;
        }
-
        status = cli_posix_mkdir_recv(req);
-
  fail:
        TALLOC_FREE(frame);
        return status;
@@ -5305,13 +5475,43 @@ static void cli_posix_unlink_internal_done(struct tevent_req *subreq)
        tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
+static NTSTATUS cli_posix_unlink_internal_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct cli_posix_unlink_state {
+       uint8_t dummy;
+};
+
+static void cli_posix_unlink_done(struct tevent_req *subreq);
+
 struct tevent_req *cli_posix_unlink_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *fname)
 {
-       return cli_posix_unlink_internal_send(mem_ctx, ev, cli, fname,
-                                             SMB_POSIX_UNLINK_FILE_TARGET);
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_posix_unlink_state *state;
+
+       req = tevent_req_create(
+               mem_ctx, &state, struct cli_posix_unlink_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       subreq = cli_posix_unlink_internal_send(
+               mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_FILE_TARGET);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_unlink_done, req);
+       return req;
+}
+
+static void cli_posix_unlink_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_posix_unlink_internal_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 NTSTATUS cli_posix_unlink_recv(struct tevent_req *req)
@@ -5368,14 +5568,37 @@ NTSTATUS cli_posix_unlink(struct cli_state *cli, const char *fname)
  rmdir - POSIX semantics.
 ****************************************************************************/
 
+struct cli_posix_rmdir_state {
+       uint8_t dummy;
+};
+
+static void cli_posix_rmdir_done(struct tevent_req *subreq);
+
 struct tevent_req *cli_posix_rmdir_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct cli_state *cli,
                                        const char *fname)
 {
-       return cli_posix_unlink_internal_send(
-               mem_ctx, ev, cli, fname,
-               SMB_POSIX_UNLINK_DIRECTORY_TARGET);
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_posix_rmdir_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_posix_rmdir_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       subreq = cli_posix_unlink_internal_send(
+               mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_DIRECTORY_TARGET);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_posix_rmdir_done, req);
+       return req;
+}
+
+static void cli_posix_rmdir_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_posix_unlink_internal_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 NTSTATUS cli_posix_rmdir_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx)
@@ -5429,12 +5652,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,
@@ -5442,7 +5668,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;
 
@@ -5451,6 +5677,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);
@@ -5460,7 +5711,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. */
@@ -5482,13 +5733,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(
@@ -5503,6 +5766,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;
@@ -5560,6 +5824,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)
@@ -5582,11 +5866,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