smbd: Don't store num_read_oplocks in brlock.tdb
[obnox/samba/samba-obnox.git] / source3 / smbd / open.c
index d845ec2a03b0286720a1ae6d1c92a85ac46ee42f..c15e8f67d90473aeb7ee1f96122e2c66c638f1ea 100644 (file)
@@ -4,6 +4,7 @@
    Copyright (C) Andrew Tridgell 1992-1998
    Copyright (C) Jeremy Allison 2001-2004
    Copyright (C) Volker Lendecke 2005
+   Copyright (C) Ralph Boehme 2017
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -28,7 +29,7 @@
 #include "fake_file.h"
 #include "../libcli/security/security.h"
 #include "../librpc/gen_ndr/ndr_security.h"
-#include "../librpc/gen_ndr/open_files.h"
+#include "../librpc/gen_ndr/ndr_open_files.h"
 #include "../librpc/gen_ndr/idmap.h"
 #include "../librpc/gen_ndr/ioctl.h"
 #include "passdb/lookup_sid.h"
@@ -45,6 +46,13 @@ struct deferred_open_record {
         bool delayed_for_oplocks;
        bool async_open;
         struct file_id id;
+
+       /*
+        * Timer for async opens, needed because they don't use a watch on
+        * a locking.tdb record. This is currently only used for real async
+        * opens and just terminates smbd if the async open times out.
+        */
+       struct tevent_timer *te;
 };
 
 /****************************************************************************
@@ -245,21 +253,29 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        struct security_descriptor *parent_sd = NULL;
        uint32_t access_granted = 0;
        struct smb_filename *parent_smb_fname = NULL;
+       struct share_mode_lock *lck = NULL;
+       struct file_id id = {0};
+       uint32_t name_hash;
+       bool delete_on_close_set;
+       int ret;
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       if (!parent_dirname(talloc_tos(),
+       if (!parent_dirname(frame,
                                smb_fname->base_name,
                                &parent_dir,
                                NULL)) {
-               return NT_STATUS_NO_MEMORY;
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
        }
 
-       parent_smb_fname = synthetic_smb_fname(talloc_tos(),
+       parent_smb_fname = synthetic_smb_fname(frame,
                                parent_dir,
                                NULL,
                                NULL,
                                smb_fname->flags);
        if (parent_smb_fname == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               status = NT_STATUS_NO_MEMORY;
+               goto out;
        }
 
        if (get_current_uid(conn) == (uid_t)0) {
@@ -268,13 +284,14 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                        "on %s. Granting 0x%x\n",
                        smb_fname_str_dbg(smb_fname),
                        (unsigned int)access_mask ));
-               return NT_STATUS_OK;
+               status = NT_STATUS_OK;
+               goto out;
        }
 
        status = SMB_VFS_GET_NT_ACL(conn,
                                parent_smb_fname,
                                SECINFO_DACL,
-                                   talloc_tos(),
+                               frame,
                                &parent_sd);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -282,7 +299,7 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                        "%s with error %s\n",
                        parent_dir,
                        nt_errstr(status)));
-               return status;
+               goto out;
        }
 
        /*
@@ -309,10 +326,49 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                        access_mask,
                        access_granted,
                        nt_errstr(status) ));
-               return status;
+               goto out;
        }
 
-       return NT_STATUS_OK;
+       if (!(access_mask & (SEC_DIR_ADD_FILE | SEC_DIR_ADD_SUBDIR))) {
+               status = NT_STATUS_OK;
+               goto out;
+       }
+       if (!lp_check_parent_directory_delete_on_close(SNUM(conn))) {
+               status = NT_STATUS_OK;
+               goto out;
+       }
+
+       /* Check if the directory has delete-on-close set */
+       ret = SMB_VFS_STAT(conn, parent_smb_fname);
+       if (ret != 0) {
+               status = map_nt_error_from_unix(errno);
+               goto out;
+       }
+
+       id = SMB_VFS_FILE_ID_CREATE(conn, &parent_smb_fname->st);
+
+       status = file_name_hash(conn, parent_smb_fname->base_name, &name_hash);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto out;
+       }
+
+       lck = get_existing_share_mode_lock(frame, id);
+       if (lck == NULL) {
+               status = NT_STATUS_OK;
+               goto out;
+       }
+
+       delete_on_close_set = is_delete_on_close_set(lck, name_hash);
+       if (delete_on_close_set) {
+               status = NT_STATUS_DELETE_PENDING;
+               goto out;
+       }
+
+       status = NT_STATUS_OK;
+
+out:
+       TALLOC_FREE(frame);
+       return status;
 }
 
 /****************************************************************************
@@ -355,6 +411,326 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn,
                                        access_mask);
 }
 
+/****************************************************************************
+ Handle differing symlink errno's
+****************************************************************************/
+
+static int link_errno_convert(int err)
+{
+#if defined(ENOTSUP) && defined(OSF1)
+       /* handle special Tru64 errno */
+       if (err == ENOTSUP) {
+               err = ELOOP;
+       }
+#endif /* ENOTSUP */
+#ifdef EFTYPE
+       /* fix broken NetBSD errno */
+       if (err == EFTYPE) {
+               err = ELOOP;
+       }
+#endif /* EFTYPE */
+       /* fix broken FreeBSD errno */
+       if (err == EMLINK) {
+               err = ELOOP;
+       }
+       return err;
+}
+
+static int non_widelink_open(struct connection_struct *conn,
+                       const struct smb_filename *conn_rootdir_fname,
+                       files_struct *fsp,
+                       struct smb_filename *smb_fname,
+                       int flags,
+                       mode_t mode,
+                       unsigned int link_depth);
+
+/****************************************************************************
+ Follow a symlink in userspace.
+****************************************************************************/
+
+static int process_symlink_open(struct connection_struct *conn,
+                       const struct smb_filename *conn_rootdir_fname,
+                       files_struct *fsp,
+                       struct smb_filename *smb_fname,
+                       int flags,
+                       mode_t mode,
+                       unsigned int link_depth)
+{
+       int fd = -1;
+       char *link_target = NULL;
+       struct smb_filename target_fname = {0};
+       int link_len = -1;
+       struct smb_filename *oldwd_fname = NULL;
+       size_t rootdir_len = 0;
+       struct smb_filename *resolved_fname = NULL;
+       char *resolved_name = NULL;
+       bool matched = false;
+       int saved_errno = 0;
+
+       /*
+        * Ensure we don't get stuck in a symlink loop.
+        */
+       link_depth++;
+       if (link_depth >= 20) {
+               errno = ELOOP;
+               goto out;
+       }
+
+       /* Allocate space for the link target. */
+       link_target = talloc_array(talloc_tos(), char, PATH_MAX);
+       if (link_target == NULL) {
+               errno = ENOMEM;
+               goto out;
+       }
+
+       /* Read the link target. */
+       link_len = SMB_VFS_READLINK(conn,
+                               smb_fname,
+                               link_target,
+                               PATH_MAX - 1);
+       if (link_len == -1) {
+               goto out;
+       }
+
+       /* Ensure it's at least null terminated. */
+       link_target[link_len] = '\0';
+       target_fname = (struct smb_filename){ .base_name = link_target };
+
+       /* Convert to an absolute path. */
+       resolved_fname = SMB_VFS_REALPATH(conn, talloc_tos(), &target_fname);
+       if (resolved_fname == NULL) {
+               goto out;
+       }
+       resolved_name = resolved_fname->base_name;
+
+       /*
+        * We know conn_rootdir starts with '/' and
+        * does not end in '/'. FIXME ! Should we
+        * smb_assert this ?
+        */
+       rootdir_len = strlen(conn_rootdir_fname->base_name);
+
+       matched = (strncmp(conn_rootdir_fname->base_name,
+                               resolved_name,
+                               rootdir_len) == 0);
+       if (!matched) {
+               errno = EACCES;
+               goto out;
+       }
+
+       /*
+        * Turn into a path relative to the share root.
+        */
+       if (resolved_name[rootdir_len] == '\0') {
+               /* Link to the root of the share. */
+               TALLOC_FREE(smb_fname->base_name);
+               smb_fname->base_name = talloc_strdup(smb_fname, ".");
+       } else if (resolved_name[rootdir_len] == '/') {
+               TALLOC_FREE(smb_fname->base_name);
+               smb_fname->base_name = talloc_strdup(smb_fname,
+                                       &resolved_name[rootdir_len+1]);
+       } else {
+               errno = EACCES;
+               goto out;
+       }
+
+       if (smb_fname->base_name == NULL) {
+               errno = ENOMEM;
+               goto out;
+       }
+
+       oldwd_fname = vfs_GetWd(talloc_tos(), conn);
+       if (oldwd_fname == NULL) {
+               goto out;
+       }
+
+       /* Ensure we operate from the root of the share. */
+       if (vfs_ChDir(conn, conn_rootdir_fname) == -1) {
+               goto out;
+       }
+
+       /* And do it all again.. */
+       fd = non_widelink_open(conn,
+                               conn_rootdir_fname,
+                               fsp,
+                               smb_fname,
+                               flags,
+                               mode,
+                               link_depth);
+       if (fd == -1) {
+               saved_errno = errno;
+       }
+
+  out:
+
+       TALLOC_FREE(resolved_fname);
+       TALLOC_FREE(link_target);
+       if (oldwd_fname != NULL) {
+               int ret = vfs_ChDir(conn, oldwd_fname);
+               if (ret == -1) {
+                       smb_panic("unable to get back to old directory\n");
+               }
+               TALLOC_FREE(oldwd_fname);
+       }
+       if (saved_errno != 0) {
+               errno = saved_errno;
+       }
+       return fd;
+}
+
+/****************************************************************************
+ Non-widelink open.
+****************************************************************************/
+
+static int non_widelink_open(struct connection_struct *conn,
+                       const struct smb_filename *conn_rootdir_fname,
+                       files_struct *fsp,
+                       struct smb_filename *smb_fname,
+                       int flags,
+                       mode_t mode,
+                       unsigned int link_depth)
+{
+       NTSTATUS status;
+       int fd = -1;
+       struct smb_filename *smb_fname_rel = NULL;
+       int saved_errno = 0;
+       struct smb_filename *oldwd_fname = NULL;
+       char *parent_dir = NULL;
+       struct smb_filename parent_dir_fname = {0};
+       const char *final_component = NULL;
+       bool is_directory = false;
+       bool ok;
+
+#ifdef O_DIRECTORY
+       if (flags & O_DIRECTORY) {
+               is_directory = true;
+       }
+#endif
+
+       if (is_directory) {
+               parent_dir = talloc_strdup(talloc_tos(), smb_fname->base_name);
+               if (parent_dir == NULL) {
+                       saved_errno = errno;
+                       goto out;
+               }
+
+               final_component = ".";
+       } else {
+               ok = parent_dirname(talloc_tos(),
+                                   smb_fname->base_name,
+                                   &parent_dir,
+                                   &final_component);
+               if (!ok) {
+                       saved_errno = errno;
+                       goto out;
+               }
+       }
+
+       parent_dir_fname = (struct smb_filename) { .base_name = parent_dir };
+
+       oldwd_fname = vfs_GetWd(talloc_tos(), conn);
+       if (oldwd_fname == NULL) {
+               goto out;
+       }
+
+       /* Pin parent directory in place. */
+       if (vfs_ChDir(conn, &parent_dir_fname) == -1) {
+               goto out;
+       }
+
+       smb_fname_rel = synthetic_smb_fname(talloc_tos(),
+                               final_component,
+                               smb_fname->stream_name,
+                               &smb_fname->st,
+                               smb_fname->flags);
+       if (smb_fname_rel == NULL) {
+               saved_errno = ENOMEM;
+               goto out;
+       }
+
+       /* Ensure the relative path is below the share. */
+       status = check_reduced_name(conn, &parent_dir_fname, smb_fname_rel);
+       if (!NT_STATUS_IS_OK(status)) {
+               saved_errno = map_errno_from_nt_status(status);
+               goto out;
+       }
+
+       flags |= O_NOFOLLOW;
+
+       {
+               struct smb_filename *tmp_name = fsp->fsp_name;
+               fsp->fsp_name = smb_fname_rel;
+               fd = SMB_VFS_OPEN(conn, smb_fname_rel, fsp, flags, mode);
+               fsp->fsp_name = tmp_name;
+       }
+
+       if (fd == -1) {
+               saved_errno = link_errno_convert(errno);
+               /*
+                * Trying to open a symlink to a directory with O_NOFOLLOW and
+                * O_DIRECTORY can return either of ELOOP and ENOTDIR. So
+                * ENOTDIR really means: might be a symlink, but we're not sure.
+                * In this case, we just assume there's a symlink. If we were
+                * wrong, process_symlink_open() will return EINVAL. We check
+                * this below, and fall back to returning the initial
+                * saved_errno.
+                *
+                * BUG: https://bugzilla.samba.org/show_bug.cgi?id=12860
+                */
+               if (saved_errno == ELOOP || saved_errno == ENOTDIR) {
+                       if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) {
+                               /* Never follow symlinks on posix open. */
+                               goto out;
+                       }
+                       if (!lp_follow_symlinks(SNUM(conn))) {
+                               /* Explicitly no symlinks. */
+                               goto out;
+                       }
+                       /*
+                        * We may have a symlink. Follow in userspace
+                        * to ensure it's under the share definition.
+                        */
+                       fd = process_symlink_open(conn,
+                                       conn_rootdir_fname,
+                                       fsp,
+                                       smb_fname_rel,
+                                       flags,
+                                       mode,
+                                       link_depth);
+                       if (fd == -1) {
+                               if (saved_errno == ENOTDIR &&
+                                               errno == EINVAL) {
+                                       /*
+                                        * O_DIRECTORY on neither a directory,
+                                        * nor a symlink. Just return
+                                        * saved_errno from initial open()
+                                        */
+                                       goto out;
+                               }
+                               saved_errno =
+                                       link_errno_convert(errno);
+                       }
+               }
+       }
+
+  out:
+
+       TALLOC_FREE(parent_dir);
+       TALLOC_FREE(smb_fname_rel);
+
+       if (oldwd_fname != NULL) {
+               int ret = vfs_ChDir(conn, oldwd_fname);
+               if (ret == -1) {
+                       smb_panic("unable to get back to old directory\n");
+               }
+               TALLOC_FREE(oldwd_fname);
+       }
+       if (saved_errno != 0) {
+               errno = saved_errno;
+       }
+       return fd;
+}
+
 /****************************************************************************
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
@@ -367,8 +743,7 @@ NTSTATUS fd_open(struct connection_struct *conn,
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
 
-#ifdef O_NOFOLLOW
-       /* 
+       /*
         * Never follow symlinks on a POSIX client. The
         * client should be doing this.
         */
@@ -376,29 +751,50 @@ NTSTATUS fd_open(struct connection_struct *conn,
        if ((fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) || !lp_follow_symlinks(SNUM(conn))) {
                flags |= O_NOFOLLOW;
        }
-#endif
 
-       fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
-       if (fsp->fh->fd == -1) {
-               int posix_errno = errno;
-#ifdef O_NOFOLLOW
-#if defined(ENOTSUP) && defined(OSF1)
-               /* handle special Tru64 errno */
-               if (errno == ENOTSUP) {
-                       posix_errno = ELOOP;
+       /* Ensure path is below share definition. */
+       if (!lp_widelinks(SNUM(conn))) {
+               struct smb_filename *conn_rootdir_fname = NULL;
+               const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn,
+                                               smb_fname);
+               int saved_errno = 0;
+
+               if (conn_rootdir == NULL) {
+                       return NT_STATUS_NO_MEMORY;
                }
-#endif /* ENOTSUP */
-#ifdef EFTYPE
-               /* fix broken NetBSD errno */
-               if (errno == EFTYPE) {
-                       posix_errno = ELOOP;
+               conn_rootdir_fname = synthetic_smb_fname(talloc_tos(),
+                                               conn_rootdir,
+                                               NULL,
+                                               NULL,
+                                               0);
+               if (conn_rootdir_fname == NULL) {
+                       return NT_STATUS_NO_MEMORY;
                }
-#endif /* EFTYPE */
-               /* fix broken FreeBSD errno */
-               if (errno == EMLINK) {
-                       posix_errno = ELOOP;
+
+               /*
+                * Only follow symlinks within a share
+                * definition.
+                */
+               fsp->fh->fd = non_widelink_open(conn,
+                                       conn_rootdir_fname,
+                                       fsp,
+                                       smb_fname,
+                                       flags,
+                                       mode,
+                                       0);
+               if (fsp->fh->fd == -1) {
+                       saved_errno = errno;
+               }
+               TALLOC_FREE(conn_rootdir_fname);
+               if (saved_errno != 0) {
+                       errno = saved_errno;
                }
-#endif /* O_NOFOLLOW */
+       } else {
+               fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+       }
+
+       if (fsp->fh->fd == -1) {
+               int posix_errno = link_errno_convert(errno);
                status = map_nt_error_from_unix(posix_errno);
                if (errno == EMFILE) {
                        static time_t last_warned = 0L;
@@ -508,14 +904,14 @@ void change_file_owner_to_parent(connection_struct *conn,
        TALLOC_FREE(smb_fname_parent);
 }
 
-NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
-                                      const char *inherit_from_dir,
-                                      const char *fname,
-                                      SMB_STRUCT_STAT *psbuf)
+static NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
+                                       const char *inherit_from_dir,
+                                       struct smb_filename *smb_dname,
+                                       SMB_STRUCT_STAT *psbuf)
 {
        struct smb_filename *smb_fname_parent;
        struct smb_filename *smb_fname_cwd = NULL;
-       char *saved_dir = NULL;
+       struct smb_filename *saved_dir_fname = NULL;
        TALLOC_CTX *ctx = talloc_tos();
        NTSTATUS status = NT_STATUS_OK;
        int ret;
@@ -546,8 +942,8 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
           should work on any UNIX (thanks tridge :-). JRA.
        */
 
-       saved_dir = vfs_GetWd(ctx,conn);
-       if (!saved_dir) {
+       saved_dir_fname = vfs_GetWd(ctx,conn);
+       if (!saved_dir_fname) {
                status = map_nt_error_from_unix(errno);
                DEBUG(0,("change_dir_owner_to_parent: failed to get "
                         "current working directory. Error was %s\n",
@@ -556,11 +952,11 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
        }
 
        /* Chdir into the new path. */
-       if (vfs_ChDir(conn, fname) == -1) {
+       if (vfs_ChDir(conn, smb_dname) == -1) {
                status = map_nt_error_from_unix(errno);
                DEBUG(0,("change_dir_owner_to_parent: failed to change "
                         "current working directory to %s. Error "
-                        "was %s\n", fname, strerror(errno) ));
+                        "was %s\n", smb_dname->base_name, strerror(errno) ));
                goto chdir;
        }
 
@@ -575,7 +971,7 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                status = map_nt_error_from_unix(errno);
                DEBUG(0,("change_dir_owner_to_parent: failed to stat "
                         "directory '.' (%s) Error was %s\n",
-                        fname, strerror(errno)));
+                        smb_dname->base_name, strerror(errno)));
                goto chdir;
        }
 
@@ -584,7 +980,8 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
            smb_fname_cwd->st.st_ex_ino != psbuf->st_ex_ino) {
                DEBUG(0,("change_dir_owner_to_parent: "
                         "device/inode on directory %s changed. "
-                        "Refusing to chown !\n", fname ));
+                        "Refusing to chown !\n",
+                       smb_dname->base_name ));
                status = NT_STATUS_ACCESS_DENIED;
                goto chdir;
        }
@@ -593,7 +990,7 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                /* Already this uid - no need to change. */
                DEBUG(10,("change_dir_owner_to_parent: directory %s "
                        "is already owned by uid %d\n",
-                       fname,
+                       smb_dname->base_name,
                        (int)smb_fname_cwd->st.st_ex_uid ));
                status = NT_STATUS_OK;
                goto chdir;
@@ -609,20 +1006,23 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                status = map_nt_error_from_unix(errno);
                DEBUG(10,("change_dir_owner_to_parent: failed to chown "
                          "directory %s to parent directory uid %u. "
-                         "Error was %s\n", fname,
+                         "Error was %s\n",
+                         smb_dname->base_name,
                          (unsigned int)smb_fname_parent->st.st_ex_uid,
                          strerror(errno) ));
        } else {
                DEBUG(10,("change_dir_owner_to_parent: changed ownership of new "
                        "directory %s to parent directory uid %u.\n",
-                       fname, (unsigned int)smb_fname_parent->st.st_ex_uid ));
+                       smb_dname->base_name,
+                       (unsigned int)smb_fname_parent->st.st_ex_uid ));
                /* Ensure the uid entry is updated. */
                psbuf->st_ex_uid = smb_fname_parent->st.st_ex_uid;
        }
 
  chdir:
-       vfs_ChDir(conn,saved_dir);
+       vfs_ChDir(conn, saved_dir_fname);
  out:
+       TALLOC_FREE(saved_dir_fname);
        TALLOC_FREE(smb_fname_parent);
        TALLOC_FREE(smb_fname_cwd);
        return status;
@@ -806,7 +1206,10 @@ static NTSTATUS open_file(files_struct *fsp,
                local_flags = (flags & ~O_ACCMODE)|O_RDWR;
        }
 
-       if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) ||
+       if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|
+                                FILE_APPEND_DATA|FILE_EXECUTE|
+                                WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|
+                                READ_CONTROL_ACCESS))||
            (!file_existed && (local_flags & O_CREAT)) ||
            ((local_flags & O_TRUNC) == O_TRUNC) ) {
                const char *wild;
@@ -915,7 +1318,7 @@ static NTSTATUS open_file(files_struct *fsp,
                         * 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
+                        * could imagine this happens elsewhere
                         * too. With blocking file descriptors this
                         * does not happen.
                         */
@@ -952,7 +1355,7 @@ static NTSTATUS open_file(files_struct *fsp,
                        /* Inherit the ACL if required */
                        if (lp_inherit_permissions(SNUM(conn))) {
                                inherit_access_posix_acl(conn, parent_dir,
-                                                        smb_fname->base_name,
+                                                        smb_fname,
                                                         unx_mode);
                                need_re_stat = true;
                        }
@@ -1072,10 +1475,6 @@ static bool share_conflict(struct share_mode_entry *entry,
                  (unsigned int)entry->private_options));
 
        if (server_id_is_disconnected(&entry->pid)) {
-               /*
-                * note: cleanup should have been done by
-                * delay_for_batch_oplocks()
-                */
                return false;
        }
 
@@ -1149,6 +1548,7 @@ sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (u
 
 #if defined(DEVELOPER)
 static void validate_my_share_entries(struct smbd_server_connection *sconn,
+                                     const struct file_id id,
                                      int num,
                                      struct share_mode_entry *share_entry)
 {
@@ -1168,11 +1568,11 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                return;
        }
 
-       fsp = file_find_dif(sconn, share_entry->id,
-                           share_entry->share_file_id);
+       fsp = file_find_dif(sconn, id, share_entry->share_file_id);
        if (!fsp) {
-               DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
-                        share_mode_str(talloc_tos(), num, share_entry) ));
+               DBG_ERR("PANIC : %s\n",
+                       share_mode_str(talloc_tos(), num, &id,
+                                      share_entry));
                smb_panic("validate_my_share_entries: Cannot match a "
                          "share entry with an open file\n");
        }
@@ -1186,8 +1586,9 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
  panic:
        {
                char *str;
-               DEBUG(0,("validate_my_share_entries: PANIC : %s\n",
-                        share_mode_str(talloc_tos(), num, share_entry) ));
+               DBG_ERR("validate_my_share_entries: PANIC : %s\n",
+                       share_mode_str(talloc_tos(), num, &id,
+                                      share_entry));
                str = talloc_asprintf(talloc_tos(),
                        "validate_my_share_entries: "
                        "file %s, oplock_type = 0x%x, op_type = 0x%x\n",
@@ -1259,7 +1660,7 @@ static NTSTATUS open_mode_check(connection_struct *conn,
 
 #if defined(DEVELOPER)
        for(i = 0; i < lck->data->num_share_modes; i++) {
-               validate_my_share_entries(conn->sconn, i,
+               validate_my_share_entries(conn->sconn, lck->data->id, i,
                                          &lck->data->share_modes[i]);
        }
 #endif
@@ -1293,29 +1694,40 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  */
 
 NTSTATUS send_break_message(struct messaging_context *msg_ctx,
-                                  const struct share_mode_entry *exclusive,
-                                  uint16_t break_to)
+                           const struct file_id *id,
+                           const struct share_mode_entry *exclusive,
+                           uint16_t break_to)
 {
+       struct oplock_break_message msg = {
+               .id = *id,
+               .share_file_id = exclusive->share_file_id,
+               .break_to = break_to,
+       };
+       enum ndr_err_code ndr_err;
+       DATA_BLOB blob;
        NTSTATUS status;
-       char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
-       struct server_id_buf tmp;
-
-       DEBUG(10, ("Sending break request to PID %s\n",
-                  server_id_str_buf(exclusive->pid, &tmp)));
 
-       /* Create the message. */
-       share_mode_entry_to_message(msg, exclusive);
+       if (DEBUGLVL(10)) {
+               struct server_id_buf buf;
+               DBG_DEBUG("Sending break message to %s\n",
+                         server_id_str_buf(exclusive->pid, &buf));
+               NDR_PRINT_DEBUG(oplock_break_message, &msg);
+       }
 
-       /* 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);
+       ndr_err = ndr_push_struct_blob(
+               &blob,
+               talloc_tos(),
+               &msg,
+               (ndr_push_flags_fn_t)ndr_push_oplock_break_message);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DBG_WARNING("ndr_push_oplock_break_message failed: %s\n",
+                           ndr_errstr(ndr_err));
+               return ndr_map_error2ntstatus(ndr_err);
+       }
 
-       status = messaging_send_buf(msg_ctx, exclusive->pid,
-                                   MSG_SMB_BREAK_REQUEST,
-                                   (uint8_t *)msg, sizeof(msg));
+       status = messaging_send(
+               msg_ctx, exclusive->pid, MSG_SMB_BREAK_REQUEST, &blob);
+       TALLOC_FREE(blob.data);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(3, ("Could not send oplock break message: %s\n",
                          nt_errstr(status)));
@@ -1440,6 +1852,8 @@ static bool delay_for_oplock(files_struct *fsp,
        uint32_t i;
        bool delay = false;
        bool will_overwrite;
+       const uint32_t delay_mask = have_sharing_violation ?
+               SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
 
        if ((oplock_request & INTERNAL_OPEN_ONLY) ||
            is_stat_open(fsp->access_mask)) {
@@ -1459,50 +1873,51 @@ static bool delay_for_oplock(files_struct *fsp,
 
        for (i=0; i<d->num_share_modes; i++) {
                struct share_mode_entry *e = &d->share_modes[i];
-               struct share_mode_lease *l = NULL;
+               bool e_is_lease = (e->op_type == LEASE_OPLOCK);
                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];
-               }
+               bool lease_is_breaking = false;
+
+               if (e_is_lease) {
+                       NTSTATUS status;
+
+                       if (lease != NULL) {
+                               bool our_lease = smb2_lease_equal(
+                                       fsp_client_guid(fsp),
+                                       &lease->lease_key,
+                                       &e->client_guid,
+                                       &e->lease_key);
+                               if (our_lease) {
+                                       DBG_DEBUG("Ignoring our own lease\n");
+                                       continue;
+                               }
+                       }
 
-               if (have_sharing_violation) {
-                       delay_mask = SMB2_LEASE_HANDLE;
-               } else {
-                       delay_mask = SMB2_LEASE_WRITE;
+                       status = leases_db_get(
+                               &e->client_guid,
+                               &e->lease_key,
+                               &fsp->file_id,
+                               NULL, /* current_state */
+                               &lease_is_breaking,
+                               NULL, /* breaking_to_requested */
+                               NULL, /* breaking_to_required */
+                               NULL, /* lease_version */
+                               NULL); /* epoch */
+                       SMB_ASSERT(NT_STATUS_IS_OK(status));
                }
 
                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;
+                       break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_READ);
                }
 
                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) {
+                       if (lease_is_breaking) {
                                delay = true;
                        }
                        continue;
@@ -1521,7 +1936,7 @@ static bool delay_for_oplock(files_struct *fsp,
                        break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
                }
 
-               if (e->op_type != LEASE_OPLOCK) {
+               if (!e_is_lease) {
                        /*
                         * Oplocks only support breaking to R or NONE.
                         */
@@ -1530,15 +1945,14 @@ static bool delay_for_oplock(files_struct *fsp,
 
                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);
+               send_break_message(fsp->conn->sconn->msg_ctx, &fsp->file_id,
+                                  e, break_to);
                if (e_lease_type & delay_mask) {
                        delay = true;
                }
-               if (l != NULL && l->breaking && !first_open_attempt) {
+               if (lease_is_breaking && !first_open_attempt) {
                        delay = true;
                }
-               continue;
        }
 
        return delay;
@@ -1555,29 +1969,11 @@ static bool file_has_brlocks(files_struct *fsp)
        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;
-               }
-       }
-
-       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)
+                                uint32_t current_state,
+                                uint16_t lease_version,
+                                uint16_t lease_epoch)
 {
        struct files_struct *fsp;
 
@@ -1610,100 +2006,124 @@ struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
        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;
+       new_fsp->lease->lease.lease_state = 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;
+       new_fsp->lease->lease.lease_version = lease_version;
+       new_fsp->lease->lease.lease_epoch = lease_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)
+static NTSTATUS try_lease_upgrade(struct files_struct *fsp,
+                                 struct share_mode_lock *lck,
+                                 const struct GUID *client_guid,
+                                 const struct smb2_lease *lease,
+                                 uint32_t granted)
 {
-       struct share_mode_data *d = lck->data;
-       const struct GUID *client_guid = fsp_client_guid(fsp);
-       struct share_mode_lease *tmp;
+       bool do_upgrade;
+       uint32_t current_state, breaking_to_requested, breaking_to_required;
+       bool breaking;
+       uint16_t lease_version, epoch;
+       uint32_t existing, requested;
        NTSTATUS status;
-       int idx;
 
-       idx = find_share_mode_lease(d, client_guid, &lease->lease_key);
+       status = leases_db_get(
+               client_guid,
+               &lease->lease_key,
+               &fsp->file_id,
+               &current_state,
+               &breaking,
+               &breaking_to_requested,
+               &breaking_to_required,
+               &lease_version,
+               &epoch);
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
 
-       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,
+               current_state,
+               lease_version,
+               epoch);
+       if (fsp->lease == NULL) {
+               DEBUG(1, ("Did not find existing lease for file %s\n",
+                         fsp_str_dbg(fsp)));
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               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;
-               }
+       /*
+        * Upgrade only if the requested lease is a strict upgrade.
+        */
+       existing = current_state;
+       requested = lease->lease_state;
 
-               *p_lease_idx = idx;
+       /*
+        * Tricky: This test makes sure that "requested" is a
+        * strict bitwise superset of "existing".
+        */
+       do_upgrade = ((existing & requested) == existing);
 
-               /*
-                * Upgrade only if the requested lease is a strict upgrade.
-                */
-               existing = l->current_state;
-               requested = lease->lease_state;
+       /*
+        * Upgrade only if there's a change.
+        */
+       do_upgrade &= (granted != existing);
 
-               /*
-                * Tricky: This test makes sure that "requested" is a
-                * strict bitwise superset of "existing".
-                */
-               do_upgrade = ((existing & requested) == existing);
+       /*
+        * Upgrade only if other leases don't prevent what was asked
+        * for.
+        */
+       do_upgrade &= (granted == requested);
 
-               /*
-                * Upgrade only if there's a change.
-                */
-               do_upgrade &= (granted != existing);
+       /*
+        * only upgrade if we are not in breaking state
+        */
+       do_upgrade &= !breaking;
 
-               /*
-                * Upgrade only if other leases don't prevent what was asked
-                * for.
-                */
-               do_upgrade &= (granted == requested);
+       DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+                  "granted=%"PRIu32", do_upgrade=%d\n",
+                  existing, requested, granted, (int)do_upgrade));
 
-               /*
-                * only upgrade if we are not in breaking state
-                */
-               do_upgrade &= !l->breaking;
+       if (do_upgrade) {
+               NTSTATUS set_status;
 
-               DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
-                          "granted=%"PRIu32", do_upgrade=%d\n",
-                          existing, requested, granted, (int)do_upgrade));
+               current_state = granted;
+               epoch += 1;
 
-               if (do_upgrade) {
-                       l->current_state = granted;
-                       l->epoch += 1;
-               }
+               set_status = leases_db_set(
+                       client_guid,
+                       &lease->lease_key,
+                       current_state,
+                       breaking,
+                       breaking_to_requested,
+                       breaking_to_required,
+                       lease_version,
+                       epoch);
 
-               /* Ensure we're in sync with current lease state. */
-               fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
-               return NT_STATUS_OK;
+               if (!NT_STATUS_IS_OK(set_status)) {
+                       DBG_DEBUG("leases_db_set failed: %s\n",
+                                 nt_errstr(set_status));
+                       return set_status;
+               }
        }
 
-       /*
-        * Create new lease
-        */
+       fsp_lease_update(fsp);
 
-       tmp = talloc_realloc(d, d->leases, struct share_mode_lease,
-                            d->num_leases+1);
-       if (tmp == NULL) {
-               /*
-                * See [MS-SMB2]
-                */
-               return NT_STATUS_INSUFFICIENT_RESOURCES;
-       }
-       d->leases = tmp;
+       return NT_STATUS_OK;
+}
+
+static NTSTATUS grant_new_fsp_lease(struct files_struct *fsp,
+                                   struct share_mode_lock *lck,
+                                   const struct GUID *client_guid,
+                                   const struct smb2_lease *lease,
+                                   uint32_t granted)
+{
+       struct share_mode_data *d = lck->data;
+       NTSTATUS status;
 
        fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease);
        if (fsp->lease == NULL) {
@@ -1716,19 +2136,12 @@ static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
        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->lease->lease.lease_state,
+                              fsp->lease->lease.lease_version,
+                              fsp->lease->lease.lease_epoch,
                               fsp->conn->connectpath,
                               fsp->fsp_name->base_name,
                               fsp->fsp_name->stream_name);
@@ -1739,12 +2152,29 @@ static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
                return NT_STATUS_INSUFFICIENT_RESOURCES;
        }
 
-       d->num_leases += 1;
        d->modified = true;
 
        return NT_STATUS_OK;
 }
 
+static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
+                               struct share_mode_lock *lck,
+                               const struct smb2_lease *lease,
+                               uint32_t granted)
+{
+       const struct GUID *client_guid = fsp_client_guid(fsp);
+       NTSTATUS status;
+
+       status = try_lease_upgrade(fsp, lck, client_guid, lease, granted);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               status = grant_new_fsp_lease(
+                       fsp, lck, client_guid, lease, granted);
+       }
+
+       return status;
+}
+
 static bool is_same_lease(const files_struct *fsp,
                          const struct share_mode_data *d,
                          const struct share_mode_entry *e,
@@ -1759,8 +2189,28 @@ static bool is_same_lease(const files_struct *fsp,
 
        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);
+                               &e->client_guid,
+                               &e->lease_key);
+}
+
+static int map_lease_type_to_oplock(uint32_t lease_type)
+{
+       int result = NO_OPLOCK;
+
+       switch (lease_type) {
+       case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+               result = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+               break;
+       case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+               result = EXCLUSIVE_OPLOCK;
+               break;
+       case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+       case SMB2_LEASE_READ:
+               result = LEVEL_II_OPLOCK;
+               break;
+       }
+
+       return result;
 }
 
 static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
@@ -1774,7 +2224,8 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
        bool got_oplock = false;
        uint32_t i;
        uint32_t granted;
-       uint32_t lease_idx = UINT32_MAX;
+       const struct GUID *client_guid = NULL;
+       const struct smb2_lease_key *lease_key = NULL;
        bool ok;
        NTSTATUS status;
 
@@ -1865,34 +2316,23 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 
                fsp->oplock_type = LEASE_OPLOCK;
 
-               status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
-                                        granted);
+               status = grant_fsp_lease(fsp, lck, lease, granted);
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
 
                }
                *lease = fsp->lease->lease;
+
+               lease_key = &fsp->lease->lease.lease_key;
+               client_guid = fsp_client_guid(fsp);
+
                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;
-               }
+               fsp->oplock_type = map_lease_type_to_oplock(granted);
 
                status = set_file_oplock(fsp);
                if (!NT_STATUS_IS_OK(status)) {
@@ -1903,18 +2343,20 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
                }
        }
 
-       ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
-                           req ? req->mid : 0,
-                           fsp->oplock_type,
-                           lease_idx);
+       ok = set_share_mode(
+               lck,
+               fsp,
+               get_current_uid(fsp->conn),
+               req ? req->mid : 0,
+               fsp->oplock_type,
+               client_guid,
+               lease_key);
        if (!ok) {
                return NT_STATUS_NO_MEMORY;
        }
 
-       ok = update_num_read_oplocks(fsp, lck);
-       if (!ok) {
-               del_share_mode(lck, fsp);
-               return NT_STATUS_INTERNAL_ERROR;
+       if (granted & SMB2_LEASE_READ) {
+               lck->data->flags |= SHARE_MODE_HAS_READ_LEASE;
        }
 
        DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
@@ -1960,72 +2402,72 @@ struct defer_open_state {
 
 static void defer_open_done(struct tevent_req *req);
 
-/****************************************************************************
- Handle the 1 second delay in returning a SHARING_VIOLATION error.
-****************************************************************************/
-
+/**
+ * Defer an open and watch a locking.tdb record
+ *
+ * This defers an open that gets rescheduled once the locking.tdb record watch
+ * is triggered by a change to the record.
+ *
+ * It is used to defer opens that triggered an oplock break and for the SMB1
+ * sharing violation delay.
+ **/
 static void defer_open(struct share_mode_lock *lck,
                       struct timeval request_time,
                       struct timeval timeout,
                       struct smb_request *req,
                       bool delayed_for_oplocks,
-                      bool async_open,
                       struct file_id id)
 {
        struct deferred_open_record *open_rec = NULL;
        struct timeval abs_timeout;
+       struct defer_open_state *watch_state;
+       struct tevent_req *watch_req;
+       bool ok;
 
        abs_timeout = timeval_sum(&request_time, &timeout);
 
        DBG_DEBUG("request time [%s] timeout [%s] mid [%" PRIu64 "] "
-                 "delayed_for_oplocks [%s] async_open [%s] file_id [%s]\n",
+                 "delayed_for_oplocks [%s] file_id [%s]\n",
                  timeval_string(talloc_tos(), &request_time, false),
                  timeval_string(talloc_tos(), &abs_timeout, false),
                  req->mid,
                  delayed_for_oplocks ? "yes" : "no",
-                 async_open ? "yes" : "no",
                  file_id_string_tos(&id));
 
        open_rec = deferred_open_record_create(delayed_for_oplocks,
-                                              async_open,
+                                              false,
                                               id);
        if (open_rec == NULL) {
                TALLOC_FREE(lck);
                exit_server("talloc failed");
        }
 
-       if (lck) {
-               struct defer_open_state *watch_state;
-               struct tevent_req *watch_req;
-               bool ret;
-
-               watch_state = talloc(open_rec, struct defer_open_state);
-               if (watch_state == NULL) {
-                       exit_server("talloc failed");
-               }
-               watch_state->xconn = req->xconn;
-               watch_state->mid = req->mid;
+       watch_state = talloc(open_rec, struct defer_open_state);
+       if (watch_state == NULL) {
+               exit_server("talloc failed");
+       }
+       watch_state->xconn = req->xconn;
+       watch_state->mid = req->mid;
 
-               DEBUG(10, ("defering mid %llu\n",
-                          (unsigned long long)req->mid));
+       DBG_DEBUG("defering mid %" PRIu64 "\n", req->mid);
 
-               watch_req = dbwrap_watched_watch_send(
-                       watch_state, req->sconn->ev_ctx, lck->data->record,
-                       (struct server_id){0});
-               if (watch_req == NULL) {
-                       exit_server("Could not watch share mode record");
-               }
-               tevent_req_set_callback(watch_req, defer_open_done,
-                                       watch_state);
+       watch_req = dbwrap_watched_watch_send(watch_state,
+                                             req->sconn->ev_ctx,
+                                             lck->data->record,
+                                             (struct server_id){0});
+       if (watch_req == NULL) {
+               exit_server("Could not watch share mode record");
+       }
+       tevent_req_set_callback(watch_req, defer_open_done, watch_state);
 
-               ret = tevent_req_set_endtime(
-                       watch_req, req->sconn->ev_ctx,
-                       abs_timeout);
-               SMB_ASSERT(ret);
+       ok = tevent_req_set_endtime(watch_req, req->sconn->ev_ctx, abs_timeout);
+       if (!ok) {
+               exit_server("tevent_req_set_endtime failed");
        }
 
-       if (!push_deferred_open_message_smb(req, request_time, timeout,
-                                           open_rec->id, open_rec)) {
+       ok = push_deferred_open_message_smb(req, request_time, timeout,
+                                           open_rec->id, open_rec);
+       if (!ok) {
                TALLOC_FREE(lck);
                exit_server("push_deferred_open_message_smb failed");
        }
@@ -2038,8 +2480,7 @@ static void defer_open_done(struct tevent_req *req)
        NTSTATUS status;
        bool ret;
 
-       status = dbwrap_watched_watch_recv(req, talloc_tos(), NULL, NULL,
-                                         NULL);
+       status = dbwrap_watched_watch_recv(req, NULL, NULL);
        TALLOC_FREE(req);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
@@ -2058,19 +2499,40 @@ static void defer_open_done(struct tevent_req *req)
 }
 
 /**
- * Reschedule an open for immediate execution
+ * Actually attempt the kernel oplock polling open.
+ */
+
+static void kernel_oplock_poll_open_timer(struct tevent_context *ev,
+                                     struct tevent_timer *te,
+                                     struct timeval current_time,
+                                     void *private_data)
+{
+       bool ok;
+       struct smb_request *req = (struct smb_request *)private_data;
+
+       ok = schedule_deferred_open_message_smb(req->xconn, req->mid);
+       if (!ok) {
+               exit_server("schedule_deferred_open_message_smb failed");
+       }
+       DBG_DEBUG("kernel_oplock_poll_open_timer fired. Retying open !\n");
+}
+
+/**
+ * Reschedule an open for 1 second from now, if not timed out.
  **/
-static void retry_open(struct timeval request_time,
+static void setup_kernel_oplock_poll_open(struct timeval request_time,
                       struct smb_request *req,
                       struct file_id id)
 {
-       struct deferred_open_record *open_rec = NULL;
+
        bool ok;
+       struct deferred_open_record *open_rec = NULL;
+       /* Maximum wait time. */
+       struct timeval timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
 
-       DBG_DEBUG("request time [%s] mid [%" PRIu64 "] file_id [%s]\n",
-                 timeval_string(talloc_tos(), &request_time, false),
-                 req->mid,
-                 file_id_string_tos(&id));
+       if (request_timed_out(request_time, timeout)) {
+               return;
+       }
 
        open_rec = deferred_open_record_create(false, false, id);
        if (open_rec == NULL) {
@@ -2079,17 +2541,30 @@ static void retry_open(struct timeval request_time,
 
        ok = push_deferred_open_message_smb(req,
                                            request_time,
-                                           timeval_set(0, 0),
+                                           timeout,
                                            id,
                                            open_rec);
        if (!ok) {
                exit_server("push_deferred_open_message_smb failed");
        }
 
-       ok = schedule_deferred_open_message_smb(req->xconn, req->mid);
-       if (!ok) {
-               exit_server("schedule_deferred_open_message_smb failed");
+       /*
+        * As this timer event is owned by req, it will
+        * disappear if req it talloc_freed.
+        */
+       open_rec->te = tevent_add_timer(req->sconn->ev_ctx,
+                                       req,
+                                       timeval_current_ofs(1, 0),
+                                       kernel_oplock_poll_open_timer,
+                                       req);
+       if (open_rec->te == NULL) {
+               exit_server("tevent_add_timer failed");
        }
+
+       DBG_DEBUG("poll request time [%s] mid [%" PRIu64 "] file_id [%s]\n",
+                 timeval_string(talloc_tos(), &request_time, false),
+                 req->mid,
+                 file_id_string_tos(&id));
 }
 
 /****************************************************************************
@@ -2099,7 +2574,6 @@ static void retry_open(struct timeval request_time,
 static bool open_match_attributes(connection_struct *conn,
                                  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)
 {
@@ -2116,10 +2590,9 @@ static bool open_match_attributes(connection_struct *conn,
        }
 
        DEBUG(10,("open_match_attributes: old_dos_attr = 0x%x, "
-                 "existing_unx_mode = 0%o, new_dos_attr = 0x%x "
+                 "new_dos_attr = 0x%x "
                  "returned_unx_mode = 0%o\n",
                  (unsigned int)old_dos_attr,
-                 (unsigned int)existing_unx_mode,
                  (unsigned int)new_dos_attr,
                  (unsigned int)*returned_unx_mode ));
 
@@ -2229,26 +2702,51 @@ static void schedule_defer_open(struct share_mode_lock *lck,
                return;
        }
 
-       defer_open(lck, request_time, timeout, req, true, false, id);
+       defer_open(lck, request_time, timeout, req, true, id);
 }
 
 /****************************************************************************
  Reschedule an open call that went asynchronous.
 ****************************************************************************/
 
+static void schedule_async_open_timer(struct tevent_context *ev,
+                                     struct tevent_timer *te,
+                                     struct timeval current_time,
+                                     void *private_data)
+{
+       exit_server("async open timeout");
+}
+
 static void schedule_async_open(struct timeval request_time,
                                struct smb_request *req)
 {
-       struct timeval timeout;
-
-       timeout = timeval_set(20, 0);
+       struct deferred_open_record *open_rec = NULL;
+       struct timeval timeout = timeval_set(20, 0);
+       bool ok;
 
        if (request_timed_out(request_time, timeout)) {
                return;
        }
 
-       defer_open(NULL, request_time, timeout, req,
-                  false, true, (struct file_id){0});
+       open_rec = deferred_open_record_create(false, true, (struct file_id){0});
+       if (open_rec == NULL) {
+               exit_server("deferred_open_record_create failed");
+       }
+
+       ok = push_deferred_open_message_smb(req, request_time, timeout,
+                                           (struct file_id){0}, open_rec);
+       if (!ok) {
+               exit_server("push_deferred_open_message_smb failed");
+       }
+
+       open_rec->te = tevent_add_timer(req->sconn->ev_ctx,
+                                       req,
+                                       timeval_current_ofs(20, 0),
+                                       schedule_async_open_timer,
+                                       open_rec);
+       if (open_rec->te == NULL) {
+               exit_server("tevent_add_timer failed");
+       }
 }
 
 /****************************************************************************
@@ -2623,7 +3121,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
                if (file_existed) {
                        /*
-                        * Only use strored DOS attributes for checks
+                        * Only use stored DOS attributes for checks
                         * against requested attributes (below via
                         * open_match_attributes()), cf bug #11992
                         * for details. -slow
@@ -2718,9 +3216,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
             (create_disposition == FILE_OVERWRITE_IF))) {
                if (!open_match_attributes(conn, existing_dos_attributes,
                                           new_dos_attributes,
-                                          smb_fname->st.st_ex_mode,
                                           unx_mode, &new_unx_mode)) {
-                       DEBUG(5,("open_file_ntcreate: attributes missmatch "
+                       DEBUG(5,("open_file_ntcreate: attributes mismatch "
                                 "for file %s (%x %x) (0%o, 0%o)\n",
                                 smb_fname_str_dbg(smb_fname),
                                 existing_dos_attributes,
@@ -2783,20 +3280,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                flags2 &= ~(O_CREAT|O_TRUNC);
        }
 
-       if (first_open_attempt && lp_kernel_oplocks(SNUM(conn))) {
+       if (lp_kernel_oplocks(SNUM(conn))) {
                /*
                 * With kernel oplocks the open breaking an oplock
                 * blocks until the oplock holder has given up the
-                * oplock or closed the file. We prevent this by first
+                * oplock or closed the file. We prevent this by always
                 * trying to open the file with O_NONBLOCK (see "man
-                * fcntl" on Linux). For the second try, triggered by
-                * an oplock break response, we do not need this
-                * anymore.
+                * fcntl" on Linux).
                 *
-                * This is true under the assumption that only Samba
-                * requests kernel oplocks. Once someone else like
-                * NFSv4 starts to use that API, we will have to
-                * modify this by communicating with the NFSv4 server.
+                * If a process that doesn't use the smbd open files
+                * database or communication methods holds a kernel
+                * oplock we must periodically poll for available open
+                * using O_NONBLOCK.
                 */
                flags2 |= O_NONBLOCK;
        }
@@ -2829,6 +3324,18 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                request_time = fsp->open_time;
        }
 
+       if ((create_options & FILE_DELETE_ON_CLOSE) &&
+                       (flags2 & O_CREAT) &&
+                       !file_existed) {
+               /* Delete on close semantics for new files. */
+               status = can_set_delete_on_close(fsp,
+                                               new_dos_attributes);
+               if (!NT_STATUS_IS_OK(status)) {
+                       fd_close(fsp);
+                       return status;
+               }
+       }
+
        /*
         * Ensure we pay attention to default ACLs on directories if required.
         */
@@ -2875,9 +3382,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
                if (lck == NULL) {
-                       retry_open(request_time, req, fsp->file_id);
-                       DEBUG(10, ("No share mode lock found after "
-                                  "EWOULDBLOCK, retrying sync\n"));
+                       /*
+                        * No oplock from Samba around. Set up a poll every 1
+                        * second to retry a non-blocking open until the time
+                        * expires.
+                        */
+                       setup_kernel_oplock_poll_open(request_time,
+                                               req,
+                                               fsp->file_id);
+                       DBG_DEBUG("No Samba oplock around after EWOULDBLOCK. "
+                               "Retrying with poll\n");
                        return NT_STATUS_SHARING_VIOLATION;
                }
 
@@ -2898,14 +3412,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                }
 
                /*
-                * No oplock from Samba around. Immediately retry with
-                * a blocking open.
+                * No oplock from Samba around. Set up a poll every 1
+                * second to retry a non-blocking open until the time
+                * expires.
                 */
-               retry_open(request_time, req, fsp->file_id);
+               setup_kernel_oplock_poll_open(request_time, req, fsp->file_id);
 
                TALLOC_FREE(lck);
-               DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
-                          "Retrying sync\n"));
+               DBG_DEBUG("No Samba oplock around after EWOULDBLOCK. "
+                       "Retrying with poll\n");
                return NT_STATUS_SHARING_VIOLATION;
        }
 
@@ -2938,15 +3453,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 * in the open file db having the wrong dev/ino key.
                 */
                fd_close(fsp);
-               DEBUG(1,("open_file_ntcreate: file %s - dev/ino mismatch. "
-                       "Old (dev=0x%llu, ino =0x%llu). "
-                       "New (dev=0x%llu, ino=0x%llu). Failing open "
-                       with NT_STATUS_ACCESS_DENIED.\n",
-                        smb_fname_str_dbg(smb_fname),
-                        (unsigned long long)saved_stat.st_ex_dev,
-                        (unsigned long long)saved_stat.st_ex_ino,
-                        (unsigned long long)smb_fname->st.st_ex_dev,
-                        (unsigned long long)smb_fname->st.st_ex_ino));
+               DBG_WARNING("file %s - dev/ino mismatch. "
+                           "Old (dev=%ju, ino=%ju). "
+                           "New (dev=%ju, ino=%ju). Failing open "
+                           "with NT_STATUS_ACCESS_DENIED.\n",
+                           smb_fname_str_dbg(smb_fname),
+                           (uintmax_t)saved_stat.st_ex_dev,
+                           (uintmax_t)saved_stat.st_ex_ino,
+                           (uintmax_t)smb_fname->st.st_ex_dev,
+                           (uintmax_t)smb_fname->st.st_ex_ino);
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -3127,7 +3642,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                        if (!request_timed_out(request_time, timeout)) {
                                defer_open(lck, request_time, timeout, req,
-                                          false, false, id);
+                                          false, id);
                        }
                }
 
@@ -3151,13 +3666,17 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
            (!S_ISFIFO(fsp->fsp_name->st.st_ex_mode))) {
                int ret;
 
-               ret = vfs_set_filelen(fsp, 0);
+               ret = SMB_VFS_FTRUNCATE(fsp, 0);
                if (ret != 0) {
                        status = map_nt_error_from_unix(errno);
                        TALLOC_FREE(lck);
                        fd_close(fsp);
                        return status;
                }
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_SIZE
+                            | FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
        }
 
        /*
@@ -3273,21 +3792,27 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        /* Handle strange delete on close create semantics. */
        if (create_options & FILE_DELETE_ON_CLOSE) {
+               if (!new_file_created) {
+                       status = can_set_delete_on_close(fsp,
+                                        existing_dos_attributes);
 
-               status = can_set_delete_on_close(fsp, new_dos_attributes);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       /* Remember to delete the mode we just added. */
-                       del_share_mode(lck, fsp);
-                       TALLOC_FREE(lck);
-                       fd_close(fsp);
-                       return status;
+                       if (!NT_STATUS_IS_OK(status)) {
+                               /* Remember to delete the mode we just added. */
+                               del_share_mode(lck, fsp);
+                               TALLOC_FREE(lck);
+                               fd_close(fsp);
+                               return status;
+                       }
                }
-               /* Note that here we set the *inital* delete on close flag,
+               /* Note that here we set the *initial* delete on close flag,
                   not the regular one. The magic gets handled in close. */
                fsp->initial_delete_on_close = True;
        }
 
+       if (info == FILE_WAS_CREATED) {
+               smb_fname->st.st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_ITIME;
+       }
+
        if (info != FILE_WAS_OPENED) {
                /* Overwritten files should be initially set as archive */
                if ((info == FILE_WAS_OVERWRITTEN && lp_map_archive(SNUM(conn))) ||
@@ -3317,43 +3842,46 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         */
 
        if (!posix_open && new_file_created && !def_acl) {
-
-               int saved_errno = errno; /* We might get ENOSYS in the next
-                                         * call.. */
-
-               if (SMB_VFS_FCHMOD_ACL(fsp, unx_mode) == -1 &&
-                   errno == ENOSYS) {
-                       errno = saved_errno; /* Ignore ENOSYS */
+               if (unx_mode != smb_fname->st.st_ex_mode) {
+                       int ret = SMB_VFS_FCHMOD(fsp, unx_mode);
+                       if (ret == -1) {
+                               DBG_INFO("failed to reset "
+                                 "attributes of file %s to 0%o\n",
+                                 smb_fname_str_dbg(smb_fname),
+                                 (unsigned int)unx_mode);
+                       }
                }
 
        } else if (new_unx_mode) {
+               /*
+                * We only get here in the case of:
+                *
+                * a). Not a POSIX open.
+                * b). File already existed.
+                * c). File was overwritten.
+                * d). Requested DOS attributes didn't match
+                *     the DOS attributes on the existing file.
+                *
+                * In that case new_unx_mode has been set
+                * equal to the calculated mode (including
+                * possible inheritance of the mode from the
+                * containing directory).
+                *
+                * Note this mode was calculated with the
+                * DOS attribute FILE_ATTRIBUTE_ARCHIVE added,
+                * so the mode change here is suitable for
+                * an overwritten file.
+                */
 
-               int ret = -1;
-
-               /* Attributes need changing. File already existed. */
-
-               {
-                       int saved_errno = errno; /* We might get ENOSYS in the
-                                                 * next call.. */
-                       ret = SMB_VFS_FCHMOD_ACL(fsp, new_unx_mode);
-
-                       if (ret == -1 && errno == ENOSYS) {
-                               errno = saved_errno; /* Ignore ENOSYS */
-                       } else {
-                               DEBUG(5, ("open_file_ntcreate: reset "
-                                         "attributes of file %s to 0%o\n",
-                                         smb_fname_str_dbg(smb_fname),
-                                         (unsigned int)new_unx_mode));
-                               ret = 0; /* Don't do the fchmod below. */
-                       }
-               }
-
-               if ((ret == -1) &&
-                   (SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1))
-                       DEBUG(5, ("open_file_ntcreate: failed to reset "
+               if (new_unx_mode != smb_fname->st.st_ex_mode) {
+                       int ret = SMB_VFS_FCHMOD(fsp, new_unx_mode);
+                       if (ret == -1) {
+                               DBG_INFO("failed to reset "
                                  "attributes of file %s to 0%o\n",
                                  smb_fname_str_dbg(smb_fname),
-                                 (unsigned int)new_unx_mode));
+                                 (unsigned int)new_unx_mode);
+                       }
+               }
        }
 
        {
@@ -3432,6 +3960,8 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                return NT_STATUS_NOT_A_DIRECTORY;
        }
 
+       smb_dname->st.st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_ITIME;
+
        if (lp_store_dos_attributes(SNUM(conn))) {
                if (!posix_open) {
                        file_set_dosmode(conn, smb_dname,
@@ -3442,7 +3972,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
 
        if (lp_inherit_permissions(SNUM(conn))) {
                inherit_access_posix_acl(conn, parent_dir,
-                                        smb_dname->base_name, mode);
+                                        smb_dname, mode);
                need_re_stat = true;
        }
 
@@ -3465,7 +3995,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        /* Change the owner if required. */
        if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
                change_dir_owner_to_parent(conn, parent_dir,
-                                          smb_dname->base_name,
+                                          smb_dname,
                                           &smb_dname->st);
                need_re_stat = true;
        }
@@ -3711,30 +4241,19 @@ static NTSTATUS open_directory(connection_struct *conn,
        */
        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;
-               }
-       } else {
-               fsp->fh->fd = -1;
-               DEBUG(10, ("Not opening Directory %s\n",
-                       smb_fname_str_dbg(smb_dname)));
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_INFO("Could not open fd for "
+                       "%s (%s)\n",
+                       smb_fname_str_dbg(smb_dname),
+                       nt_errstr(status));
+               file_free(req, fsp);
+               return status;
        }
 
        status = vfs_stat_fsp(fsp);
@@ -3793,9 +4312,14 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
-       ok = set_share_mode(lck, fsp, get_current_uid(conn),
-                           req ? req->mid : 0, NO_OPLOCK,
-                           UINT32_MAX);
+       ok = set_share_mode(
+               lck,
+               fsp,
+               get_current_uid(conn),
+               req ? req->mid : 0,
+               NO_OPLOCK,
+               NULL,
+               NULL);
        if (!ok) {
                TALLOC_FREE(lck);
                fd_close(fsp);
@@ -3816,7 +4340,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                }
 
                if (NT_STATUS_IS_OK(status)) {
-                       /* Note that here we set the *inital* delete on close flag,
+                       /* Note that here we set the *initial* delete on close flag,
                           not the regular one. The magic gets handled in close. */
                        fsp->initial_delete_on_close = True;
                }
@@ -3882,87 +4406,89 @@ NTSTATUS create_directory(connection_struct *conn, struct smb_request *req,
  smbd process.
 ****************************************************************************/
 
-void msg_file_was_renamed(struct messaging_context *msg,
+void msg_file_was_renamed(struct messaging_context *msg_ctx,
                          void *private_data,
                          uint32_t msg_type,
-                         struct server_id server_id,
+                         struct server_id src,
                          DATA_BLOB *data)
 {
+       struct file_rename_message *msg = NULL;
+       enum ndr_err_code ndr_err;
        files_struct *fsp;
-       char *frm = (char *)data->data;
-       struct file_id id;
-       const char *sharepath;
-       const char *base_name;
-       const char *stream_name;
        struct smb_filename *smb_fname = NULL;
-       size_t sp_len, bn_len;
-       NTSTATUS status;
        struct smbd_server_connection *sconn =
                talloc_get_type_abort(private_data,
                struct smbd_server_connection);
 
-       if (data->data == NULL
-           || data->length < MSG_FILE_RENAMED_MIN_SIZE + 2) {
-                DEBUG(0, ("msg_file_was_renamed: Got invalid msg len %d\n",
-                         (int)data->length));
-                return;
-        }
-
-       /* Unpack the message. */
-       pull_file_id_24(frm, &id);
-       sharepath = &frm[24];
-       sp_len = strlen(sharepath);
-       base_name = sharepath + sp_len + 1;
-       bn_len = strlen(base_name);
-       stream_name = sharepath + sp_len + 1 + bn_len + 1;
+       msg = talloc(talloc_tos(), struct file_rename_message);
+       if (msg == NULL) {
+               DBG_WARNING("talloc failed\n");
+               return;
+       }
+
+       ndr_err = ndr_pull_struct_blob_all(
+               data,
+               msg,
+               msg,
+               (ndr_pull_flags_fn_t)ndr_pull_file_rename_message);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DBG_DEBUG("ndr_pull_oplock_break_message failed: %s\n",
+                         ndr_errstr(ndr_err));
+               goto out;
+       }
+       if (DEBUGLEVEL >= 10) {
+               struct server_id_buf buf;
+               DBG_DEBUG("Got rename message from %s\n",
+                         server_id_str_buf(src, &buf));
+               NDR_PRINT_DEBUG(file_rename_message, msg);
+       }
 
        /* stream_name must always be NULL if there is no stream. */
-       if (stream_name[0] == '\0') {
-               stream_name = NULL;
+       if ((msg->stream_name != NULL) && (msg->stream_name[0] == '\0')) {
+               msg->stream_name = NULL;
        }
 
-       smb_fname = synthetic_smb_fname(talloc_tos(),
-                                       base_name,
-                                       stream_name,
-                                       NULL,
-                                       0);
+       smb_fname = synthetic_smb_fname(
+               msg, msg->base_name, msg->stream_name, NULL, 0);
        if (smb_fname == NULL) {
-               return;
+               DBG_DEBUG("synthetic_smb_fname failed\n");
+               goto out;
        }
 
-       DEBUG(10,("msg_file_was_renamed: Got rename message for sharepath %s, new name %s, "
-               "file_id %s\n",
-               sharepath, smb_fname_str_dbg(smb_fname),
-               file_id_string_tos(&id)));
-
-       for(fsp = file_find_di_first(sconn, id); fsp;
-           fsp = file_find_di_next(fsp)) {
-               if (memcmp(fsp->conn->connectpath, sharepath, sp_len) == 0) {
+       fsp = file_find_dif(sconn, msg->id, msg->share_file_id);
+       if (fsp == NULL) {
+               DBG_DEBUG("fsp not found\n");
+               goto out;
+       }
 
-                       DEBUG(10,("msg_file_was_renamed: renaming file %s from %s -> %s\n",
-                               fsp_fnum_dbg(fsp), fsp_str_dbg(fsp),
-                               smb_fname_str_dbg(smb_fname)));
-                       status = fsp_set_smb_fname(fsp, smb_fname);
-                       if (!NT_STATUS_IS_OK(status)) {
-                               goto out;
-                       }
-               } else {
-                       /* TODO. JRA. */
-                       /* Now we have the complete path we can work out if this is
-                          actually within this share and adjust newname accordingly. */
-                       DEBUG(10,("msg_file_was_renamed: share mismatch (sharepath %s "
-                               "not sharepath %s) "
-                               "%s from %s -> %s\n",
-                               fsp->conn->connectpath,
-                               sharepath,
-                               fsp_fnum_dbg(fsp),
-                               fsp_str_dbg(fsp),
-                               smb_fname_str_dbg(smb_fname)));
-               }
-        }
+       if (strcmp(fsp->conn->connectpath, msg->servicepath) == 0) {
+               NTSTATUS status;
+               DBG_DEBUG("renaming file %s from %s -> %s\n",
+                         fsp_fnum_dbg(fsp),
+                         fsp_str_dbg(fsp),
+                         smb_fname_str_dbg(smb_fname));
+               status = fsp_set_smb_fname(fsp, smb_fname);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_DEBUG("fsp_set_smb_fname failed: %s\n",
+                                 nt_errstr(status));
+               }
+       } else {
+               /* TODO. JRA. */
+               /*
+                * Now we have the complete path we can work out if
+                * this is actually within this share and adjust
+                * newname accordingly.
+                */
+               DBG_DEBUG("share mismatch (sharepath %s not sharepath %s) "
+                         "%s from %s -> %s\n",
+                         fsp->conn->connectpath,
+                         msg->servicepath,
+                         fsp_fnum_dbg(fsp),
+                         fsp_str_dbg(fsp),
+                         smb_fname_str_dbg(smb_fname));
+       }
  out:
-       TALLOC_FREE(smb_fname);
-       return;
+       TALLOC_FREE(msg);
 }
 
 /*
@@ -4272,7 +4798,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 
        /* If inheritable_components == false,
           se_create_child_secdesc()
-          creates a security desriptor with a NULL dacl
+          creates a security descriptor with a NULL dacl
           entry, but with SEC_DESC_DACL_PRESENT. We need
           to remove that flag. */
 
@@ -4462,8 +4988,6 @@ static NTSTATUS lease_match(connection_struct *conn,
        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,
@@ -4486,6 +5010,7 @@ static NTSTATUS lease_match(connection_struct *conn,
        for (i = 0; i < state.num_file_ids; i++) {
                struct share_mode_lock *lck;
                struct share_mode_data *d;
+               struct share_mode_entry *lease_entry = NULL;
                uint32_t j;
 
                if (file_id_equal(&state.ids[i], &state.id)) {
@@ -4502,27 +5027,24 @@ static NTSTATUS lease_match(connection_struct *conn,
                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,
+                               if (!smb2_lease_key_equal(&e->lease_key,
                                                          lease_key)) {
                                        continue;
                                }
-                               *p_epoch = l->epoch;
-                               *p_version = l->lease_version;
+                               lease_entry = e;
                        }
 
                        if (e_lease_type == SMB2_LEASE_NONE) {
                                continue;
                        }
 
-                       send_break_message(conn->sconn->msg_ctx, e,
+                       send_break_message(conn->sconn->msg_ctx, &d->id, e,
                                           SMB2_LEASE_NONE);
 
                        /*
@@ -4543,12 +5065,31 @@ static NTSTATUS lease_match(connection_struct *conn,
                         * Send the breaks and then return
                         * SMB2_LEASE_NONE in the lease handle
                         * to cause them to acknowledge the
-                        * lease break. Consulatation with
+                        * lease break. Consultation with
                         * Microsoft engineering confirmed
                         * this approach is safe.
                         */
 
                }
+
+               if (lease_entry != NULL) {
+                       status = leases_db_get(
+                               &lease_entry->client_guid,
+                               &lease_entry->lease_key,
+                               &d->id,
+                               NULL, /* current_state */
+                               NULL, /* breaking */
+                               NULL, /* breaking_to_requested */
+                               NULL, /* breaking_to_required */
+                               p_version, /* lease_version */
+                               p_epoch); /* epoch */
+                       if (!NT_STATUS_IS_OK(status)) {
+                               DBG_WARNING("Could not find version/epoch: "
+                                           "%s\n",
+                                           nt_errstr(status));
+                       }
+               }
+
                TALLOC_FREE(lck);
        }
        /*
@@ -4585,11 +5126,11 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        files_struct *fsp = NULL;
        NTSTATUS status;
 
-       DEBUG(10,("create_file_unixpath: access_mask = 0x%x "
+       DBG_DEBUG("create_file_unixpath: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
                  "create_disposition = 0x%x create_options = 0x%x "
                  "oplock_request = 0x%x private_flags = 0x%x "
-                 "ea_list = 0x%p, sd = 0x%p, "
+                 "ea_list = %p, sd = %p, "
                  "fname = %s\n",
                  (unsigned int)access_mask,
                  (unsigned int)file_attributes,
@@ -4598,7 +5139,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                  (unsigned int)create_options,
                  (unsigned int)oplock_request,
                  (unsigned int)private_flags,
-                 ea_list, sd, smb_fname_str_dbg(smb_fname)));
+                 ea_list, sd, smb_fname_str_dbg(smb_fname));
 
        if (create_options & FILE_OPEN_BY_FILE_ID) {
                status = NT_STATUS_NOT_SUPPORTED;
@@ -4617,6 +5158,13 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        if (lease != NULL) {
                uint16_t epoch = lease->lease_epoch;
                uint16_t version = lease->lease_version;
+
+               if (req == NULL) {
+                       DBG_WARNING("Got lease on internal open\n");
+                       status = NT_STATUS_INTERNAL_ERROR;
+                       goto fail;
+               }
+
                status = lease_match(conn,
                                req,
                                &lease->lease_key,
@@ -4658,11 +5206,24 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                goto fail;
        }
 
+       /*
+        * Files or directories can't be opened DELETE_ON_CLOSE without
+        * delete access.
+        * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13358
+        */
+       if (create_options & FILE_DELETE_ON_CLOSE) {
+               if ((access_mask & DELETE_ACCESS) == 0) {
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto fail;
+               }
+       }
+
        if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
            && is_ntfs_stream_smb_fname(smb_fname)
            && (!(private_flags & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
                uint32_t base_create_disposition;
                struct smb_filename *smb_fname_base = NULL;
+               uint32_t base_privflags;
 
                if (create_options & FILE_DIRECTORY_FILE) {
                        status = NT_STATUS_NOT_A_DIRECTORY;
@@ -4713,13 +5274,17 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                        }
                }
 
+               base_privflags = NTCREATEX_OPTIONS_PRIVATE_STREAM_BASEOPEN;
+
                /* Open the base file. */
                status = create_file_unixpath(conn, NULL, smb_fname_base, 0,
                                              FILE_SHARE_READ
                                              | FILE_SHARE_WRITE
                                              | FILE_SHARE_DELETE,
                                              base_create_disposition,
-                                             0, 0, 0, NULL, 0, 0, NULL, NULL,
+                                             0, 0, 0, NULL, 0,
+                                             base_privflags,
+                                             NULL, NULL,
                                              &base_fsp, NULL);
                TALLOC_FREE(smb_fname_base);
 
@@ -4976,8 +5541,7 @@ 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);
+       uint32_t ucf_flags = ucf_flags_from_smb_request(req);
        NTSTATUS status;
 
        if (root_dir_fid == 0 || !smb_fname) {
@@ -5070,10 +5634,10 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
 
        status = filename_convert(req,
                                conn,
-                               req->flags2 & FLAGS2_DFS_PATHNAMES,
                                new_base_name,
                                ucf_flags,
                                NULL,
+                               NULL,
                                smb_fname_out);
        if (!NT_STATUS_IS_OK(status)) {
                goto out;
@@ -5110,12 +5674,12 @@ NTSTATUS create_file_default(connection_struct *conn,
        NTSTATUS status;
        bool stream_name = false;
 
-       DEBUG(10,("create_file: access_mask = 0x%x "
+       DBG_DEBUG("create_file: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
                  "create_disposition = 0x%x create_options = 0x%x "
                  "oplock_request = 0x%x "
                  "private_flags = 0x%x "
-                 "root_dir_fid = 0x%x, ea_list = 0x%p, sd = 0x%p, "
+                 "root_dir_fid = 0x%x, ea_list = %p, sd = %p, "
                  "fname = %s\n",
                  (unsigned int)access_mask,
                  (unsigned int)file_attributes,
@@ -5125,7 +5689,7 @@ NTSTATUS create_file_default(connection_struct *conn,
                  (unsigned int)oplock_request,
                  (unsigned int)private_flags,
                  (unsigned int)root_dir_fid,
-                 ea_list, sd, smb_fname_str_dbg(smb_fname)));
+                 ea_list, sd, smb_fname_str_dbg(smb_fname));
 
        /*
         * Calculate the filename from the root_dir_if if necessary.