s3/smbd: add comments and some reformatting to open_file_ntcreate()
[samba.git] / source3 / smbd / open.c
index c33a4cf357d90e66ddabe4b6477a775ed9296084..660a5bb6f6f8142cb50a8f3c1a40fef420abb4b0 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "includes.h"
 #include "system/filesys.h"
+#include "lib/util/server_id.h"
 #include "printing.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../librpc/gen_ndr/open_files.h"
 #include "../librpc/gen_ndr/idmap.h"
+#include "../librpc/gen_ndr/ioctl.h"
 #include "passdb/lookup_sid.h"
 #include "auth.h"
 #include "serverid.h"
 #include "messages.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
+#include "librpc/gen_ndr/ndr_leases_db.h"
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -116,7 +120,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                return NT_STATUS_OK;
        }
 
-       status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
+       status = SMB_VFS_GET_NT_ACL(conn, smb_fname,
                        (SECINFO_OWNER |
                        SECINFO_GROUP |
                         SECINFO_DACL), talloc_tos(), &sd);
@@ -150,7 +154,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
         * Samba 3.6 and earlier granted execute access even
         * if the ACL did not contain execute rights.
         * Samba 4.0 is more correct and checks it.
-        * The compatibilty mode allows to skip this check
+        * The compatibilty mode allows one to skip this check
         * to smoothen upgrades.
         */
        if (lp_acl_allow_execute_always(SNUM(conn))) {
@@ -232,7 +236,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        return NT_STATUS_OK;
 }
 
-static NTSTATUS check_parent_access(struct connection_struct *conn,
+NTSTATUS check_parent_access(struct connection_struct *conn,
                                struct smb_filename *smb_fname,
                                uint32_t access_mask)
 {
@@ -240,6 +244,7 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        char *parent_dir = NULL;
        struct security_descriptor *parent_sd = NULL;
        uint32_t access_granted = 0;
+       struct smb_filename *parent_smb_fname = NULL;
 
        if (!parent_dirname(talloc_tos(),
                                smb_fname->base_name,
@@ -248,6 +253,15 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
                return NT_STATUS_NO_MEMORY;
        }
 
+       parent_smb_fname = synthetic_smb_fname(talloc_tos(),
+                               parent_dir,
+                               NULL,
+                               NULL,
+                               smb_fname->flags);
+       if (parent_smb_fname == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
        if (get_current_uid(conn) == (uid_t)0) {
                /* I'm sorry sir, I didn't know you were root... */
                DEBUG(10,("check_parent_access: root override "
@@ -258,7 +272,7 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        }
 
        status = SMB_VFS_GET_NT_ACL(conn,
-                               parent_dir,
+                               parent_smb_fname,
                                SECINFO_DACL,
                                    talloc_tos(),
                                &parent_sd);
@@ -301,6 +315,46 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        return NT_STATUS_OK;
 }
 
+/****************************************************************************
+ Ensure when opening a base file for a stream open that we have permissions
+ to do so given the access mask on the base file.
+****************************************************************************/
+
+static NTSTATUS check_base_file_access(struct connection_struct *conn,
+                               struct smb_filename *smb_fname,
+                               uint32_t access_mask)
+{
+       NTSTATUS status;
+
+       status = smbd_calculate_access_mask(conn, smb_fname,
+                                       false,
+                                       access_mask,
+                                       &access_mask);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("smbd_calculate_access_mask "
+                       "on file %s returned %s\n",
+                       smb_fname_str_dbg(smb_fname),
+                       nt_errstr(status)));
+               return status;
+       }
+
+       if (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) {
+               uint32_t dosattrs;
+               if (!CAN_WRITE(conn)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+               dosattrs = dos_mode(conn, smb_fname);
+               if (IS_DOS_READONLY(dosattrs)) {
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+       }
+
+       return smbd_check_access_rights(conn,
+                                       smb_fname,
+                                       false,
+                                       access_mask);
+}
+
 /****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
@@ -319,7 +373,7 @@ NTSTATUS fd_open(struct connection_struct *conn,
         * client should be doing this.
         */
 
-       if (fsp->posix_open || !lp_symlinks(SNUM(conn))) {
+       if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) || !lp_follow_symlinks(SNUM(conn))) {
                flags |= O_NOFOLLOW;
        }
 #endif
@@ -405,8 +459,11 @@ void change_file_owner_to_parent(connection_struct *conn,
        struct smb_filename *smb_fname_parent;
        int ret;
 
-       smb_fname_parent = synthetic_smb_fname(talloc_tos(), inherit_from_dir,
-                                              NULL, NULL);
+       smb_fname_parent = synthetic_smb_fname(talloc_tos(),
+                                       inherit_from_dir,
+                                       NULL,
+                                       NULL,
+                                       0);
        if (smb_fname_parent == NULL) {
                return;
        }
@@ -463,8 +520,11 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
        NTSTATUS status = NT_STATUS_OK;
        int ret;
 
-       smb_fname_parent = synthetic_smb_fname(ctx, inherit_from_dir,
-                                              NULL, NULL);
+       smb_fname_parent = synthetic_smb_fname(ctx,
+                                       inherit_from_dir,
+                                       NULL,
+                                       NULL,
+                                       0);
        if (smb_fname_parent == NULL) {
                return NT_STATUS_NO_MEMORY;
        }
@@ -504,7 +564,7 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                goto chdir;
        }
 
-       smb_fname_cwd = synthetic_smb_fname(ctx, ".", NULL, NULL);
+       smb_fname_cwd = synthetic_smb_fname(ctx, ".", NULL, NULL, 0);
        if (smb_fname_cwd == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto chdir;
@@ -540,8 +600,10 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
        }
 
        become_root();
-       ret = SMB_VFS_LCHOWN(conn, ".", smb_fname_parent->st.st_ex_uid,
-                           (gid_t)-1);
+       ret = SMB_VFS_LCHOWN(conn,
+                       smb_fname_cwd,
+                       smb_fname_parent->st.st_ex_uid,
+                       (gid_t)-1);
        unbecome_root();
        if (ret == -1) {
                status = map_nt_error_from_unix(errno);
@@ -578,7 +640,9 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                        bool *file_created)
 {
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       NTSTATUS retry_status;
        bool file_existed = VALID_STAT(fsp->fsp_name->st);
+       int curr_flags;
 
        *file_created = false;
 
@@ -610,59 +674,65 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
         * we can never call O_CREAT without O_EXCL. So if
         * we think the file existed, try without O_CREAT|O_EXCL.
         * If we think the file didn't exist, try with
-        * O_CREAT|O_EXCL. Keep bouncing between these two
-        * requests until either the file is created, or
-        * opened. Either way, we keep going until we get
-        * a returnable result (error, or open/create).
+        * O_CREAT|O_EXCL.
+        *
+        * The big problem here is dangling symlinks. Opening
+        * without O_NOFOLLOW means both bad symlink
+        * and missing path return -1, ENOENT from open(). As POSIX
+        * is pathname based it's not possible to tell
+        * the difference between these two cases in a
+        * non-racy way, so change to try only two attempts before
+        * giving up.
+        *
+        * We don't have this problem for the O_NOFOLLOW
+        * case as it just returns NT_STATUS_OBJECT_PATH_NOT_FOUND
+        * mapped from the ELOOP POSIX error.
         */
 
-       while(1) {
-               int curr_flags = flags;
+       curr_flags = flags;
 
-               if (file_existed) {
-                       /* Just try open, do not create. */
-                       curr_flags &= ~(O_CREAT);
-                       status = fd_open(conn, fsp, curr_flags, mode);
-                       if (NT_STATUS_EQUAL(status,
-                                       NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
-                               /*
-                                * Someone deleted it in the meantime.
-                                * Retry with O_EXCL.
-                                */
-                               file_existed = false;
-                               DEBUG(10,("fd_open_atomic: file %s existed. "
-                                       "Retry.\n",
-                                       smb_fname_str_dbg(fsp->fsp_name)));
-                                       continue;
-                       }
-               } else {
-                       /* Try create exclusively, fail if it exists. */
-                       curr_flags |= O_EXCL;
-                       status = fd_open(conn, fsp, curr_flags, mode);
-                       if (NT_STATUS_EQUAL(status,
-                                       NT_STATUS_OBJECT_NAME_COLLISION)) {
-                               /*
-                                * Someone created it in the meantime.
-                                * Retry without O_CREAT.
-                                */
-                               file_existed = true;
-                               DEBUG(10,("fd_open_atomic: file %s "
-                                       "did not exist. Retry.\n",
-                                       smb_fname_str_dbg(fsp->fsp_name)));
-                               continue;
-                       }
-                       if (NT_STATUS_IS_OK(status)) {
-                               /*
-                                * Here we've opened with O_CREAT|O_EXCL
-                                * and got success. We *know* we created
-                                * this file.
-                                */
-                               *file_created = true;
-                       }
+       if (file_existed) {
+               curr_flags &= ~(O_CREAT);
+               retry_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       } else {
+               curr_flags |= O_EXCL;
+               retry_status = NT_STATUS_OBJECT_NAME_COLLISION;
+       }
+
+       status = fd_open(conn, fsp, curr_flags, mode);
+       if (NT_STATUS_IS_OK(status)) {
+               if (!file_existed) {
+                       *file_created = true;
                }
-               /* Create is done, or failed. */
-               break;
+               return NT_STATUS_OK;
        }
+       if (!NT_STATUS_EQUAL(status, retry_status)) {
+               return status;
+       }
+
+       curr_flags = flags;
+
+       /*
+        * Keep file_existed up to date for clarity.
+        */
+       if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+               file_existed = false;
+               curr_flags |= O_EXCL;
+               DBG_DEBUG("file %s did not exist. Retry.\n",
+                       smb_fname_str_dbg(fsp->fsp_name));
+       } else {
+               file_existed = true;
+               curr_flags &= ~(O_CREAT);
+               DBG_DEBUG("file %s existed. Retry.\n",
+                       smb_fname_str_dbg(fsp->fsp_name));
+       }
+
+       status = fd_open(conn, fsp, curr_flags, mode);
+
+       if (NT_STATUS_IS_OK(status) && (!file_existed)) {
+               *file_created = true;
+       }
+
        return status;
 }
 
@@ -676,8 +746,8 @@ static NTSTATUS open_file(files_struct *fsp,
                          const char *parent_dir,
                          int flags,
                          mode_t unx_mode,
-                         uint32 access_mask, /* client requested access mask. */
-                         uint32 open_access_mask, /* what we're actually using in the open. */
+                         uint32_t access_mask, /* client requested access mask. */
+                         uint32_t open_access_mask, /* what we're actually using in the open. */
                          bool *p_file_created)
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
@@ -766,6 +836,7 @@ static NTSTATUS open_file(files_struct *fsp,
                        wild = smb_fname->base_name;
                }
                if ((local_flags & O_CREAT) && !file_existed &&
+                   !(fsp->posix_flags & FSP_POSIX_FLAGS_PATHNAMES) &&
                    ms_has_wild(wild))  {
                        return NT_STATUS_OBJECT_NAME_INVALID;
                }
@@ -778,29 +849,59 @@ static NTSTATUS open_file(files_struct *fsp,
                                                smb_fname,
                                                false,
                                                access_mask);
-                       } else if (local_flags & O_CREAT){
-                               status = check_parent_access(conn,
-                                               smb_fname,
-                                               SEC_DIR_ADD_FILE);
-                       } else {
-                               /* File didn't exist and no O_CREAT. */
-                               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       DEBUG(10, ("open_file: "
+                                                  "smbd_check_access_rights "
+                                                  "on file %s returned %s\n",
+                                                  smb_fname_str_dbg(smb_fname),
+                                                  nt_errstr(status)));
+                               }
+
+                               if (!NT_STATUS_IS_OK(status) &&
+                                   !NT_STATUS_EQUAL(status,
+                                       NT_STATUS_OBJECT_NAME_NOT_FOUND))
+                               {
+                                       return status;
+                               }
+
+                               if (NT_STATUS_EQUAL(status,
+                                       NT_STATUS_OBJECT_NAME_NOT_FOUND))
+                               {
+                                       DEBUG(10, ("open_file: "
+                                               "file %s vanished since we "
+                                               "checked for existence.\n",
+                                               smb_fname_str_dbg(smb_fname)));
+                                       file_existed = false;
+                                       SET_STAT_INVALID(fsp->fsp_name->st);
+                               }
                        }
-                       if (!NT_STATUS_IS_OK(status)) {
-                               DEBUG(10,("open_file: "
-                                       "%s on file "
-                                       "%s returned %s\n",
-                                       file_existed ?
-                                               "smbd_check_access_rights" :
-                                               "check_parent_access",
-                                       smb_fname_str_dbg(smb_fname),
-                                       nt_errstr(status) ));
-                               return status;
+
+                       if (!file_existed) {
+                               if (!(local_flags & O_CREAT)) {
+                                       /* File didn't exist and no O_CREAT. */
+                                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+                               }
+
+                               status = check_parent_access(conn,
+                                                            smb_fname,
+                                                            SEC_DIR_ADD_FILE);
+                               if (!NT_STATUS_IS_OK(status)) {
+                                       DEBUG(10, ("open_file: "
+                                                  "check_parent_access on "
+                                                  "file %s returned %s\n",
+                                                  smb_fname_str_dbg(smb_fname),
+                                                  nt_errstr(status) ));
+                                       return status;
+                               }
                        }
                }
 
-               /* Actually do the open */
-               status = fd_open_atomic(conn, fsp, local_flags,
+               /*
+                * Actually do the open - if O_TRUNC is needed handle it
+                * below under the share mode lock.
+                */
+               status = fd_open_atomic(conn, fsp, local_flags & ~O_TRUNC,
                                unx_mode, p_file_created);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
@@ -809,6 +910,25 @@ static NTSTATUS open_file(files_struct *fsp,
                        return status;
                }
 
+               if (local_flags & O_NONBLOCK) {
+                       /*
+                        * GPFS can return ETIMEDOUT for pread on
+                        * nonblocking file descriptors when files
+                        * migrated to tape need to be recalled. I
+                        * could imagine this happens elsehwere
+                        * too. With blocking file descriptors this
+                        * does not happen.
+                        */
+                       ret = set_blocking(fsp->fh->fd, true);
+                       if (ret == -1) {
+                               status = map_nt_error_from_unix(errno);
+                               DBG_WARNING("Could not set fd to blocking: "
+                                           "%s\n", strerror(errno));
+                               fd_close(fsp);
+                               return status;
+                       }
+               }
+
                ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
                if (ret == -1) {
                        /* If we have an fd, this stat should succeed. */
@@ -830,7 +950,7 @@ static NTSTATUS open_file(files_struct *fsp,
                           in the stat struct in fsp->fsp_name. */
 
                        /* Inherit the ACL if required */
-                       if (lp_inherit_perms(SNUM(conn))) {
+                       if (lp_inherit_permissions(SNUM(conn))) {
                                inherit_access_posix_acl(conn, parent_dir,
                                                         smb_fname->base_name,
                                                         unx_mode);
@@ -838,7 +958,7 @@ static NTSTATUS open_file(files_struct *fsp,
                        }
 
                        /* Change the owner if required. */
-                       if (lp_inherit_owner(SNUM(conn))) {
+                       if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
                                change_file_owner_to_parent(conn, parent_dir,
                                                            fsp);
                                need_re_stat = true;
@@ -872,7 +992,7 @@ static NTSTATUS open_file(files_struct *fsp,
                                access_mask);
 
                if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-                               fsp->posix_open &&
+                               (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
                                S_ISLNK(smb_fname->st.st_ex_mode)) {
                        /* This is a POSIX stat open for delete
                         * or rename on a symlink that points
@@ -941,8 +1061,8 @@ static NTSTATUS open_file(files_struct *fsp,
 ****************************************************************************/
 
 static bool share_conflict(struct share_mode_entry *entry,
-                          uint32 access_mask,
-                          uint32 share_access)
+                          uint32_t access_mask,
+                          uint32_t share_access)
 {
        DEBUG(10,("share_conflict: entry->access_mask = 0x%x, "
                  "entry->share_access = 0x%x, "
@@ -1039,6 +1159,11 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                return;
        }
 
+       if (share_entry->op_mid == 0) {
+               /* INTERNAL_OPEN_ONLY */
+               return;
+       }
+
        if (!is_valid_share_mode_entry(share_entry)) {
                return;
        }
@@ -1052,7 +1177,7 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                          "share entry with an open file\n");
        }
 
-       if (((uint16)fsp->oplock_type) != share_entry->op_type) {
+       if (((uint16_t)fsp->oplock_type) != share_entry->op_type) {
                goto panic;
        }
 
@@ -1074,7 +1199,7 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
 }
 #endif
 
-bool is_stat_open(uint32 access_mask)
+bool is_stat_open(uint32_t access_mask)
 {
        const uint32_t stat_open_bits =
                (SYNCHRONIZE_ACCESS|
@@ -1107,16 +1232,16 @@ static bool has_delete_on_close(struct share_mode_lock *lck,
 
 /****************************************************************************
  Deal with share modes
- Invarient: Share mode must be locked on entry and exit.
+ Invariant: Share mode must be locked on entry and exit.
  Returns -1 on error, or number of share modes on success (may be zero).
 ****************************************************************************/
 
 static NTSTATUS open_mode_check(connection_struct *conn,
                                struct share_mode_lock *lck,
-                               uint32 access_mask,
-                               uint32 share_access)
+                               uint32_t access_mask,
+                               uint32_t share_access)
 {
-       int i;
+       uint32_t i;
 
        if(lck->data->num_share_modes == 0) {
                return NT_STATUS_OK;
@@ -1167,25 +1292,30 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  * our client.
  */
 
-static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
+NTSTATUS send_break_message(struct messaging_context *msg_ctx,
                                   const struct share_mode_entry *exclusive,
                                   uint16_t break_to)
 {
        NTSTATUS status;
        char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+       struct server_id_buf tmp;
 
        DEBUG(10, ("Sending break request to PID %s\n",
-                  procid_str_static(&exclusive->pid)));
+                  server_id_str_buf(exclusive->pid, &tmp)));
 
        /* Create the message. */
        share_mode_entry_to_message(msg, exclusive);
 
        /* Overload entry->op_type */
+       /*
+        * This is a cut from uint32_t to uint16_t, but so far only the lower 3
+        * bits (LEASE_WRITE/HANDLE/READ are used anyway.
+        */
        SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
 
        status = messaging_send_buf(msg_ctx, exclusive->pid,
                                    MSG_SMB_BREAK_REQUEST,
-                                   (uint8 *)msg, sizeof(msg));
+                                   (uint8_t *)msg, sizeof(msg));
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(3, ("Could not send oplock break message: %s\n",
                          nt_errstr(status)));
@@ -1215,6 +1345,11 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
                        continue;
                }
 
+               if (e->op_mid == 0) {
+                       /* INTERNAL_OPEN_ONLY */
+                       continue;
+               }
+
                if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
                        /* We ignore stat opens in the table - they
                           always have NO_OPLOCK and never get or
@@ -1295,201 +1430,497 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 
 static bool delay_for_oplock(files_struct *fsp,
                             int oplock_request,
+                            const struct smb2_lease *lease,
                             struct share_mode_lock *lck,
                             bool have_sharing_violation,
-                            uint32_t create_disposition)
+                            uint32_t create_disposition,
+                            bool first_open_attempt)
 {
        struct share_mode_data *d = lck->data;
-       struct share_mode_entry *entry;
-       uint32_t num_non_stat_opens = 0;
        uint32_t i;
-       uint16_t break_to;
+       bool delay = false;
+       bool will_overwrite;
 
-       if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+       if ((oplock_request & INTERNAL_OPEN_ONLY) ||
+           is_stat_open(fsp->access_mask)) {
                return false;
        }
+
+       switch (create_disposition) {
+       case FILE_SUPERSEDE:
+       case FILE_OVERWRITE:
+       case FILE_OVERWRITE_IF:
+               will_overwrite = true;
+               break;
+       default:
+               will_overwrite = false;
+               break;
+       }
+
        for (i=0; i<d->num_share_modes; i++) {
                struct share_mode_entry *e = &d->share_modes[i];
-               if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
+               struct share_mode_lease *l = NULL;
+               uint32_t e_lease_type = get_lease_type(d, e);
+               uint32_t break_to;
+               uint32_t delay_mask = 0;
+
+               if (e->op_type == LEASE_OPLOCK) {
+                       l = &d->leases[e->lease_idx];
+               }
+
+               if (have_sharing_violation) {
+                       delay_mask = SMB2_LEASE_HANDLE;
+               } else {
+                       delay_mask = SMB2_LEASE_WRITE;
+               }
+
+               break_to = e_lease_type & ~delay_mask;
+
+               if (will_overwrite) {
+                       /*
+                        * we'll decide about SMB2_LEASE_READ later.
+                        *
+                        * Maybe the break will be deferred
+                        */
+                       break_to &= ~SMB2_LEASE_HANDLE;
+               }
+
+               DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+                          (unsigned)i, (unsigned)e_lease_type,
+                          (unsigned)will_overwrite));
+
+               if (lease != NULL && l != NULL) {
+                       bool ign;
+
+                       ign = smb2_lease_equal(fsp_client_guid(fsp),
+                                              &lease->lease_key,
+                                              &l->client_guid,
+                                              &l->lease_key);
+                       if (ign) {
+                               continue;
+                       }
+               }
+
+               if ((e_lease_type & ~break_to) == 0) {
+                       if (l != NULL && l->breaking) {
+                               delay = true;
+                       }
                        continue;
                }
-               num_non_stat_opens += 1;
 
-               /*
-                * We found the a non-stat open, which in the exclusive/batch
-                * case will be inspected further down.
-                */
-               entry = e;
+               if (share_mode_stale_pid(d, i)) {
+                       continue;
+               }
+
+               if (will_overwrite) {
+                       /*
+                        * If we break anyway break to NONE directly.
+                        * Otherwise vfs_set_filelen() will trigger the
+                        * break.
+                        */
+                       break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
+               }
+
+               if (e->op_type != LEASE_OPLOCK) {
+                       /*
+                        * Oplocks only support breaking to R or NONE.
+                        */
+                       break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+               }
+
+               DEBUG(10, ("breaking from %d to %d\n",
+                          (int)e_lease_type, (int)break_to));
+               send_break_message(fsp->conn->sconn->msg_ctx, e,
+                                  break_to);
+               if (e_lease_type & delay_mask) {
+                       delay = true;
+               }
+               if (l != NULL && l->breaking && !first_open_attempt) {
+                       delay = true;
+               }
+               continue;
        }
-       if (num_non_stat_opens == 0) {
-               /*
-                * Nothing to wait for around
-                */
+
+       return delay;
+}
+
+static bool file_has_brlocks(files_struct *fsp)
+{
+       struct byte_range_lock *br_lck;
+
+       br_lck = brl_get_locks_readonly(fsp);
+       if (!br_lck)
                return false;
+
+       return (brl_num_locks(br_lck) > 0);
+}
+
+int find_share_mode_lease(struct share_mode_data *d,
+                         const struct GUID *client_guid,
+                         const struct smb2_lease_key *key)
+{
+       uint32_t i;
+
+       for (i=0; i<d->num_leases; i++) {
+               struct share_mode_lease *l = &d->leases[i];
+
+               if (smb2_lease_equal(client_guid,
+                                    key,
+                                    &l->client_guid,
+                                    &l->lease_key)) {
+                       return i;
+               }
        }
-       if (num_non_stat_opens != 1) {
+
+       return -1;
+}
+
+struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
+                                const struct smb2_lease_key *key,
+                                const struct share_mode_lease *l)
+{
+       struct files_struct *fsp;
+
+       /*
+        * TODO: Measure how expensive this loop is with thousands of open
+        * handles...
+        */
+
+       for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id);
+            fsp != NULL;
+            fsp = file_find_di_next(fsp)) {
+
+               if (fsp == new_fsp) {
+                       continue;
+               }
+               if (fsp->oplock_type != LEASE_OPLOCK) {
+                       continue;
+               }
+               if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+                       fsp->lease->ref_count += 1;
+                       return fsp->lease;
+               }
+       }
+
+       /* Not found - must be leased in another smbd. */
+       new_fsp->lease = talloc_zero(new_fsp->conn->sconn, struct fsp_lease);
+       if (new_fsp->lease == NULL) {
+               return NULL;
+       }
+       new_fsp->lease->ref_count = 1;
+       new_fsp->lease->sconn = new_fsp->conn->sconn;
+       new_fsp->lease->lease.lease_key = *key;
+       new_fsp->lease->lease.lease_state = l->current_state;
+       /*
+        * We internally treat all leases as V2 and update
+        * the epoch, but when sending breaks it matters if
+        * the requesting lease was v1 or v2.
+        */
+       new_fsp->lease->lease.lease_version = l->lease_version;
+       new_fsp->lease->lease.lease_epoch = l->epoch;
+       return new_fsp->lease;
+}
+
+static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
+                               struct share_mode_lock *lck,
+                               const struct smb2_lease *lease,
+                               uint32_t *p_lease_idx,
+                               uint32_t granted)
+{
+       struct share_mode_data *d = lck->data;
+       const struct GUID *client_guid = fsp_client_guid(fsp);
+       struct share_mode_lease *tmp;
+       NTSTATUS status;
+       int idx;
+
+       idx = find_share_mode_lease(d, client_guid, &lease->lease_key);
+
+       if (idx != -1) {
+               struct share_mode_lease *l = &d->leases[idx];
+               bool do_upgrade;
+               uint32_t existing, requested;
+
+               fsp->lease = find_fsp_lease(fsp, &lease->lease_key, l);
+               if (fsp->lease == NULL) {
+                       DEBUG(1, ("Did not find existing lease for file %s\n",
+                                 fsp_str_dbg(fsp)));
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               *p_lease_idx = idx;
+
                /*
-                * More than one open around. There can't be any exclusive or
-                * batch left, this is all level2.
+                * Upgrade only if the requested lease is a strict upgrade.
                 */
-               return false;
-       }
+               existing = l->current_state;
+               requested = lease->lease_state;
 
-       if (server_id_is_disconnected(&entry->pid)) {
                /*
-                * TODO: clean up.
-                * This could be achieved by sending a break message
-                * to ourselves. Special considerations for files
-                * with delete_on_close flag set!
-                *
-                * For now we keep it simple and do not
-                * allow delete on close for durable handles.
+                * Tricky: This test makes sure that "requested" is a
+                * strict bitwise superset of "existing".
                 */
-               return false;
-       }
+               do_upgrade = ((existing & requested) == existing);
 
-       switch (create_disposition) {
-       case FILE_SUPERSEDE:
-       case FILE_OVERWRITE_IF:
-               break_to = NO_OPLOCK;
-               break;
-       default:
-               break_to = LEVEL_II_OPLOCK;
-               break;
-       }
+               /*
+                * Upgrade only if there's a change.
+                */
+               do_upgrade &= (granted != existing);
 
-       if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
-               if (share_mode_stale_pid(d, 0)) {
-                       return false;
-               }
-               send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-               return true;
-       }
-       if (have_sharing_violation) {
                /*
-                * Non-batch exclusive is not broken if we have a sharing
-                * violation
+                * Upgrade only if other leases don't prevent what was asked
+                * for.
                 */
-               return false;
-       }
-       if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
-           (break_to == NO_OPLOCK)) {
-               if (share_mode_stale_pid(d, 0)) {
-                       return false;
+               do_upgrade &= (granted == requested);
+
+               /*
+                * only upgrade if we are not in breaking state
+                */
+               do_upgrade &= !l->breaking;
+
+               DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+                          "granted=%"PRIu32", do_upgrade=%d\n",
+                          existing, requested, granted, (int)do_upgrade));
+
+               if (do_upgrade) {
+                       l->current_state = granted;
+                       l->epoch += 1;
                }
-               DEBUG(10, ("Asynchronously breaking level2 oplock for "
-                          "create_disposition=%u\n",
-                          (unsigned)create_disposition));
-               send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-               return false;
+
+               /* Ensure we're in sync with current lease state. */
+               fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+               return NT_STATUS_OK;
        }
-       if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
+
+       /*
+        * Create new lease
+        */
+
+       tmp = talloc_realloc(d, d->leases, struct share_mode_lease,
+                            d->num_leases+1);
+       if (tmp == NULL) {
                /*
-                * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
+                * See [MS-SMB2]
                 */
-               return false;
-       }
-       if (share_mode_stale_pid(d, 0)) {
-               return false;
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
+       }
+       d->leases = tmp;
+
+       fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease);
+       if (fsp->lease == NULL) {
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
+       }
+       fsp->lease->ref_count = 1;
+       fsp->lease->sconn = fsp->conn->sconn;
+       fsp->lease->lease.lease_version = lease->lease_version;
+       fsp->lease->lease.lease_key = lease->lease_key;
+       fsp->lease->lease.lease_state = granted;
+       fsp->lease->lease.lease_epoch = lease->lease_epoch + 1;
+
+       *p_lease_idx = d->num_leases;
+
+       d->leases[d->num_leases] = (struct share_mode_lease) {
+               .client_guid = *client_guid,
+               .lease_key = fsp->lease->lease.lease_key,
+               .current_state = fsp->lease->lease.lease_state,
+               .lease_version = fsp->lease->lease.lease_version,
+               .epoch = fsp->lease->lease.lease_epoch,
+       };
+
+       status = leases_db_add(client_guid,
+                              &lease->lease_key,
+                              &fsp->file_id,
+                              fsp->conn->connectpath,
+                              fsp->fsp_name->base_name,
+                              fsp->fsp_name->stream_name);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__,
+                          nt_errstr(status)));
+               TALLOC_FREE(fsp->lease);
+               return NT_STATUS_INSUFFICIENT_RESOURCES;
        }
 
-       send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-       return true;
+       d->num_leases += 1;
+       d->modified = true;
+
+       return NT_STATUS_OK;
 }
 
-static bool file_has_brlocks(files_struct *fsp)
+static bool is_same_lease(const files_struct *fsp,
+                         const struct share_mode_data *d,
+                         const struct share_mode_entry *e,
+                         const struct smb2_lease *lease)
 {
-       struct byte_range_lock *br_lck;
-
-       br_lck = brl_get_locks_readonly(fsp);
-       if (!br_lck)
+       if (e->op_type != LEASE_OPLOCK) {
+               return false;
+       }
+       if (lease == NULL) {
                return false;
+       }
 
-       return (brl_num_locks(br_lck) > 0);
+       return smb2_lease_equal(fsp_client_guid(fsp),
+                               &lease->lease_key,
+                               &d->leases[e->lease_idx].client_guid,
+                               &d->leases[e->lease_idx].lease_key);
 }
 
-static void grant_fsp_oplock_type(files_struct *fsp,
-                                 struct share_mode_lock *lck,
-                                 int oplock_request)
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
+                                     struct files_struct *fsp,
+                                     struct share_mode_lock *lck,
+                                     int oplock_request,
+                                     struct smb2_lease *lease)
 {
-       bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-                           lp_level2_oplocks(SNUM(fsp->conn));
-       bool got_level2_oplock, got_a_none_oplock;
+       struct share_mode_data *d = lck->data;
+       bool got_handle_lease = false;
+       bool got_oplock = false;
        uint32_t i;
-
-       /* Start by granting what the client asked for,
-          but ensure no SAMBA_PRIVATE bits can be set. */
-       fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+       uint32_t granted;
+       uint32_t lease_idx = UINT32_MAX;
+       bool ok;
+       NTSTATUS status;
 
        if (oplock_request & INTERNAL_OPEN_ONLY) {
                /* No oplocks on internal open. */
-               fsp->oplock_type = NO_OPLOCK;
+               oplock_request = NO_OPLOCK;
                DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
                        fsp->oplock_type, fsp_str_dbg(fsp)));
-               return;
+       }
+
+       if (oplock_request == LEASE_OPLOCK) {
+               if (lease == NULL) {
+                       /*
+                        * The SMB2 layer should have checked this
+                        */
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+
+               granted = lease->lease_state;
+
+               if (lp_kernel_oplocks(SNUM(fsp->conn))) {
+                       DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
+                       granted = SMB2_LEASE_NONE;
+               }
+               if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
+                       DEBUG(10, ("No read or write lease requested\n"));
+                       granted = SMB2_LEASE_NONE;
+               }
+               if (granted == SMB2_LEASE_WRITE) {
+                       DEBUG(10, ("pure write lease requested\n"));
+                       granted = SMB2_LEASE_NONE;
+               }
+               if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
+                       DEBUG(10, ("write and handle lease requested\n"));
+                       granted = SMB2_LEASE_NONE;
+               }
+       } else {
+               granted = map_oplock_to_lease_type(
+                       oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
        }
 
        if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
                DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
                        fsp_str_dbg(fsp)));
-               fsp->oplock_type = NO_OPLOCK;
+               granted &= ~SMB2_LEASE_READ;
        }
 
-       if (is_stat_open(fsp->access_mask)) {
-               /* Leave the value already set. */
-               DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-                       fsp->oplock_type, fsp_str_dbg(fsp)));
-               return;
-       }
+       for (i=0; i<d->num_share_modes; i++) {
+               struct share_mode_entry *e = &d->share_modes[i];
+               uint32_t e_lease_type;
 
-       got_level2_oplock = false;
-       got_a_none_oplock = false;
+               e_lease_type = get_lease_type(d, e);
 
-       for (i=0; i<lck->data->num_share_modes; i++) {
-               int op_type = lck->data->share_modes[i].op_type;
+               if ((granted & SMB2_LEASE_WRITE) &&
+                   !is_same_lease(fsp, d, e, lease) &&
+                   !share_mode_stale_pid(d, i)) {
+                       /*
+                        * Can grant only one writer
+                        */
+                       granted &= ~SMB2_LEASE_WRITE;
+               }
 
-               if (LEVEL_II_OPLOCK_TYPE(op_type)) {
-                       got_level2_oplock = true;
+               if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+                   !share_mode_stale_pid(d, i)) {
+                       got_handle_lease = true;
                }
-               if (op_type == NO_OPLOCK) {
-                       got_a_none_oplock = true;
+
+               if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+                   !share_mode_stale_pid(d, i)) {
+                       got_oplock = true;
                }
        }
 
-       /*
-        * Match what was requested (fsp->oplock_type) with
-        * what was found in the existing share modes.
-        */
+       if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) {
+               bool allow_level2 =
+                       (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+                       lp_level2_oplocks(SNUM(fsp->conn));
 
-       if (got_level2_oplock || got_a_none_oplock) {
-               if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-                       fsp->oplock_type = LEVEL_II_OPLOCK;
+               if (!allow_level2) {
+                       granted = SMB2_LEASE_NONE;
                }
        }
 
-       /*
-        * Don't grant level2 to clients that don't want them
-        * or if we've turned them off.
-        */
-       if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
-               fsp->oplock_type = NO_OPLOCK;
+       if (oplock_request == LEASE_OPLOCK) {
+               if (got_oplock) {
+                       granted &= ~SMB2_LEASE_HANDLE;
+               }
+
+               fsp->oplock_type = LEASE_OPLOCK;
+
+               status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
+                                        granted);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+
+               }
+               *lease = fsp->lease->lease;
+               DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+       } else {
+               if (got_handle_lease) {
+                       granted = SMB2_LEASE_NONE;
+               }
+
+               switch (granted) {
+               case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+                       fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+                       break;
+               case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+                       fsp->oplock_type = EXCLUSIVE_OPLOCK;
+                       break;
+               case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+               case SMB2_LEASE_READ:
+                       fsp->oplock_type = LEVEL_II_OPLOCK;
+                       break;
+               default:
+                       fsp->oplock_type = NO_OPLOCK;
+                       break;
+               }
+
+               status = set_file_oplock(fsp);
+               if (!NT_STATUS_IS_OK(status)) {
+                       /*
+                        * Could not get the kernel oplock
+                        */
+                       fsp->oplock_type = NO_OPLOCK;
+               }
        }
 
-       if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
-               /*
-                * We're the first level2 oplock. Indicate that in brlock.tdb.
-                */
-               struct byte_range_lock *brl;
+       ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+                           req ? req->mid : 0,
+                           fsp->oplock_type,
+                           lease_idx);
+       if (!ok) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               brl = brl_get_locks(talloc_tos(), fsp);
-               if (brl != NULL) {
-                       brl_set_have_read_oplocks(brl, true);
-                       TALLOC_FREE(brl);
-               }
+       ok = update_num_read_oplocks(fsp, lck);
+       if (!ok) {
+               del_share_mode(lck, fsp);
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
        DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
                  fsp->oplock_type, fsp_str_dbg(fsp)));
+
+       return NT_STATUS_OK;
 }
 
 static bool request_timed_out(struct timeval request_time,
@@ -1502,7 +1933,7 @@ static bool request_timed_out(struct timeval request_time,
 }
 
 struct defer_open_state {
-       struct smbd_server_connection *sconn;
+       struct smbXsrv_connection *xconn;
        uint64_t mid;
 };
 
@@ -1518,35 +1949,40 @@ static void defer_open(struct share_mode_lock *lck,
                       struct smb_request *req,
                       struct deferred_open_record *state)
 {
+       struct deferred_open_record *open_rec;
+
        DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
                  "open entry for mid %llu\n",
                  (unsigned int)request_time.tv_sec,
                  (unsigned int)request_time.tv_usec,
                  (unsigned long long)req->mid));
 
-       if (!push_deferred_open_message_smb(req, request_time, timeout,
-                                      state->id, (char *)state, sizeof(*state))) {
+       open_rec = talloc(NULL, struct deferred_open_record);
+       if (open_rec == NULL) {
                TALLOC_FREE(lck);
-               exit_server("push_deferred_open_message_smb failed");
+               exit_server("talloc failed");
        }
+
+       *open_rec = *state;
+
        if (lck) {
                struct defer_open_state *watch_state;
                struct tevent_req *watch_req;
                bool ret;
 
-               watch_state = talloc(req->sconn, struct defer_open_state);
+               watch_state = talloc(open_rec, struct defer_open_state);
                if (watch_state == NULL) {
                        exit_server("talloc failed");
                }
-               watch_state->sconn = req->sconn;
+               watch_state->xconn = req->xconn;
                watch_state->mid = req->mid;
 
                DEBUG(10, ("defering mid %llu\n",
                           (unsigned long long)req->mid));
 
-               watch_req = dbwrap_record_watch_send(
+               watch_req = dbwrap_watched_watch_send(
                        watch_state, req->sconn->ev_ctx, lck->data->record,
-                       req->sconn->msg_ctx);
+                       (struct server_id){0});
                if (watch_req == NULL) {
                        exit_server("Could not watch share mode record");
                }
@@ -1558,6 +1994,12 @@ static void defer_open(struct share_mode_lock *lck,
                        timeval_sum(&request_time, &timeout));
                SMB_ASSERT(ret);
        }
+
+       if (!push_deferred_open_message_smb(req, request_time, timeout,
+                                           state->id, open_rec)) {
+               TALLOC_FREE(lck);
+               exit_server("push_deferred_open_message_smb failed");
+       }
 }
 
 static void defer_open_done(struct tevent_req *req)
@@ -1567,10 +2009,11 @@ static void defer_open_done(struct tevent_req *req)
        NTSTATUS status;
        bool ret;
 
-       status = dbwrap_record_watch_recv(req, talloc_tos(), NULL);
+       status = dbwrap_watched_watch_recv(req, talloc_tos(), NULL, NULL,
+                                         NULL);
        TALLOC_FREE(req);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
+               DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
                          nt_errstr(status)));
                /*
                 * Even if it failed, retry anyway. TODO: We need a way to
@@ -1580,7 +2023,7 @@ static void defer_open_done(struct tevent_req *req)
 
        DEBUG(10, ("scheduling mid %llu\n", (unsigned long long)state->mid));
 
-       ret = schedule_deferred_open_message_smb(state->sconn, state->mid);
+       ret = schedule_deferred_open_message_smb(state->xconn, state->mid);
        SMB_ASSERT(ret);
        TALLOC_FREE(state);
 }
@@ -1591,13 +2034,13 @@ static void defer_open_done(struct tevent_req *req)
 ****************************************************************************/
 
 static bool open_match_attributes(connection_struct *conn,
-                                 uint32 old_dos_attr,
-                                 uint32 new_dos_attr,
+                                 uint32_t old_dos_attr,
+                                 uint32_t new_dos_attr,
                                  mode_t existing_unx_mode,
                                  mode_t new_unx_mode,
                                  mode_t *returned_unx_mode)
 {
-       uint32 noarch_old_dos_attr, noarch_new_dos_attr;
+       uint32_t noarch_old_dos_attr, noarch_new_dos_attr;
 
        noarch_old_dos_attr = (old_dos_attr & ~FILE_ATTRIBUTE_ARCHIVE);
        noarch_new_dos_attr = (new_dos_attr & ~FILE_ATTRIBUTE_ARCHIVE);
@@ -1643,11 +2086,11 @@ static NTSTATUS fcb_or_dos_open(struct smb_request *req,
                                files_struct *fsp_to_dup_into,
                                const struct smb_filename *smb_fname,
                                struct file_id id,
-                               uint16 file_pid,
+                               uint16_t file_pid,
                                uint64_t vuid,
-                               uint32 access_mask,
-                               uint32 share_access,
-                               uint32 create_options)
+                               uint32_t access_mask,
+                               uint32_t share_access,
+                               uint32_t create_options)
 {
        files_struct *fsp;
 
@@ -1697,6 +2140,7 @@ static NTSTATUS fcb_or_dos_open(struct smb_request *req,
 }
 
 static void schedule_defer_open(struct share_mode_lock *lck,
+                               struct file_id id,
                                struct timeval request_time,
                                struct smb_request *req)
 {
@@ -1727,7 +2171,7 @@ static void schedule_defer_open(struct share_mode_lock *lck,
 
        state.delayed_for_oplocks = True;
        state.async_open = false;
-       state.id = lck->data->id;
+       state.id = id;
 
        if (!request_timed_out(request_time, timeout)) {
                defer_open(lck, request_time, timeout, req, &state);
@@ -1774,7 +2218,7 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
                return NT_STATUS_OK;
        }
 
-       status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
+       status = SMB_VFS_GET_NT_ACL(conn, smb_fname,
                                    (SECINFO_OWNER |
                                     SECINFO_GROUP |
                                     SECINFO_DACL),
@@ -1839,6 +2283,12 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
        uint32_t orig_access_mask = access_mask;
        uint32_t rejected_share_access;
 
+       if (access_mask & SEC_MASK_INVALID) {
+               DBG_DEBUG("access_mask [%8x] contains invalid bits\n",
+                         access_mask);
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
        /*
         * Convert GENERIC bits to specific bits.
         */
@@ -1883,11 +2333,9 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
  Return true if this is a state pointer to an asynchronous create.
 ****************************************************************************/
 
-bool is_deferred_open_async(const void *ptr)
+bool is_deferred_open_async(const struct deferred_open_record *rec)
 {
-       const struct deferred_open_record *state = (const struct deferred_open_record *)ptr;
-
-       return state->async_open;
+       return rec->async_open;
 }
 
 static bool clear_ads(uint32_t create_disposition)
@@ -1960,7 +2408,6 @@ static int disposition_to_open_flags(uint32_t create_disposition)
 }
 
 static int calculate_open_access_flags(uint32_t access_mask,
-                                      int oplock_request,
                                       uint32_t private_flags)
 {
        bool need_write, need_read;
@@ -1996,12 +2443,13 @@ static int calculate_open_access_flags(uint32_t access_mask,
 
 static NTSTATUS open_file_ntcreate(connection_struct *conn,
                            struct smb_request *req,
-                           uint32 access_mask,         /* access bits (FILE_READ_DATA etc.) */
-                           uint32 share_access,        /* share constants (FILE_SHARE_READ etc) */
-                           uint32 create_disposition,  /* FILE_OPEN_IF etc. */
-                           uint32 create_options,      /* options such as delete on close. */
-                           uint32 new_dos_attributes,  /* attributes used for new file. */
+                           uint32_t access_mask,               /* access bits (FILE_READ_DATA etc.) */
+                           uint32_t share_access,      /* share constants (FILE_SHARE_READ etc) */
+                           uint32_t create_disposition,        /* FILE_OPEN_IF etc. */
+                           uint32_t create_options,    /* options such as delete on close. */
+                           uint32_t new_dos_attributes,        /* attributes used for new file. */
                            int oplock_request,         /* internal Samba oplock codes. */
+                           struct smb2_lease *lease,
                                                        /* Information (FILE_EXISTS etc.) */
                            uint32_t private_flags,     /* Samba specific flags. */
                            int *pinfo,
@@ -2019,10 +2467,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        mode_t new_unx_mode = (mode_t)0;
        mode_t unx_mode = (mode_t)0;
        int info;
-       uint32 existing_dos_attributes = 0;
+       uint32_t existing_dos_attributes = 0;
        struct timeval request_time = timeval_zero();
        struct share_mode_lock *lck = NULL;
-       uint32 open_access_mask = access_mask;
+       uint32_t open_access_mask = access_mask;
        NTSTATUS status;
        char *parent_dir;
        SMB_STRUCT_STAT saved_stat = smb_fname->st;
@@ -2083,9 +2531,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                   create_options, (unsigned int)unx_mode, oplock_request,
                   (unsigned int)private_flags));
 
-       if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
-               DEBUG(0, ("No smb request but not an internal only open!\n"));
-               return NT_STATUS_INTERNAL_ERROR;
+       if (req == NULL) {
+               /* Ensure req == NULL means INTERNAL_OPEN_ONLY */
+               SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) != 0));
+       } else {
+               /* And req != NULL means no INTERNAL_OPEN_ONLY */
+               SMB_ASSERT(((oplock_request & INTERNAL_OPEN_ONLY) == 0));
        }
 
        /*
@@ -2093,10 +2544,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         */
 
        if (req) {
-               void *ptr;
+               struct deferred_open_record *open_rec;
                if (get_deferred_open_message_state(req,
                                &request_time,
-                               &ptr)) {
+                               &open_rec)) {
                        /* Remember the absolute time of the original
                           request with this mid. We'll use it later to
                           see if this has timed out. */
@@ -2104,13 +2555,13 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        /* If it was an async create retry, the file
                           didn't exist. */
 
-                       if (is_deferred_open_async(ptr)) {
+                       if (is_deferred_open_async(open_rec)) {
                                SET_STAT_INVALID(smb_fname->st);
                                file_existed = false;
                        }
 
                        /* Ensure we don't reprocess this message. */
-                       remove_deferred_open_message_smb(req->sconn, req->mid);
+                       remove_deferred_open_message_smb(req->xconn, req->mid);
 
                        first_open_attempt = false;
                }
@@ -2119,7 +2570,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        if (!posix_open) {
                new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
                if (file_existed) {
-                       existing_dos_attributes = dos_mode(conn, smb_fname);
+                       /*
+                        * Only use strored DOS attributes for checks
+                        * against requested attributes (below via
+                        * open_match_attributes()), cf bug #11992
+                        * for details. -slow
+                        */
+                       uint32_t attr = 0;
+
+                       status = SMB_VFS_GET_DOS_ATTRIBUTES(conn, smb_fname, &attr);
+                       if (NT_STATUS_IS_OK(status)) {
+                               existing_dos_attributes = attr;
+                       }
                }
        }
 
@@ -2131,7 +2593,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        /* this is for OS/2 long file names - say we don't support them */
-       if (!lp_posix_pathnames() && strstr(smb_fname->base_name,".+,;=[].")) {
+       if (req != NULL && !req->posix_pathnames &&
+                       strstr(smb_fname->base_name,".+,;=[].")) {
                /* OS/2 Workplace shell fix may be main code stream in a later
                 * release. */
                DEBUG(5,("open_file_ntcreate: OS/2 long filenames are not "
@@ -2243,8 +2706,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * mean the same thing under DOS and Unix.
         */
 
-       flags = calculate_open_access_flags(access_mask, oplock_request,
-                                           private_flags);
+       flags = calculate_open_access_flags(access_mask, private_flags);
 
        /*
         * Currently we only look at FILE_WRITE_THROUGH for create options.
@@ -2307,10 +2769,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        fsp->access_mask = open_access_mask; /* We change this to the
                                              * requested access_mask after
                                              * the open is done. */
-       fsp->posix_open = posix_open;
-
-       /* Ensure no SAMBA_PRIVATE bits can be set. */
-       fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+       if (posix_open) {
+               fsp->posix_flags |= FSP_POSIX_FLAGS_ALL;
+       }
 
        if (timeval_is_zero(&request_time)) {
                request_time = fsp->open_time;
@@ -2337,9 +2798,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
                struct deferred_open_record state;
+               bool delay;
 
                /*
-                * EWOULDBLOCK/EAGAIN maps to NETWORK_BUSY.
+                * This handles the kernel oplock case:
+                *
+                * the file has an active kernel oplock and the open() returned
+                * EWOULDBLOCK/EAGAIN which maps to NETWORK_BUSY.
+                *
+                * "Samba locking.tdb oplocks" are handled below after acquiring
+                * the sharemode lock with get_share_mode_lock().
                 */
                if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
                        DEBUG(10, ("FIFO busy\n"));
@@ -2370,8 +2838,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        smb_panic("validate_oplock_types failed");
                }
 
-               if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
-                       schedule_defer_open(lck, request_time, req);
+               delay = delay_for_oplock(fsp, 0, lease, lck, false,
+                                        create_disposition,
+                                        first_open_attempt);
+               if (delay) {
+                       schedule_defer_open(lck, fsp->file_id, request_time,
+                                           req);
                        TALLOC_FREE(lck);
                        DEBUG(10, ("Sent oplock break request to kernel "
                                   "oplock holder\n"));
@@ -2384,7 +2856,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 */
                state.delayed_for_oplocks = false;
                state.async_open = false;
-               state.id = lck->data->id;
+               state.id = fsp->file_id;
                defer_open(lck, request_time, timeval_set(0, 0), req, &state);
                TALLOC_FREE(lck);
                DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
@@ -2399,6 +2871,17 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return fsp_open;
        }
 
+       if (new_file_created) {
+               /*
+                * As we atomically create using O_CREAT|O_EXCL,
+                * then if new_file_created is true, then
+                * file_existed *MUST* have been false (even
+                * if the file was previously detected as being
+                * there).
+                */
+               file_existed = false;
+       }
+
        if (file_existed && !check_same_dev_ino(&saved_stat, &smb_fname->st)) {
                /*
                 * The file did exist, but some other (local or NFS)
@@ -2479,19 +2962,31 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                file_existed = true;
        }
 
-       if ((req != NULL) &&
-           delay_for_oplock(
-                   fsp, oplock_request, lck,
-                   NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
-                   create_disposition)) {
-               schedule_defer_open(lck, request_time, req);
-               TALLOC_FREE(lck);
-               fd_close(fsp);
-               return NT_STATUS_SHARING_VIOLATION;
+       if (req != NULL) {
+               /*
+                * Handle oplocks, deferring the request if delay_for_oplock()
+                * triggered a break message and we have to wait for the break
+                * response.
+                */
+               bool delay;
+               bool sharing_violation = NT_STATUS_EQUAL(
+                       status, NT_STATUS_SHARING_VIOLATION);
+
+               delay = delay_for_oplock(fsp, oplock_request, lease, lck,
+                                        sharing_violation,
+                                        create_disposition,
+                                        first_open_attempt);
+               if (delay) {
+                       schedule_defer_open(lck, fsp->file_id,
+                                           request_time, req);
+                       TALLOC_FREE(lck);
+                       fd_close(fsp);
+                       return NT_STATUS_SHARING_VIOLATION;
+               }
        }
 
        if (!NT_STATUS_IS_OK(status)) {
-               uint32 can_access_mask;
+               uint32_t can_access_mask;
                bool can_access = True;
 
                SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION));
@@ -2617,7 +3112,20 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return status;
        }
 
-       grant_fsp_oplock_type(fsp, lck, oplock_request);
+       /* Should we atomically (to the client at least) truncate ? */
+       if ((!new_file_created) &&
+           (flags2 & O_TRUNC) &&
+           (!S_ISFIFO(fsp->fsp_name->st.st_ex_mode))) {
+               int ret;
+
+               ret = vfs_set_filelen(fsp, 0);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix(errno);
+                       TALLOC_FREE(lck);
+                       fd_close(fsp);
+                       return status;
+               }
+       }
 
        /*
         * We have the share entry *locked*.....
@@ -2626,7 +3134,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        /* Delete streams if create_disposition requires it */
        if (!new_file_created && clear_ads(create_disposition) &&
            !is_ntfs_stream_smb_fname(smb_fname)) {
-               status = delete_all_streams(conn, smb_fname->base_name);
+               status = delete_all_streams(conn, smb_fname);
                if (!NT_STATUS_IS_OK(status)) {
                        TALLOC_FREE(lck);
                        fd_close(fsp);
@@ -2644,6 +3152,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        if (fsp->fh->fd != -1 && lp_kernel_share_modes(SNUM(conn))) {
                int ret_flock;
+               /*
+                * Beware: streams implementing VFS modules may
+                * implement streams in a way that fsp will have the
+                * basefile open in the fsp fd, so lacking a distinct
+                * fd for the stream kernel_flock will apply on the
+                * basefile which is wrong. The actual check is
+                * deffered to the VFS module implementing the
+                * kernel_flock call.
+                */
                ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access, access_mask);
                if(ret_flock == -1 ){
 
@@ -2652,6 +3169,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                        return NT_STATUS_SHARING_VIOLATION;
                }
+
+               fsp->kernel_share_modes_taken = true;
        }
 
        /*
@@ -2678,9 +3197,19 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        if (file_existed) {
-               /* stat opens on existing files don't get oplocks. */
-               if (is_stat_open(open_access_mask)) {
-                       fsp->oplock_type = NO_OPLOCK;
+               /*
+                * stat opens on existing files don't get oplocks.
+                * They can get leases.
+                *
+                * Note that we check for stat open on the *open_access_mask*,
+                * i.e. the access mask we actually used to do the open,
+                * not the one the client asked for (which is in
+                * fsp->access_mask). This is due to the fact that
+                * FILE_OVERWRITE and FILE_OVERWRITE_IF add in O_TRUNC,
+                * which adds FILE_WRITE_DATA to open_access_mask.
+                */
+               if (is_stat_open(open_access_mask) && lease == NULL) {
+                       oplock_request = NO_OPLOCK;
                }
        }
 
@@ -2702,21 +3231,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * Setup the oplock info in both the shared memory and
         * file structs.
         */
-
-       status = set_file_oplock(fsp, fsp->oplock_type);
+       status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
        if (!NT_STATUS_IS_OK(status)) {
-               /*
-                * Could not get the kernel oplock
-                */
-               fsp->oplock_type = NO_OPLOCK;
-       }
-
-       if (!set_share_mode(lck, fsp, get_current_uid(conn),
-                           req ? req->mid : 0,
-                           fsp->oplock_type)) {
                TALLOC_FREE(lck);
                fd_close(fsp);
-               return NT_STATUS_NO_MEMORY;
+               return status;
        }
 
        /* Handle strange delete on close create semantics. */
@@ -2737,8 +3256,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        if (info != FILE_WAS_OPENED) {
-               /* Files should be initially set as archive */
-               if (lp_map_archive(SNUM(conn)) ||
+               /* Overwritten files should be initially set as archive */
+               if ((info == FILE_WAS_OVERWRITTEN && lp_map_archive(SNUM(conn))) ||
                    lp_store_dos_attributes(SNUM(conn))) {
                        if (!posix_open) {
                                if (file_set_dosmode(conn, smb_fname,
@@ -2804,47 +3323,25 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                  (unsigned int)new_unx_mode));
        }
 
-       TALLOC_FREE(lck);
-
-       return NT_STATUS_OK;
-}
-
-
-/****************************************************************************
- Open a file for for write to ensure that we can fchmod it.
-****************************************************************************/
+       {
+               /*
+                * Deal with other opens having a modified write time.
+                */
+               struct timespec write_time = get_share_mode_write_time(lck);
 
-NTSTATUS open_file_fchmod(connection_struct *conn,
-                         struct smb_filename *smb_fname,
-                         files_struct **result)
-{
-       if (!VALID_STAT(smb_fname->st)) {
-               return NT_STATUS_INVALID_PARAMETER;
+               if (!null_timespec(write_time)) {
+                       update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+               }
        }
 
-        return SMB_VFS_CREATE_FILE(
-               conn,                                   /* conn */
-               NULL,                                   /* req */
-               0,                                      /* root_dir_fid */
-               smb_fname,                              /* fname */
-               FILE_WRITE_DATA,                        /* access_mask */
-               (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
-                   FILE_SHARE_DELETE),
-               FILE_OPEN,                              /* create_disposition*/
-               0,                                      /* create_options */
-               0,                                      /* file_attributes */
-               INTERNAL_OPEN_ONLY,                     /* oplock_request */
-               0,                                      /* allocation_size */
-               0,                                      /* private_flags */
-               NULL,                                   /* sd */
-               NULL,                                   /* ea_list */
-               result,                                 /* result */
-               NULL);                                  /* pinfo */
+       TALLOC_FREE(lck);
+
+       return NT_STATUS_OK;
 }
 
 static NTSTATUS mkdir_internal(connection_struct *conn,
                               struct smb_filename *smb_dname,
-                              uint32 file_attributes)
+                              uint32_t file_attributes)
 {
        mode_t mode;
        char *parent_dir = NULL;
@@ -2883,7 +3380,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                return status;
        }
 
-       if (SMB_VFS_MKDIR(conn, smb_dname->base_name, mode) != 0) {
+       if (SMB_VFS_MKDIR(conn, smb_dname, mode) != 0) {
                return map_nt_error_from_unix(errno);
        }
 
@@ -2910,7 +3407,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                }
        }
 
-       if (lp_inherit_perms(SNUM(conn))) {
+       if (lp_inherit_permissions(SNUM(conn))) {
                inherit_access_posix_acl(conn, parent_dir,
                                         smb_dname->base_name, mode);
                need_re_stat = true;
@@ -2925,7 +3422,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                 */
                if ((mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) &&
                    (mode & ~smb_dname->st.st_ex_mode)) {
-                       SMB_VFS_CHMOD(conn, smb_dname->base_name,
+                       SMB_VFS_CHMOD(conn, smb_dname,
                                      (smb_dname->st.st_ex_mode |
                                          (mode & ~smb_dname->st.st_ex_mode)));
                        need_re_stat = true;
@@ -2933,7 +3430,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        }
 
        /* Change the owner if required. */
-       if (lp_inherit_owner(SNUM(conn))) {
+       if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
                change_dir_owner_to_parent(conn, parent_dir,
                                           smb_dname->base_name,
                                           &smb_dname->st);
@@ -2961,11 +3458,11 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
 static NTSTATUS open_directory(connection_struct *conn,
                               struct smb_request *req,
                               struct smb_filename *smb_dname,
-                              uint32 access_mask,
-                              uint32 share_access,
-                              uint32 create_disposition,
-                              uint32 create_options,
-                              uint32 file_attributes,
+                              uint32_t access_mask,
+                              uint32_t share_access,
+                              uint32_t create_disposition,
+                              uint32_t create_options,
+                              uint32_t file_attributes,
                               int *pinfo,
                               files_struct **result)
 {
@@ -2975,6 +3472,7 @@ static NTSTATUS open_directory(connection_struct *conn,
        NTSTATUS status;
        struct timespec mtimespec;
        int info = 0;
+       bool ok;
 
        if (is_ntfs_stream_smb_fname(smb_dname)) {
                DEBUG(2, ("open_directory: %s is a stream name!\n",
@@ -3079,6 +3577,25 @@ static NTSTATUS open_directory(connection_struct *conn,
                                                        nt_errstr(status)));
                                                return status;
                                        }
+
+                                       /*
+                                        * If mkdir_internal() returned
+                                        * NT_STATUS_OBJECT_NAME_COLLISION
+                                        * we still must lstat the path.
+                                        */
+
+                                       if (SMB_VFS_LSTAT(conn, smb_dname)
+                                                       == -1) {
+                                               DEBUG(2, ("Could not stat "
+                                                       "directory '%s' just "
+                                                       "opened: %s\n",
+                                                       smb_fname_str_dbg(
+                                                               smb_dname),
+                                                       strerror(errno)));
+                                               return map_nt_error_from_unix(
+                                                               errno);
+                                       }
+
                                        info = FILE_WAS_OPENED;
                                }
                        }
@@ -3143,28 +3660,48 @@ static NTSTATUS open_directory(connection_struct *conn,
        fsp->oplock_type = NO_OPLOCK;
        fsp->sent_oplock_break = NO_BREAK_SENT;
        fsp->is_directory = True;
-       fsp->posix_open = (file_attributes & FILE_FLAG_POSIX_SEMANTICS) ? True : False;
+       if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
+               fsp->posix_flags |= FSP_POSIX_FLAGS_ALL;
+       }
        status = fsp_set_smb_fname(fsp, smb_dname);
        if (!NT_STATUS_IS_OK(status)) {
                file_free(req, fsp);
                return status;
        }
 
-       mtimespec = smb_dname->st.st_ex_mtime;
-
+       /* Don't store old timestamps for directory
+          handles in the internal database. We don't
+          update them in there if new objects
+          are creaded in the directory. Currently
+          we only update timestamps on file writes.
+          See bug #9870.
+       */
+       ZERO_STRUCT(mtimespec);
+
+       if (access_mask & (FILE_LIST_DIRECTORY|
+                          FILE_ADD_FILE|
+                          FILE_ADD_SUBDIRECTORY|
+                          FILE_TRAVERSE|
+                          DELETE_ACCESS|
+                          FILE_DELETE_CHILD)) {
 #ifdef O_DIRECTORY
-       status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
+               status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
 #else
-       /* POSIX allows us to open a directory with O_RDONLY. */
-       status = fd_open(conn, fsp, O_RDONLY, 0);
+               /* POSIX allows us to open a directory with O_RDONLY. */
+               status = fd_open(conn, fsp, O_RDONLY, 0);
 #endif
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(5, ("open_directory: Could not open fd for "
-                       "%s (%s)\n",
-                       smb_fname_str_dbg(smb_dname),
-                       nt_errstr(status)));
-               file_free(req, fsp);
-               return status;
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(5, ("open_directory: Could not open fd for "
+                               "%s (%s)\n",
+                               smb_fname_str_dbg(smb_dname),
+                               nt_errstr(status)));
+                       file_free(req, fsp);
+                       return status;
+               }
+       } else {
+               fsp->fh->fd = -1;
+               DEBUG(10, ("Not opening Directory %s\n",
+                       smb_fname_str_dbg(smb_dname)));
        }
 
        status = vfs_stat_fsp(fsp);
@@ -3174,8 +3711,18 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       /* Ensure there was no race condition. */
-       if (!check_same_stat(&smb_dname->st, &fsp->fsp_name->st)) {
+       if(!S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
+               DEBUG(5,("open_directory: %s is not a directory !\n",
+                        smb_fname_str_dbg(smb_dname)));
+                fd_close(fsp);
+                file_free(req, fsp);
+               return NT_STATUS_NOT_A_DIRECTORY;
+       }
+
+       /* Ensure there was no race condition.  We need to check
+        * dev/inode but not permissions, as these can change
+        * legitimately */
+       if (!check_same_dev_ino(&smb_dname->st, &fsp->fsp_name->st)) {
                DEBUG(5,("open_directory: stat struct differs for "
                        "directory %s.\n",
                        smb_fname_str_dbg(smb_dname)));
@@ -3213,8 +3760,10 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       if (!set_share_mode(lck, fsp, get_current_uid(conn),
-                           req ? req->mid : 0, NO_OPLOCK)) {
+       ok = set_share_mode(lck, fsp, get_current_uid(conn),
+                           req ? req->mid : 0, NO_OPLOCK,
+                           UINT32_MAX);
+       if (!ok) {
                TALLOC_FREE(lck);
                fd_close(fsp);
                file_free(req, fsp);
@@ -3240,6 +3789,18 @@ static NTSTATUS open_directory(connection_struct *conn,
                }
        }
 
+       {
+               /*
+                * Deal with other opens having a modified write time. Is this
+                * possible for directories?
+                */
+               struct timespec write_time = get_share_mode_write_time(lck);
+
+               if (!null_timespec(write_time)) {
+                       update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
+               }
+       }
+
        TALLOC_FREE(lck);
 
        if (pinfo) {
@@ -3267,12 +3828,14 @@ NTSTATUS create_directory(connection_struct *conn, struct smb_request *req,
                FILE_DIRECTORY_FILE,                    /* create_options */
                FILE_ATTRIBUTE_DIRECTORY,               /* file_attributes */
                0,                                      /* oplock_request */
+               NULL,                                   /* lease */
                0,                                      /* allocation_size */
                0,                                      /* private_flags */
                NULL,                                   /* sd */
                NULL,                                   /* ea_list */
                &fsp,                                   /* result */
-               NULL);                                  /* pinfo */
+               NULL,                                   /* pinfo */
+               NULL, NULL);                            /* create context */
 
        if (NT_STATUS_IS_OK(status)) {
                close_file(req, fsp, NORMAL_CLOSE);
@@ -3325,8 +3888,11 @@ void msg_file_was_renamed(struct messaging_context *msg,
                stream_name = NULL;
        }
 
-       smb_fname = synthetic_smb_fname(talloc_tos(), base_name,
-                                       stream_name, NULL);
+       smb_fname = synthetic_smb_fname(talloc_tos(),
+                                       base_name,
+                                       stream_name,
+                                       NULL,
+                                       0);
        if (smb_fname == NULL) {
                return;
        }
@@ -3372,8 +3938,8 @@ void msg_file_was_renamed(struct messaging_context *msg,
  * If that works, delete them all by setting the delete on close and close.
  */
 
-NTSTATUS open_streams_for_delete(connection_struct *conn,
-                                       const char *fname)
+static NTSTATUS open_streams_for_delete(connection_struct *conn,
+                                       const struct smb_filename *smb_fname)
 {
        struct stream_struct *stream_info = NULL;
        files_struct **streams = NULL;
@@ -3382,7 +3948,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        TALLOC_CTX *frame = talloc_stackframe();
        NTSTATUS status;
 
-       status = vfs_streaminfo(conn, NULL, fname, talloc_tos(),
+       status = vfs_streaminfo(conn, NULL, smb_fname, talloc_tos(),
                                &num_streams, &stream_info);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)
@@ -3414,30 +3980,34 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        }
 
        for (i=0; i<num_streams; i++) {
-               struct smb_filename *smb_fname;
+               struct smb_filename *smb_fname_cp;
 
                if (strequal(stream_info[i].name, "::$DATA")) {
                        streams[i] = NULL;
                        continue;
                }
 
-               smb_fname = synthetic_smb_fname(
-                       talloc_tos(), fname, stream_info[i].name, NULL);
-               if (smb_fname == NULL) {
+               smb_fname_cp = synthetic_smb_fname(talloc_tos(),
+                                       smb_fname->base_name,
+                                       stream_info[i].name,
+                                       NULL,
+                                       (smb_fname->flags &
+                                               ~SMB_FILENAME_POSIX_PATH));
+               if (smb_fname_cp == NULL) {
                        status = NT_STATUS_NO_MEMORY;
                        goto fail;
                }
 
-               if (SMB_VFS_STAT(conn, smb_fname) == -1) {
+               if (SMB_VFS_STAT(conn, smb_fname_cp) == -1) {
                        DEBUG(10, ("Unable to stat stream: %s\n",
-                                  smb_fname_str_dbg(smb_fname)));
+                                  smb_fname_str_dbg(smb_fname_cp)));
                }
 
                status = SMB_VFS_CREATE_FILE(
                         conn,                  /* conn */
                         NULL,                  /* req */
                         0,                     /* root_dir_fid */
-                        smb_fname,             /* fname */
+                        smb_fname_cp,          /* fname */
                         DELETE_ACCESS,         /* access_mask */
                         (FILE_SHARE_READ |     /* share_access */
                             FILE_SHARE_WRITE | FILE_SHARE_DELETE),
@@ -3445,22 +4015,24 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
                         0,                     /* create_options */
                         FILE_ATTRIBUTE_NORMAL, /* file_attributes */
                         0,                     /* oplock_request */
+                        NULL,                  /* lease */
                         0,                     /* allocation_size */
                         NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* private_flags */
                         NULL,                  /* sd */
                         NULL,                  /* ea_list */
                         &streams[i],           /* result */
-                        NULL);                 /* pinfo */
+                        NULL,                  /* pinfo */
+                        NULL, NULL);           /* create context */
 
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("Could not open stream %s: %s\n",
-                                  smb_fname_str_dbg(smb_fname),
+                                  smb_fname_str_dbg(smb_fname_cp),
                                   nt_errstr(status)));
 
-                       TALLOC_FREE(smb_fname);
+                       TALLOC_FREE(smb_fname_cp);
                        break;
                }
-               TALLOC_FREE(smb_fname);
+               TALLOC_FREE(smb_fname_cp);
        }
 
        /*
@@ -3500,7 +4072,8 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        const struct dom_sid *group_sid = NULL;
        uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL);
        struct security_token *token = fsp->conn->session_info->security_token;
-       bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn));
+       bool inherit_owner =
+           (lp_inherit_owner(SNUM(fsp->conn)) == INHERIT_OWNER_WINDOWS_AND_UNIX);
        bool inheritable_components = false;
        bool try_builtin_administrators = false;
        const struct dom_sid *BA_U_sid = NULL;
@@ -3509,14 +4082,25 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        const struct dom_sid *SY_U_sid = NULL;
        const struct dom_sid *SY_G_sid = NULL;
        size_t size = 0;
+       struct smb_filename *parent_smb_fname = NULL;
 
        if (!parent_dirname(frame, fsp->fsp_name->base_name, &parent_name, NULL)) {
                TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
+       parent_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                               parent_name,
+                                               NULL,
+                                               NULL,
+                                               fsp->fsp_name->flags);
+
+       if (parent_smb_fname == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
 
        status = SMB_VFS_GET_NT_ACL(fsp->conn,
-                                   parent_name,
+                                   parent_smb_fname,
                                    (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
                                    frame,
                                    &parent_desc);
@@ -3684,6 +4268,263 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        return status;
 }
 
+/*
+ * If we already have a lease, it must match the new file id. [MS-SMB2]
+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is
+ * used for a different file name.
+ */
+
+struct lease_match_state {
+       /* Input parameters. */
+       TALLOC_CTX *mem_ctx;
+       const char *servicepath;
+       const struct smb_filename *fname;
+       bool file_existed;
+       struct file_id id;
+       /* Return parameters. */
+       uint32_t num_file_ids;
+       struct file_id *ids;
+       NTSTATUS match_status;
+};
+
+/*************************************************************
+ File doesn't exist but this lease key+guid is already in use.
+
+ This is only allowable in the dynamic share case where the
+ service path must be different.
+
+ There is a small race condition here in the multi-connection
+ case where a client sends two create calls on different connections,
+ where the file doesn't exist and one smbd creates the leases_db
+ entry first, but this will get fixed by the multichannel cleanup
+ when all identical client_guids get handled by a single smbd.
+**************************************************************/
+
+static void lease_match_parser_new_file(
+       uint32_t num_files,
+       const struct leases_db_file *files,
+       struct lease_match_state *state)
+{
+       uint32_t i;
+
+       for (i = 0; i < num_files; i++) {
+               const struct leases_db_file *f = &files[i];
+               if (strequal(state->servicepath, f->servicepath)) {
+                       state->match_status = NT_STATUS_INVALID_PARAMETER;
+                       return;
+               }
+       }
+
+       /* Dynamic share case. Break leases on all other files. */
+       state->match_status = leases_db_copy_file_ids(state->mem_ctx,
+                                       num_files,
+                                       files,
+                                       &state->ids);
+       if (!NT_STATUS_IS_OK(state->match_status)) {
+               return;
+       }
+
+       state->num_file_ids = num_files;
+       state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+       return;
+}
+
+static void lease_match_parser(
+       uint32_t num_files,
+       const struct leases_db_file *files,
+       void *private_data)
+{
+       struct lease_match_state *state =
+               (struct lease_match_state *)private_data;
+       uint32_t i;
+
+       if (!state->file_existed) {
+               /*
+                * Deal with name mismatch or
+                * possible dynamic share case separately
+                * to make code clearer.
+                */
+               lease_match_parser_new_file(num_files,
+                                               files,
+                                               state);
+               return;
+       }
+
+       /* File existed. */
+       state->match_status = NT_STATUS_OK;
+
+       for (i = 0; i < num_files; i++) {
+               const struct leases_db_file *f = &files[i];
+
+               /* Everything should be the same. */
+               if (!file_id_equal(&state->id, &f->id)) {
+                       /* This should catch all dynamic share cases. */
+                       state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+                       break;
+               }
+               if (!strequal(f->servicepath, state->servicepath)) {
+                       state->match_status = NT_STATUS_INVALID_PARAMETER;
+                       break;
+               }
+               if (!strequal(f->base_name, state->fname->base_name)) {
+                       state->match_status = NT_STATUS_INVALID_PARAMETER;
+                       break;
+               }
+               if (!strequal(f->stream_name, state->fname->stream_name)) {
+                       state->match_status = NT_STATUS_INVALID_PARAMETER;
+                       break;
+               }
+       }
+
+       if (NT_STATUS_IS_OK(state->match_status)) {
+               /*
+                * Common case - just opening another handle on a
+                * file on a non-dynamic share.
+                */
+               return;
+       }
+
+       if (NT_STATUS_EQUAL(state->match_status, NT_STATUS_INVALID_PARAMETER)) {
+               /* Mismatched path. Error back to client. */
+               return;
+       }
+
+       /*
+        * File id mismatch. Dynamic share case NT_STATUS_OPLOCK_NOT_GRANTED.
+        * Don't allow leases.
+        */
+
+       state->match_status = leases_db_copy_file_ids(state->mem_ctx,
+                                       num_files,
+                                       files,
+                                       &state->ids);
+       if (!NT_STATUS_IS_OK(state->match_status)) {
+               return;
+       }
+
+       state->num_file_ids = num_files;
+       state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+       return;
+}
+
+static NTSTATUS lease_match(connection_struct *conn,
+                           struct smb_request *req,
+                           struct smb2_lease_key *lease_key,
+                           const char *servicepath,
+                           const struct smb_filename *fname,
+                           uint16_t *p_version,
+                           uint16_t *p_epoch)
+{
+       struct smbd_server_connection *sconn = req->sconn;
+       TALLOC_CTX *tos = talloc_tos();
+       struct lease_match_state state = {
+               .mem_ctx = tos,
+               .servicepath = servicepath,
+               .fname = fname,
+               .match_status = NT_STATUS_OK
+       };
+       uint32_t i;
+       NTSTATUS status;
+
+       state.file_existed = VALID_STAT(fname->st);
+       if (state.file_existed) {
+               state.id = vfs_file_id_from_sbuf(conn, &fname->st);
+       } else {
+               memset(&state.id, '\0', sizeof(state.id));
+       }
+
+       status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
+                                lease_key, lease_match_parser, &state);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * Not found or error means okay: We can make the lease pass
+                */
+               return NT_STATUS_OK;
+       }
+       if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+               /*
+                * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller
+                * deal with it.
+                */
+               return state.match_status;
+       }
+
+       /* We have to break all existing leases. */
+       for (i = 0; i < state.num_file_ids; i++) {
+               struct share_mode_lock *lck;
+               struct share_mode_data *d;
+               uint32_t j;
+
+               if (file_id_equal(&state.ids[i], &state.id)) {
+                       /* Don't need to break our own file. */
+                       continue;
+               }
+
+               lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+               if (lck == NULL) {
+                       /* Race condition - file already closed. */
+                       continue;
+               }
+               d = lck->data;
+               for (j=0; j<d->num_share_modes; j++) {
+                       struct share_mode_entry *e = &d->share_modes[j];
+                       uint32_t e_lease_type = get_lease_type(d, e);
+                       struct share_mode_lease *l = NULL;
+
+                       if (share_mode_stale_pid(d, j)) {
+                               continue;
+                       }
+
+                       if (e->op_type == LEASE_OPLOCK) {
+                               l = &lck->data->leases[e->lease_idx];
+                               if (!smb2_lease_key_equal(&l->lease_key,
+                                                         lease_key)) {
+                                       continue;
+                               }
+                               *p_epoch = l->epoch;
+                               *p_version = l->lease_version;
+                       }
+
+                       if (e_lease_type == SMB2_LEASE_NONE) {
+                               continue;
+                       }
+
+                       send_break_message(conn->sconn->msg_ctx, e,
+                                          SMB2_LEASE_NONE);
+
+                       /*
+                        * Windows 7 and 8 lease clients
+                        * are broken in that they will not
+                        * respond to lease break requests
+                        * whilst waiting for an outstanding
+                        * open request on that lease handle
+                        * on the same TCP connection, due
+                        * to holding an internal inode lock.
+                        *
+                        * This means we can't reschedule
+                        * ourselves here, but must return
+                        * from the create.
+                        *
+                        * Work around:
+                        *
+                        * Send the breaks and then return
+                        * SMB2_LEASE_NONE in the lease handle
+                        * to cause them to acknowledge the
+                        * lease break. Consulatation with
+                        * Microsoft engineering confirmed
+                        * this approach is safe.
+                        */
+
+               }
+               TALLOC_FREE(lck);
+       }
+       /*
+        * Ensure we don't grant anything more so we
+        * never upgrade.
+        */
+       return NT_STATUS_OPLOCK_NOT_GRANTED;
+}
+
 /*
  * Wrapper around open_file_ntcreate and open_directory
  */
@@ -3697,6 +4538,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                                     uint32_t create_options,
                                     uint32_t file_attributes,
                                     uint32_t oplock_request,
+                                    struct smb2_lease *lease,
                                     uint64_t allocation_size,
                                     uint32_t private_flags,
                                     struct security_descriptor *sd,
@@ -3739,6 +4581,26 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                oplock_request |= INTERNAL_OPEN_ONLY;
        }
 
+       if (lease != NULL) {
+               uint16_t epoch = lease->lease_epoch;
+               uint16_t version = lease->lease_version;
+               status = lease_match(conn,
+                               req,
+                               &lease->lease_key,
+                               conn->connectpath,
+                               smb_fname,
+                               &version,
+                               &epoch);
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+                       /* Dynamic share file. No leases and update epoch... */
+                       lease->lease_state = SMB2_LEASE_NONE;
+                       lease->lease_epoch = epoch;
+                       lease->lease_version = version;
+               } else if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+       }
+
        if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
            && (access_mask & DELETE_ACCESS)
            && !is_ntfs_stream_smb_fname(smb_fname)) {
@@ -3746,7 +4608,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                 * We can't open a file with DELETE access if any of the
                 * streams is open without FILE_SHARE_DELETE
                 */
-               status = open_streams_for_delete(conn, smb_fname->base_name);
+               status = open_streams_for_delete(conn, smb_fname);
 
                if (!NT_STATUS_IS_OK(status)) {
                        goto fail;
@@ -3766,7 +4628,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
            && is_ntfs_stream_smb_fname(smb_fname)
            && (!(private_flags & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
-               uint32 base_create_disposition;
+               uint32_t base_create_disposition;
                struct smb_filename *smb_fname_base = NULL;
 
                if (create_options & FILE_DIRECTORY_FILE) {
@@ -3785,8 +4647,10 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 
                /* Create an smb_filename with stream_name == NULL. */
                smb_fname_base = synthetic_smb_fname(talloc_tos(),
-                                                    smb_fname->base_name,
-                                                    NULL, NULL);
+                                               smb_fname->base_name,
+                                               NULL,
+                                               NULL,
+                                               smb_fname->flags);
                if (smb_fname_base == NULL) {
                        status = NT_STATUS_NO_MEMORY;
                        goto fail;
@@ -3795,6 +4659,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                if (SMB_VFS_STAT(conn, smb_fname_base) == -1) {
                        DEBUG(10, ("Unable to stat stream: %s\n",
                                   smb_fname_str_dbg(smb_fname_base)));
+               } else {
+                       /*
+                        * https://bugzilla.samba.org/show_bug.cgi?id=10229
+                        * We need to check if the requested access mask
+                        * could be used to open the underlying file (if
+                        * it existed), as we're passing in zero for the
+                        * access mask to the base filename.
+                        */
+                       status = check_base_file_access(conn,
+                                                       smb_fname_base,
+                                                       access_mask);
+
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DEBUG(10, ("Permission check "
+                                       "for base %s failed: "
+                                       "%s\n", smb_fname->base_name,
+                                       nt_errstr(status)));
+                               goto fail;
+                       }
                }
 
                /* Open the base file. */
@@ -3803,7 +4686,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                                              | FILE_SHARE_WRITE
                                              | FILE_SHARE_DELETE,
                                              base_create_disposition,
-                                             0, 0, 0, 0, 0, NULL, NULL,
+                                             0, 0, 0, NULL, 0, 0, NULL, NULL,
                                              &base_fsp, NULL);
                TALLOC_FREE(smb_fname_base);
 
@@ -3813,7 +4696,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                                   nt_errstr(status)));
                        goto fail;
                }
-               /* we don't need to low level fd */
+               /* we don't need the low level fd */
                fd_close(base_fsp);
        }
 
@@ -3884,6 +4767,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                                            create_options,
                                            file_attributes,
                                            oplock_request,
+                                           lease,
                                            private_flags,
                                            &info,
                                            fsp);
@@ -3942,15 +4826,11 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 
        /* Save the requested allocation size. */
        if ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN)) {
-               if (allocation_size
-                   && (allocation_size > fsp->fsp_name->st.st_ex_size)) {
+               if ((allocation_size > fsp->fsp_name->st.st_ex_size)
+                   && !(fsp->is_directory))
+               {
                        fsp->initial_allocation_size = smb_roundup(
                                fsp->conn, allocation_size);
-                       if (fsp->is_directory) {
-                               /* Can't set allocation size on a directory. */
-                               status = NT_STATUS_ACCESS_DENIED;
-                               goto fail;
-                       }
                        if (vfs_allocate_file_space(
                                    fsp, fsp->initial_allocation_size) == -1) {
                                status = NT_STATUS_DISK_FULL;
@@ -4008,6 +4888,17 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
+       if ((conn->fs_capabilities & FILE_FILE_COMPRESSION)
+        && (create_options & FILE_NO_COMPRESSION)
+        && (info == FILE_WAS_CREATED)) {
+               status = SMB_VFS_SET_COMPRESSION(conn, fsp, fsp,
+                                                COMPRESSION_FORMAT_NONE);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(1, ("failed to disable compression: %s\n",
+                                 nt_errstr(status)));
+               }
+       }
+
        DEBUG(10, ("create_file_unixpath: info=%d\n", info));
 
        *result = fsp;
@@ -4052,6 +4943,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
        files_struct *dir_fsp;
        char *parent_fname = NULL;
        char *new_base_name = NULL;
+       uint32_t ucf_flags = ((req != NULL && req->posix_pathnames) ?
+                       UCF_POSIX_PATHNAMES : 0);
        NTSTATUS status;
 
        if (root_dir_fid == 0 || !smb_fname) {
@@ -4146,7 +5039,7 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
                                conn,
                                req->flags2 & FLAGS2_DFS_PATHNAMES,
                                new_base_name,
-                               0,
+                               ucf_flags,
                                NULL,
                                smb_fname_out);
        if (!NT_STATUS_IS_OK(status)) {
@@ -4169,12 +5062,15 @@ NTSTATUS create_file_default(connection_struct *conn,
                             uint32_t create_options,
                             uint32_t file_attributes,
                             uint32_t oplock_request,
+                            struct smb2_lease *lease,
                             uint64_t allocation_size,
                             uint32_t private_flags,
                             struct security_descriptor *sd,
                             struct ea_list *ea_list,
                             files_struct **result,
-                            int *pinfo)
+                            int *pinfo,
+                            const struct smb2_create_blobs *in_context_blobs,
+                            struct smb2_create_blobs *out_context_blobs)
 {
        int info = FILE_WAS_OPENED;
        files_struct *fsp = NULL;
@@ -4260,7 +5156,7 @@ NTSTATUS create_file_default(connection_struct *conn,
                        status = NT_STATUS_NOT_A_DIRECTORY;
                        goto fail;
                }
-               if (lp_posix_pathnames()) {
+               if (req != NULL && req->posix_pathnames) {
                        ret = SMB_VFS_LSTAT(conn, smb_fname);
                } else {
                        ret = SMB_VFS_STAT(conn, smb_fname);
@@ -4275,7 +5171,7 @@ NTSTATUS create_file_default(connection_struct *conn,
        status = create_file_unixpath(
                conn, req, smb_fname, access_mask, share_access,
                create_disposition, create_options, file_attributes,
-               oplock_request, allocation_size, private_flags,
+               oplock_request, lease, allocation_size, private_flags,
                sd, ea_list,
                &fsp, &info);