s3: smbd: Add async internals of reply_exit().
[amitay/samba.git] / source3 / smbd / reply.c
index 0242e66d92e91ea5438cd19f875f53139b1756a5..a2cc9e104ad53a00903f0fb9d758929f2630f65a 100644 (file)
 #include "smbprofile.h"
 #include "../lib/tsocket/tsocket.h"
 #include "lib/tevent_wait.h"
+#include "lib/util/tevent_ntstatus.h"
 #include "libcli/smb/smb_signing.h"
 #include "lib/util/sys_rw_data.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "smb1_utils.h"
+#include "libcli/smb/smb2_posix.h"
 
 /****************************************************************************
  Ensure we check the path in *exactly* the same way as W2K for a findfirst/findnext
@@ -252,7 +256,7 @@ NTSTATUS check_path_syntax_posix(char *path)
 }
 
 /****************************************************************************
- Pull a string and check the path allowing a wilcard - provide for error return.
+ Pull a string and check the path allowing a wildcard - provide for error return.
  Passes in posix flag.
 ****************************************************************************/
 
@@ -300,7 +304,7 @@ static size_t srvstr_get_path_wcard_internal(TALLOC_CTX *ctx,
 }
 
 /****************************************************************************
- Pull a string and check the path allowing a wilcard - provide for error return.
+ Pull a string and check the path allowing a wildcard - provide for error return.
 ****************************************************************************/
 
 size_t srvstr_get_path_wcard(TALLOC_CTX *ctx,
@@ -326,7 +330,7 @@ size_t srvstr_get_path_wcard(TALLOC_CTX *ctx,
 }
 
 /****************************************************************************
- Pull a string and check the path allowing a wilcard - provide for error return.
+ Pull a string and check the path allowing a wildcard - provide for error return.
  posix_pathnames version.
 ****************************************************************************/
 
@@ -866,6 +870,8 @@ void reply_tcon(struct smb_request *req)
 
 void reply_tcon_and_X(struct smb_request *req)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        connection_struct *conn = req->conn;
        const char *service = NULL;
        TALLOC_CTX *ctx = talloc_tos();
@@ -1046,6 +1052,7 @@ void reply_tcon_and_X(struct smb_request *req)
                        if (!NT_STATUS_IS_OK(status)) {
                                DBG_ERR("smb_key_derivation failed: %s\n",
                                        nt_errstr(status));
+                               END_PROFILE(SMBtconX);
                                return;
                        }
                        optional_support |= SMB_EXTENDED_SIGNATURES;
@@ -1139,7 +1146,7 @@ void reply_tcon_and_X(struct smb_request *req)
 
                if (lp_msdfs_root(SNUM(conn)) && lp_host_msdfs()) {
                        DEBUG(2,("Serving %s as a Dfs root\n",
-                                lp_servicename(ctx, SNUM(conn)) ));
+                                lp_servicename(ctx, lp_sub, SNUM(conn)) ));
                        optional_support |= SMB_SHARE_IN_DFS;
                }
 
@@ -1180,6 +1187,8 @@ void reply_unknown_new(struct smb_request *req, uint8_t type)
 
 void reply_ioctl(struct smb_request *req)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        connection_struct *conn = req->conn;
        uint16_t device;
        uint16_t function;
@@ -1246,6 +1255,7 @@ void reply_ioctl(struct smb_request *req)
                                status = srvstr_push((char *)req->outbuf, req->flags2,
                                            p+18,
                                            lp_servicename(talloc_tos(),
+                                                          lp_sub,
                                                           SNUM(conn)),
                                            13, STR_TERMINATE|STR_ASCII, &len);
                                if (!NT_STATUS_IS_OK(status)) {
@@ -1384,7 +1394,7 @@ void reply_getatr(struct smb_request *req)
        const char *p;
        NTSTATUS status;
        TALLOC_CTX *ctx = talloc_tos();
-       bool ask_sharemode = lp_parm_bool(SNUM(conn), "smbd", "search ask sharemode", true);
+       bool ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn));
 
        START_PROFILE(SMBgetatr);
 
@@ -1441,7 +1451,7 @@ void reply_getatr(struct smb_request *req)
                        ZERO_STRUCT(write_time_ts);
                        fileid = vfs_file_id_from_sbuf(conn, &smb_fname->st);
                        get_file_infos(fileid, 0, NULL, &write_time_ts);
-                       if (!null_timespec(write_time_ts)) {
+                       if (!is_omit_timespec(&write_time_ts)) {
                                update_stat_ex_mtime(&smb_fname->st, write_time_ts);
                        }
                }
@@ -1495,6 +1505,7 @@ void reply_setatr(struct smb_request *req)
        TALLOC_CTX *ctx = talloc_tos();
 
        START_PROFILE(SMBsetatr);
+       init_smb_file_time(&ft);
 
        if (req->wct < 2) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -1557,9 +1568,7 @@ void reply_setatr(struct smb_request *req)
                }
        }
 
-       ft = (struct smb_file_time) {
-               .mtime = convert_time_t_to_timespec(mtime)
-       };
+       ft.mtime = time_t_to_full_timespec(mtime);
 
        status = smb_set_file_time(conn, NULL, smb_fname, &ft, true);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1767,10 +1776,12 @@ void reply_search(struct smb_request *req)
        bool mask_contains_wcard = False;
        bool allow_long_path_components = (req->flags2 & FLAGS2_LONG_PATH_COMPONENTS) ? True : False;
        TALLOC_CTX *ctx = talloc_tos();
-       bool ask_sharemode = lp_parm_bool(SNUM(conn), "smbd", "search ask sharemode", true);
-       struct dptr_struct *dirptr = NULL;
+       bool ask_sharemode = lp_smbd_search_ask_sharemode(SNUM(conn));
        struct smbXsrv_connection *xconn = req->xconn;
        struct smbd_server_connection *sconn = req->sconn;
+       files_struct *fsp = NULL;
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
 
        START_PROFILE(SMBsearch);
 
@@ -1800,6 +1811,11 @@ void reply_search(struct smb_request *req)
                goto out;
        }
 
+       if (smbreq_bufrem(req, p) < 3) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               goto out;
+       }
+
        p++;
        status_len = SVAL(p, 0);
        p += 2;
@@ -1807,6 +1823,7 @@ void reply_search(struct smb_request *req)
        /* dirtype &= ~FILE_ATTRIBUTE_DIRECTORY; */
 
        if (status_len == 0) {
+               int ret;
                struct smb_filename *smb_dname = NULL;
                uint32_t ucf_flags = UCF_ALWAYS_ALLOW_WCARD_LCOMP |
                        ucf_flags_from_smb_request(req);
@@ -1856,37 +1873,99 @@ void reply_search(struct smb_request *req)
                        goto out;
                }
 
+               /*
+                * As we've cut off the last component from
+                * smb_fname we need to re-stat smb_dname
+                * so FILE_OPEN disposition knows the directory
+                * exists.
+                */
+               if (req->posix_pathnames) {
+                       ret = SMB_VFS_LSTAT(conn, smb_dname);
+               } else {
+                       ret = SMB_VFS_STAT(conn, smb_dname);
+               }
+               if (ret == -1) {
+                       nt_status = map_nt_error_from_unix(errno);
+                       reply_nterror(req, nt_status);
+                       goto out;
+               }
+
+               /*
+                * Open an fsp on this directory for the dptr.
+                */
+               nt_status = SMB_VFS_CREATE_FILE(
+                               conn, /* conn */
+                               req, /* req */
+                               0, /* root_dir_fid */
+                               smb_dname, /* dname */
+                               FILE_LIST_DIRECTORY, /* access_mask */
+                               FILE_SHARE_READ|
+                               FILE_SHARE_WRITE, /* share_access */
+                               FILE_OPEN, /* create_disposition*/
+                               FILE_DIRECTORY_FILE, /* create_options */
+                               FILE_ATTRIBUTE_DIRECTORY,/* file_attributes */
+                               NO_OPLOCK, /* oplock_request */
+                               NULL, /* lease */
+                               0, /* allocation_size */
+                               0, /* private_flags */
+                               NULL, /* sd */
+                               NULL, /* ea_list */
+                               &fsp, /* result */
+                               NULL, /* pinfo */
+                               NULL, /* in_context */
+                               NULL);/* out_context */
+
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       DBG_ERR("failed to open directory %s\n",
+                               smb_fname_str_dbg(smb_dname));
+                       reply_nterror(req, nt_status);
+                       goto out;
+               }
+
                nt_status = dptr_create(conn,
                                        NULL, /* req */
-                                       NULL, /* fsp */
-                                       smb_dname,
+                                       fsp, /* fsp */
                                        True,
                                        expect_close,
                                        req->smbpid,
                                        mask,
                                        mask_contains_wcard,
                                        dirtype,
-                                       &dirptr);
+                                       &fsp->dptr);
 
                TALLOC_FREE(smb_dname);
 
                if (!NT_STATUS_IS_OK(nt_status)) {
+                       /*
+                        * Use NULL here for the first parameter (req)
+                        * as this is not a client visible handle so
+                        * can'tbe part of an SMB1 chain.
+                        */
+                       close_file(NULL, fsp, NORMAL_CLOSE);
+                       fsp = NULL;
                        reply_nterror(req, nt_status);
                        goto out;
                }
-               dptr_num = dptr_dnum(dirptr);
+
+               dptr_num = dptr_dnum(fsp->dptr);
+
        } else {
                int status_dirtype;
                const char *dirpath;
 
+               if (smbreq_bufrem(req, p) < 21) {
+                       reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+                       goto out;
+               }
+
                memcpy(status,p,21);
                status_dirtype = CVAL(status,0) & 0x1F;
                if (status_dirtype != (dirtype & 0x1F)) {
                        dirtype = status_dirtype;
                }
 
-               dirptr = dptr_fetch(sconn, status+12,&dptr_num);
-               if (!dirptr) {
+               fsp = dptr_fetch_fsp(sconn, status+12,&dptr_num);
+               if (fsp == NULL) {
                        goto SearchEmpty;
                }
                dirpath = dptr_path(sconn, dptr_num);
@@ -1940,14 +2019,14 @@ void reply_search(struct smb_request *req)
                maxentries = MIN(maxentries, available_space/DIR_STRUCT_SIZE);
 
                DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",
-                        directory,lp_dont_descend(ctx, SNUM(conn))));
-               if (in_list(directory, lp_dont_descend(ctx, SNUM(conn)),True)) {
+                        directory,lp_dont_descend(ctx, lp_sub, SNUM(conn))));
+               if (in_list(directory, lp_dont_descend(ctx, lp_sub, SNUM(conn)),True)) {
                        check_descend = True;
                }
 
                for (i=numentries;(i<maxentries) && !finished;i++) {
                        finished = !get_dir_entry(ctx,
-                                                 dirptr,
+                                                 fsp->dptr,
                                                  mask,
                                                  dirtype,
                                                  &fname,
@@ -1987,19 +2066,32 @@ void reply_search(struct smb_request *req)
   SearchEmpty:
 
        /* If we were called as SMBffirst with smb_search_id == NULL
-               and no entries were found then return error and close dirptr 
+               and no entries were found then return error and close fsp->dptr
                (X/Open spec) */
 
        if (numentries == 0) {
-               dptr_close(sconn, &dptr_num);
+               dptr_num = -1;
+               if (fsp != NULL) {
+                       close_file(NULL, fsp, NORMAL_CLOSE);
+                       fsp = NULL;
+               }
        } else if(expect_close && status_len == 0) {
                /* Close the dptr - we know it's gone */
-               dptr_close(sconn, &dptr_num);
+               dptr_num = -1;
+               if (fsp != NULL) {
+                       close_file(NULL, fsp, NORMAL_CLOSE);
+                       fsp = NULL;
+               }
        }
 
-       /* If we were called as SMBfunique, then we can close the dirptr now ! */
+       /* If we were called as SMBfunique, then we can close the fsp->dptr now ! */
        if(dptr_num >= 0 && req->cmd == SMBfunique) {
-               dptr_close(sconn, &dptr_num);
+               dptr_num = -1;
+               /* fsp may have been closed above. */
+               if (fsp != NULL) {
+                       close_file(NULL, fsp, NORMAL_CLOSE);
+                       fsp = NULL;
+               }
        }
 
        if ((numentries == 0) && !mask_contains_wcard) {
@@ -2055,6 +2147,7 @@ void reply_fclose(struct smb_request *req)
        bool path_contains_wcard = False;
        TALLOC_CTX *ctx = talloc_tos();
        struct smbd_server_connection *sconn = req->sconn;
+       files_struct *fsp = NULL;
 
        START_PROFILE(SMBfclose);
 
@@ -2072,6 +2165,13 @@ void reply_fclose(struct smb_request *req)
                END_PROFILE(SMBfclose);
                return;
        }
+
+       if (smbreq_bufrem(req, p) < 3) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               END_PROFILE(SMBfclose);
+               return;
+       }
+
        p++;
        status_len = SVAL(p,0);
        p += 2;
@@ -2082,11 +2182,20 @@ void reply_fclose(struct smb_request *req)
                return;
        }
 
+       if (smbreq_bufrem(req, p) < 21) {
+               reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               END_PROFILE(SMBfclose);
+               return;
+       }
+
        memcpy(status,p,21);
 
-       if(dptr_fetch(sconn, status+12,&dptr_num)) {
-               /*  Close the dptr - we know it's gone */
-               dptr_close(sconn, &dptr_num);
+       fsp = dptr_fetch_fsp(sconn, status+12,&dptr_num);
+       if(fsp != NULL) {
+               /*  Close the file - we know it's gone */
+               close_file(NULL, fsp, NORMAL_CLOSE);
+               fsp = NULL;
+               dptr_num = -1;
        }
 
        reply_outbuf(req, 1, 0);
@@ -2195,8 +2304,26 @@ void reply_open(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
-               reply_openerror(req, status);
-               goto out;
+
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       reply_openerror(req, status);
+                       goto out;
+               }
+
+               fsp = fcb_or_dos_open(
+                       req,
+                       smb_fname,
+                       access_mask,
+                       create_options,
+                       private_flags);
+               if (fsp == NULL) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+                       reply_openerror(req, status);
+                       goto out;
+               }
        }
 
        /* Ensure we're pointing at the correct stat struct. */
@@ -2368,8 +2495,29 @@ void reply_open_and_X(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
-               reply_openerror(req, status);
-               goto out;
+
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       reply_openerror(req, status);
+                       goto out;
+               }
+
+               fsp = fcb_or_dos_open(
+                       req,
+                       smb_fname,
+                       access_mask,
+                       create_options,
+                       private_flags);
+               if (fsp == NULL) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+                       reply_openerror(req, status);
+                       goto out;
+               }
+
+
+               smb_action = FILE_WAS_OPENED;
        }
 
        /* Setting the "size" field in vwv9 and vwv10 causes the file to be set to this size,
@@ -2396,12 +2544,12 @@ void reply_open_and_X(struct smb_request *req)
        }
 
        fattr = dos_mode(conn, fsp->fsp_name);
-       mtime = convert_timespec_to_time_t(fsp->fsp_name->st.st_ex_mtime);
        if (fattr & FILE_ATTRIBUTE_DIRECTORY) {
                close_file(req, fsp, ERROR_CLOSE);
                reply_nterror(req, NT_STATUS_ACCESS_DENIED);
                goto out;
        }
+       mtime = convert_timespec_to_time_t(fsp->fsp_name->st.st_ex_mtime);
 
        /* If the caller set the extended oplock request bit
                and we granted one (by whatever means) - set the
@@ -2465,42 +2613,196 @@ void reply_open_and_X(struct smb_request *req)
  Reply to a SMBulogoffX.
 ****************************************************************************/
 
-void reply_ulogoffX(struct smb_request *req)
+static struct tevent_req *reply_ulogoffX_send(struct smb_request *smb1req,
+                               struct smbXsrv_session *session);
+static void reply_ulogoffX_done(struct tevent_req *req);
+
+void reply_ulogoffX(struct smb_request *smb1req)
 {
-       struct smbd_server_connection *sconn = req->sconn;
-       struct user_struct *vuser;
+       struct timeval now = timeval_current();
        struct smbXsrv_session *session = NULL;
+       struct tevent_req *req;
        NTSTATUS status;
 
-       START_PROFILE(SMBulogoffX);
+       /*
+        * Don't setup the profile charge here, take
+        * it in reply_ulogoffX_done(). Not strictly correct
+        * but better than the other SMB1 async
+        * code that double-charges at the moment.
+        */
 
-       vuser = get_valid_user_struct(sconn, req->vuid);
+       status = smb1srv_session_lookup(smb1req->xconn,
+                                       smb1req->vuid,
+                                       timeval_to_nttime(&now),
+                                       &session);
+       if (!NT_STATUS_IS_OK(status)) {
+               /* Not going async, profile here. */
+               START_PROFILE(SMBulogoffX);
+               DBG_WARNING("ulogoff, vuser id %llu does not map to user.\n",
+                        (unsigned long long)smb1req->vuid);
 
-       if(vuser == NULL) {
-               DEBUG(3,("ulogoff, vuser id %llu does not map to user.\n",
-                        (unsigned long long)req->vuid));
+               smb1req->vuid = UID_FIELD_INVALID;
+               reply_force_doserror(smb1req, ERRSRV, ERRbaduid);
+               END_PROFILE(SMBulogoffX);
+               return;
+       }
 
-               req->vuid = UID_FIELD_INVALID;
-               reply_force_doserror(req, ERRSRV, ERRbaduid);
+       req = reply_ulogoffX_send(smb1req, session);
+       if (req == NULL) {
+               /* Not going async, profile here. */
+               START_PROFILE(SMBulogoffX);
+               reply_force_doserror(smb1req, ERRDOS, ERRnomem);
                END_PROFILE(SMBulogoffX);
                return;
        }
 
-       session = vuser->session;
-       vuser = NULL;
+       /* We're async. This will complete later. */
+       tevent_req_set_callback(req, reply_ulogoffX_done, smb1req);
+       return;
+}
+
+struct reply_ulogoffX_state {
+       struct tevent_queue *wait_queue;
+       struct smbXsrv_session *session;
+};
+
+static void reply_ulogoffX_wait_done(struct tevent_req *subreq);
+
+/****************************************************************************
+ Async SMB1 ulogoffX.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_ulogoffX_send(struct smb_request *smb1req,
+                                       struct smbXsrv_session *session)
+{
+       struct tevent_req *req;
+       struct reply_ulogoffX_state *state;
+       struct tevent_req *subreq;
+       files_struct *fsp;
+       struct smbd_server_connection *sconn = session->client->sconn;
+       uint64_t vuid = session->global->session_wire_id;
+
+       req = tevent_req_create(smb1req, &state,
+                       struct reply_ulogoffX_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->wait_queue = tevent_queue_create(state,
+                               "reply_ulogoffX_wait_queue");
+       if (tevent_req_nomem(state->wait_queue, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+       state->session = session;
 
        /*
-        * TODO: cancel all outstanding requests on the session
+        * Make sure that no new request will be able to use this session.
+        * This ensures that once all outstanding fsp->aio_requests
+        * on this session are done, we are safe to close it.
         */
-       status = smbXsrv_session_logoff(session);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("reply_ulogoff: "
-                         "smbXsrv_session_logoff() failed: %s\n",
-                         nt_errstr(status)));
+       session->status = NT_STATUS_USER_SESSION_DELETED;
+
+       for (fsp = sconn->files; fsp; fsp = fsp->next) {
+               if (fsp->vuid != vuid) {
+                       continue;
+               }
                /*
-                * If we hit this case, there is something completely
-                * wrong, so we better disconnect the transport connection.
+                * Flag the file as close in progress.
+                * This will prevent any more IO being
+                * done on it.
                 */
+               fsp->closing = true;
+
+               if (fsp->num_aio_requests > 0) {
+                       /*
+                        * Now wait until all aio requests on this fsp are
+                        * finished.
+                        *
+                        * We don't set a callback, as we just want to block the
+                        * wait queue and the talloc_free() of fsp->aio_request
+                        * will remove the item from the wait queue.
+                        */
+                       subreq = tevent_queue_wait_send(fsp->aio_requests,
+                                               sconn->ev_ctx,
+                                               state->wait_queue);
+                       if (tevent_req_nomem(subreq, req)) {
+                               TALLOC_FREE(req);
+                               return NULL;
+                       }
+               }
+       }
+
+       /*
+        * Now we add our own waiter to the end of the queue,
+        * this way we get notified when all pending requests are finished
+        * and reply to the outstanding SMB1 request.
+        */
+       subreq = tevent_queue_wait_send(state,
+                               sconn->ev_ctx,
+                               state->wait_queue);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       /*
+        * We're really going async - move the SMB1 request from
+        * a talloc stackframe above us to the sconn talloc-context.
+        * We need this to stick around until the wait_done
+        * callback is invoked.
+        */
+       smb1req = talloc_move(sconn, &smb1req);
+
+       tevent_req_set_callback(subreq, reply_ulogoffX_wait_done, req);
+
+       return req;
+}
+
+static void reply_ulogoffX_wait_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+
+       tevent_queue_wait_recv(subreq);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static NTSTATUS reply_ulogoffX_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_ulogoffX_done(struct tevent_req *req)
+{
+       struct smb_request *smb1req = tevent_req_callback_data(
+               req, struct smb_request);
+       struct reply_ulogoffX_state *state = tevent_req_data(req,
+                                               struct reply_ulogoffX_state);
+       struct smbXsrv_session *session = state->session;
+       NTSTATUS status;
+
+       /*
+        * Take the profile charge here. Not strictly
+        * correct but better than the other SMB1 async
+        * code that double-charges at the moment.
+        */
+       START_PROFILE(SMBulogoffX);
+
+       status = reply_ulogoffX_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb1req);
+               END_PROFILE(SMBulogoffX);
+               exit_server(__location__ ": reply_ulogoffX_recv failed");
+               return;
+       }
+
+       status = smbXsrv_session_logoff(session);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb1req);
                END_PROFILE(SMBulogoffX);
                exit_server(__location__ ": smbXsrv_session_logoff failed");
                return;
@@ -2508,15 +2810,21 @@ void reply_ulogoffX(struct smb_request *req)
 
        TALLOC_FREE(session);
 
-       reply_outbuf(req, 2, 0);
-       SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
-       SSVAL(req->outbuf, smb_vwv1, 0);    /* no andx offset */
+       reply_outbuf(smb1req, 2, 0);
+       SSVAL(smb1req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
+       SSVAL(smb1req->outbuf, smb_vwv1, 0);    /* no andx offset */
 
-       DEBUG(3, ("ulogoffX vuid=%llu\n",
-                 (unsigned long long)req->vuid));
+       DBG_NOTICE("ulogoffX vuid=%llu\n",
+                 (unsigned long long)smb1req->vuid);
 
+       smb1req->vuid = UID_FIELD_INVALID;
+       /*
+        * The following call is needed to push the
+        * reply data back out the socket after async
+        * return. Plus it frees smb1req.
+        */
+       smb_request_done(smb1req);
        END_PROFILE(SMBulogoffX);
-       req->vuid = UID_FIELD_INVALID;
 }
 
 /****************************************************************************
@@ -2541,7 +2849,7 @@ void reply_mknew(struct smb_request *req)
        TALLOC_CTX *ctx = talloc_tos();
 
        START_PROFILE(SMBcreate);
-       ZERO_STRUCT(ft);
+       init_smb_file_time(&ft);
 
         if (req->wct < 3) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -2560,7 +2868,7 @@ void reply_mknew(struct smb_request *req)
        }
 
        /* mtime. */
-       ft.mtime = convert_time_t_to_timespec(srv_make_unix_date3(req->vwv+1));
+       ft.mtime = time_t_to_full_timespec(srv_make_unix_date3(req->vwv+1));
 
        srvstr_get_path_req(ctx, req, &fname, (const char *)req->buf + 1,
                            STR_TERMINATE, &status);
@@ -2619,6 +2927,12 @@ void reply_mknew(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+               }
                reply_openerror(req, status);
                goto out;
        }
@@ -2756,6 +3070,13 @@ void reply_ctemp(struct smb_request *req)
                                /* We have re-scheduled this call. */
                                goto out;
                        }
+                       if (NT_STATUS_EQUAL(
+                                   status, NT_STATUS_SHARING_VIOLATION)) {
+                               bool ok = defer_smb1_sharing_violation(req);
+                               if (ok) {
+                                       goto out;
+                               }
+                       }
                        reply_openerror(req, status);
                        goto out;
                }
@@ -2878,6 +3199,7 @@ static NTSTATUS do_unlink(connection_struct *conn,
        NTSTATUS status;
        int ret;
        bool posix_paths = (req != NULL && req->posix_pathnames);
+       struct smb2_create_blobs *posx = NULL;
 
        DEBUG(10,("do_unlink: %s, dirtype = %d\n",
                  smb_fname_str_dbg(smb_fname),
@@ -2949,6 +3271,16 @@ static NTSTATUS do_unlink(connection_struct *conn,
                return NT_STATUS_OBJECT_NAME_INVALID;
 #endif /* JRATEST */
 
+       if (posix_paths) {
+               status = make_smb2_posix_create_ctx(
+                       talloc_tos(), &posx, 0777);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+                                   nt_errstr(status));
+                       return status;
+               }
+       }
+
        /* On open checks the open itself will check the share mode, so
           don't do it here as we'll get it wrong. */
 
@@ -2961,9 +3293,7 @@ static NTSTATUS do_unlink(connection_struct *conn,
                 FILE_SHARE_NONE,       /* share_access */
                 FILE_OPEN,             /* create_disposition*/
                 FILE_NON_DIRECTORY_FILE, /* create_options */
-                                       /* file_attributes */
-                posix_paths ? FILE_FLAG_POSIX_SEMANTICS|0777 :
-                               FILE_ATTRIBUTE_NORMAL,
+                FILE_ATTRIBUTE_NORMAL, /* file_attributes */
                 0,                     /* oplock_request */
                 NULL,                  /* lease */
                 0,                     /* allocation_size */
@@ -2972,7 +3302,10 @@ static NTSTATUS do_unlink(connection_struct *conn,
                 NULL,                  /* ea_list */
                 &fsp,                  /* result */
                 NULL,                  /* pinfo */
-                NULL, NULL);           /* create context */
+                posx,                  /* in_context_blobs */
+                NULL);                 /* out_context_blobs */
+
+       TALLOC_FREE(posx);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("SMB_VFS_CREATEFILE failed: %s\n",
@@ -3273,6 +3606,12 @@ void reply_unlink(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+               }
                reply_nterror(req, status);
                goto out;
        }
@@ -3461,6 +3800,25 @@ static void reply_readbraw_error(struct smbXsrv_connection *xconn)
        smbd_unlock_socket(xconn);
 }
 
+/*******************************************************************
+ Ensure we don't use sendfile if server smb signing is active.
+********************************************************************/
+
+static bool lp_use_sendfile(int snum, struct smb_signing_state *signing_state)
+{
+       bool sign_active = false;
+
+       /* Using sendfile blows the brains out of any DOS or Win9x TCP stack... JRA. */
+       if (get_Protocol() < PROTOCOL_NT1) {
+               return false;
+       }
+       if (signing_state) {
+               sign_active = smb_signing_is_active(signing_state);
+       }
+       return (lp__use_sendfile(snum) &&
+                       (get_remote_arch() != RA_WIN95) &&
+                       !sign_active);
+}
 /****************************************************************************
  Use sendfile in readbraw.
 ****************************************************************************/
@@ -3484,7 +3842,6 @@ static void send_file_readbraw(connection_struct *conn,
         */
 
        if ( !req_is_in_chain(req) && (nread > 0) && (fsp->base_fsp == NULL) &&
-           (fsp->wcp == NULL) &&
            lp_use_sendfile(SNUM(conn), xconn->smb1.signing_state) ) {
                ssize_t sendfile_read = -1;
                char header[4];
@@ -3606,6 +3963,7 @@ void reply_readbraw(struct smb_request *req)
        files_struct *fsp;
        struct lock_struct lock;
        off_t size = 0;
+       NTSTATUS status;
 
        START_PROFILE(SMBreadbraw);
 
@@ -3667,8 +4025,6 @@ void reply_readbraw(struct smb_request *req)
                return;
        }
 
-       flush_write_cache(fsp, SAMBA_READRAW_FLUSH);
-
        startpos = IVAL_TO_SMB_OFF_T(req->vwv+1, 0);
        if(req->wct == 10) {
                /*
@@ -3703,7 +4059,8 @@ void reply_readbraw(struct smb_request *req)
                return;
        }
 
-       if (fsp_stat(fsp) == 0) {
+       status = vfs_stat_fsp(fsp);
+       if (NT_STATUS_IS_OK(status)) {
                size = fsp->fsp_name->st.st_ex_size;
        }
 
@@ -3740,19 +4097,14 @@ void reply_readbraw(struct smb_request *req)
  Reply to a lockread (core+ protocol).
 ****************************************************************************/
 
+static void reply_lockread_locked(struct tevent_req *subreq);
+
 void reply_lockread(struct smb_request *req)
 {
+       struct tevent_req *subreq = NULL;
        connection_struct *conn = req->conn;
-       ssize_t nread = -1;
-       char *data;
-       off_t startpos;
-       size_t numtoread;
-       size_t maxtoread;
-       NTSTATUS status;
        files_struct *fsp;
-       struct byte_range_lock *br_lck = NULL;
-       char *p = NULL;
-       struct smbXsrv_connection *xconn = req->xconn;
+       struct smbd_lock_element *lck = NULL;
 
        START_PROFILE(SMBlockread);
 
@@ -3775,8 +4127,12 @@ void reply_lockread(struct smb_request *req)
                return;
        }
 
-       numtoread = SVAL(req->vwv+1, 0);
-       startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0);
+       lck = talloc(req, struct smbd_lock_element);
+       if (lck == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
+               END_PROFILE(SMBlockread);
+               return;
+       }
 
        /*
         * NB. Discovered by Menny Hamburger at Mainsoft. This is a core+
@@ -3786,28 +4142,71 @@ void reply_lockread(struct smb_request *req)
         * Note that the requested lock size is unaffected by max_send.
         */
 
-       br_lck = do_lock(req->sconn->msg_ctx,
-                       fsp,
-                       (uint64_t)req->smbpid,
-                       (uint64_t)numtoread,
-                       (uint64_t)startpos,
-                       WRITE_LOCK,
-                       WINDOWS_LOCK,
-                       False, /* Non-blocking lock. */
-                       &status,
-                       NULL);
-       TALLOC_FREE(br_lck);
+       *lck = (struct smbd_lock_element) {
+               .req_guid = smbd_request_guid(req, 0),
+               .smblctx = req->smbpid,
+               .brltype = WRITE_LOCK,
+               .count = SVAL(req->vwv+1, 0),
+               .offset = IVAL_TO_SMB_OFF_T(req->vwv+2, 0),
+       };
 
-       if (NT_STATUS_V(status)) {
-               reply_nterror(req, status);
+       subreq = smbd_smb1_do_locks_send(
+               fsp,
+               req->sconn->ev_ctx,
+               &req,
+               fsp,
+               0,
+               false,          /* large_offset */
+               WINDOWS_LOCK,
+               1,
+               lck);
+       if (subreq == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
                END_PROFILE(SMBlockread);
                return;
        }
+       tevent_req_set_callback(subreq, reply_lockread_locked, NULL);
+       END_PROFILE(SMBlockread);
+}
+
+static void reply_lockread_locked(struct tevent_req *subreq)
+{
+       struct smb_request *req = NULL;
+       ssize_t nread = -1;
+       char *data = NULL;
+       NTSTATUS status;
+       bool ok;
+       off_t startpos;
+       size_t numtoread, maxtoread;
+       struct files_struct *fsp = NULL;
+       char *p = NULL;
+
+       START_PROFILE(SMBlockread);
+
+       ok = smbd_smb1_do_locks_extract_smbreq(subreq, talloc_tos(), &req);
+       SMB_ASSERT(ok);
+
+       status = smbd_smb1_do_locks_recv(subreq);
+       TALLOC_FREE(subreq);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               reply_nterror(req, status);
+               goto send;
+       }
+
+       fsp = file_fsp(req, SVAL(req->vwv+0, 0));
+       if (fsp == NULL) {
+               reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               goto send;
+       }
+
+       numtoread = SVAL(req->vwv+1, 0);
+       startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0);
 
        /*
         * However the requested READ size IS affected by max_send. Insanity.... JRA.
         */
-       maxtoread = xconn->smb1.sessions.max_send - (smb_size + 5*2 + 3);
+       maxtoread = req->xconn->smb1.sessions.max_send - (smb_size + 5*2 + 3);
 
        if (numtoread > maxtoread) {
                DBG_WARNING("requested read size (%zu) is greater than "
@@ -3816,7 +4215,7 @@ void reply_lockread(struct smb_request *req)
                            "compatibility with Windows 2000.\n",
                            numtoread,
                            maxtoread,
-                           xconn->smb1.sessions.max_send);
+                           req->xconn->smb1.sessions.max_send);
                numtoread = maxtoread;
        }
 
@@ -3828,8 +4227,7 @@ void reply_lockread(struct smb_request *req)
 
        if (nread < 0) {
                reply_nterror(req, map_nt_error_from_unix(errno));
-               END_PROFILE(SMBlockread);
-               return;
+               goto send;
        }
 
        srv_set_message((char *)req->outbuf, 5, nread+3, False);
@@ -3843,6 +4241,17 @@ void reply_lockread(struct smb_request *req)
        DEBUG(3,("lockread %s num=%d nread=%d\n",
                 fsp_fnum_dbg(fsp), (int)numtoread, (int)nread));
 
+send:
+       ok = srv_send_smb(req->xconn,
+                         (char *)req->outbuf,
+                         true,
+                         req->seqnum+1,
+                         IS_CONN_ENCRYPTED(req->conn),
+                         NULL);
+       if (!ok) {
+               exit_server_cleanly("reply_lock_done: srv_send_smb failed.");
+       }
+       TALLOC_FREE(req);
        END_PROFILE(SMBlockread);
        return;
 }
@@ -3982,6 +4391,7 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
        ssize_t nread = -1;
        struct lock_struct lock;
        int saved_errno = 0;
+       NTSTATUS status;
 
        init_strict_lock_struct(fsp, (uint64_t)req->smbpid,
            (uint64_t)startpos, (uint64_t)smb_maxcnt, READ_LOCK,
@@ -4001,13 +4411,13 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req,
        if (!req_is_in_chain(req) &&
            !req->encrypted &&
            (fsp->base_fsp == NULL) &&
-           (fsp->wcp == NULL) &&
            lp_use_sendfile(SNUM(conn), xconn->smb1.signing_state) ) {
                uint8_t headerbuf[smb_size + 12 * 2 + 1 /* padding byte */];
                DATA_BLOB header;
 
-               if(fsp_stat(fsp) == -1) {
-                       reply_nterror(req, map_nt_error_from_unix(errno));
+               status = vfs_stat_fsp(fsp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       reply_nterror(req, status);
                        goto out;
                }
 
@@ -4761,13 +5171,14 @@ void reply_writeunlock(struct smb_request *req)
        }
 
        if (numtowrite && !fsp->print_file) {
-               status = do_unlock(req->sconn->msg_ctx,
-                               fsp,
-                               (uint64_t)req->smbpid,
-                               (uint64_t)numtowrite, 
-                               (uint64_t)startpos,
-                               WINDOWS_LOCK);
-
+               struct smbd_lock_element l = {
+                       .req_guid = smbd_request_guid(req, 0),
+                       .smblctx = req->smbpid,
+                       .brltype = UNLOCK_LOCK,
+                       .offset = startpos,
+                       .count = numtowrite,
+               };
+               status = smbd_do_unlocking(req, fsp, 1, &l, WINDOWS_LOCK);
                if (NT_STATUS_V(status)) {
                        reply_nterror(req, status);
                        goto out;
@@ -5214,6 +5625,7 @@ void reply_lseek(struct smb_request *req)
        off_t res= -1;
        int mode,umode;
        files_struct *fsp;
+       NTSTATUS status;
 
        START_PROFILE(SMBlseek);
 
@@ -5229,8 +5641,6 @@ void reply_lseek(struct smb_request *req)
                return;
        }
 
-       flush_write_cache(fsp, SAMBA_SEEK_FLUSH);
-
        mode = SVAL(req->vwv+1, 0) & 3;
        /* NB. This doesn't use IVAL_TO_SMB_OFF_T as startpos can be signed in this case. */
        startpos = (off_t)IVALS(req->vwv+2, 0);
@@ -5258,9 +5668,9 @@ void reply_lseek(struct smb_request *req)
                        if(errno == EINVAL) {
                                off_t current_pos = startpos;
 
-                               if(fsp_stat(fsp) == -1) {
-                                       reply_nterror(req,
-                                               map_nt_error_from_unix(errno));
+                               status = vfs_stat_fsp(fsp);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       reply_nterror(req, status);
                                        END_PROFILE(SMBlseek);
                                        return;
                                }
@@ -5370,21 +5780,222 @@ void reply_exit(struct smb_request *req)
        return;
 }
 
-struct reply_close_state {
-       files_struct *fsp;
-       struct smb_request *smbreq;
+#if 0
+struct reply_exit_state {
+       struct tevent_queue *wait_queue;
 };
 
-static void do_smb1_close(struct tevent_req *req);
+static void reply_exit_wait_done(struct tevent_req *subreq);
 
-void reply_close(struct smb_request *req)
+/****************************************************************************
+ Async SMB1 exit.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_exit_send(struct smb_request *smb1req)
 {
-       connection_struct *conn = req->conn;
-       NTSTATUS status = NT_STATUS_OK;
-       files_struct *fsp = NULL;
-       START_PROFILE(SMBclose);
+       struct tevent_req *req;
+       struct reply_exit_state *state;
+       struct tevent_req *subreq;
+       files_struct *fsp;
+       struct smbd_server_connection *sconn = smb1req->sconn;
 
-       if (req->wct < 3) {
+       req = tevent_req_create(smb1req, &state,
+                       struct reply_exit_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->wait_queue = tevent_queue_create(state,
+                               "reply_exit_wait_queue");
+       if (tevent_req_nomem(state->wait_queue, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       for (fsp = sconn->files; fsp; fsp = fsp->next) {
+               if (fsp->file_pid != smb1req->smbpid) {
+                       continue;
+               }
+               if (fsp->vuid != smb1req->vuid) {
+                       continue;
+               }
+               /*
+                * Flag the file as close in progress.
+                * This will prevent any more IO being
+                * done on it.
+                */
+               fsp->closing = true;
+
+               if (fsp->num_aio_requests > 0) {
+                       /*
+                        * Now wait until all aio requests on this fsp are
+                        * finished.
+                        *
+                        * We don't set a callback, as we just want to block the
+                        * wait queue and the talloc_free() of fsp->aio_request
+                        * will remove the item from the wait queue.
+                        */
+                       subreq = tevent_queue_wait_send(fsp->aio_requests,
+                                               sconn->ev_ctx,
+                                               state->wait_queue);
+                       if (tevent_req_nomem(subreq, req)) {
+                               TALLOC_FREE(req);
+                               return NULL;
+                       }
+               }
+       }
+
+       /*
+        * Now we add our own waiter to the end of the queue,
+        * this way we get notified when all pending requests are finished
+        * and reply to the outstanding SMB1 request.
+        */
+       subreq = tevent_queue_wait_send(state,
+                               sconn->ev_ctx,
+                               state->wait_queue);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       /*
+        * We're really going async - move the SMB1 request from
+        * a talloc stackframe above us to the conn talloc-context.
+        * We need this to stick around until the wait_done
+        * callback is invoked.
+        */
+       smb1req = talloc_move(sconn, &smb1req);
+
+       tevent_req_set_callback(subreq, reply_exit_wait_done, req);
+
+       return req;
+}
+
+static void reply_exit_wait_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+
+       tevent_queue_wait_recv(subreq);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static NTSTATUS reply_exit_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_exit_done(struct tevent_req *req)
+{
+       struct smb_request *smb1req = tevent_req_callback_data(
+               req, struct smb_request);
+       struct smbd_server_connection *sconn = smb1req->sconn;
+       struct smbXsrv_connection *xconn = smb1req->xconn;
+       NTTIME now = timeval_to_nttime(&smb1req->request_time);
+       struct smbXsrv_session *session = NULL;
+       files_struct *fsp, *next;
+       NTSTATUS status;
+
+       /*
+        * Take the profile charge here. Not strictly
+        * correct but better than the other SMB1 async
+        * code that double-charges at the moment.
+        */
+       START_PROFILE(SMBexit);
+
+       status = reply_exit_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb1req);
+               END_PROFILE(SMBexit);
+               exit_server(__location__ ": reply_exit_recv failed");
+               return;
+       }
+
+       /*
+        * Ensure the session is still valid.
+        */
+       status = smb1srv_session_lookup(xconn,
+                                       smb1req->vuid,
+                                       now,
+                                       &session);
+       if (!NT_STATUS_IS_OK(status)) {
+               reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+               smb_request_done(smb1req);
+               END_PROFILE(SMBexit);
+       }
+
+       /*
+        * Ensure the vuid is still valid - no one
+        * called reply_ulogoffX() in the meantime.
+        * reply_exit() doesn't have AS_USER set, so
+        * use set_current_user_info() directly.
+        * This is the same logic as in switch_message().
+        */
+       if (session->global->auth_session_info != NULL) {
+               set_current_user_info(
+                       session->global->auth_session_info->unix_info->sanitized_username,
+                       session->global->auth_session_info->unix_info->unix_name,
+                       session->global->auth_session_info->info->domain_name);
+       }
+
+       /* No more aio - do the actual closes. */
+       for (fsp = sconn->files; fsp; fsp = next) {
+               bool ok;
+               next = fsp->next;
+
+               if (fsp->file_pid != smb1req->smbpid) {
+                       continue;
+               }
+               if (fsp->vuid != smb1req->vuid) {
+                       continue;
+               }
+               if (!fsp->closing) {
+                       continue;
+               }
+
+               /*
+                * reply_exit() has the DO_CHDIR flag set.
+                */
+               ok = chdir_current_service(fsp->conn);
+               if (!ok) {
+                       reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+                       smb_request_done(smb1req);
+                       END_PROFILE(SMBexit);
+               }
+               close_file(NULL, fsp, SHUTDOWN_CLOSE);
+       }
+
+       reply_outbuf(smb1req, 0, 0);
+       /*
+        * The following call is needed to push the
+        * reply data back out the socket after async
+        * return. Plus it frees smb1req.
+        */
+       smb_request_done(smb1req);
+       DBG_INFO("reply_exit complete\n");
+       END_PROFILE(SMBexit);
+       return;
+}
+#endif
+
+struct reply_close_state {
+       files_struct *fsp;
+       struct smb_request *smbreq;
+};
+
+static void do_smb1_close(struct tevent_req *req);
+
+void reply_close(struct smb_request *req)
+{
+       connection_struct *conn = req->conn;
+       NTSTATUS status = NT_STATUS_OK;
+       files_struct *fsp = NULL;
+       START_PROFILE(SMBclose);
+
+       if (req->wct < 3) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
                END_PROFILE(SMBclose);
                return;
@@ -5415,7 +6026,7 @@ void reply_close(struct smb_request *req)
                 */
 
                t = srv_make_unix_date3(req->vwv+1);
-               set_close_write_time(fsp, convert_time_t_to_timespec(t));
+               set_close_write_time(fsp, time_t_to_full_timespec(t));
        }
 
        if (fsp->num_aio_requests != 0) {
@@ -5425,6 +6036,13 @@ void reply_close(struct smb_request *req)
                DEBUG(10, ("closing with aio %u requests pending\n",
                           fsp->num_aio_requests));
 
+               /*
+                * Flag the file as close in progress.
+                * This will prevent any more IO being
+                * done on it.
+                */
+               fsp->closing = true;
+
                /*
                 * We depend on the aio_extra destructor to take care of this
                 * close request once fsp->num_aio_request drops to 0.
@@ -5552,7 +6170,7 @@ void reply_writeclose(struct smb_request *req)
 
        numtowrite = SVAL(req->vwv+1, 0);
        startpos = IVAL_TO_SMB_OFF_T(req->vwv+2, 0);
-       mtime = convert_time_t_to_timespec(srv_make_unix_date3(req->vwv+4));
+       mtime = time_t_to_full_timespec(srv_make_unix_date3(req->vwv+4));
        data = (const char *)req->buf + 1;
 
        /*
@@ -5625,13 +6243,14 @@ out:
  Reply to a lock.
 ****************************************************************************/
 
+static void reply_lock_done(struct tevent_req *subreq);
+
 void reply_lock(struct smb_request *req)
 {
+       struct tevent_req *subreq = NULL;
        connection_struct *conn = req->conn;
-       uint64_t count,offset;
-       NTSTATUS status;
        files_struct *fsp;
-       struct byte_range_lock *br_lck = NULL;
+       struct smbd_lock_element *lck = NULL;
 
        START_PROFILE(SMBlock);
 
@@ -5648,35 +6267,77 @@ void reply_lock(struct smb_request *req)
                return;
        }
 
-       count = (uint64_t)IVAL(req->vwv+1, 0);
-       offset = (uint64_t)IVAL(req->vwv+3, 0);
-
-       DEBUG(3,("lock fd=%d %s offset=%.0f count=%.0f\n",
-                fsp->fh->fd, fsp_fnum_dbg(fsp), (double)offset, (double)count));
-
-       br_lck = do_lock(req->sconn->msg_ctx,
-                       fsp,
-                       (uint64_t)req->smbpid,
-                       count,
-                       offset,
-                       WRITE_LOCK,
-                       WINDOWS_LOCK,
-                       False, /* Non-blocking lock. */
-                       &status,
-                       NULL);
+       lck = talloc(req, struct smbd_lock_element);
+       if (lck == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
+               END_PROFILE(SMBlock);
+               return;
+       }
 
-       TALLOC_FREE(br_lck);
+       *lck = (struct smbd_lock_element) {
+               .req_guid = smbd_request_guid(req, 0),
+               .smblctx = req->smbpid,
+               .brltype = WRITE_LOCK,
+               .count = IVAL(req->vwv+1, 0),
+               .offset = IVAL(req->vwv+3, 0),
+       };
 
-       if (NT_STATUS_V(status)) {
-               reply_nterror(req, status);
+       DBG_NOTICE("lock fd=%d %s offset=%"PRIu64" count=%"PRIu64"\n",
+                  fsp->fh->fd,
+                  fsp_fnum_dbg(fsp),
+                  lck->offset,
+                  lck->count);
+
+       subreq = smbd_smb1_do_locks_send(
+               fsp,
+               req->sconn->ev_ctx,
+               &req,
+               fsp,
+               0,
+               false,          /* large_offset */
+               WINDOWS_LOCK,
+               1,
+               lck);
+       if (subreq == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
                END_PROFILE(SMBlock);
                return;
        }
+       tevent_req_set_callback(subreq, reply_lock_done, NULL);
+       END_PROFILE(SMBlock);
+}
 
-       reply_outbuf(req, 0, 0);
+static void reply_lock_done(struct tevent_req *subreq)
+{
+       struct smb_request *req = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       START_PROFILE(SMBlock);
+
+       ok = smbd_smb1_do_locks_extract_smbreq(subreq, talloc_tos(), &req);
+       SMB_ASSERT(ok);
+
+       status = smbd_smb1_do_locks_recv(subreq);
+       TALLOC_FREE(subreq);
 
+       if (NT_STATUS_IS_OK(status)) {
+               reply_outbuf(req, 0, 0);
+       } else {
+               reply_nterror(req, status);
+       }
+
+       ok = srv_send_smb(req->xconn,
+                         (char *)req->outbuf,
+                         true,
+                         req->seqnum+1,
+                         IS_CONN_ENCRYPTED(req->conn),
+                         NULL);
+       if (!ok) {
+               exit_server_cleanly("reply_lock_done: srv_send_smb failed.");
+       }
+       TALLOC_FREE(req);
        END_PROFILE(SMBlock);
-       return;
 }
 
 /****************************************************************************
@@ -5686,9 +6347,9 @@ void reply_lock(struct smb_request *req)
 void reply_unlock(struct smb_request *req)
 {
        connection_struct *conn = req->conn;
-       uint64_t count,offset;
        NTSTATUS status;
        files_struct *fsp;
+       struct smbd_lock_element lck;
 
        START_PROFILE(SMBunlock);
 
@@ -5705,17 +6366,17 @@ void reply_unlock(struct smb_request *req)
                return;
        }
 
-       count = (uint64_t)IVAL(req->vwv+1, 0);
-       offset = (uint64_t)IVAL(req->vwv+3, 0);
+       lck = (struct smbd_lock_element) {
+               .req_guid = smbd_request_guid(req, 0),
+               .smblctx = req->smbpid,
+               .brltype = UNLOCK_LOCK,
+               .offset = IVAL(req->vwv+3, 0),
+               .count = IVAL(req->vwv+1, 0),
+       };
 
-       status = do_unlock(req->sconn->msg_ctx,
-                       fsp,
-                       (uint64_t)req->smbpid,
-                       count,
-                       offset,
-                       WINDOWS_LOCK);
+       status = smbd_do_unlocking(req, fsp, 1, &lck, WINDOWS_LOCK);
 
-       if (NT_STATUS_V(status)) {
+       if (!NT_STATUS_IS_OK(status)) {
                reply_nterror(req, status);
                END_PROFILE(SMBunlock);
                return;
@@ -5724,8 +6385,8 @@ void reply_unlock(struct smb_request *req)
        DBG_NOTICE("unlock fd=%d %s offset=%"PRIu64" count=%"PRIu64"\n",
                   fsp->fh->fd,
                   fsp_fnum_dbg(fsp),
-                  offset,
-                  count);
+                  lck.offset,
+                  lck.count);
 
        reply_outbuf(req, 0, 0);
 
@@ -5741,46 +6402,211 @@ void reply_unlock(struct smb_request *req)
  conn POINTER CAN BE NULL HERE !
 ****************************************************************************/
 
-void reply_tdis(struct smb_request *req)
+static struct tevent_req *reply_tdis_send(struct smb_request *smb1req);
+static void reply_tdis_done(struct tevent_req *req);
+
+void reply_tdis(struct smb_request *smb1req)
 {
-       NTSTATUS status;
-       connection_struct *conn = req->conn;
-       struct smbXsrv_tcon *tcon;
+       connection_struct *conn = smb1req->conn;
+       struct tevent_req *req;
 
-       START_PROFILE(SMBtdis);
+       /*
+        * Don't setup the profile charge here, take
+        * it in reply_tdis_done(). Not strictly correct
+        * but better than the other SMB1 async
+        * code that double-charges at the moment.
+        */
 
-       if (!conn) {
-               DEBUG(4,("Invalid connection in tdis\n"));
-               reply_force_doserror(req, ERRSRV, ERRinvnid);
+       if (conn == NULL) {
+               /* Not going async, profile here. */
+               START_PROFILE(SMBtdis);
+               DBG_INFO("Invalid connection in tdis\n");
+               reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
                END_PROFILE(SMBtdis);
                return;
        }
 
-       tcon = conn->tcon;
-       req->conn = NULL;
+       req = reply_tdis_send(smb1req);
+       if (req == NULL) {
+               /* Not going async, profile here. */
+               START_PROFILE(SMBtdis);
+               reply_force_doserror(smb1req, ERRDOS, ERRnomem);
+               END_PROFILE(SMBtdis);
+               return;
+       }
+       /* We're async. This will complete later. */
+       tevent_req_set_callback(req, reply_tdis_done, smb1req);
+       return;
+}
+
+struct reply_tdis_state {
+       struct tevent_queue *wait_queue;
+};
+
+static void reply_tdis_wait_done(struct tevent_req *subreq);
+
+/****************************************************************************
+ Async SMB1 tdis.
+ Note, on failure here we deallocate and return NULL to allow the caller to
+ SMB1 return an error of ERRnomem immediately.
+****************************************************************************/
+
+static struct tevent_req *reply_tdis_send(struct smb_request *smb1req)
+{
+       struct tevent_req *req;
+       struct reply_tdis_state *state;
+       struct tevent_req *subreq;
+       connection_struct *conn = smb1req->conn;
+       files_struct *fsp;
+
+       req = tevent_req_create(smb1req, &state,
+                       struct reply_tdis_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->wait_queue = tevent_queue_create(state, "reply_tdis_wait_queue");
+       if (tevent_req_nomem(state->wait_queue, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
 
        /*
-        * TODO: cancel all outstanding requests on the tcon
+        * Make sure that no new request will be able to use this tcon.
+        * This ensures that once all outstanding fsp->aio_requests
+        * on this tcon are done, we are safe to close it.
         */
-       status = smbXsrv_tcon_disconnect(tcon, req->vuid);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("reply_tdis: "
-                         "smbXsrv_tcon_disconnect() failed: %s\n",
-                         nt_errstr(status)));
+       conn->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
+
+       for (fsp = conn->sconn->files; fsp; fsp = fsp->next) {
+               if (fsp->conn != conn) {
+                       continue;
+               }
                /*
-                * If we hit this case, there is something completely
-                * wrong, so we better disconnect the transport connection.
+                * Flag the file as close in progress.
+                * This will prevent any more IO being
+                * done on it. Not strictly needed, but
+                * doesn't hurt to flag it as closing.
                 */
+               fsp->closing = true;
+
+               if (fsp->num_aio_requests > 0) {
+                       /*
+                        * Now wait until all aio requests on this fsp are
+                        * finished.
+                        *
+                        * We don't set a callback, as we just want to block the
+                        * wait queue and the talloc_free() of fsp->aio_request
+                        * will remove the item from the wait queue.
+                        */
+                       subreq = tevent_queue_wait_send(fsp->aio_requests,
+                                               conn->sconn->ev_ctx,
+                                               state->wait_queue);
+                       if (tevent_req_nomem(subreq, req)) {
+                               TALLOC_FREE(req);
+                               return NULL;
+                       }
+               }
+       }
+
+       /*
+        * Now we add our own waiter to the end of the queue,
+        * this way we get notified when all pending requests are finished
+        * and reply to the outstanding SMB1 request.
+        */
+       subreq = tevent_queue_wait_send(state,
+                               conn->sconn->ev_ctx,
+                               state->wait_queue);
+       if (tevent_req_nomem(subreq, req)) {
+               TALLOC_FREE(req);
+               return NULL;
+       }
+
+       /*
+        * We're really going async - move the SMB1 request from
+        * a talloc stackframe above us to the sconn talloc-context.
+        * We need this to stick around until the wait_done
+        * callback is invoked.
+        */
+       smb1req = talloc_move(smb1req->sconn, &smb1req);
+
+       tevent_req_set_callback(subreq, reply_tdis_wait_done, req);
+
+       return req;
+}
+
+static void reply_tdis_wait_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+
+       tevent_queue_wait_recv(subreq);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static NTSTATUS reply_tdis_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+static void reply_tdis_done(struct tevent_req *req)
+{
+       struct smb_request *smb1req = tevent_req_callback_data(
+               req, struct smb_request);
+       NTSTATUS status;
+       struct smbXsrv_tcon *tcon = smb1req->conn->tcon;
+       bool ok;
+
+       /*
+        * Take the profile charge here. Not strictly
+        * correct but better than the other SMB1 async
+        * code that double-charges at the moment.
+        */
+       START_PROFILE(SMBtdis);
+
+       status = reply_tdis_recv(req);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb1req);
+               END_PROFILE(SMBtdis);
+               exit_server(__location__ ": reply_tdis_recv failed");
+               return;
+       }
+
+       /*
+        * As we've been awoken, we may have changed
+        * directory in the meantime.
+        * reply_tdis() has the DO_CHDIR flag set.
+        */
+       ok = chdir_current_service(smb1req->conn);
+       if (!ok) {
+               reply_force_doserror(smb1req, ERRSRV, ERRinvnid);
+               smb_request_done(smb1req);
+               END_PROFILE(SMBtdis);
+       }
+
+       status = smbXsrv_tcon_disconnect(tcon,
+                                        smb1req->vuid);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(smb1req);
                END_PROFILE(SMBtdis);
                exit_server(__location__ ": smbXsrv_tcon_disconnect failed");
                return;
        }
 
+       /* smbXsrv_tcon_disconnect frees smb1req->conn. */
+       smb1req->conn = NULL;
+
        TALLOC_FREE(tcon);
 
-       reply_outbuf(req, 0, 0);
+       reply_outbuf(smb1req, 0, 0);
+       /*
+        * The following call is needed to push the
+        * reply data back out the socket after async
+        * return. Plus it frees smb1req.
+        */
+       smb_request_done(smb1req);
        END_PROFILE(SMBtdis);
-       return;
 }
 
 /****************************************************************************
@@ -5954,6 +6780,8 @@ void reply_printclose(struct smb_request *req)
 
 void reply_printqueue(struct smb_request *req)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        connection_struct *conn = req->conn;
        int max_count;
        int start_index;
@@ -5992,7 +6820,7 @@ void reply_printqueue(struct smb_request *req)
                TALLOC_CTX *mem_ctx = talloc_tos();
                NTSTATUS status;
                WERROR werr;
-               const char *sharename = lp_servicename(mem_ctx, SNUM(conn));
+               const char *sharename = lp_servicename(mem_ctx, lp_sub, SNUM(conn));
                struct rpc_pipe_client *cli = NULL;
                struct dcerpc_binding_handle *b = NULL;
                struct policy_handle handle;
@@ -6267,7 +7095,6 @@ void reply_rmdir(struct smb_request *req)
        files_struct *fsp = NULL;
        int info = 0;
        uint32_t ucf_flags = ucf_flags_from_smb_request(req);
-       struct smbd_server_connection *sconn = req->sconn;
 
        START_PROFILE(SMBrmdir);
 
@@ -6325,6 +7152,12 @@ void reply_rmdir(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+               }
                reply_nterror(req, status);
                goto out;
        }
@@ -6351,8 +7184,6 @@ void reply_rmdir(struct smb_request *req)
                reply_outbuf(req, 0, 0);
        }
 
-       dptr_closepath(sconn, smb_dname->base_name, req->smbpid);
-
        DEBUG(3, ("rmdir %s\n", smb_fname_str_dbg(smb_dname)));
  out:
        TALLOC_FREE(smb_dname);
@@ -6505,6 +7336,7 @@ static void rename_open_files(connection_struct *conn,
 
        for(fsp = file_find_di_first(conn->sconn, id); fsp;
            fsp = file_find_di_next(fsp)) {
+               struct file_id_buf idbuf;
                /* fsp_name is a relative path under the fsp. To change this for other
                   sharepaths we need to manipulate relative paths. */
                /* TODO - create the absolute path and manipulate the newname
@@ -6515,10 +7347,12 @@ static void rename_open_files(connection_struct *conn,
                if (fsp->name_hash != orig_name_hash) {
                        continue;
                }
-               DEBUG(10, ("rename_open_files: renaming file %s "
-                          "(file_id %s) from %s -> %s\n", fsp_fnum_dbg(fsp),
-                          file_id_string_tos(&fsp->file_id), fsp_str_dbg(fsp),
-                          smb_fname_str_dbg(smb_fname_dst)));
+               DBG_DEBUG("renaming file %s "
+                         "(file_id %s) from %s -> %s\n",
+                         fsp_fnum_dbg(fsp),
+                         file_id_str_buf(fsp->file_id, &idbuf),
+                         fsp_str_dbg(fsp),
+                         smb_fname_str_dbg(smb_fname_dst));
 
                status = fsp_set_smb_fname(fsp, smb_fname_dst);
                if (NT_STATUS_IS_OK(status)) {
@@ -6528,9 +7362,11 @@ static void rename_open_files(connection_struct *conn,
        }
 
        if (!did_rename) {
-               DEBUG(10, ("rename_open_files: no open files on file_id %s "
-                          "for %s\n", file_id_string_tos(&id),
-                          smb_fname_str_dbg(smb_fname_dst)));
+               struct file_id_buf idbuf;
+               DBG_DEBUG("no open files on file_id %s "
+                         "for %s\n",
+                         file_id_str_buf(id, &idbuf),
+                         smb_fname_str_dbg(smb_fname_dst));
        }
 
        /* Send messages to all smbd's (not ourself) that the name has changed. */
@@ -6676,6 +7512,7 @@ NTSTATUS rename_internals_fsp(connection_struct *conn,
        struct share_mode_lock *lck = NULL;
        uint32_t access_mask = SEC_DIR_ADD_FILE;
        bool dst_exists, old_is_stream, new_is_stream;
+       int ret;
 
        status = check_name(conn, smb_fname_dst_in);
        if (!NT_STATUS_IS_OK(status)) {
@@ -6904,7 +7741,12 @@ NTSTATUS rename_internals_fsp(connection_struct *conn,
 
        SMB_ASSERT(lck != NULL);
 
-       if(SMB_VFS_RENAME(conn, fsp->fsp_name, smb_fname_dst) == 0) {
+       ret = SMB_VFS_RENAMEAT(conn,
+                       conn->cwd_fsp,
+                       fsp->fsp_name,
+                       conn->cwd_fsp,
+                       smb_fname_dst);
+       if (ret == 0) {
                uint32_t create_options = fsp->fh->private_options;
 
                DEBUG(3, ("rename_internals_fsp: succeeded doing rename on "
@@ -7002,6 +7844,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
        long offset = 0;
        int create_options = 0;
        bool posix_pathnames = (req != NULL && req->posix_pathnames);
+       struct smb2_create_blobs *posx = NULL;
        int rc;
 
        /*
@@ -7041,6 +7884,15 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                }
        }
 
+       if (posix_pathnames) {
+               status = make_smb2_posix_create_ctx(talloc_tos(), &posx, 0777);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_WARNING("make_smb2_posix_create_ctx failed: %s\n",
+                                   nt_errstr(status));
+                       goto out;
+               }
+       }
+
        if (!src_has_wild) {
                files_struct *fsp;
 
@@ -7118,7 +7970,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                            FILE_SHARE_WRITE),
                        FILE_OPEN,                      /* create_disposition*/
                        create_options,                 /* create_options */
-                       posix_pathnames ? FILE_FLAG_POSIX_SEMANTICS|0777 : 0, /* file_attributes */
+                       0,                              /* file_attributes */
                        0,                              /* oplock_request */
                        NULL,                           /* lease */
                        0,                              /* allocation_size */
@@ -7127,7 +7979,8 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                        NULL,                           /* ea_list */
                        &fsp,                           /* result */
                        NULL,                           /* pinfo */
-                       NULL, NULL);                    /* create context */
+                       posx,                           /* in_context_blobs */
+                       NULL);                          /* out_context_blobs */
 
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3, ("Could not open rename source %s: %s\n",
@@ -7276,7 +8129,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                            FILE_SHARE_WRITE),
                        FILE_OPEN,                      /* create_disposition*/
                        create_options,                 /* create_options */
-                       posix_pathnames ? FILE_FLAG_POSIX_SEMANTICS|0777 : 0, /* file_attributes */
+                       0,                              /* file_attributes */
                        0,                              /* oplock_request */
                        NULL,                           /* lease */
                        0,                              /* allocation_size */
@@ -7285,7 +8138,8 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
                        NULL,                           /* ea_list */
                        &fsp,                           /* result */
                        NULL,                           /* pinfo */
-                       NULL, NULL);                    /* create context */
+                       posx,                           /* in_context_blobs */
+                       NULL);                          /* out_context_blobs */
 
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("rename_internals: SMB_VFS_CREATE_FILE "
@@ -7330,6 +8184,7 @@ NTSTATUS rename_internals(TALLOC_CTX *ctx,
        }
 
  out:
+       TALLOC_FREE(posx);
        TALLOC_FREE(talloced);
        TALLOC_FREE(smb_fname_src_dir);
        TALLOC_FREE(fname_src_dir);
@@ -7460,6 +8315,12 @@ void reply_mv(struct smb_request *req)
                        /* We have re-scheduled this call. */
                        goto out;
                }
+               if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
+                       bool ok = defer_smb1_sharing_violation(req);
+                       if (ok) {
+                               goto out;
+                       }
+               }
                reply_nterror(req, status);
                goto out;
        }
@@ -8079,184 +8940,81 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset,
        return offset;
 }
 
-NTSTATUS smbd_do_locking(struct smb_request *req,
-                        files_struct *fsp,
-                        int32_t timeout,
-                        uint16_t num_locks,
-                        struct smbd_lock_element *locks,
-                        bool *async)
-{
-       connection_struct *conn = req->conn;
-       int i;
-       NTSTATUS status = NT_STATUS_OK;
-
-       *async = false;
-
-       /* Setup the timeout in seconds. */
+struct smbd_do_unlocking_state {
+       struct files_struct *fsp;
+       uint16_t num_ulocks;
+       struct smbd_lock_element *ulocks;
+       enum brl_flavour lock_flav;
+       NTSTATUS status;
+};
 
-       if (!lp_blocking_locks(SNUM(conn))) {
-               timeout = 0;
-       }
+static void smbd_do_unlocking_fn(
+       TDB_DATA value, bool *pmodified_dependent, void *private_data)
+{
+       struct smbd_do_unlocking_state *state = private_data;
+       struct files_struct *fsp = state->fsp;
+       enum brl_flavour lock_flav = state->lock_flav;
+       uint16_t i;
 
-       for(i = 0; i < (int)num_locks; i++) {
-               struct smbd_lock_element *e = &locks[i];
+       for (i = 0; i < state->num_ulocks; i++) {
+               struct smbd_lock_element *e = &state->ulocks[i];
 
-               DBG_DEBUG("lock start=%"PRIu64", len=%"PRIu64" for smblctx "
-                         "%"PRIu64", file %s timeout = %"PRIi32"\n",
+               DBG_DEBUG("unlock start=%"PRIu64", len=%"PRIu64" for "
+                         "pid %"PRIu64", file %s\n",
                          e->offset,
                          e->count,
                          e->smblctx,
-                         fsp_str_dbg(fsp),
-                         timeout);
-
-               {
-                       bool blocking_lock = (timeout != 0);
-                       bool defer_lock = false;
-                       struct byte_range_lock *br_lck;
-                       uint64_t block_smblctx;
-
-                       br_lck = do_lock(req->sconn->msg_ctx,
-                                       fsp,
-                                       e->smblctx,
-                                       e->count,
-                                       e->offset, 
-                                       e->brltype,
-                                       WINDOWS_LOCK,
-                                       blocking_lock,
-                                       &status,
-                                       &block_smblctx);
-
-                       if (br_lck && blocking_lock && ERROR_WAS_LOCK_DENIED(status)) {
-                               /* Windows internal resolution for blocking locks seems
-                                  to be about 200ms... Don't wait for less than that. JRA. */
-                               if (timeout != -1) {
-                                       timeout = MAX(timeout, lp_lock_spin_time());
-                               }
-                               defer_lock = true;
-                       }
-
-                       /* If a lock sent with timeout of zero would fail, and
-                        * this lock has been requested multiple times,
-                        * according to brl_lock_failed() we convert this
-                        * request to a blocking lock with a timeout of between
-                        * 150 - 300 milliseconds.
-                        *
-                        * If lp_lock_spin_time() has been set to 0, we skip
-                        * this blocking retry and fail immediately.
-                        *
-                        * Replacement for do_lock_spin(). JRA. */
-
-                       if (!req->sconn->using_smb2 &&
-                           br_lck && lp_blocking_locks(SNUM(conn)) &&
-                           lp_lock_spin_time() && !blocking_lock &&
-                           NT_STATUS_EQUAL((status),
-                               NT_STATUS_FILE_LOCK_CONFLICT))
-                       {
-                               defer_lock = true;
-                               timeout = lp_lock_spin_time();
-                       }
-
-                       if (br_lck && defer_lock) {
-                               /*
-                                * A blocking lock was requested. Package up
-                                * this smb into a queued request and push it
-                                * onto the blocking lock queue.
-                                */
-                               if(push_blocking_lock_request(br_lck,
-                                                       req,
-                                                       fsp,
-                                                       timeout,
-                                                       i,
-                                                       e->smblctx,
-                                                       e->brltype,
-                                                       WINDOWS_LOCK,
-                                                       e->offset,
-                                                       e->count,
-                                                       block_smblctx)) {
-                                       TALLOC_FREE(br_lck);
-                                       *async = true;
-                                       return NT_STATUS_OK;
-                               }
-                       }
-
-                       TALLOC_FREE(br_lck);
-               }
+                         fsp_str_dbg(fsp));
 
-               if (!NT_STATUS_IS_OK(status)) {
-                       break;
+               if (e->brltype != UNLOCK_LOCK) {
+                       /* this can only happen with SMB2 */
+                       state->status = NT_STATUS_INVALID_PARAMETER;
+                       return;
                }
-       }
-
-       /* If any of the above locks failed, then we must unlock
-          all of the previous locks (X/Open spec). */
 
-       if (num_locks != 0 && !NT_STATUS_IS_OK(status)) {
+               state->status = do_unlock(
+                       fsp, e->smblctx, e->count, e->offset, lock_flav);
 
-               /*
-                * Ensure we don't do a remove on the lock that just failed,
-                * as under POSIX rules, if we have a lock already there, we
-                * will delete it (and we shouldn't) .....
-                */
-               for(i--; i >= 0; i--) {
-                       struct smbd_lock_element *e = &locks[i];
+               DBG_DEBUG("do_unlock returned %s\n",
+                         nt_errstr(state->status));
 
-                       do_unlock(req->sconn->msg_ctx,
-                               fsp,
-                               e->smblctx,
-                               e->count,
-                               e->offset,
-                               WINDOWS_LOCK);
+               if (!NT_STATUS_IS_OK(state->status)) {
+                       return;
                }
-               return status;
        }
 
-       DBG_NOTICE("%s num_locks=%"PRIu16"\n",
-                  fsp_fnum_dbg(fsp),
-                  num_locks);
-
-       return NT_STATUS_OK;
+       *pmodified_dependent = true;
 }
 
 NTSTATUS smbd_do_unlocking(struct smb_request *req,
                           files_struct *fsp,
                           uint16_t num_ulocks,
-                          struct smbd_lock_element *ulocks)
+                          struct smbd_lock_element *ulocks,
+                          enum brl_flavour lock_flav)
 {
-       uint16_t i;
-
-       for(i = 0; i < num_ulocks; i++) {
-               struct smbd_lock_element *e = &ulocks[i];
-               NTSTATUS status;
-
-               DBG_DEBUG("unlock start=%"PRIu64", len=%"PRIu64" for "
-                         "pid %"PRIu64", file %s\n",
-                         e->offset,
-                         e->count,
-                         e->smblctx,
-                         fsp_str_dbg(fsp));
-
-               if (e->brltype != UNLOCK_LOCK) {
-                       /* this can only happen with SMB2 */
-                       return NT_STATUS_INVALID_PARAMETER;
-               }
+       struct smbd_do_unlocking_state state = {
+               .fsp = fsp,
+               .num_ulocks = num_ulocks,
+               .ulocks = ulocks,
+               .lock_flav = lock_flav,
+       };
+       NTSTATUS status;
 
-               status = do_unlock(req->sconn->msg_ctx,
-                               fsp,
-                               e->smblctx,
-                               e->count,
-                               e->offset,
-                               WINDOWS_LOCK);
+       DBG_NOTICE("%s num_ulocks=%"PRIu16"\n", fsp_fnum_dbg(fsp), num_ulocks);
 
-               DEBUG(10, ("%s: unlock returned %s\n", __func__,
-                          nt_errstr(status)));
+       status = share_mode_do_locked(
+               fsp->file_id, smbd_do_unlocking_fn, &state);
 
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("share_mode_do_locked failed: %s\n",
+                         nt_errstr(status));
+               return status;
+       }
+       if (!NT_STATUS_IS_OK(state.status)) {
+               DBG_DEBUG("smbd_do_unlocking_fn failed: %s\n",
+                         nt_errstr(status));
+               return state.status;
        }
-
-       DEBUG(3, ("%s: %s num_ulocks=%d\n", __func__, fsp_fnum_dbg(fsp),
-                 num_ulocks));
 
        return NT_STATUS_OK;
 }
@@ -8265,6 +9023,8 @@ NTSTATUS smbd_do_unlocking(struct smb_request *req,
  Reply to a lockingX request.
 ****************************************************************************/
 
+static void reply_lockingx_done(struct tevent_req *subreq);
+
 void reply_lockingX(struct smb_request *req)
 {
        connection_struct *conn = req->conn;
@@ -8279,9 +9039,8 @@ void reply_lockingX(struct smb_request *req)
        const uint8_t *data;
        bool large_file_format;
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
-       struct smbd_lock_element *ulocks = NULL;
        struct smbd_lock_element *locks = NULL;
-       bool async = false;
+       struct tevent_req *subreq = NULL;
 
        START_PROFILE(SMBlockingX);
 
@@ -8394,28 +9153,60 @@ void reply_lockingX(struct smb_request *req)
                return;
        }
 
-       ulocks = talloc_array(req, struct smbd_lock_element, num_ulocks);
-       if (ulocks == NULL) {
-               reply_nterror(req, NT_STATUS_NO_MEMORY);
-               END_PROFILE(SMBlockingX);
-               return;
-       }
+       if (num_ulocks != 0) {
+               struct smbd_lock_element *ulocks = NULL;
+               bool ok;
 
-       /* Data now points at the beginning of the list
-          of smb_unlkrng structs */
-       for (i = 0; i < num_ulocks; i++) {
-               ulocks[i].smblctx = get_lock_pid(data, i, large_file_format);
-               ulocks[i].count = get_lock_count(data, i, large_file_format);
-               ulocks[i].offset = get_lock_offset(data, i, large_file_format);
-               ulocks[i].brltype = UNLOCK_LOCK;
-       }
+               ulocks = talloc_array(
+                       req, struct smbd_lock_element, num_ulocks);
+               if (ulocks == NULL) {
+                       reply_nterror(req, NT_STATUS_NO_MEMORY);
+                       END_PROFILE(SMBlockingX);
+                       return;
+               }
 
-       status = smbd_do_unlocking(req, fsp, num_ulocks, ulocks);
-       TALLOC_FREE(ulocks);
-       if (!NT_STATUS_IS_OK(status)) {
-               END_PROFILE(SMBlockingX);
-               reply_nterror(req, status);
-               return;
+               /*
+                * Data now points at the beginning of the list of
+                * smb_unlkrng structs
+                */
+               for (i = 0; i < num_ulocks; i++) {
+                       ulocks[i].req_guid = smbd_request_guid(req,
+                               UINT16_MAX - i),
+                       ulocks[i].smblctx = get_lock_pid(
+                               data, i, large_file_format);
+                       ulocks[i].count = get_lock_count(
+                               data, i, large_file_format);
+                       ulocks[i].offset = get_lock_offset(
+                               data, i, large_file_format);
+                       ulocks[i].brltype = UNLOCK_LOCK;
+               }
+
+               /*
+                * Unlock cancels pending locks
+                */
+
+               ok = smbd_smb1_brl_finish_by_lock(
+                       fsp,
+                       large_file_format,
+                       WINDOWS_LOCK,
+                       ulocks[0],
+                       NT_STATUS_OK);
+               if (ok) {
+                       reply_outbuf(req, 2, 0);
+                       SSVAL(req->outbuf, smb_vwv0, 0xff);
+                       SSVAL(req->outbuf, smb_vwv1, 0);
+                       END_PROFILE(SMBlockingX);
+                       return;
+               }
+
+               status = smbd_do_unlocking(
+                       req, fsp, num_ulocks, ulocks, WINDOWS_LOCK);
+               TALLOC_FREE(ulocks);
+               if (!NT_STATUS_IS_OK(status)) {
+                       END_PROFILE(SMBlockingX);
+                       reply_nterror(req, status);
+                       return;
+               }
        }
 
        /* Now do any requested locks */
@@ -8425,17 +9216,9 @@ void reply_lockingX(struct smb_request *req)
           of smb_lkrng structs */
 
        if (locktype & LOCKING_ANDX_SHARED_LOCK) {
-               if (locktype & LOCKING_ANDX_CANCEL_LOCK) {
-                       brltype = PENDING_READ_LOCK;
-               } else {
-                       brltype = READ_LOCK;
-               }
+               brltype = READ_LOCK;
        } else {
-               if (locktype & LOCKING_ANDX_CANCEL_LOCK) {
-                       brltype = PENDING_WRITE_LOCK;
-               } else {
-                       brltype = WRITE_LOCK;
-               }
+               brltype = WRITE_LOCK;
        }
 
        locks = talloc_array(req, struct smbd_lock_element, num_locks);
@@ -8446,6 +9229,7 @@ void reply_lockingX(struct smb_request *req)
        }
 
        for (i = 0; i < num_locks; i++) {
+               locks[i].req_guid = smbd_request_guid(req, i),
                locks[i].smblctx = get_lock_pid(data, i, large_file_format);
                locks[i].count = get_lock_count(data, i, large_file_format);
                locks[i].offset = get_lock_offset(data, i, large_file_format);
@@ -8453,81 +9237,92 @@ void reply_lockingX(struct smb_request *req)
        }
 
        if (locktype & LOCKING_ANDX_CANCEL_LOCK) {
-               struct smbd_lock_element *e = NULL;
+
+               bool ok;
 
                if (num_locks == 0) {
                        /* See smbtorture3 lock11 test */
-                       goto done;
+                       reply_outbuf(req, 2, 0);
+                       /* andx chain ends */
+                       SSVAL(req->outbuf, smb_vwv0, 0xff);
+                       SSVAL(req->outbuf, smb_vwv1, 0);
+                       END_PROFILE(SMBlockingX);
+                       return;
                }
 
-               e = &locks[0];
-
-               /*
-                * MS-CIFS (2.2.4.32.1) states that a cancel is
-                * honored if and only if the lock vector contains one
-                * entry. When given multiple cancel requests in a
-                * single PDU we expect the server to return an
-                * error. Windows servers seem to accept the request
-                * but only cancel the first lock.
-                *
-                * JRA - Do what Windows does (tm) :-).
-                */
+               ok = smbd_smb1_brl_finish_by_lock(
+                       fsp,
+                       large_file_format,
+                       WINDOWS_LOCK,
+                       locks[0], /* Windows only cancels the first lock */
+                       NT_STATUS_FILE_LOCK_CONFLICT);
 
-               if (lp_blocking_locks(SNUM(conn))) {
-                       struct blocking_lock_record *blr = NULL;
-
-                       /* Schedule a message to ourselves to
-                          remove the blocking lock record and
-                          return the right error. */
-
-                       blr = blocking_lock_cancel_smb1(
-                               fsp,
-                               e->smblctx,
-                               e->offset,
-                               e->count,
-                               WINDOWS_LOCK,
-                               locktype,
-                               NT_STATUS_FILE_LOCK_CONFLICT);
-                       if (blr == NULL) {
-                               reply_force_doserror(
-                                       req, ERRDOS, ERRcancelviolation);
-                               END_PROFILE(SMBlockingX);
-                               return;
-                       }
+               if (!ok) {
+                       reply_force_doserror(req, ERRDOS, ERRcancelviolation);
+                       END_PROFILE(SMBlockingX);
+                       return;
                }
 
-               /* Remove a matching pending lock. */
-               status = do_lock_cancel(fsp,
-                                       e->smblctx,
-                                       e->count,
-                                       e->offset,
-                                       WINDOWS_LOCK);
+               reply_outbuf(req, 2, 0);
+               SSVAL(req->outbuf, smb_vwv0, 0xff);
+               SSVAL(req->outbuf, smb_vwv1, 0);
                END_PROFILE(SMBlockingX);
-               reply_nterror(req, status);
                return;
        }
 
-       status = smbd_do_locking(
-               req, fsp, lock_timeout, num_locks, locks, &async);
-       TALLOC_FREE(locks);
-       if (!NT_STATUS_IS_OK(status)) {
-               END_PROFILE(SMBlockingX);
-               reply_nterror(req, status);
-               return;
-       }
-       if (async) {
+       subreq = smbd_smb1_do_locks_send(
+               fsp,
+               req->sconn->ev_ctx,
+               &req,
+               fsp,
+               lock_timeout,
+               large_file_format,
+               WINDOWS_LOCK,
+               num_locks,
+               locks);
+       if (subreq == NULL) {
+               reply_nterror(req, NT_STATUS_NO_MEMORY);
                END_PROFILE(SMBlockingX);
                return;
        }
+       tevent_req_set_callback(subreq, reply_lockingx_done, NULL);
+       END_PROFILE(SMBlockingX);
+}
 
-done:
-       reply_outbuf(req, 2, 0);
-       SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
-       SSVAL(req->outbuf, smb_vwv1, 0);    /* no andx offset */
+static void reply_lockingx_done(struct tevent_req *subreq)
+{
+       struct smb_request *req = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       START_PROFILE(SMBlockingX);
+
+       ok = smbd_smb1_do_locks_extract_smbreq(subreq, talloc_tos(), &req);
+       SMB_ASSERT(ok);
+
+       status = smbd_smb1_do_locks_recv(subreq);
+       TALLOC_FREE(subreq);
+
+       DBG_DEBUG("smbd_smb1_do_locks_recv returned %s\n", nt_errstr(status));
 
-       DEBUG(3, ("lockingX %s type=%d num_locks=%d num_ulocks=%d\n",
-                 fsp_fnum_dbg(fsp), (unsigned int)locktype, num_locks, num_ulocks));
+       if (NT_STATUS_IS_OK(status)) {
+               reply_outbuf(req, 2, 0);
+               SSVAL(req->outbuf, smb_vwv0, 0xff); /* andx chain ends */
+               SSVAL(req->outbuf, smb_vwv1, 0);    /* no andx offset */
+       } else {
+               reply_nterror(req, status);
+       }
 
+       ok = srv_send_smb(req->xconn,
+                         (char *)req->outbuf,
+                         true,
+                         req->seqnum+1,
+                         IS_CONN_ENCRYPTED(req->conn),
+                         NULL);
+       if (!ok) {
+               exit_server_cleanly("reply_lock_done: srv_send_smb failed.");
+       }
+       TALLOC_FREE(req);
        END_PROFILE(SMBlockingX);
 }
 
@@ -8574,7 +9369,7 @@ void reply_setattrE(struct smb_request *req)
        NTSTATUS status;
 
        START_PROFILE(SMBsetattrE);
-       ZERO_STRUCT(ft);
+       init_smb_file_time(&ft);
 
        if (req->wct < 7) {
                reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
@@ -8592,11 +9387,11 @@ void reply_setattrE(struct smb_request *req)
         * Convert the DOS times into unix times.
         */
 
-       ft.atime = convert_time_t_to_timespec(
+       ft.atime = time_t_to_full_timespec(
            srv_make_unix_date2(req->vwv+3));
-       ft.mtime = convert_time_t_to_timespec(
+       ft.mtime = time_t_to_full_timespec(
            srv_make_unix_date2(req->vwv+5));
-       ft.create_time = convert_time_t_to_timespec(
+       ft.create_time = time_t_to_full_timespec(
            srv_make_unix_date2(req->vwv+1));
 
        reply_outbuf(req, 0, 0);
@@ -8677,6 +9472,7 @@ void reply_getattrE(struct smb_request *req)
        int mode;
        files_struct *fsp;
        struct timespec create_ts;
+       NTSTATUS status;
 
        START_PROFILE(SMBgetattrE);
 
@@ -8695,8 +9491,9 @@ void reply_getattrE(struct smb_request *req)
        }
 
        /* Do an fstat on this file */
-       if(fsp_stat(fsp)) {
-               reply_nterror(req, map_nt_error_from_unix(errno));
+       status = vfs_stat_fsp(fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               reply_nterror(req, status);
                END_PROFILE(SMBgetattrE);
                return;
        }