s3/smbd: add in_oplock_level to smbd_smb2_create_state
[samba.git] / source3 / smbd / smb2_create.c
index 4c1b81d96731caa98710f1513aa28aaaae9fbcc1..05a1c24ab1403abcae0b13cc8f9109810de5aabd 100644 (file)
@@ -374,12 +374,12 @@ static bool smb2_lease_key_valid(const struct smb2_lease_key *key)
        return ((key->data[0] != 0) || (key->data[1] != 0));
 }
 
-static NTSTATUS smbd_smb2_create_durable_lease_check(
+static NTSTATUS smbd_smb2_create_durable_lease_check(struct smb_request *smb1req,
        const char *requested_filename, const struct files_struct *fsp,
        const struct smb2_lease *lease_ptr)
 {
        struct smb_filename *smb_fname = NULL;
-       uint32_t ucf_flags = UCF_PREP_CREATEFILE;
+       uint32_t ucf_flags;
        NTSTATUS status;
 
        if (lease_ptr == NULL) {
@@ -404,7 +404,8 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(
                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
-       status = filename_convert(talloc_tos(), fsp->conn, false,
+       ucf_flags = filename_create_ucf_flags(smb1req, FILE_OPEN);
+       status = filename_convert(talloc_tos(), fsp->conn,
                                  requested_filename, ucf_flags,
                                  NULL, &smb_fname);
        if (!NT_STATUS_IS_OK(status)) {
@@ -427,14 +428,18 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(
 }
 
 struct smbd_smb2_create_state {
+       struct tevent_context *ev;
        struct smbd_smb2_request *smb2req;
        struct smb_request *smb1req;
        bool open_was_deferred;
-       struct tevent_timer *te;
        struct tevent_immediate *im;
        struct timeval request_time;
        struct file_id id;
        struct deferred_open_record *open_rec;
+       files_struct *result;
+       bool replay_operation;
+       uint8_t in_oplock_level;
+       int requested_oplock_level;
        uint8_t out_oplock_level;
        uint32_t out_create_action;
        struct timespec out_creation_ts;
@@ -449,6 +454,15 @@ struct smbd_smb2_create_state {
        struct smb2_create_blobs *out_context_blobs;
 };
 
+static void smbd_smb2_create_finish(struct tevent_req *req,
+                                   struct smbd_smb2_request *smb2req,
+                                   struct smb_request *smb1req,
+                                   files_struct *result,
+                                   const bool replay_operation,
+                                   const int in_oplock_level,
+                                   const int in_create_disposition,
+                                   const int info);
+
 static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                        struct tevent_context *ev,
                        struct smbd_smb2_request *smb2req,
@@ -466,54 +480,95 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        struct smbd_smb2_create_state *state = NULL;
        NTSTATUS status;
        struct smb_request *smb1req = NULL;
-       files_struct *result = NULL;
        int info;
-       int requested_oplock_level;
        struct smb2_create_blob *dhnc = NULL;
        struct smb2_create_blob *dh2c = NULL;
        struct smb2_create_blob *dhnq = NULL;
        struct smb2_create_blob *dh2q = NULL;
        struct smb2_create_blob *rqls = NULL;
-       bool replay_operation = false;
+       char *fname = NULL;
+       struct smb2_create_blob *exta = NULL;
+       struct ea_list *ea_list = NULL;
+       struct smb2_create_blob *mxac = NULL;
+       NTTIME max_access_time = 0;
+       struct smb2_create_blob *secd = NULL;
+       struct security_descriptor *sec_desc = NULL;
+       struct smb2_create_blob *alsi = NULL;
+       uint64_t allocation_size = 0;
+       struct smb2_create_blob *twrp = NULL;
+       struct smb2_create_blob *qfid = NULL;
+       struct GUID _create_guid = GUID_zero();
+       struct GUID *create_guid = NULL;
+       bool update_open = false;
+       bool durable_requested = false;
+       uint32_t durable_timeout_msec = 0;
+       bool do_durable_reconnect = false;
+       uint64_t persistent_id = 0;
+       struct smb2_lease lease;
+       struct smb2_lease *lease_ptr = NULL;
+       ssize_t lease_len = -1;
+       bool need_replay_cache = false;
+       struct smbXsrv_open *op = NULL;
+#if 0
+       struct smb2_create_blob *svhdx = NULL;
+#endif
 
-       if(lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
-               requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
-       } else {
-               requested_oplock_level = in_oplock_level;
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smbd_smb2_create_state);
+       if (req == NULL) {
+               return NULL;
        }
+       *state = (struct smbd_smb2_create_state) {
+               .ev = ev,
+               .smb2req = smb2req,
+               .in_oplock_level = in_oplock_level,
+       };
 
+       smb1req = smbd_smb2_fake_smb_request(smb2req);
+       if (tevent_req_nomem(smb1req, req)) {
+               return tevent_req_post(req, state->ev);
+       }
+       state->smb1req = smb1req;
 
        if (smb2req->subreq == NULL) {
-               /* New create call. */
-               req = tevent_req_create(mem_ctx, &state,
-                               struct smbd_smb2_create_state);
-               if (req == NULL) {
-                       return NULL;
-               }
-               state->smb2req = smb2req;
-
-               smb1req = smbd_smb2_fake_smb_request(smb2req);
-               if (tevent_req_nomem(smb1req, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               state->smb1req = smb1req;
-               smb2req->subreq = req;
                DEBUG(10,("smbd_smb2_create: name[%s]\n",
                        in_name));
        } else {
-               /* Re-entrant create call. */
-               req = smb2req->subreq;
-               state = tevent_req_data(req,
-                               struct smbd_smb2_create_state);
-               smb1req = state->smb1req;
-               TALLOC_FREE(state->out_context_blobs);
+               struct smbd_smb2_create_state *old_state = tevent_req_data(
+                       smb2req->subreq, struct smbd_smb2_create_state);
+
                DEBUG(10,("smbd_smb2_create_send: reentrant for file %s\n",
                        in_name ));
+
+               state->id = old_state->id;
+               state->request_time = old_state->request_time;
+               state->open_rec = talloc_move(state, &old_state->open_rec);
+               state->open_was_deferred = old_state->open_was_deferred;
+       }
+
+       TALLOC_FREE(smb2req->subreq);
+       smb2req->subreq = req;
+
+       if (lp_fake_oplocks(SNUM(smb2req->tcon->compat))) {
+               state->requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+       } else {
+               state->requested_oplock_level = state->in_oplock_level;
+       }
+
+       /* these are ignored for SMB2 */
+       in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
+       in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
+
+       in_file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS;
+
+       fname = talloc_strdup(state, in_name);
+       if (tevent_req_nomem(fname, req)) {
+               return tevent_req_post(req, state->ev);
        }
 
        state->out_context_blobs = talloc_zero(state, struct smb2_create_blobs);
        if (tevent_req_nomem(state->out_context_blobs, req)) {
-               return tevent_req_post(req, ev);
+               return tevent_req_post(req, state->ev);
        }
 
        dhnq = smb2_create_blob_find(&in_context_blobs,
@@ -534,7 +589,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        {
                /* not both are allowed at the same time */
                tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-               return tevent_req_post(req, ev);
+               return tevent_req_post(req, state->ev);
        }
 
        if (dhnc) {
@@ -542,7 +597,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                if (dhnc->data.length != 16) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
                /*
@@ -569,7 +624,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                if (in_context_blobs.num_blobs != num_blobs_allowed) {
                        tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
        }
 
@@ -578,7 +633,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                if (dh2c->data.length != 36) {
                        tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
                /*
@@ -601,7 +656,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                if (in_context_blobs.num_blobs != num_blobs_allowed) {
                        tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
        }
 
@@ -611,740 +666,753 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                if (dhnc || dh2c) {
                        /* durable handles are not supported on IPC$ */
                        tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
                if (!lp_nt_pipe_support()) {
                        tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
-               status = open_np_file(smb1req, pipe_name, &result);
+               status = open_np_file(smb1req, pipe_name, &state->result);
                if (!NT_STATUS_IS_OK(status)) {
                        tevent_req_nterror(req, status);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
                info = FILE_WAS_OPENED;
+
+               smbd_smb2_create_finish(req,
+                                       smb2req,
+                                       smb1req,
+                                       state->result,
+                                       state->replay_operation,
+                                       state->in_oplock_level,
+                                       in_create_disposition,
+                                       info);
+               return req;
+
        } else if (CAN_PRINT(smb1req->conn)) {
                if (dhnc || dh2c) {
                        /* durable handles are not supported on printers */
                        tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
-               status = file_new(smb1req, smb1req->conn, &result);
+               status = file_new(smb1req, smb1req->conn, &state->result);
                if(!NT_STATUS_IS_OK(status)) {
                        tevent_req_nterror(req, status);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
 
-               status = print_spool_open(result, in_name,
+               status = print_spool_open(state->result, in_name,
                                          smb1req->vuid);
                if (!NT_STATUS_IS_OK(status)) {
-                       file_free(smb1req, result);
+                       file_free(smb1req, state->result);
                        tevent_req_nterror(req, status);
-                       return tevent_req_post(req, ev);
+                       return tevent_req_post(req, state->ev);
                }
                info = FILE_WAS_CREATED;
-       } else {
-               char *fname;
-               struct smb2_create_blob *exta = NULL;
-               struct ea_list *ea_list = NULL;
-               struct smb2_create_blob *mxac = NULL;
-               NTTIME max_access_time = 0;
-               struct smb2_create_blob *secd = NULL;
-               struct security_descriptor *sec_desc = NULL;
-               struct smb2_create_blob *alsi = NULL;
-               uint64_t allocation_size = 0;
-               struct smb2_create_blob *twrp = NULL;
-               struct smb2_create_blob *qfid = NULL;
-               struct GUID _create_guid = GUID_zero();
-               struct GUID *create_guid = NULL;
-               bool update_open = false;
-               bool durable_requested = false;
-               uint32_t durable_timeout_msec = 0;
-               bool do_durable_reconnect = false;
-               uint64_t persistent_id = 0;
-               struct smb2_lease lease;
-               struct smb2_lease *lease_ptr = NULL;
-               ssize_t lease_len = -1;
-               bool need_replay_cache = false;
-               struct smbXsrv_open *op = NULL;
-#if 0
-               struct smb2_create_blob *svhdx = NULL;
-#endif
 
-               exta = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_EXTA);
-               mxac = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_MXAC);
-               secd = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_SECD);
-               alsi = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_ALSI);
-               twrp = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_TWRP);
-               qfid = smb2_create_blob_find(&in_context_blobs,
-                                            SMB2_CREATE_TAG_QFID);
+               smbd_smb2_create_finish(req,
+                                       smb2req,
+                                       smb1req,
+                                       state->result,
+                                       state->replay_operation,
+                                       state->in_oplock_level,
+                                       in_create_disposition,
+                                       info);
+               return req;
+       }
+
+       exta = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_EXTA);
+       mxac = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_MXAC);
+       secd = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_SECD);
+       alsi = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_ALSI);
+       twrp = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_TWRP);
+       qfid = smb2_create_blob_find(&in_context_blobs,
+                                    SMB2_CREATE_TAG_QFID);
 #if 0
-               if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
-                       /*
-                        * This was introduced with SMB3_02
-                        */
-                       svhdx = smb2_create_blob_find(&in_context_blobs,
-                                                     SVHDX_OPEN_DEVICE_CONTEXT);
-               }
+       if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
+               /*
+                * This was introduced with SMB3_02
+                */
+               svhdx = smb2_create_blob_find(&in_context_blobs,
+                                             SVHDX_OPEN_DEVICE_CONTEXT);
+       }
 #endif
 
-               fname = talloc_strdup(state, in_name);
-               if (tevent_req_nomem(fname, req)) {
-                       return tevent_req_post(req, ev);
+       if (exta) {
+               if (!lp_ea_support(SNUM(smb2req->tcon->compat))) {
+                       tevent_req_nterror(req,
+                                          NT_STATUS_EAS_NOT_SUPPORTED);
+                       return tevent_req_post(req, state->ev);
                }
 
-               if (exta) {
-                       if (!lp_ea_support(SNUM(smb2req->tcon->compat))) {
-                               tevent_req_nterror(req,
-                                       NT_STATUS_EAS_NOT_SUPPORTED);
-                               return tevent_req_post(req, ev);
-                       }
+               ea_list = read_nttrans_ea_list(mem_ctx,
+                                              (const char *)exta->data.data, exta->data.length);
+               if (!ea_list) {
+                       DEBUG(10,("smbd_smb2_create_send: read_ea_name_list failed.\n"));
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       ea_list = read_nttrans_ea_list(mem_ctx,
-                               (const char *)exta->data.data, exta->data.length);
-                       if (!ea_list) {
-                               DEBUG(10,("smbd_smb2_create_send: read_ea_name_list failed.\n"));
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               /*
+                * NB. When SMB2+ unix extensions are added,
+                * we need to relax this check in invalid
+                * names - we used to not do this if
+                * lp_posix_pathnames() was false.
+                */
+               if (ea_list_has_invalid_name(ea_list)) {
+                       tevent_req_nterror(req, STATUS_INVALID_EA_NAME);
+                       return tevent_req_post(req, state->ev);
+               }
+       }
 
-                       /*
-                        * NB. When SMB2+ unix extensions are added,
-                        * we need to relax this check in invalid
-                        * names - we used to not do this if
-                        * lp_posix_pathnames() was false.
-                        */
-                       if (ea_list_has_invalid_name(ea_list)) {
-                               tevent_req_nterror(req, STATUS_INVALID_EA_NAME);
-                               return tevent_req_post(req, ev);
-                       }
+       if (mxac) {
+               if (mxac->data.length == 0) {
+                       max_access_time = 0;
+               } else if (mxac->data.length == 8) {
+                       max_access_time = BVAL(mxac->data.data, 0);
+               } else {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
+       }
 
-               if (mxac) {
-                       if (mxac->data.length == 0) {
-                               max_access_time = 0;
-                       } else if (mxac->data.length == 8) {
-                               max_access_time = BVAL(mxac->data.data, 0);
-                       } else {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+       if (secd) {
+               enum ndr_err_code ndr_err;
+
+               sec_desc = talloc_zero(state, struct security_descriptor);
+               if (tevent_req_nomem(sec_desc, req)) {
+                       return tevent_req_post(req, state->ev);
                }
 
-               if (secd) {
-                       enum ndr_err_code ndr_err;
+               ndr_err = ndr_pull_struct_blob(&secd->data,
+                                              sec_desc, sec_desc,
+                                              (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(2,("ndr_pull_security_descriptor failed: %s\n",
+                                ndr_errstr(ndr_err)));
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
+               }
+       }
 
-                       sec_desc = talloc_zero(state, struct security_descriptor);
-                       if (tevent_req_nomem(sec_desc, req)) {
-                               return tevent_req_post(req, ev);
-                       }
+       if (dhnq) {
+               if (dhnq->data.length != 16) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       ndr_err = ndr_pull_struct_blob(&secd->data,
-                               sec_desc, sec_desc,
-                               (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
-                       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                               DEBUG(2,("ndr_pull_security_descriptor failed: %s\n",
-                                        ndr_errstr(ndr_err)));
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               if (dh2q) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
 
-               if (dhnq) {
-                       if (dhnq->data.length != 16) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               /*
+                * durable handle request is processed below.
+                */
+               durable_requested = true;
+               /*
+                * Set the timeout to 16 mins.
+                *
+                * TODO: test this against Windows 2012
+                *       as the default for durable v2 is 1 min.
+                */
+               durable_timeout_msec = (16*60*1000);
+       }
 
-                       if (dh2q) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+       if (dh2q) {
+               const uint8_t *p = dh2q->data.data;
+               uint32_t durable_v2_timeout = 0;
+               DATA_BLOB create_guid_blob;
+               const uint8_t *hdr;
+               uint32_t flags;
 
-                       /*
-                        * durable handle request is processed below.
-                        */
-                       durable_requested = true;
-                       /*
-                        * Set the timeout to 16 mins.
-                        *
-                        * TODO: test this against Windows 2012
-                        *       as the default for durable v2 is 1 min.
-                        */
-                       durable_timeout_msec = (16*60*1000);
+               if (dh2q->data.length != 32) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
 
-               if (dh2q) {
-                       const uint8_t *p = dh2q->data.data;
-                       uint32_t durable_v2_timeout = 0;
-                       DATA_BLOB create_guid_blob;
-                       const uint8_t *hdr;
-                       uint32_t flags;
-
-                       if (dh2q->data.length != 32) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
-
-                       if (dhnq) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               if (dhnq) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       durable_v2_timeout = IVAL(p, 0);
-                       create_guid_blob = data_blob_const(p + 16, 16);
+               durable_v2_timeout = IVAL(p, 0);
+               create_guid_blob = data_blob_const(p + 16, 16);
 
-                       status = GUID_from_ndr_blob(&create_guid_blob,
-                                                   &_create_guid);
-                       if (tevent_req_nterror(req, status)) {
-                               return tevent_req_post(req, ev);
-                       }
-                       create_guid = &_create_guid;
-                       /*
-                        * we need to store the create_guid later
-                        */
-                       update_open = true;
+               status = GUID_from_ndr_blob(&create_guid_blob,
+                                           &_create_guid);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, state->ev);
+               }
+               create_guid = &_create_guid;
+               /*
+                * we need to store the create_guid later
+                */
+               update_open = true;
 
-                       /*
-                        * And we need to create a cache for replaying the
-                        * create.
-                        */
-                       need_replay_cache = true;
+               /*
+                * And we need to create a cache for replaying the
+                * create.
+                */
+               need_replay_cache = true;
 
+               /*
+                * durable handle v2 request processed below
+                */
+               durable_requested = true;
+               durable_timeout_msec = durable_v2_timeout;
+               if (durable_timeout_msec == 0) {
                        /*
-                        * durable handle v2 request processed below
+                        * Set the timeout to 1 min as default.
+                        *
+                        * This matches Windows 2012.
                         */
-                       durable_requested = true;
-                       durable_timeout_msec = durable_v2_timeout;
-                       if (durable_timeout_msec == 0) {
-                               /*
-                                * Set the timeout to 1 min as default.
-                                *
-                                * This matches Windows 2012.
-                                */
-                               durable_timeout_msec = (60*1000);
-                       }
+                       durable_timeout_msec = (60*1000);
+               }
 
+               /*
+                * Check for replay operation.
+                * Only consider it when we have dh2q.
+                * If we do not have a replay operation, verify that
+                * the create_guid is not cached for replay.
+                */
+               hdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
+               flags = IVAL(hdr, SMB2_HDR_FLAGS);
+               state->replay_operation =
+                       flags & SMB2_HDR_FLAG_REPLAY_OPERATION;
+
+               status = smb2srv_open_lookup_replay_cache(
+                       smb2req->xconn, create_guid,
+                       0 /* now */, &op);
+
+               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+                       state->replay_operation = false;
+               } else if (tevent_req_nterror(req, status)) {
+                       DBG_WARNING("smb2srv_open_lookup_replay_cache "
+                                   "failed: %s\n", nt_errstr(status));
+                       return tevent_req_post(req, state->ev);
+               } else if (!state->replay_operation) {
                        /*
-                        * Check for replay operation.
-                        * Only consider it when we have dh2q.
-                        * If we do not have a replay operation, verify that
-                        * the create_guid is not cached for replay.
+                        * If a create without replay operation flag
+                        * is sent but with a create_guid that is
+                        * currently in the replay cache -- fail.
                         */
-                       hdr = SMBD_SMB2_IN_HDR_PTR(smb2req);
-                       flags = IVAL(hdr, SMB2_HDR_FLAGS);
-                       replay_operation =
-                               flags & SMB2_HDR_FLAG_REPLAY_OPERATION;
-
-                       status = smb2srv_open_lookup_replay_cache(
-                                       smb2req->xconn, create_guid,
-                                       0 /* now */, &op);
-
-                       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
-                               replay_operation = false;
-                       } else if (tevent_req_nterror(req, status)) {
-                               DBG_WARNING("smb2srv_open_lookup_replay_cache "
-                                           "failed: %s\n", nt_errstr(status));
-                               return tevent_req_post(req, ev);
-                       } else if (!replay_operation) {
-                               /*
-                                * If a create without replay operation flag
-                                * is sent but with a create_guid that is
-                                * currently in the replay cache -- fail.
-                                */
-                               status = NT_STATUS_DUPLICATE_OBJECTID;
-                               (void)tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+                       status = NT_STATUS_DUPLICATE_OBJECTID;
+                       (void)tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
                }
+       }
 
-               if (dhnc) {
-                       persistent_id = BVAL(dhnc->data.data, 0);
-
-                       do_durable_reconnect = true;
-               }
+       if (dhnc) {
+               persistent_id = BVAL(dhnc->data.data, 0);
 
-               if (dh2c) {
-                       const uint8_t *p = dh2c->data.data;
-                       DATA_BLOB create_guid_blob;
+               do_durable_reconnect = true;
+       }
 
-                       persistent_id = BVAL(p, 0);
-                       create_guid_blob = data_blob_const(p + 16, 16);
+       if (dh2c) {
+               const uint8_t *p = dh2c->data.data;
+               DATA_BLOB create_guid_blob;
 
-                       status = GUID_from_ndr_blob(&create_guid_blob,
-                                                   &_create_guid);
-                       if (tevent_req_nterror(req, status)) {
-                               return tevent_req_post(req, ev);
-                       }
-                       create_guid = &_create_guid;
+               persistent_id = BVAL(p, 0);
+               create_guid_blob = data_blob_const(p + 16, 16);
 
-                       do_durable_reconnect = true;
+               status = GUID_from_ndr_blob(&create_guid_blob,
+                                           &_create_guid);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, state->ev);
                }
+               create_guid = &_create_guid;
 
-               if (alsi) {
-                       if (alsi->data.length != 8) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
-                       allocation_size = BVAL(alsi->data.data, 0);
+               do_durable_reconnect = true;
+       }
+
+       if (alsi) {
+               if (alsi->data.length != 8) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
+               allocation_size = BVAL(alsi->data.data, 0);
+       }
 
-               if (twrp) {
-                       NTTIME nttime;
-                       time_t t;
-                       struct tm *tm;
+       if (twrp) {
+               NTTIME nttime;
+               time_t t;
+               struct tm *tm;
 
-                       if (twrp->data.length != 8) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               if (twrp->data.length != 8) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       nttime = BVAL(twrp->data.data, 0);
-                       t = nt_time_to_unix(nttime);
-                       tm = gmtime(&t);
+               nttime = BVAL(twrp->data.data, 0);
+               t = nt_time_to_unix(nttime);
+               tm = gmtime(&t);
 
-                       TALLOC_FREE(fname);
-                       fname = talloc_asprintf(state,
-                                       "@GMT-%04u.%02u.%02u-%02u.%02u.%02u\\%s",
+               TALLOC_FREE(fname);
+               fname = talloc_asprintf(state,
+                                       "%s\\@GMT-%04u.%02u.%02u-%02u.%02u.%02u",
+                                       in_name,
                                        tm->tm_year + 1900,
                                        tm->tm_mon + 1,
                                        tm->tm_mday,
                                        tm->tm_hour,
                                        tm->tm_min,
-                                       tm->tm_sec,
-                                       in_name);
-                       if (tevent_req_nomem(fname, req)) {
-                               return tevent_req_post(req, ev);
-                       }
+                                       tm->tm_sec);
+               if (tevent_req_nomem(fname, req)) {
+                       return tevent_req_post(req, state->ev);
                }
+               /*
+                * Tell filename_create_ucf_flags() this
+                * is an @GMT path.
+                */
+               smb1req->flags2 |= FLAGS2_REPARSE_PATH;
+       }
 
-               if (qfid) {
-                       if (qfid->data.length != 0) {
-                               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+       if (qfid) {
+               if (qfid->data.length != 0) {
+                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
+       }
 
-               if (rqls) {
-                       lease_len = smb2_lease_pull(
-                               rqls->data.data, rqls->data.length, &lease);
-                       if (lease_len == -1) {
-                               tevent_req_nterror(
-                                       req, NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
-                       lease_ptr = &lease;
-
-                       if (DEBUGLEVEL >= 10) {
-                               DEBUG(10, ("Got lease request size %d\n",
-                                          (int)lease_len));
-                               NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
-                       }
-
-                       if (!smb2_lease_key_valid(&lease.lease_key)) {
-                               lease_ptr = NULL;
-                               requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
-                       }
-
-                       if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
-                           (lease.lease_version != 1)) {
-                               DEBUG(10, ("v2 lease key only for SMB3\n"));
-                               lease_ptr = NULL;
-                       }
-
-                       /*
-                        * Replay with a lease is only allowed if the
-                        * established open carries a lease with the
-                        * same lease key.
-                        */
-                       if (replay_operation) {
-                               struct smb2_lease *op_ls =
-                                               &op->compat->lease->lease;
-                               int op_oplock = op->compat->oplock_type;
-
-                               if (map_samba_oplock_levels_to_smb2(op_oplock)
-                                   != SMB2_OPLOCK_LEVEL_LEASE)
-                               {
-                                       status = NT_STATUS_ACCESS_DENIED;
-                                       (void)tevent_req_nterror(req, status);
-                                       return tevent_req_post(req, ev);
-                               }
-                               if (!smb2_lease_key_equal(&lease.lease_key,
-                                                         &op_ls->lease_key))
-                               {
-                                       status = NT_STATUS_ACCESS_DENIED;
-                                       (void)tevent_req_nterror(req, status);
-                                       return tevent_req_post(req, ev);
-                               }
-                       }
+       if (rqls) {
+               lease_len = smb2_lease_pull(
+                       rqls->data.data, rqls->data.length, &lease);
+               if (lease_len == -1) {
+                       tevent_req_nterror(
+                               req, NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
+               lease_ptr = &lease;
 
-               /* these are ignored for SMB2 */
-               in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
-               in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
+               if (DEBUGLEVEL >= 10) {
+                       DEBUG(10, ("Got lease request size %d\n",
+                                  (int)lease_len));
+                       NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
+               }
 
-               in_file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS;
+               if (!smb2_lease_key_valid(&lease.lease_key)) {
+                       lease_ptr = NULL;
+                       state->requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+               }
 
-               DEBUG(10, ("smbd_smb2_create_send: open execution phase\n"));
+               if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
+                   (lease.lease_version != 1)) {
+                       DEBUG(10, ("v2 lease key only for SMB3\n"));
+                       lease_ptr = NULL;
+               }
 
                /*
-                * For the backend file open procedure, there are
-                * three possible modes: replay operation (in which case
-                * there is nothing else to do), durable_reconnect or
-                * new open.
+                * Replay with a lease is only allowed if the
+                * established open carries a lease with the
+                * same lease key.
                 */
-               if (replay_operation) {
-                       result = op->compat;
-                       result->op = op;
-                       update_open = false;
-                       info = op->create_action;
-               } else if (do_durable_reconnect) {
-                       DATA_BLOB new_cookie = data_blob_null;
-                       NTTIME now = timeval_to_nttime(&smb2req->request_time);
-
-                       status = smb2srv_open_recreate(smb2req->xconn,
-                                               smb1req->conn->session_info,
-                                               persistent_id, create_guid,
-                                               now, &op);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               DEBUG(3, ("smbd_smb2_create_send: "
-                                         "smb2srv_open_recreate failed: %s\n",
-                                         nt_errstr(status)));
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
+               if (state->replay_operation) {
+                       struct smb2_lease *op_ls =
+                               &op->compat->lease->lease;
+                       int op_oplock = op->compat->oplock_type;
+
+                       if (map_samba_oplock_levels_to_smb2(op_oplock)
+                           != SMB2_OPLOCK_LEVEL_LEASE)
+                       {
+                               status = NT_STATUS_ACCESS_DENIED;
+                               (void)tevent_req_nterror(req, status);
+                               return tevent_req_post(req, state->ev);
                        }
-
-                       DEBUG(10, ("smb2_create_send: %s to recreate the "
-                                  "smb2srv_open struct for a durable handle.\n",
-                                  op->global->durable ? "succeded" : "failed"));
-
-                       if (!op->global->durable) {
-                               talloc_free(op);
-                               tevent_req_nterror(req,
-                                       NT_STATUS_OBJECT_NAME_NOT_FOUND);
-                               return tevent_req_post(req, ev);
+                       if (!smb2_lease_key_equal(&lease.lease_key,
+                                                 &op_ls->lease_key))
+                       {
+                               status = NT_STATUS_ACCESS_DENIED;
+                               (void)tevent_req_nterror(req, status);
+                               return tevent_req_post(req, state->ev);
                        }
+               }
+       }
 
-                       status = SMB_VFS_DURABLE_RECONNECT(smb1req->conn,
-                                               smb1req,
-                                               op, /* smbXsrv_open input */
-                                               op->global->backend_cookie,
-                                               op, /* TALLOC_CTX */
-                                               &result, &new_cookie);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               NTSTATUS return_status;
+       DEBUG(10, ("smbd_smb2_create_send: open execution phase\n"));
 
-                               return_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       /*
+        * For the backend file open procedure, there are
+        * three possible modes: replay operation (in which case
+        * there is nothing else to do), durable_reconnect or
+        * new open.
+        */
+       if (state->replay_operation) {
+               state->result = op->compat;
+               state->result->op = op;
+               update_open = false;
+               info = op->create_action;
+       } else if (do_durable_reconnect) {
+               DATA_BLOB new_cookie = data_blob_null;
+               NTTIME now = timeval_to_nttime(&smb2req->request_time);
+
+               status = smb2srv_open_recreate(smb2req->xconn,
+                                              smb1req->conn->session_info,
+                                              persistent_id, create_guid,
+                                              now, &op);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(3, ("smbd_smb2_create_send: "
+                                 "smb2srv_open_recreate failed: %s\n",
+                                 nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                               DEBUG(3, ("smbd_smb2_create_send: "
-                                         "durable_reconnect failed: %s => %s\n",
-                                         nt_errstr(status),
-                                         nt_errstr(return_status)));
+               DEBUG(10, ("smb2_create_send: %s to recreate the "
+                          "smb2srv_open struct for a durable handle.\n",
+                          op->global->durable ? "succeeded" : "failed"));
 
-                               tevent_req_nterror(req, return_status);
-                               return tevent_req_post(req, ev);
-                       }
+               if (!op->global->durable) {
+                       talloc_free(op);
+                       tevent_req_nterror(req,
+                                          NT_STATUS_OBJECT_NAME_NOT_FOUND);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
-                                  (unsigned)result->oplock_type, lease_ptr));
+               status = SMB_VFS_DURABLE_RECONNECT(smb1req->conn,
+                                                  smb1req,
+                                                  op, /* smbXsrv_open input */
+                                                  op->global->backend_cookie,
+                                                  op, /* TALLOC_CTX */
+                                                  &state->result,
+                                                  &new_cookie);
+               if (!NT_STATUS_IS_OK(status)) {
+                       NTSTATUS return_status;
 
-                       status = smbd_smb2_create_durable_lease_check(
-                               fname, result, lease_ptr);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               close_file(smb1req, result, SHUTDOWN_CLOSE);
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+                       return_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
 
-                       data_blob_free(&op->global->backend_cookie);
-                       op->global->backend_cookie = new_cookie;
+                       DEBUG(3, ("smbd_smb2_create_send: "
+                                 "durable_reconnect failed: %s => %s\n",
+                                 nt_errstr(status),
+                                 nt_errstr(return_status)));
 
-                       op->status = NT_STATUS_OK;
-                       op->global->disconnect_time = 0;
+                       tevent_req_nterror(req, return_status);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       /* save the timout for later update */
-                       durable_timeout_msec = op->global->durable_timeout_msec;
+               DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
+                          (unsigned)state->result->oplock_type, lease_ptr));
 
-                       update_open = true;
+               status = smbd_smb2_create_durable_lease_check(
+                       smb1req, fname, state->result, lease_ptr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       close_file(smb1req, state->result, SHUTDOWN_CLOSE);
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
+               }
 
-                       info = FILE_WAS_OPENED;
-               } else {
-                       struct smb_filename *smb_fname = NULL;
-                       uint32_t ucf_flags = UCF_PREP_CREATEFILE;
-
-                       if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
-                               if (lease_ptr == NULL) {
-                                       requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
-                               }
-                       } else {
-                               lease_ptr = NULL;
-                       }
+               data_blob_free(&op->global->backend_cookie);
+               op->global->backend_cookie = new_cookie;
 
-                       /*
-                        * For a DFS path the function parse_dfs_path()
-                        * will do the path processing.
-                        */
+               op->status = NT_STATUS_OK;
+               op->global->disconnect_time = 0;
 
-                       if (!(smb1req->flags2 & FLAGS2_DFS_PATHNAMES)) {
-                               /* convert '\\' into '/' */
-                               status = check_path_syntax(fname);
-                               if (!NT_STATUS_IS_OK(status)) {
-                                       tevent_req_nterror(req, status);
-                                       return tevent_req_post(req, ev);
-                               }
-                       }
+               /* save the timout for later update */
+               durable_timeout_msec = op->global->durable_timeout_msec;
 
-                       status = filename_convert(req,
-                                                 smb1req->conn,
-                                                 smb1req->flags2 & FLAGS2_DFS_PATHNAMES,
-                                                 fname,
-                                                 ucf_flags,
-                                                 NULL, /* ppath_contains_wcards */
-                                                 &smb_fname);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+               update_open = true;
 
-                       /*
-                        * MS-SMB2: 2.2.13 SMB2 CREATE Request
-                        * ImpersonationLevel ... MUST contain one of the
-                        * following values. The server MUST validate this
-                        * field, but otherwise ignore it.
-                        *
-                        * NB. The source4/torture/smb2/durable_open.c test
-                        * shows this check is only done on real opens, not
-                        * on durable handle-reopens.
-                        */
+               info = FILE_WAS_OPENED;
+       } else {
+               struct smb_filename *smb_fname = NULL;
+               uint32_t ucf_flags;
 
-                       if (in_impersonation_level >
-                                       SMB2_IMPERSONATION_DELEGATE) {
-                               tevent_req_nterror(req,
-                                       NT_STATUS_BAD_IMPERSONATION_LEVEL);
-                               return tevent_req_post(req, ev);
+               if (state->requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
+                       if (lease_ptr == NULL) {
+                               state->requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
                        }
+               } else {
+                       lease_ptr = NULL;
+               }
 
-                       /*
-                        * We know we're going to do a local open, so now
-                        * we must be protocol strict. JRA.
-                        *
-                        * MS-SMB2: 3.3.5.9 - Receiving an SMB2 CREATE Request
-                        * If the file name length is greater than zero and the
-                        * first character is a path separator character, the
-                        * server MUST fail the request with
-                        * STATUS_INVALID_PARAMETER.
-                        */
-                       if (in_name[0] == '\\' || in_name[0] == '/') {
-                               tevent_req_nterror(req,
-                                       NT_STATUS_INVALID_PARAMETER);
-                               return tevent_req_post(req, ev);
-                       }
+               /*
+                * For a DFS path the function parse_dfs_path()
+                * will do the path processing.
+                */
 
-                       status = SMB_VFS_CREATE_FILE(smb1req->conn,
-                                                    smb1req,
-                                                    0, /* root_dir_fid */
-                                                    smb_fname,
-                                                    in_desired_access,
-                                                    in_share_access,
-                                                    in_create_disposition,
-                                                    in_create_options,
-                                                    in_file_attributes,
-                                                    map_smb2_oplock_levels_to_samba(requested_oplock_level),
-                                                    lease_ptr,
-                                                    allocation_size,
-                                                    0, /* private_flags */
-                                                    sec_desc,
-                                                    ea_list,
-                                                    &result,
-                                                    &info,
-                                                    &in_context_blobs,
-                                                    state->out_context_blobs);
+               if (!(smb1req->flags2 & FLAGS2_DFS_PATHNAMES)) {
+                       /* convert '\\' into '/' */
+                       status = check_path_syntax(fname);
                        if (!NT_STATUS_IS_OK(status)) {
-                               if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
-                                       SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
-                                       return req;
-                               }
                                tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
+                               return tevent_req_post(req, state->ev);
                        }
-                       op = result->op;
+               }
+
+               ucf_flags = filename_create_ucf_flags(smb1req, in_create_disposition);
+               status = filename_convert(req,
+                                         smb1req->conn,
+                                         fname,
+                                         ucf_flags,
+                                         NULL, /* ppath_contains_wcards */
+                                         &smb_fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
                }
 
                /*
-                * here we have op == result->op
+                * MS-SMB2: 2.2.13 SMB2 CREATE Request
+                * ImpersonationLevel ... MUST contain one of the
+                * following values. The server MUST validate this
+                * field, but otherwise ignore it.
+                *
+                * NB. The source4/torture/smb2/durable_open.c test
+                * shows this check is only done on real opens, not
+                * on durable handle-reopens.
                 */
 
-               DEBUG(10, ("smbd_smb2_create_send: "
-                          "response construction phase\n"));
-
-               if (mxac) {
-                       NTTIME last_write_time;
-
-                       last_write_time = unix_timespec_to_nt_time(
-                                                result->fsp_name->st.st_ex_mtime);
-                       if (last_write_time != max_access_time) {
-                               uint8_t p[8];
-                               uint32_t max_access_granted;
-                               DATA_BLOB blob = data_blob_const(p, sizeof(p));
-
-                               status = smbd_calculate_access_mask(smb1req->conn,
-                                                       result->fsp_name,
-                                                       false,
-                                                       SEC_FLAG_MAXIMUM_ALLOWED,
-                                                       &max_access_granted);
-
-                               SIVAL(p, 0, NT_STATUS_V(status));
-                               SIVAL(p, 4, max_access_granted);
-
-                               status = smb2_create_blob_add(
-                                   state->out_context_blobs,
-                                   state->out_context_blobs,
-                                   SMB2_CREATE_TAG_MXAC,
-                                   blob);
-                               if (!NT_STATUS_IS_OK(status)) {
-                                       tevent_req_nterror(req, status);
-                                       return tevent_req_post(req, ev);
-                               }
-                       }
+               if (in_impersonation_level >
+                   SMB2_IMPERSONATION_DELEGATE) {
+                       tevent_req_nterror(req,
+                                          NT_STATUS_BAD_IMPERSONATION_LEVEL);
+                       return tevent_req_post(req, state->ev);
                }
 
-               if (!replay_operation && durable_requested &&
-                   (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
-               {
-                       status = SMB_VFS_DURABLE_COOKIE(result,
-                                               op,
-                                               &op->global->backend_cookie);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               op->global->backend_cookie = data_blob_null;
-                       }
+               /*
+                * We know we're going to do a local open, so now
+                * we must be protocol strict. JRA.
+                *
+                * MS-SMB2: 3.3.5.9 - Receiving an SMB2 CREATE Request
+                * If the file name length is greater than zero and the
+                * first character is a path separator character, the
+                * server MUST fail the request with
+                * STATUS_INVALID_PARAMETER.
+                */
+               if (in_name[0] == '\\' || in_name[0] == '/') {
+                       tevent_req_nterror(req,
+                                          NT_STATUS_INVALID_PARAMETER);
+                       return tevent_req_post(req, state->ev);
                }
-               if (!replay_operation && op->global->backend_cookie.length > 0)
-               {
-                       update_open = true;
 
-                       op->global->durable = true;
-                       op->global->durable_timeout_msec = durable_timeout_msec;
+               status = SMB_VFS_CREATE_FILE(smb1req->conn,
+                                            smb1req,
+                                            0, /* root_dir_fid */
+                                            smb_fname,
+                                            in_desired_access,
+                                            in_share_access,
+                                            in_create_disposition,
+                                            in_create_options,
+                                            in_file_attributes,
+                                            map_smb2_oplock_levels_to_samba(
+                                                    state->requested_oplock_level),
+                                            lease_ptr,
+                                            allocation_size,
+                                            0, /* private_flags */
+                                            sec_desc,
+                                            ea_list,
+                                            &state->result,
+                                            &info,
+                                            &in_context_blobs,
+                                            state->out_context_blobs);
+               if (!NT_STATUS_IS_OK(status)) {
+                       if (open_was_deferred(smb1req->xconn, smb1req->mid)) {
+                               SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(smb2req->profile);
+                               return req;
+                       }
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
                }
+               op = state->result->op;
+       }
 
-               if (update_open) {
-                       op->global->create_guid = _create_guid;
-                       if (need_replay_cache) {
-                               op->flags |= SMBXSRV_OPEN_NEED_REPLAY_CACHE;
-                       }
+       /*
+        * here we have op == state->result->op
+        */
 
-                       status = smbXsrv_open_update(op);
-                       DEBUG(10, ("smb2_create_send: smbXsrv_open_update "
-                                  "returned %s\n",
-                                  nt_errstr(status)));
-                       if (!NT_STATUS_IS_OK(status)) {
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
-               }
+       DEBUG(10, ("smbd_smb2_create_send: "
+                  "response construction phase\n"));
+
+       if (mxac) {
+               NTTIME last_write_time;
 
-               if (dhnq && op->global->durable) {
-                       uint8_t p[8] = { 0, };
+               last_write_time = unix_timespec_to_nt_time(
+                       state->result->fsp_name->st.st_ex_mtime);
+               if (last_write_time != max_access_time) {
+                       uint8_t p[8];
+                       uint32_t max_access_granted;
                        DATA_BLOB blob = data_blob_const(p, sizeof(p));
 
-                       status = smb2_create_blob_add(state->out_context_blobs,
-                                                     state->out_context_blobs,
-                                                     SMB2_CREATE_TAG_DHNQ,
-                                                     blob);
+                       status = smbd_calculate_access_mask(smb1req->conn,
+                                                           state->result->fsp_name,
+                                                           false,
+                                                           SEC_FLAG_MAXIMUM_ALLOWED,
+                                                           &max_access_granted);
+
+                       SIVAL(p, 0, NT_STATUS_V(status));
+                       SIVAL(p, 4, max_access_granted);
+
+                       status = smb2_create_blob_add(
+                               state->out_context_blobs,
+                               state->out_context_blobs,
+                               SMB2_CREATE_TAG_MXAC,
+                               blob);
                        if (!NT_STATUS_IS_OK(status)) {
                                tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
+                               return tevent_req_post(req, state->ev);
                        }
                }
+       }
 
-               if (dh2q && op->global->durable &&
-                   /*
-                    * For replay operations, we return the dh2q blob
-                    * in the case of oplocks not based on the state of
-                    * the open, but on whether it could have been granted
-                    * for the request data. In the case of leases instead,
-                    * the state of the open is used...
-                    */
-                   (!replay_operation ||
-                    in_oplock_level == SMB2_OPLOCK_LEVEL_BATCH ||
-                    in_oplock_level == SMB2_OPLOCK_LEVEL_LEASE))
-               {
-                       uint8_t p[8] = { 0, };
-                       DATA_BLOB blob = data_blob_const(p, sizeof(p));
-                       uint32_t durable_v2_response_flags = 0;
+       if (!state->replay_operation && durable_requested &&
+           (fsp_lease_type(state->result) & SMB2_LEASE_HANDLE))
+       {
+               status = SMB_VFS_DURABLE_COOKIE(state->result,
+                                               op,
+                                               &op->global->backend_cookie);
+               if (!NT_STATUS_IS_OK(status)) {
+                       op->global->backend_cookie = data_blob_null;
+               }
+       }
+       if (!state->replay_operation && op->global->backend_cookie.length > 0)
+       {
+               update_open = true;
 
-                       SIVAL(p, 0, op->global->durable_timeout_msec);
-                       SIVAL(p, 4, durable_v2_response_flags);
+               op->global->durable = true;
+               op->global->durable_timeout_msec = durable_timeout_msec;
+       }
 
-                       status = smb2_create_blob_add(state->out_context_blobs,
-                                                     state->out_context_blobs,
-                                                     SMB2_CREATE_TAG_DH2Q,
-                                                     blob);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+       if (update_open) {
+               op->global->create_guid = _create_guid;
+               if (need_replay_cache) {
+                       op->flags |= SMBXSRV_OPEN_NEED_REPLAY_CACHE;
                }
 
-               if (qfid) {
-                       uint8_t p[32];
-                       uint64_t file_index = get_FileIndex(result->conn,
-                                                       &result->fsp_name->st);
-                       DATA_BLOB blob = data_blob_const(p, sizeof(p));
+               status = smbXsrv_open_update(op);
+               DEBUG(10, ("smb2_create_send: smbXsrv_open_update "
+                          "returned %s\n",
+                          nt_errstr(status)));
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
+               }
+       }
+
+       if (dhnq && op->global->durable) {
+               uint8_t p[8] = { 0, };
+               DATA_BLOB blob = data_blob_const(p, sizeof(p));
+
+               status = smb2_create_blob_add(state->out_context_blobs,
+                                             state->out_context_blobs,
+                                             SMB2_CREATE_TAG_DHNQ,
+                                             blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
+               }
+       }
 
-                       ZERO_STRUCT(p);
+       if (dh2q && op->global->durable &&
+           /*
+            * For replay operations, we return the dh2q blob
+            * in the case of oplocks not based on the state of
+            * the open, but on whether it could have been granted
+            * for the request data. In the case of leases instead,
+            * the state of the open is used...
+            */
+           (!state->replay_operation ||
+            state->in_oplock_level == SMB2_OPLOCK_LEVEL_BATCH ||
+            state->in_oplock_level == SMB2_OPLOCK_LEVEL_LEASE))
+       {
+               uint8_t p[8] = { 0, };
+               DATA_BLOB blob = data_blob_const(p, sizeof(p));
+               uint32_t durable_v2_response_flags = 0;
 
-                       /* From conversations with Microsoft engineers at
-                          the MS plugfest. The first 8 bytes are the "volume index"
-                          == inode, the second 8 bytes are the "volume id",
-                          == dev. This will be updated in the SMB2 doc. */
-                       SBVAL(p, 0, file_index);
-                       SIVAL(p, 8, result->fsp_name->st.st_ex_dev);/* FileIndexHigh */
+               SIVAL(p, 0, op->global->durable_timeout_msec);
+               SIVAL(p, 4, durable_v2_response_flags);
 
-                       status = smb2_create_blob_add(state->out_context_blobs,
-                                                     state->out_context_blobs,
-                                                     SMB2_CREATE_TAG_QFID,
-                                                     blob);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+               status = smb2_create_blob_add(state->out_context_blobs,
+                                             state->out_context_blobs,
+                                             SMB2_CREATE_TAG_DH2Q,
+                                             blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
                }
+       }
 
-               if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
-                       uint8_t buf[52];
+       if (qfid) {
+               uint8_t p[32];
+               uint64_t file_index = get_FileIndex(state->result->conn,
+                                                   &state->result->fsp_name->st);
+               DATA_BLOB blob = data_blob_const(p, sizeof(p));
 
-                       lease = result->lease->lease;
+               ZERO_STRUCT(p);
 
-                       lease_len = sizeof(buf);
-                       if (lease.lease_version == 1) {
-                               lease_len = 32;
-                       }
+               /* From conversations with Microsoft engineers at
+                  the MS plugfest. The first 8 bytes are the "volume index"
+                  == inode, the second 8 bytes are the "volume id",
+                  == dev. This will be updated in the SMB2 doc. */
+               SBVAL(p, 0, file_index);
+               SIVAL(p, 8, state->result->fsp_name->st.st_ex_dev);/* FileIndexHigh */
 
-                       if (!smb2_lease_push(&lease, buf, lease_len)) {
-                               tevent_req_nterror(
-                                       req, NT_STATUS_INTERNAL_ERROR);
-                               return tevent_req_post(req, ev);
-                       }
+               status = smb2_create_blob_add(state->out_context_blobs,
+                                             state->out_context_blobs,
+                                             SMB2_CREATE_TAG_QFID,
+                                             blob);
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
+               }
+       }
 
-                       status = smb2_create_blob_add(
-                               state, state->out_context_blobs,
-                               SMB2_CREATE_TAG_RQLS,
-                               data_blob_const(buf, lease_len));
-                       if (!NT_STATUS_IS_OK(status)) {
-                               tevent_req_nterror(req, status);
-                               return tevent_req_post(req, ev);
-                       }
+       if ((rqls != NULL) && (state->result->oplock_type == LEASE_OPLOCK)) {
+               uint8_t buf[52];
+
+               lease = state->result->lease->lease;
+
+               lease_len = sizeof(buf);
+               if (lease.lease_version == 1) {
+                       lease_len = 32;
+               }
+
+               if (!smb2_lease_push(&lease, buf, lease_len)) {
+                       tevent_req_nterror(
+                               req, NT_STATUS_INTERNAL_ERROR);
+                       return tevent_req_post(req, state->ev);
+               }
+
+               status = smb2_create_blob_add(
+                       state, state->out_context_blobs,
+                       SMB2_CREATE_TAG_RQLS,
+                       data_blob_const(buf, lease_len));
+               if (!NT_STATUS_IS_OK(status)) {
+                       tevent_req_nterror(req, status);
+                       return tevent_req_post(req, state->ev);
                }
        }
 
+       smbd_smb2_create_finish(req,
+                               smb2req,
+                               smb1req,
+                               state->result,
+                               state->replay_operation,
+                               state->in_oplock_level,
+                               in_create_disposition,
+                               info);
+       return req;
+}
+
+static void smbd_smb2_create_finish(struct tevent_req *req,
+                                   struct smbd_smb2_request *smb2req,
+                                   struct smb_request *smb1req,
+                                   files_struct *result,
+                                   const bool replay_operation,
+                                   const int in_oplock_level,
+                                   const int in_create_disposition,
+                                   const int info)
+{
+       struct smbd_smb2_create_state *state = tevent_req_data(
+               req, struct smbd_smb2_create_state);
+
        smb2req->compat_chain_fsp = smb1req->chain_fsp;
 
        if (replay_operation) {
@@ -1389,11 +1457,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        state->out_file_id_persistent = result->op->global->open_persistent_id;
        state->out_file_id_volatile = result->op->global->open_volatile_id;
 
-       DEBUG(10,("smbd_smb2_create_send: %s - %s\n",
-                 fsp_str_dbg(result), fsp_fnum_dbg(result)));
+       DBG_DEBUG("%s - %s\n", fsp_str_dbg(result), fsp_fnum_dbg(result));
 
        tevent_req_done(req);
-       return tevent_req_post(req, ev);
+       tevent_req_post(req, state->ev);
+       return;
 }
 
 static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
@@ -1557,8 +1625,6 @@ static void remove_deferred_open_message_smb2_internal(struct smbd_smb2_request
                (unsigned long long)mid ));
 
        state->open_was_deferred = false;
-       /* Ensure we don't have any outstanding timer event. */
-       TALLOC_FREE(state->te);
        /* Ensure we don't have any outstanding immediate event. */
        TALLOC_FREE(state->im);
 }
@@ -1626,8 +1692,6 @@ bool schedule_deferred_open_message_smb2(
                return false;
        }
 
-       /* Ensure we don't have any outstanding timer event. */
-       TALLOC_FREE(state->te);
        /* Ensure we don't have any outstanding immediate event. */
        TALLOC_FREE(state->im);