smbd: move files_struct.can_write to a bitfield
[gd/samba-autobuild/.git] / source3 / smbd / open.c
index 7797df03e4b87ce41bad114911fe1402cd7edd47..4e0b375237126a2f2f90536fe268d80ffa5030e0 100644 (file)
@@ -612,16 +612,9 @@ static int non_widelink_open(struct connection_struct *conn,
        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) {
+       if (fsp->is_directory) {
                parent_dir = talloc_strdup(talloc_tos(), smb_fname->base_name);
                if (parent_dir == NULL) {
                        saved_errno = errno;
@@ -1327,7 +1320,7 @@ static NTSTATUS open_file(files_struct *fsp,
                         * too. With blocking file descriptors this
                         * does not happen.
                         */
-                       ret = set_blocking(fsp->fh->fd, true);
+                       ret = vfs_set_blocking(fsp, true);
                        if (ret == -1) {
                                status = map_nt_error_from_unix(errno);
                                DBG_WARNING("Could not set fd to blocking: "
@@ -1436,9 +1429,9 @@ static NTSTATUS open_file(files_struct *fsp,
        fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
        fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
        fsp->file_pid = req ? req->smbpid : 0;
-       fsp->can_lock = True;
-       fsp->can_read = ((access_mask & FILE_READ_DATA) != 0);
-       fsp->can_write =
+       fsp->fsp_flags.can_lock = true;
+       fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
+       fsp->fsp_flags.can_write =
                CAN_WRITE(conn) &&
                ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
        fsp->print_file = NULL;
@@ -1451,157 +1444,158 @@ static NTSTATUS open_file(files_struct *fsp,
                fsp->aio_write_behind = True;
        }
 
-       fsp->wcp = NULL; /* Write cache pointer. */
-
        DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
                 conn->session_info->unix_info->unix_name,
                 smb_fname_str_dbg(smb_fname),
-                BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+                BOOLSTR(fsp->fsp_flags.can_read),
+                BOOLSTR(fsp->fsp_flags.can_write),
                 conn->num_files_open));
 
        errno = 0;
        return NT_STATUS_OK;
 }
 
+static bool mask_conflict(
+       uint32_t new_access,
+       uint32_t existing_access,
+       uint32_t access_mask,
+       uint32_t new_sharemode,
+       uint32_t existing_sharemode,
+       uint32_t sharemode_mask)
+{
+       bool want_access = (new_access & access_mask);
+       bool allow_existing = (existing_sharemode & sharemode_mask);
+       bool have_access = (existing_access & access_mask);
+       bool allow_new = (new_sharemode & sharemode_mask);
+
+       if (want_access && !allow_existing) {
+               DBG_DEBUG("Access request 0x%"PRIx32"/0x%"PRIx32" conflicts "
+                         "with existing sharemode 0x%"PRIx32"/0x%"PRIx32"\n",
+                         new_access,
+                         access_mask,
+                         existing_sharemode,
+                         sharemode_mask);
+               return true;
+       }
+       if (have_access && !allow_new) {
+               DBG_DEBUG("Sharemode request 0x%"PRIx32"/0x%"PRIx32" conflicts "
+                         "with existing access 0x%"PRIx32"/0x%"PRIx32"\n",
+                         new_sharemode,
+                         sharemode_mask,
+                         existing_access,
+                         access_mask);
+               return true;
+       }
+       return false;
+}
+
 /****************************************************************************
  Check if we can open a file with a share mode.
  Returns True if conflict, False if not.
 ****************************************************************************/
 
-static bool share_conflict(struct share_mode_entry *entry,
+static bool share_conflict(uint32_t e_access_mask,
+                          uint32_t e_share_access,
                           uint32_t access_mask,
                           uint32_t share_access)
 {
-       DBG_DEBUG("entry->access_mask = 0x%"PRIx32", "
-                 "entry->share_access = 0x%"PRIx32", "
-                 "entry->private_options = 0x%"PRIx32", "
+       const uint32_t conflicting_access =
+               FILE_WRITE_DATA|
+               FILE_APPEND_DATA|
+               FILE_READ_DATA|
+               FILE_EXECUTE|
+               DELETE_ACCESS;
+       bool conflict;
+
+       DBG_DEBUG("existing access_mask = 0x%"PRIx32", "
+                 "existing share access = 0x%"PRIx32", "
                  "access_mask = 0x%"PRIx32", "
                  "share_access = 0x%"PRIx32"\n",
-                 entry->access_mask,
-                 entry->share_access,
-                 entry->private_options,
+                 e_access_mask,
+                 e_share_access,
                  access_mask,
                  share_access);
 
-       if (server_id_is_disconnected(&entry->pid)) {
-               return false;
-       }
-
-       if ((entry->access_mask & (FILE_WRITE_DATA|
-                                  FILE_APPEND_DATA|
-                                  FILE_READ_DATA|
-                                  FILE_EXECUTE|
-                                  DELETE_ACCESS)) == 0) {
+       if ((e_access_mask & conflicting_access) == 0) {
                DBG_DEBUG("No conflict due to "
-                         "entry->access_mask = 0x%"PRIx32"\n",
-                         entry->access_mask);
-               return False;
+                         "existing access_mask = 0x%"PRIx32"\n",
+                         e_access_mask);
+               return false;
        }
-
-       if ((access_mask & (FILE_WRITE_DATA|
-                           FILE_APPEND_DATA|
-                           FILE_READ_DATA|
-                           FILE_EXECUTE|
-                           DELETE_ACCESS)) == 0) {
+       if ((access_mask & conflicting_access) == 0) {
                DBG_DEBUG("No conflict due to access_mask = 0x%"PRIx32"\n",
                          access_mask);
-               return False;
-       }
-
-#if 1 /* JRA TEST - Superdebug. */
-#define CHECK_MASK(num, am, right, sa, share) \
-       DEBUG(10,("share_conflict: [%d] am (0x%x) & right (0x%x) = 0x%x\n", \
-               (unsigned int)(num), (unsigned int)(am), \
-               (unsigned int)(right), (unsigned int)(am)&(right) )); \
-       DEBUG(10,("share_conflict: [%d] sa (0x%x) & share (0x%x) = 0x%x\n", \
-               (unsigned int)(num), (unsigned int)(sa), \
-               (unsigned int)(share), (unsigned int)(sa)&(share) )); \
-       if (((am) & (right)) && !((sa) & (share))) { \
-               DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
-sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
-                       (unsigned int)(share) )); \
-               return True; \
-       }
-#else
-#define CHECK_MASK(num, am, right, sa, share) \
-       if (((am) & (right)) && !((sa) & (share))) { \
-               DEBUG(10,("share_conflict: check %d conflict am = 0x%x, right = 0x%x, \
-sa = 0x%x, share = 0x%x\n", (num), (unsigned int)(am), (unsigned int)(right), (unsigned int)(sa), \
-                       (unsigned int)(share) )); \
-               return True; \
+               return false;
        }
-#endif
 
-       CHECK_MASK(1, entry->access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
-                  share_access, FILE_SHARE_WRITE);
-       CHECK_MASK(2, access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
-                  entry->share_access, FILE_SHARE_WRITE);
+       conflict = mask_conflict(
+               access_mask, e_access_mask, FILE_WRITE_DATA | FILE_APPEND_DATA,
+               share_access, e_share_access, FILE_SHARE_WRITE);
+       conflict |= mask_conflict(
+               access_mask, e_access_mask, FILE_READ_DATA | FILE_EXECUTE,
+               share_access, e_share_access, FILE_SHARE_READ);
+       conflict |= mask_conflict(
+               access_mask, e_access_mask, DELETE_ACCESS,
+               share_access, e_share_access, FILE_SHARE_DELETE);
 
-       CHECK_MASK(3, entry->access_mask, FILE_READ_DATA | FILE_EXECUTE,
-                  share_access, FILE_SHARE_READ);
-       CHECK_MASK(4, access_mask, FILE_READ_DATA | FILE_EXECUTE,
-                  entry->share_access, FILE_SHARE_READ);
-
-       CHECK_MASK(5, entry->access_mask, DELETE_ACCESS,
-                  share_access, FILE_SHARE_DELETE);
-       CHECK_MASK(6, access_mask, DELETE_ACCESS,
-                  entry->share_access, FILE_SHARE_DELETE);
-
-       DEBUG(10,("share_conflict: No conflict.\n"));
-       return False;
+       DBG_DEBUG("conflict=%s\n", conflict ? "true" : "false");
+       return conflict;
 }
 
 #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)
+
+struct validate_my_share_entries_state {
+       struct smbd_server_connection *sconn;
+       struct file_id fid;
+       struct server_id self;
+};
+
+static bool validate_my_share_entries_fn(
+       struct share_mode_entry *e,
+       bool *modified,
+       void *private_data)
 {
-       struct server_id self = messaging_server_id(sconn->msg_ctx);
+       struct validate_my_share_entries_state *state = private_data;
        files_struct *fsp;
 
-       if (!serverid_equal(&self, &share_entry->pid)) {
-               return;
+       if (!server_id_equal(&state->self, &e->pid)) {
+               return false;
        }
 
-       if (share_entry->op_mid == 0) {
+       if (e->op_mid == 0) {
                /* INTERNAL_OPEN_ONLY */
-               return;
-       }
-
-       if (!is_valid_share_mode_entry(share_entry)) {
-               return;
+               return false;
        }
 
-       fsp = file_find_dif(sconn, id, share_entry->share_file_id);
+       fsp = file_find_dif(state->sconn, state->fid, e->share_file_id);
        if (!fsp) {
                DBG_ERR("PANIC : %s\n",
-                       share_mode_str(talloc_tos(), num, &id,
-                                      share_entry));
+                       share_mode_str(talloc_tos(), 0, &state->fid, e));
                smb_panic("validate_my_share_entries: Cannot match a "
                          "share entry with an open file\n");
        }
 
-       if (((uint16_t)fsp->oplock_type) != share_entry->op_type) {
+       if (((uint16_t)fsp->oplock_type) != e->op_type) {
                goto panic;
        }
 
-       return;
+       return false;
 
  panic:
        {
                char *str;
                DBG_ERR("validate_my_share_entries: PANIC : %s\n",
-                       share_mode_str(talloc_tos(), num, &id,
-                                      share_entry));
+                       share_mode_str(talloc_tos(), 0, &state->fid, e));
                str = talloc_asprintf(talloc_tos(),
                        "validate_my_share_entries: "
                        "file %s, oplock_type = 0x%x, op_type = 0x%x\n",
                         fsp->fsp_name->base_name,
                         (unsigned int)fsp->oplock_type,
-                        (unsigned int)share_entry->op_type );
+                        (unsigned int)e->op_type);
                smb_panic(str);
        }
+
+       return false;
 }
 #endif
 
@@ -1616,24 +1610,142 @@ bool is_stat_open(uint32_t access_mask)
                ((access_mask & ~stat_open_bits) == 0));
 }
 
+struct has_delete_on_close_state {
+       bool ret;
+};
+
+static bool has_delete_on_close_fn(
+       struct share_mode_entry *e,
+       bool *modified,
+       void *private_data)
+{
+       struct has_delete_on_close_state *state = private_data;
+       state->ret = !share_entry_stale_pid(e);
+       return state->ret;
+}
+
 static bool has_delete_on_close(struct share_mode_lock *lck,
                                uint32_t name_hash)
 {
-       struct share_mode_data *d = lck->data;
-       uint32_t i;
+       struct has_delete_on_close_state state = { .ret = false };
+       bool ok;
 
-       if (d->num_share_modes == 0) {
+       if (!is_delete_on_close_set(lck, name_hash)) {
                return false;
        }
-       if (!is_delete_on_close_set(lck, name_hash)) {
+
+       ok= share_mode_forall_entries(lck, has_delete_on_close_fn, &state);
+       if (!ok) {
+               DBG_DEBUG("share_mode_forall_entries failed\n");
                return false;
        }
-       for (i=0; i<d->num_share_modes; i++) {
-               if (!share_mode_stale_pid(d, i)) {
-                       return true;
-               }
+       return state.ret;
+}
+
+static void share_mode_flags_get(
+       uint16_t flags,
+       uint32_t *access_mask,
+       uint32_t *share_mode,
+       uint32_t *lease_type)
+{
+       if (access_mask != NULL) {
+               *access_mask =
+                       ((flags & SHARE_MODE_ACCESS_READ) ?
+                        FILE_READ_DATA : 0) |
+                       ((flags & SHARE_MODE_ACCESS_WRITE) ?
+                        FILE_WRITE_DATA : 0) |
+                       ((flags & SHARE_MODE_ACCESS_DELETE) ?
+                        DELETE_ACCESS : 0);
+       }
+       if (share_mode != NULL) {
+               *share_mode =
+                       ((flags & SHARE_MODE_SHARE_READ) ?
+                        FILE_SHARE_READ : 0) |
+                       ((flags & SHARE_MODE_SHARE_WRITE) ?
+                        FILE_SHARE_WRITE : 0) |
+                       ((flags & SHARE_MODE_SHARE_DELETE) ?
+                        FILE_SHARE_DELETE : 0);
+       }
+       if (lease_type != NULL) {
+               *lease_type =
+                       ((flags & SHARE_MODE_LEASE_READ) ?
+                        SMB2_LEASE_READ : 0) |
+                       ((flags & SHARE_MODE_LEASE_WRITE) ?
+                        SMB2_LEASE_WRITE : 0) |
+                       ((flags & SHARE_MODE_LEASE_HANDLE) ?
+                        SMB2_LEASE_HANDLE : 0);
        }
-       return false;
+}
+
+static uint16_t share_mode_flags_set(
+       uint16_t flags,
+       uint32_t access_mask,
+       uint32_t share_mode,
+       uint32_t lease_type)
+{
+       if (access_mask != UINT32_MAX) {
+               flags &= ~(SHARE_MODE_ACCESS_READ|
+                          SHARE_MODE_ACCESS_WRITE|
+                          SHARE_MODE_ACCESS_DELETE);
+               flags |= (access_mask & (FILE_READ_DATA | FILE_EXECUTE)) ?
+                       SHARE_MODE_ACCESS_READ : 0;
+               flags |= (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ?
+                       SHARE_MODE_ACCESS_WRITE : 0;
+               flags |= (access_mask & (DELETE_ACCESS)) ?
+                       SHARE_MODE_ACCESS_DELETE : 0;
+       }
+       if (share_mode != UINT32_MAX) {
+               flags &= ~(SHARE_MODE_SHARE_READ|
+                          SHARE_MODE_SHARE_WRITE|
+                          SHARE_MODE_SHARE_DELETE);
+               flags |= (share_mode & FILE_SHARE_READ) ?
+                       SHARE_MODE_SHARE_READ : 0;
+               flags |= (share_mode & FILE_SHARE_WRITE) ?
+                       SHARE_MODE_SHARE_WRITE : 0;
+               flags |= (share_mode & FILE_SHARE_DELETE) ?
+                       SHARE_MODE_SHARE_DELETE : 0;
+       }
+       if (lease_type != UINT32_MAX) {
+               flags &= ~(SHARE_MODE_LEASE_READ|
+                          SHARE_MODE_LEASE_WRITE|
+                          SHARE_MODE_LEASE_HANDLE);
+               flags |= (lease_type & SMB2_LEASE_READ) ?
+                       SHARE_MODE_LEASE_READ : 0;
+               flags |= (lease_type & SMB2_LEASE_WRITE) ?
+                       SHARE_MODE_LEASE_WRITE : 0;
+               flags |= (lease_type & SMB2_LEASE_HANDLE) ?
+                       SHARE_MODE_LEASE_HANDLE : 0;
+       }
+
+       return flags;
+}
+
+static uint16_t share_mode_flags_restrict(
+       uint16_t flags,
+       uint32_t access_mask,
+       uint32_t share_mode,
+       uint32_t lease_type)
+{
+       uint32_t existing_access_mask, existing_share_mode;
+       uint32_t existing_lease_type;
+       uint16_t ret;
+
+       share_mode_flags_get(
+               flags,
+               &existing_access_mask,
+               &existing_share_mode,
+               &existing_lease_type);
+
+       existing_access_mask |= access_mask;
+       existing_share_mode &= share_mode;
+       existing_lease_type |= lease_type;
+
+       ret = share_mode_flags_set(
+               flags,
+               existing_access_mask,
+               existing_share_mode,
+               existing_lease_type);
+       return ret;
 }
 
 /****************************************************************************
@@ -1642,12 +1754,58 @@ static bool has_delete_on_close(struct share_mode_lock *lck,
  Returns -1 on error, or number of share modes on success (may be zero).
 ****************************************************************************/
 
+struct open_mode_check_state {
+       struct file_id fid;
+       uint32_t access_mask;
+       uint32_t share_access;
+       uint32_t lease_type;
+};
+
+static bool open_mode_check_fn(
+       struct share_mode_entry *e,
+       bool *modified,
+       void *private_data)
+{
+       struct open_mode_check_state *state = private_data;
+       bool disconnected, stale;
+       uint32_t access_mask, share_access, lease_type;
+
+       disconnected = server_id_is_disconnected(&e->pid);
+       if (disconnected) {
+               return false;
+       }
+
+       access_mask = state->access_mask | e->access_mask;
+       share_access = state->share_access & e->share_access;
+       lease_type = state->lease_type | get_lease_type(e, state->fid);
+
+       if ((access_mask == state->access_mask) &&
+           (share_access == state->share_access) &&
+           (lease_type == state->lease_type)) {
+               return false;
+       }
+
+       stale = share_entry_stale_pid(e);
+       if (stale) {
+               return false;
+       }
+
+       state->access_mask = access_mask;
+       state->share_access = share_access;
+       state->lease_type = lease_type;
+
+       return false;
+}
+
 static NTSTATUS open_mode_check(connection_struct *conn,
                                struct share_mode_lock *lck,
                                uint32_t access_mask,
                                uint32_t share_access)
 {
-       uint32_t i;
+       struct share_mode_data *d = lck->data;
+       struct open_mode_check_state state;
+       uint16_t new_flags;
+       bool ok, conflict, have_share_entries;
 
        if (is_stat_open(access_mask)) {
                /* Stat open that doesn't trigger oplock breaks or share mode
@@ -1660,32 +1818,81 @@ 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, lck->data->id, i,
-                                         &lck->data->share_modes[i]);
+       {
+               struct validate_my_share_entries_state validate_state = {
+                       .sconn = conn->sconn,
+                       .fid = d->id,
+                       .self = messaging_server_id(conn->sconn->msg_ctx),
+               };
+               ok = share_mode_forall_entries(
+                       lck, validate_my_share_entries_fn, &validate_state);
+               SMB_ASSERT(ok);
        }
 #endif
 
-       for(i = 0; i < lck->data->num_share_modes; i++) {
+       have_share_entries = share_mode_have_entries(lck);
+       if (!have_share_entries) {
+               /*
+                * This is a fresh share mode lock where no conflicts
+                * can happen.
+                */
+               return NT_STATUS_OK;
+       }
+
+       share_mode_flags_get(
+               d->flags, &state.access_mask, &state.share_access, NULL);
 
-               if (!is_valid_share_mode_entry(&lck->data->share_modes[i])) {
-                       continue;
-               }
+       conflict = share_conflict(
+               state.access_mask,
+               state.share_access,
+               access_mask,
+               share_access);
+       if (!conflict) {
+               DBG_DEBUG("No conflict due to share_mode_flags access\n");
+               return NT_STATUS_OK;
+       }
 
-               /* someone else has a share lock on it, check to see if we can
-                * too */
-               if (share_conflict(&lck->data->share_modes[i],
-                                  access_mask, share_access)) {
+       state = (struct open_mode_check_state) {
+               .fid = d->id,
+               .share_access = (FILE_SHARE_READ|
+                                FILE_SHARE_WRITE|
+                                FILE_SHARE_DELETE),
+       };
 
-                       if (share_mode_stale_pid(lck->data, i)) {
-                               continue;
-                       }
+       /*
+        * Walk the share mode array to recalculate d->flags
+        */
 
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
+       ok = share_mode_forall_entries(lck, open_mode_check_fn, &state);
+       if (!ok) {
+               DBG_DEBUG("share_mode_forall_entries failed\n");
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       return NT_STATUS_OK;
+       new_flags = share_mode_flags_set(
+               0, state.access_mask, state.share_access, state.lease_type);
+       if (new_flags == d->flags) {
+               /*
+                * We only end up here if we had a sharing violation
+                * from d->flags and have recalculated it.
+                */
+               return NT_STATUS_SHARING_VIOLATION;
+       }
+
+       d->flags = new_flags;
+       d->modified = true;
+
+       conflict = share_conflict(
+               state.access_mask,
+               state.share_access,
+               access_mask,
+               share_access);
+       if (!conflict) {
+               DBG_DEBUG("No conflict due to share_mode_flags access\n");
+               return NT_STATUS_OK;
+       }
+
+       return NT_STATUS_SHARING_VIOLATION;
 }
 
 /*
@@ -1736,104 +1943,125 @@ NTSTATUS send_break_message(struct messaging_context *msg_ctx,
        return status;
 }
 
-/*
- * Do internal consistency checks on the share mode for a file.
- */
+struct validate_oplock_types_state {
+       bool valid;
+       bool batch;
+       bool ex_or_batch;
+       bool level2;
+       bool no_oplock;
+       uint32_t num_non_stat_opens;
+};
 
-static bool validate_oplock_types(struct share_mode_lock *lck)
+static bool validate_oplock_types_fn(
+       struct share_mode_entry *e,
+       bool *modified,
+       void *private_data)
 {
-       struct share_mode_data *d = lck->data;
-       bool batch = false;
-       bool ex_or_batch = false;
-       bool level2 = false;
-       bool no_oplock = false;
-       uint32_t num_non_stat_opens = 0;
-       uint32_t i;
+       struct validate_oplock_types_state *state = private_data;
 
-       for (i=0; i<d->num_share_modes; i++) {
-               struct share_mode_entry *e = &d->share_modes[i];
+       if (e->op_mid == 0) {
+               /* INTERNAL_OPEN_ONLY */
+               return false;
+       }
 
-               if (!is_valid_share_mode_entry(e)) {
-                       continue;
-               }
+       if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
+               /*
+                * We ignore stat opens in the table - they always
+                * have NO_OPLOCK and never get or cause breaks. JRA.
+                */
+               return false;
+       }
 
-               if (e->op_mid == 0) {
-                       /* INTERNAL_OPEN_ONLY */
-                       continue;
-               }
+       state->num_non_stat_opens += 1;
 
-               if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
-                       /* We ignore stat opens in the table - they
-                          always have NO_OPLOCK and never get or
-                          cause breaks. JRA. */
-                       continue;
+       if (BATCH_OPLOCK_TYPE(e->op_type)) {
+               /* batch - can only be one. */
+               if (share_entry_stale_pid(e)) {
+                       DBG_DEBUG("Found stale batch oplock\n");
+                       return false;
                }
-
-               num_non_stat_opens += 1;
-
-               if (BATCH_OPLOCK_TYPE(e->op_type)) {
-                       /* batch - can only be one. */
-                       if (share_mode_stale_pid(d, i)) {
-                               DEBUG(10, ("Found stale batch oplock\n"));
-                               continue;
-                       }
-                       if (ex_or_batch || batch || level2 || no_oplock) {
-                               DEBUG(0, ("Bad batch oplock entry %u.",
-                                         (unsigned)i));
-                               return false;
-                       }
-                       batch = true;
+               if (state->ex_or_batch ||
+                   state->batch ||
+                   state->level2 ||
+                   state->no_oplock) {
+                       DBG_ERR("Bad batch oplock entry\n");
+                       state->valid = false;
+                       return true;
                }
+               state->batch = true;
+       }
 
-               if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
-                       if (share_mode_stale_pid(d, i)) {
-                               DEBUG(10, ("Found stale duplicate oplock\n"));
-                               continue;
-                       }
-                       /* Exclusive or batch - can only be one. */
-                       if (ex_or_batch || level2 || no_oplock) {
-                               DEBUG(0, ("Bad exclusive or batch oplock "
-                                         "entry %u.", (unsigned)i));
-                               return false;
-                       }
-                       ex_or_batch = true;
+       if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+               if (share_entry_stale_pid(e)) {
+                       DBG_DEBUG("Found stale duplicate oplock\n");
+                       return false;
                }
+               /* Exclusive or batch - can only be one. */
+               if (state->ex_or_batch ||
+                   state->level2 ||
+                   state->no_oplock) {
+                       DBG_ERR("Bad exclusive or batch oplock entry\n");
+                       state->valid = false;
+                       return true;
+               }
+               state->ex_or_batch = true;
+       }
 
-               if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
-                       if (batch || ex_or_batch) {
-                               if (share_mode_stale_pid(d, i)) {
-                                       DEBUG(10, ("Found stale LevelII "
-                                                  "oplock\n"));
-                                       continue;
-                               }
-                               DEBUG(0, ("Bad levelII oplock entry %u.",
-                                         (unsigned)i));
+       if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
+               if (state->batch || state->ex_or_batch) {
+                       if (share_entry_stale_pid(e)) {
+                               DBG_DEBUG("Found stale LevelII oplock\n");
                                return false;
                        }
-                       level2 = true;
+                       DBG_DEBUG("Bad levelII oplock entry\n");
+                       state->valid = false;
+                       return true;
                }
+               state->level2 = true;
+       }
 
-               if (e->op_type == NO_OPLOCK) {
-                       if (batch || ex_or_batch) {
-                               if (share_mode_stale_pid(d, i)) {
-                                       DEBUG(10, ("Found stale NO_OPLOCK "
-                                                  "entry\n"));
-                                       continue;
-                               }
-                               DEBUG(0, ("Bad no oplock entry %u.",
-                                         (unsigned)i));
+       if (e->op_type == NO_OPLOCK) {
+               if (state->batch || state->ex_or_batch) {
+                       if (share_entry_stale_pid(e)) {
+                               DBG_DEBUG("Found stale NO_OPLOCK entry\n");
                                return false;
                        }
-                       no_oplock = true;
+                       DBG_ERR("Bad no oplock entry\n");
+                       state->valid = false;
+                       return true;
                }
+               state->no_oplock = true;
        }
 
-       remove_stale_share_mode_entries(d);
+       return false;
+}
 
-       if ((batch || ex_or_batch) && (num_non_stat_opens != 1)) {
-               DEBUG(1, ("got batch (%d) or ex (%d) non-exclusively (%d)\n",
-                         (int)batch, (int)ex_or_batch,
-                         (int)d->num_share_modes));
+/*
+ * Do internal consistency checks on the share mode for a file.
+ */
+
+static bool validate_oplock_types(struct share_mode_lock *lck)
+{
+       struct validate_oplock_types_state state = { .valid = true };
+       bool ok;
+
+       ok = share_mode_forall_entries(lck, validate_oplock_types_fn, &state);
+       if (!ok) {
+               DBG_DEBUG("share_mode_forall_entries failed\n");
+               return false;
+       }
+       if (!state.valid) {
+               DBG_DEBUG("Got invalid oplock configuration\n");
+               return false;
+       }
+
+       if ((state.batch || state.ex_or_batch) &&
+           (state.num_non_stat_opens != 1)) {
+               DBG_WARNING("got batch (%d) or ex (%d) non-exclusively "
+                           "(%"PRIu32")\n",
+                           (int)state.batch,
+                           (int)state.ex_or_batch,
+                           state.num_non_stat_opens);
                return false;
        }
 
@@ -2094,6 +2322,125 @@ static int map_lease_type_to_oplock(uint32_t lease_type)
        return result;
 }
 
+struct delay_for_oplock_state {
+       struct files_struct *fsp;
+       const struct smb2_lease *lease;
+       bool will_overwrite;
+       uint32_t delay_mask;
+       bool first_open_attempt;
+       bool got_handle_lease;
+       bool got_oplock;
+       bool have_other_lease;
+       bool delay;
+};
+
+static bool delay_for_oplock_fn(
+       struct share_mode_entry *e,
+       bool *modified,
+       void *private_data)
+{
+       struct delay_for_oplock_state *state = private_data;
+       struct files_struct *fsp = state->fsp;
+       const struct smb2_lease *lease = state->lease;
+       bool e_is_lease = (e->op_type == LEASE_OPLOCK);
+       uint32_t e_lease_type = get_lease_type(e, fsp->file_id);
+       uint32_t break_to;
+       bool lease_is_breaking = false;
+
+       if (e_is_lease) {
+               NTSTATUS status;
+
+               if (lease != NULL) {
+                       bool our_lease = is_same_lease(fsp, e, lease);
+                       if (our_lease) {
+                               DBG_DEBUG("Ignoring our own lease\n");
+                               return false;
+                       }
+               }
+
+               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));
+       }
+
+       if (!state->got_handle_lease &&
+           ((e_lease_type & SMB2_LEASE_HANDLE) != 0) &&
+           !share_entry_stale_pid(e)) {
+               state->got_handle_lease = true;
+       }
+
+       if (!state->got_oplock &&
+           (e->op_type != LEASE_OPLOCK) &&
+           !share_entry_stale_pid(e)) {
+               state->got_oplock = true;
+       }
+
+       if (!state->have_other_lease &&
+           !is_same_lease(fsp, e, lease) &&
+           !share_entry_stale_pid(e)) {
+               state->have_other_lease = true;
+       }
+
+       break_to = e_lease_type & ~state->delay_mask;
+
+       if (state->will_overwrite) {
+               break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_READ);
+       }
+
+       DBG_DEBUG("e_lease_type %u, will_overwrite: %u\n",
+                 (unsigned)e_lease_type,
+                 (unsigned)state->will_overwrite);
+
+       if ((e_lease_type & ~break_to) == 0) {
+               if (lease_is_breaking) {
+                       state->delay = true;
+               }
+               return false;
+       }
+
+       if (share_entry_stale_pid(e)) {
+               return false;
+       }
+
+       if (state->will_overwrite) {
+               /*
+                * If we break anyway break to NONE directly.
+                * Otherwise vfs_set_filelen() will trigger the
+                * break.
+                */
+               break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
+       }
+
+       if (!e_is_lease) {
+               /*
+                * Oplocks only support breaking to R or NONE.
+                */
+               break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+       }
+
+       DBG_DEBUG("breaking from %d to %d\n",
+                 (int)e_lease_type,
+                 (int)break_to);
+       send_break_message(
+               fsp->conn->sconn->msg_ctx, &fsp->file_id, e, break_to);
+       if (e_lease_type & state->delay_mask) {
+               state->delay = true;
+       }
+       if (lease_is_breaking && !state->first_open_attempt) {
+               state->delay = true;
+       }
+
+       return false;
+};
+
 static NTSTATUS delay_for_oplock(files_struct *fsp,
                                 int oplock_request,
                                 const struct smb2_lease *lease,
@@ -2102,130 +2449,39 @@ static NTSTATUS delay_for_oplock(files_struct *fsp,
                                 uint32_t create_disposition,
                                 bool first_open_attempt)
 {
-       struct share_mode_data *d = lck->data;
-       uint32_t i;
-       bool delay = false;
-       bool will_overwrite;
-       const uint32_t delay_mask = have_sharing_violation ?
-               SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
-       bool got_handle_lease = false;
-       bool got_oplock = false;
-       bool have_other_lease = false;
+       struct delay_for_oplock_state state = {
+               .fsp = fsp,
+               .lease = lease,
+               .first_open_attempt = first_open_attempt,
+       };
        uint32_t granted;
        NTSTATUS status;
+       bool ok;
 
        if (is_stat_open(fsp->access_mask)) {
                goto grant;
        }
 
+       state.delay_mask = have_sharing_violation ?
+               SMB2_LEASE_HANDLE : SMB2_LEASE_WRITE;
+
        switch (create_disposition) {
        case FILE_SUPERSEDE:
        case FILE_OVERWRITE:
        case FILE_OVERWRITE_IF:
-               will_overwrite = true;
+               state.will_overwrite = true;
                break;
        default:
-               will_overwrite = false;
+               state.will_overwrite = false;
                break;
        }
 
-       for (i=0; i<d->num_share_modes; i++) {
-               struct share_mode_entry *e = &d->share_modes[i];
-               bool e_is_lease = (e->op_type == LEASE_OPLOCK);
-               uint32_t e_lease_type = get_lease_type(d, e);
-               uint32_t break_to;
-               bool lease_is_breaking = false;
-
-               if (e_is_lease) {
-                       if (lease != NULL) {
-                               bool our_lease = is_same_lease(fsp, e, lease);
-                               if (our_lease) {
-                                       DBG_DEBUG("Ignoring our own lease\n");
-                                       continue;
-                               }
-                       }
-
-                       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));
-               }
-
-               if (!got_handle_lease &&
-                   ((e_lease_type & SMB2_LEASE_HANDLE) != 0) &&
-                   !share_mode_stale_pid(d, i)) {
-                       got_handle_lease = true;
-               }
-
-               if (!got_oplock &&
-                   (e->op_type != LEASE_OPLOCK) &&
-                   !share_mode_stale_pid(d, i)) {
-                       got_oplock = true;
-               }
-
-               if (!have_other_lease &&
-                   !is_same_lease(fsp, e, lease) &&
-                   !share_mode_stale_pid(d, i)) {
-                       have_other_lease = true;
-               }
-
-               break_to = e_lease_type & ~delay_mask;
-
-               if (will_overwrite) {
-                       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 ((e_lease_type & ~break_to) == 0) {
-                       if (lease_is_breaking) {
-                               delay = true;
-                       }
-                       continue;
-               }
-
-               if (share_mode_stale_pid(d, i)) {
-                       continue;
-               }
-
-               if (will_overwrite) {
-                       /*
-                        * If we break anyway break to NONE directly.
-                        * Otherwise vfs_set_filelen() will trigger the
-                        * break.
-                        */
-                       break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
-               }
-
-               if (!e_is_lease) {
-                       /*
-                        * Oplocks only support breaking to R or NONE.
-                        */
-                       break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
-               }
-
-               DEBUG(10, ("breaking from %d to %d\n",
-                          (int)e_lease_type, (int)break_to));
-               send_break_message(fsp->conn->sconn->msg_ctx, &fsp->file_id,
-                                  e, break_to);
-               if (e_lease_type & delay_mask) {
-                       delay = true;
-               }
-               if (lease_is_breaking && !first_open_attempt) {
-                       delay = true;
-               }
+       ok = share_mode_forall_entries(lck, delay_for_oplock_fn, &state);
+       if (!ok) {
+               return NT_STATUS_INTERNAL_ERROR;
        }
 
-       if (delay) {
+       if (state.delay) {
                return NT_STATUS_RETRY;
        }
 
@@ -2271,7 +2527,7 @@ grant:
                granted &= ~SMB2_LEASE_READ;
        }
 
-       if (have_other_lease) {
+       if (state.have_other_lease) {
                /*
                 * Can grant only one writer
                 */
@@ -2289,7 +2545,7 @@ grant:
        }
 
        if (oplock_request == LEASE_OPLOCK) {
-               if (got_oplock) {
+               if (state.got_oplock) {
                        granted &= ~SMB2_LEASE_HANDLE;
                }
 
@@ -2303,7 +2559,7 @@ grant:
 
                DBG_DEBUG("lease_state=%d\n", fsp->lease->lease.lease_state);
        } else {
-               if (got_handle_lease) {
+               if (state.got_handle_lease) {
                        granted = SMB2_LEASE_NONE;
                }
 
@@ -2318,8 +2574,10 @@ grant:
                }
        }
 
-       if (granted & SMB2_LEASE_READ) {
-               lck->data->flags |= SHARE_MODE_HAS_READ_LEASE;
+       if ((granted & SMB2_LEASE_READ) &&
+           ((lck->data->flags & SHARE_MODE_LEASE_READ) == 0)) {
+               lck->data->flags |= SHARE_MODE_LEASE_READ;
+               lck->data->modified = true;
        }
 
        DBG_DEBUG("oplock type 0x%x on file %s\n",
@@ -2409,7 +2667,6 @@ static void defer_open_done(struct tevent_req *req);
 static void defer_open(struct share_mode_lock *lck,
                       struct timeval timeout,
                       struct smb_request *req,
-                      bool delayed_for_oplocks,
                       struct file_id id)
 {
        struct deferred_open_record *open_rec = NULL;
@@ -2417,17 +2674,17 @@ static void defer_open(struct share_mode_lock *lck,
        struct defer_open_state *watch_state;
        struct tevent_req *watch_req;
        struct timeval_buf tvbuf1, tvbuf2;
+       struct file_id_buf fbuf;
        bool ok;
 
        abs_timeout = timeval_sum(&req->request_time, &timeout);
 
        DBG_DEBUG("request time [%s] timeout [%s] mid [%" PRIu64 "] "
-                 "delayed_for_oplocks [%s] file_id [%s]\n",
+                 "file_id [%s]\n",
                  timeval_str_buf(&req->request_time, false, true, &tvbuf1),
                  timeval_str_buf(&abs_timeout, false, true, &tvbuf2),
                  req->mid,
-                 delayed_for_oplocks ? "yes" : "no",
-                 file_id_string_tos(&id));
+                 file_id_str_buf(id, &fbuf));
 
        open_rec = talloc_zero(NULL, struct deferred_open_record);
        if (open_rec == NULL) {
@@ -2444,10 +2701,11 @@ static void defer_open(struct share_mode_lock *lck,
 
        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});
+       watch_req = share_mode_watch_send(
+               watch_state,
+               req->sconn->ev_ctx,
+               lck->data->id,
+               (struct server_id){0});
        if (watch_req == NULL) {
                exit_server("Could not watch share mode record");
        }
@@ -2472,7 +2730,7 @@ static void defer_open_done(struct tevent_req *req)
        NTSTATUS status;
        bool ret;
 
-       status = dbwrap_watched_watch_recv(req, NULL, NULL);
+       status = share_mode_watch_recv(req, NULL, NULL);
        TALLOC_FREE(req);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
@@ -2525,10 +2783,10 @@ static bool setup_poll_open(
        struct timeval max_timeout,
        struct timeval interval)
 {
-
        bool ok;
        struct deferred_open_record *open_rec = NULL;
        struct timeval endtime, next_interval;
+       struct file_id_buf ftmp;
 
        if (request_timed_out(req, max_timeout)) {
                return false;
@@ -2564,13 +2822,13 @@ static bool setup_poll_open(
        }
 
        if (lck != NULL) {
-               open_rec->watch_req = dbwrap_watched_watch_send(
+               open_rec->watch_req = share_mode_watch_send(
                        open_rec,
                        req->sconn->ev_ctx,
-                       lck->data->record,
+                       lck->data->id,
                        (struct server_id) {0});
                if (open_rec->watch_req == NULL) {
-                       DBG_WARNING("dbwrap_watched_watch_send failed\n");
+                       DBG_WARNING("share_mode_watch_send failed\n");
                        TALLOC_FREE(open_rec);
                        return false;
                }
@@ -2588,7 +2846,7 @@ static bool setup_poll_open(
        DBG_DEBUG("poll request time [%s] mid [%" PRIu64 "] file_id [%s]\n",
                  timeval_string(talloc_tos(), &req->request_time, false),
                  req->mid,
-                 file_id_string_tos(&id));
+                 file_id_str_buf(id, &ftmp));
 
        return true;
 }
@@ -2600,7 +2858,7 @@ static void poll_open_done(struct tevent_req *subreq)
        NTSTATUS status;
        bool ok;
 
-       status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
+       status = share_mode_watch_recv(subreq, NULL, NULL);
        TALLOC_FREE(subreq);
        DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
                  nt_errstr(status));
@@ -2714,7 +2972,7 @@ static void schedule_defer_open(struct share_mode_lock *lck,
                return;
        }
 
-       defer_open(lck, timeout, req, true, id);
+       defer_open(lck, timeout, req, id);
 }
 
 /****************************************************************************
@@ -3037,6 +3295,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        SMB_STRUCT_STAT saved_stat = smb_fname->st;
        struct timespec old_write_time;
        struct file_id id;
+       bool setup_poll = false;
        bool ok;
 
        if (conn->printer) {
@@ -3332,7 +3591,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+       if (VALID_STAT(smb_fname->st)) {
+               /*
+                * Only try and create a file id before open
+                * for an existing file. For a file being created
+                * this won't do anything useful until the file
+                * exists and has a valid stat struct.
+                */
+               fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+       }
        fsp->fh->private_options = private_flags;
        fsp->access_mask = open_access_mask; /* We change this to the
                                              * requested access_mask after
@@ -3373,6 +3640,14 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                             open_access_mask, &new_file_created);
 
        if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
+               if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
+                       DEBUG(10, ("FIFO busy\n"));
+                       return NT_STATUS_NETWORK_BUSY;
+               }
+               if (req == NULL) {
+                       DEBUG(10, ("Internal open busy\n"));
+                       return NT_STATUS_NETWORK_BUSY;
+               }
                /*
                 * This handles the kernel oplock case:
                 *
@@ -3382,15 +3657,20 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 * "Samba locking.tdb oplocks" are handled below after acquiring
                 * the sharemode lock with get_share_mode_lock().
                 */
-               if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
-                       DEBUG(10, ("FIFO busy\n"));
-                       return NT_STATUS_NETWORK_BUSY;
-               }
-               if (req == NULL) {
-                       DEBUG(10, ("Internal open busy\n"));
-                       return NT_STATUS_NETWORK_BUSY;
-               }
+               setup_poll = true;
+       }
 
+       if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+               /*
+                * EINTR from the open(2) syscall. Just setup a retry
+                * in a bit. We can't use the sys_write() tight retry
+                * loop here, as we might have to actually deal with
+                * lease-break signals to avoid a deadlock.
+                */
+               setup_poll = true;
+       }
+
+       if (setup_poll) {
                /*
                 * From here on we assume this is an oplock break triggered
                 */
@@ -3421,7 +3701,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        if (!NT_STATUS_IS_OK(fsp_open)) {
-               if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+               bool wait_for_aio = NT_STATUS_EQUAL(
+                       fsp_open, NT_STATUS_MORE_PROCESSING_REQUIRED);
+               if (wait_for_aio) {
                        schedule_async_open(req);
                }
                return fsp_open;
@@ -3526,6 +3808,17 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return status;
        }
 
+       {
+               struct share_mode_data *d = lck->data;
+               uint16_t new_flags = share_mode_flags_restrict(
+                       d->flags, access_mask, share_access, UINT32_MAX);
+
+               if (new_flags != d->flags) {
+                       d->flags = new_flags;
+                       d->modified = true;
+               }
+       }
+
        ok = set_share_mode(
                lck,
                fsp,
@@ -3537,7 +3830,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        if (!ok) {
                if (fsp->oplock_type == LEASE_OPLOCK) {
                        status = remove_lease_if_stale(
-                               lck->data,
+                               lck,
                                fsp_client_guid(fsp),
                                &fsp->lease->lease.lease_key);
                        if (!NT_STATUS_IS_OK(status)) {
@@ -3606,7 +3899,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        return NT_STATUS_SHARING_VIOLATION;
                }
 
-               fsp->kernel_share_modes_taken = true;
+               fsp->fsp_flags.kernel_share_modes_taken = true;
        }
 
        /*
@@ -3665,7 +3958,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                fsp->initial_delete_on_close = True;
        }
 
-       if (info == FILE_WAS_CREATED) {
+       /*
+        * If we created a file and it's not a stream, this is the point where
+        * we set the itime (aka invented time) that get's stored in the DOS
+        * attribute xattr. The value is going to be either what the filesystem
+        * provided or a copy of the creation date.
+        *
+        * Either way, we turn the itime into a File-ID, unless the filesystem
+        * provided one (unlikely).
+        */
+       if (info == FILE_WAS_CREATED && !is_named_stream(smb_fname)) {
                smb_fname->st.st_ex_iflags &= ~ST_EX_IFLAG_CALCULATED_ITIME;
 
                if (lp_store_dos_attributes(SNUM(conn)) &&
@@ -3682,6 +3984,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                /* Overwritten files should be initially set as archive */
                if ((info == FILE_WAS_OVERWRITTEN && lp_map_archive(SNUM(conn))) ||
                    lp_store_dos_attributes(SNUM(conn))) {
+                       (void)dos_mode(conn, smb_fname);
                        if (!posix_open) {
                                if (file_set_dosmode(conn, smb_fname,
                                            new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
@@ -3755,7 +4058,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 */
                struct timespec write_time = get_share_mode_write_time(lck);
 
-               if (!null_timespec(write_time)) {
+               if (!is_omit_timespec(&write_time)) {
                        update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
                }
        }
@@ -3769,16 +4072,19 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                               struct smb_filename *smb_dname,
                               uint32_t file_attributes)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        mode_t mode;
        char *parent_dir = NULL;
        NTSTATUS status;
        bool posix_open = false;
        bool need_re_stat = false;
        uint32_t access_mask = SEC_DIR_ADD_SUBDIR;
+       int ret;
 
        if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
                DEBUG(5,("mkdir_internal: failing share access "
-                        "%s\n", lp_servicename(talloc_tos(), SNUM(conn))));
+                        "%s\n", lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -3806,7 +4112,11 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                return status;
        }
 
-       if (SMB_VFS_MKDIR(conn, smb_dname, mode) != 0) {
+       ret = SMB_VFS_MKDIRAT(conn,
+                       conn->cwd_fsp,
+                       smb_dname,
+                       mode);
+       if (ret != 0) {
                return map_nt_error_from_unix(errno);
        }
 
@@ -3903,7 +4213,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                               files_struct **result)
 {
        files_struct *fsp = NULL;
-       bool dir_existed = VALID_STAT(smb_dname->st) ? True : False;
+       bool dir_existed = VALID_STAT(smb_dname->st);
        struct share_mode_lock *lck = NULL;
        NTSTATUS status;
        struct timespec mtimespec;
@@ -3921,15 +4231,16 @@ static NTSTATUS open_directory(connection_struct *conn,
                file_attributes |= FILE_ATTRIBUTE_DIRECTORY;
        }
 
-       DEBUG(5,("open_directory: opening directory %s, access_mask = 0x%x, "
-                "share_access = 0x%x create_options = 0x%x, "
-                "create_disposition = 0x%x, file_attributes = 0x%x\n",
+       DBG_INFO("opening directory %s, access_mask = 0x%"PRIx32", "
+                "share_access = 0x%"PRIx32" create_options = 0x%"PRIx32", "
+                "create_disposition = 0x%"PRIx32", "
+                "file_attributes = 0x%"PRIx32"\n",
                 smb_fname_str_dbg(smb_dname),
-                (unsigned int)access_mask,
-                (unsigned int)share_access,
-                (unsigned int)create_options,
-                (unsigned int)create_disposition,
-                (unsigned int)file_attributes));
+                access_mask,
+                share_access,
+                create_options,
+                create_disposition,
+                file_attributes);
 
        status = smbd_calculate_access_mask(conn, smb_dname, false,
                                            access_mask, &access_mask);
@@ -4081,9 +4392,9 @@ static NTSTATUS open_directory(connection_struct *conn,
        fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_dname->st);
        fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
        fsp->file_pid = req ? req->smbpid : 0;
-       fsp->can_lock = False;
-       fsp->can_read = False;
-       fsp->can_write = False;
+       fsp->fsp_flags.can_lock = false;
+       fsp->fsp_flags.can_read = false;
+       fsp->fsp_flags.can_write = false;
 
        fsp->fh->private_options = 0;
        /*
@@ -4107,11 +4418,11 @@ static NTSTATUS open_directory(connection_struct *conn,
        /* Don't store old timestamps for directory
           handles in the internal database. We don't
           update them in there if new objects
-          are creaded in the directory. Currently
+          are created in the directory. Currently
           we only update timestamps on file writes.
           See bug #9870.
        */
-       ZERO_STRUCT(mtimespec);
+       mtimespec = make_omit_timespec();
 
 #ifdef O_DIRECTORY
        status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
@@ -4184,6 +4495,17 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
+       {
+               struct share_mode_data *d = lck->data;
+               uint16_t new_flags = share_mode_flags_restrict(
+                       d->flags, access_mask, share_access, UINT32_MAX);
+
+               if (new_flags != d->flags) {
+                       d->flags = new_flags;
+                       d->modified = true;
+               }
+       }
+
        ok = set_share_mode(
                lck,
                fsp,
@@ -4225,7 +4547,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                 */
                struct timespec write_time = get_share_mode_write_time(lck);
 
-               if (!null_timespec(write_time)) {
+               if (!is_omit_timespec(&write_time)) {
                        update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
                }
        }
@@ -4838,6 +5160,78 @@ static void lease_match_parser(
        return;
 }
 
+struct lease_match_break_state {
+       struct messaging_context *msg_ctx;
+       const struct smb2_lease_key *lease_key;
+       struct file_id id;
+
+       bool found_lease;
+       uint16_t version;
+       uint16_t epoch;
+};
+
+static bool lease_match_break_fn(
+       struct share_mode_entry *e,
+       void *private_data)
+{
+       struct lease_match_break_state *state = private_data;
+       bool stale, equal;
+       uint32_t e_lease_type;
+       NTSTATUS status;
+
+       stale = share_entry_stale_pid(e);
+       if (stale) {
+               return false;
+       }
+
+       equal = smb2_lease_key_equal(&e->lease_key, state->lease_key);
+       if (!equal) {
+               return false;
+       }
+
+       status = leases_db_get(
+               &e->client_guid,
+               &e->lease_key,
+               &state->id,
+               NULL, /* current_state */
+               NULL, /* breaking */
+               NULL, /* breaking_to_requested */
+               NULL, /* breaking_to_required */
+               &state->version, /* lease_version */
+               &state->epoch); /* epoch */
+       if (NT_STATUS_IS_OK(status)) {
+               state->found_lease = true;
+       } else {
+               DBG_WARNING("Could not find version/epoch: %s\n",
+                           nt_errstr(status));
+       }
+
+       e_lease_type = get_lease_type(e, state->id);
+       if (e_lease_type == SMB2_LEASE_NONE) {
+               return false;
+       }
+       send_break_message(state->msg_ctx, &state->id, e, SMB2_LEASE_NONE);
+
+       /*
+        * Windows 7 and 8 lease clients are broken in that they will
+        * not respond to lease break requests whilst waiting for an
+        * outstanding open request on that lease handle on the same
+        * TCP connection, due to holding an internal inode lock.
+        *
+        * This means we can't reschedule ourselves here, but must
+        * return from the create.
+        *
+        * Work around:
+        *
+        * Send the breaks and then return SMB2_LEASE_NONE in the
+        * lease handle to cause them to acknowledge the lease
+        * break. Consultation with Microsoft engineering confirmed
+        * this approach is safe.
+        */
+
+       return false;
+}
+
 static NTSTATUS lease_match(connection_struct *conn,
                            struct smb_request *req,
                            const struct smb2_lease_key *lease_key,
@@ -4880,89 +5274,40 @@ static NTSTATUS lease_match(connection_struct *conn,
 
        /* We have to break all existing leases. */
        for (i = 0; i < state.num_file_ids; i++) {
+               struct lease_match_break_state break_state = {
+                       .msg_ctx = conn->sconn->msg_ctx,
+                       .lease_key = lease_key,
+               };
                struct share_mode_lock *lck;
-               struct share_mode_data *d;
-               struct share_mode_entry *lease_entry = NULL;
-               uint32_t j;
+               bool ok;
 
                if (file_id_equal(&state.ids[i], &state.id)) {
                        /* Don't need to break our own file. */
                        continue;
                }
 
-               lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+               break_state.id = state.ids[i];
+
+               lck = get_existing_share_mode_lock(
+                       talloc_tos(), break_state.id);
                if (lck == NULL) {
                        /* Race condition - file already closed. */
                        continue;
                }
-               d = lck->data;
-               for (j=0; j<d->num_share_modes; j++) {
-                       struct share_mode_entry *e = &d->share_modes[j];
-                       uint32_t e_lease_type = get_lease_type(d, e);
-
-                       if (share_mode_stale_pid(d, j)) {
-                               continue;
-                       }
-
-                       if (e->op_type == LEASE_OPLOCK) {
-                               if (!smb2_lease_key_equal(&e->lease_key,
-                                                         lease_key)) {
-                                       continue;
-                               }
-                               lease_entry = e;
-                       }
-
-                       if (e_lease_type == SMB2_LEASE_NONE) {
-                               continue;
-                       }
 
-                       send_break_message(conn->sconn->msg_ctx, &d->id, e,
-                                          SMB2_LEASE_NONE);
-
-                       /*
-                        * Windows 7 and 8 lease clients
-                        * are broken in that they will not
-                        * respond to lease break requests
-                        * whilst waiting for an outstanding
-                        * open request on that lease handle
-                        * on the same TCP connection, due
-                        * to holding an internal inode lock.
-                        *
-                        * This means we can't reschedule
-                        * ourselves here, but must return
-                        * from the create.
-                        *
-                        * Work around:
-                        *
-                        * Send the breaks and then return
-                        * SMB2_LEASE_NONE in the lease handle
-                        * to cause them to acknowledge the
-                        * lease break. 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));
-                       }
+               ok = share_mode_forall_leases(
+                       lck, lease_match_break_fn, &break_state);
+               if (!ok) {
+                       DBG_DEBUG("share_mode_forall_leases failed\n");
+                       continue;
                }
 
                TALLOC_FREE(lck);
+
+               if (break_state.found_lease) {
+                       *p_version = break_state.version;
+                       *p_epoch = break_state.epoch;
+               }
        }
        /*
         * Ensure we don't grant anything more so we
@@ -5549,6 +5894,7 @@ NTSTATUS create_file_default(connection_struct *conn,
        files_struct *fsp = NULL;
        NTSTATUS status;
        bool stream_name = false;
+       struct smb2_create_blob *posx = NULL;
 
        DBG_DEBUG("create_file: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
@@ -5650,6 +5996,36 @@ NTSTATUS create_file_default(connection_struct *conn,
                }
        }
 
+       posx = smb2_create_blob_find(
+               in_context_blobs, SMB2_CREATE_TAG_POSIX);
+       if (posx != NULL) {
+               uint32_t wire_mode_bits = 0;
+               mode_t mode_bits = 0;
+               SMB_STRUCT_STAT sbuf = { 0 };
+               enum perm_type ptype =
+                       (create_options & FILE_DIRECTORY_FILE) ?
+                       PERM_NEW_DIR : PERM_NEW_FILE;
+
+               if (posx->data.length != 4) {
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto fail;
+               }
+
+               wire_mode_bits = IVAL(posx->data.data, 0);
+               status = unix_perms_from_wire(
+                       conn, &sbuf, wire_mode_bits, ptype, &mode_bits);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+               /*
+                * Remove type info from mode, leaving only the
+                * permissions and setuid/gid bits.
+                */
+               mode_bits &= ~S_IFMT;
+
+               file_attributes = (FILE_FLAG_POSIX_SEMANTICS | mode_bits);
+       }
+
        status = create_file_unixpath(
                conn, req, smb_fname, access_mask, share_access,
                create_disposition, create_options, file_attributes,