s4:smbd/open: add missing TALLOC_FREE(frame) to inherit_new_acl()
[kai/samba.git] / source3 / smbd / open.c
index 145a8a4e6af495e979031c09bd88e692c25725c7..d736f4f795bb05d935afff6060f326e9e7c5761d 100644 (file)
@@ -29,6 +29,7 @@
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../librpc/gen_ndr/open_files.h"
 #include "auth.h"
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../librpc/gen_ndr/open_files.h"
 #include "auth.h"
+#include "serverid.h"
 #include "messages.h"
 
 extern const struct generic_mapping file_generic_mapping;
 #include "messages.h"
 
 extern const struct generic_mapping file_generic_mapping;
@@ -39,20 +40,6 @@ struct deferred_open_record {
         struct file_id id;
 };
 
         struct file_id id;
 };
 
-/****************************************************************************
- Check two stats have identical dev and ino fields.
-****************************************************************************/
-
-static bool check_same_dev_ino(const SMB_STRUCT_STAT *sbuf1,
-                       const SMB_STRUCT_STAT *sbuf2)
-{
-       if (sbuf1->st_ex_dev != sbuf2->st_ex_dev ||
-                       sbuf1->st_ex_ino != sbuf2->st_ex_ino) {
-               return false;
-       }
-       return true;
-}
-
 /****************************************************************************
  If the requester wanted DELETE_ACCESS and was rejected because
  the file ACL didn't include DELETE_ACCESS, see if the parent ACL
 /****************************************************************************
  If the requester wanted DELETE_ACCESS and was rejected because
  the file ACL didn't include DELETE_ACCESS, see if the parent ACL
@@ -78,6 +65,7 @@ static bool parent_override_delete(connection_struct *conn,
 
 NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                                const struct smb_filename *smb_fname,
 
 NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                                const struct smb_filename *smb_fname,
+                               bool use_privs,
                                uint32_t access_mask)
 {
        /* Check if we have rights to open. */
                                uint32_t access_mask)
 {
        /* Check if we have rights to open. */
@@ -97,7 +85,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                return NT_STATUS_ACCESS_DENIED;
        }
 
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       if (get_current_uid(conn) == (uid_t)0) {
+       if (!use_privs && get_current_uid(conn) == (uid_t)0) {
                /* I'm sorry sir, I didn't know you were root... */
                DEBUG(10,("smbd_check_access_rights: root override "
                        "on %s. Granting 0x%x\n",
                /* I'm sorry sir, I didn't know you were root... */
                DEBUG(10,("smbd_check_access_rights: root override "
                        "on %s. Granting 0x%x\n",
@@ -127,7 +115,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
                        (SECINFO_OWNER |
                        SECINFO_GROUP |
        status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
                        (SECINFO_OWNER |
                        SECINFO_GROUP |
-                       SECINFO_DACL),&sd);
+                        SECINFO_DACL), talloc_tos(), &sd);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("smbd_check_access_rights: Could not get acl "
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("smbd_check_access_rights: Could not get acl "
@@ -143,11 +131,18 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        }
 
        /*
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of
+        * If we can access the path to this file, by
+        * default we have FILE_READ_ATTRIBUTES from the
+        * containing directory. See the section:
+        * "Algorithm to Check Access to an Existing File"
+        * in MS-FSA.pdf.
+        *
+        * se_file_access_check() also takes care of
         * owner WRITE_DAC and READ_CONTROL.
         */
         * owner WRITE_DAC and READ_CONTROL.
         */
-       status = se_access_check(sd,
+       status = se_file_access_check(sd,
                                get_current_nttok(conn),
                                get_current_nttok(conn),
+                               use_privs,
                                (access_mask & ~FILE_READ_ATTRIBUTES),
                                &rejected_mask);
 
                                (access_mask & ~FILE_READ_ATTRIBUTES),
                                &rejected_mask);
 
@@ -248,6 +243,7 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        status = SMB_VFS_GET_NT_ACL(conn,
                                parent_dir,
                                SECINFO_DACL,
        status = SMB_VFS_GET_NT_ACL(conn,
                                parent_dir,
                                SECINFO_DACL,
+                                   talloc_tos(),
                                &parent_sd);
 
        if (!NT_STATUS_IS_OK(status)) {
                                &parent_sd);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -259,11 +255,18 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        }
 
        /*
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_access_check() also takes care of
+        * If we can access the path to this file, by
+        * default we have FILE_READ_ATTRIBUTES from the
+        * containing directory. See the section:
+        * "Algorithm to Check Access to an Existing File"
+        * in MS-FSA.pdf.
+        *
+        * se_file_access_check() also takes care of
         * owner WRITE_DAC and READ_CONTROL.
         */
         * owner WRITE_DAC and READ_CONTROL.
         */
-       status = se_access_check(parent_sd,
+       status = se_file_access_check(parent_sd,
                                get_current_nttok(conn),
                                get_current_nttok(conn),
+                               false,
                                (access_mask & ~FILE_READ_ATTRIBUTES),
                                &access_granted);
        if(!NT_STATUS_IS_OK(status)) {
                                (access_mask & ~FILE_READ_ATTRIBUTES),
                                &access_granted);
        if(!NT_STATUS_IS_OK(status)) {
@@ -285,10 +288,10 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
 
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
 
-static NTSTATUS fd_open(struct connection_struct *conn,
-                   files_struct *fsp,
-                   int flags,
-                   mode_t mode)
+NTSTATUS fd_open(struct connection_struct *conn,
+                files_struct *fsp,
+                int flags,
+                mode_t mode)
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
@@ -547,6 +550,106 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
        return status;
 }
 
        return status;
 }
 
+/****************************************************************************
+ Open a file - returning a guaranteed ATOMIC indication of if the
+ file was created or not.
+****************************************************************************/
+
+static NTSTATUS fd_open_atomic(struct connection_struct *conn,
+                       files_struct *fsp,
+                       int flags,
+                       mode_t mode,
+                       bool *file_created)
+{
+       NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+       bool file_existed = VALID_STAT(fsp->fsp_name->st);
+
+       *file_created = false;
+
+       if (!(flags & O_CREAT)) {
+               /*
+                * We're not creating the file, just pass through.
+                */
+               return fd_open(conn, fsp, flags, mode);
+       }
+
+       if (flags & O_EXCL) {
+               /*
+                * Fail if already exists, just pass through.
+                */
+               status = fd_open(conn, fsp, flags, mode);
+
+               /*
+                * Here we've opened with O_CREAT|O_EXCL. If that went
+                * NT_STATUS_OK, we *know* we created this file.
+                */
+               *file_created = NT_STATUS_IS_OK(status);
+
+               return status;
+       }
+
+       /*
+        * Now it gets tricky. We have O_CREAT, but not O_EXCL.
+        * To know absolutely if we created the file or not,
+        * we can never call O_CREAT without O_EXCL. So if
+        * we think the file existed, try without O_CREAT|O_EXCL.
+        * If we think the file didn't exist, try with
+        * O_CREAT|O_EXCL. Keep bouncing between these two
+        * requests until either the file is created, or
+        * opened. Either way, we keep going until we get
+        * a returnable result (error, or open/create).
+        */
+
+       while(1) {
+               int curr_flags = flags;
+
+               if (file_existed) {
+                       /* Just try open, do not create. */
+                       curr_flags &= ~(O_CREAT);
+                       status = fd_open(conn, fsp, curr_flags, mode);
+                       if (NT_STATUS_EQUAL(status,
+                                       NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+                               /*
+                                * Someone deleted it in the meantime.
+                                * Retry with O_EXCL.
+                                */
+                               file_existed = false;
+                               DEBUG(10,("fd_open_atomic: file %s existed. "
+                                       "Retry.\n",
+                                       smb_fname_str_dbg(fsp->fsp_name)));
+                                       continue;
+                       }
+               } else {
+                       /* Try create exclusively, fail if it exists. */
+                       curr_flags |= O_EXCL;
+                       status = fd_open(conn, fsp, curr_flags, mode);
+                       if (NT_STATUS_EQUAL(status,
+                                       NT_STATUS_OBJECT_NAME_COLLISION)) {
+                               /*
+                                * Someone created it in the meantime.
+                                * Retry without O_CREAT.
+                                */
+                               file_existed = true;
+                               DEBUG(10,("fd_open_atomic: file %s "
+                                       "did not exist. Retry.\n",
+                                       smb_fname_str_dbg(fsp->fsp_name)));
+                               continue;
+                       }
+                       if (NT_STATUS_IS_OK(status)) {
+                               /*
+                                * Here we've opened with O_CREAT|O_EXCL
+                                * and got success. We *know* we created
+                                * this file.
+                                */
+                               *file_created = true;
+                       }
+               }
+               /* Create is done, or failed. */
+               break;
+       }
+       return status;
+}
+
 /****************************************************************************
  Open a file.
 ****************************************************************************/
 /****************************************************************************
  Open a file.
 ****************************************************************************/
@@ -558,14 +661,14 @@ static NTSTATUS open_file(files_struct *fsp,
                          int flags,
                          mode_t unx_mode,
                          uint32 access_mask, /* client requested access mask. */
                          int flags,
                          mode_t unx_mode,
                          uint32 access_mask, /* client requested access mask. */
-                         uint32 open_access_mask) /* what we're actually using in the open. */
+                         uint32 open_access_mask, /* what we're actually using in the open. */
+                         bool *p_file_created)
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
        int accmode = (flags & O_ACCMODE);
        int local_flags = flags;
        bool file_existed = VALID_STAT(fsp->fsp_name->st);
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
        int accmode = (flags & O_ACCMODE);
        int local_flags = flags;
        bool file_existed = VALID_STAT(fsp->fsp_name->st);
-       bool file_created = false;
 
        fsp->fh->fd = -1;
        errno = EPERM;
 
        fsp->fh->fd = -1;
        errno = EPERM;
@@ -588,7 +691,8 @@ static NTSTATUS open_file(files_struct *fsp,
                        DEBUG(3,("Permission denied opening %s\n",
                                 smb_fname_str_dbg(smb_fname)));
                        return NT_STATUS_ACCESS_DENIED;
                        DEBUG(3,("Permission denied opening %s\n",
                                 smb_fname_str_dbg(smb_fname)));
                        return NT_STATUS_ACCESS_DENIED;
-               } else if(flags & O_CREAT) {
+               }
+               if (flags & O_CREAT) {
                        /* We don't want to write - but we must make sure that
                           O_CREAT doesn't create the file if we have write
                           access into the directory.
                        /* We don't want to write - but we must make sure that
                           O_CREAT doesn't create the file if we have write
                           access into the directory.
@@ -620,13 +724,7 @@ static NTSTATUS open_file(files_struct *fsp,
            (!file_existed && (local_flags & O_CREAT)) ||
            ((local_flags & O_TRUNC) == O_TRUNC) ) {
                const char *wild;
            (!file_existed && (local_flags & O_CREAT)) ||
            ((local_flags & O_TRUNC) == O_TRUNC) ) {
                const char *wild;
-
-               /*
-                * We can't actually truncate here as the file may be locked.
-                * open_file_ntcreate will take care of the truncate later. JRA.
-                */
-
-               local_flags &= ~O_TRUNC;
+               int ret;
 
 #if defined(O_NONBLOCK) && defined(S_ISFIFO)
                /*
 
 #if defined(O_NONBLOCK) && defined(S_ISFIFO)
                /*
@@ -636,6 +734,7 @@ static NTSTATUS open_file(files_struct *fsp,
                 */
 
                if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) {
                 */
 
                if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) {
+                       local_flags &= ~O_TRUNC; /* Can't truncate a FIFO. */
                        local_flags |= O_NONBLOCK;
                }
 #endif
                        local_flags |= O_NONBLOCK;
                }
 #endif
@@ -661,6 +760,7 @@ static NTSTATUS open_file(files_struct *fsp,
                        if (file_existed) {
                                status = smbd_check_access_rights(conn,
                                                smb_fname,
                        if (file_existed) {
                                status = smbd_check_access_rights(conn,
                                                smb_fname,
+                                               false,
                                                access_mask);
                        } else if (local_flags & O_CREAT){
                                status = check_parent_access(conn,
                                                access_mask);
                        } else if (local_flags & O_CREAT){
                                status = check_parent_access(conn,
@@ -684,7 +784,8 @@ static NTSTATUS open_file(files_struct *fsp,
                }
 
                /* Actually do the open */
                }
 
                /* Actually do the open */
-               status = fd_open(conn, fsp, local_flags, unx_mode);
+               status = fd_open_atomic(conn, fsp, local_flags,
+                               unx_mode, p_file_created);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
                                 "(flags=%d)\n", smb_fname_str_dbg(smb_fname),
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
                                 "(flags=%d)\n", smb_fname_str_dbg(smb_fname),
@@ -692,70 +793,24 @@ static NTSTATUS open_file(files_struct *fsp,
                        return status;
                }
 
                        return status;
                }
 
-               if ((local_flags & O_CREAT) && !file_existed) {
-                       file_created = true;
-               }
-
-       } else {
-               fsp->fh->fd = -1; /* What we used to call a stat open. */
-               if (!file_existed) {
-                       /* File must exist for a stat open. */
-                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-               }
-
-               status = smbd_check_access_rights(conn,
-                               smb_fname,
-                               access_mask);
-
-               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
-                               fsp->posix_open &&
-                               S_ISLNK(smb_fname->st.st_ex_mode)) {
-                       /* This is a POSIX stat open for delete
-                        * or rename on a symlink that points
-                        * nowhere. Allow. */
-                       DEBUG(10,("open_file: allowing POSIX "
-                                 "open on bad symlink %s\n",
-                                 smb_fname_str_dbg(smb_fname)));
-                       status = NT_STATUS_OK;
-               }
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(10,("open_file: "
-                               "smbd_check_access_rights on file "
-                               "%s returned %s\n",
-                               smb_fname_str_dbg(smb_fname),
-                               nt_errstr(status) ));
-                       return status;
-               }
-       }
-
-       if (!file_existed) {
-               int ret;
-
-               if (fsp->fh->fd == -1) {
-                       ret = SMB_VFS_STAT(conn, smb_fname);
-               } else {
-                       ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
-                       /* If we have an fd, this stat should succeed. */
-                       if (ret == -1) {
-                               DEBUG(0,("Error doing fstat on open file %s "
-                                        "(%s)\n",
-                                        smb_fname_str_dbg(smb_fname),
-                                        strerror(errno) ));
-                       }
-               }
-
-               /* For a non-io open, this stat failing means file not found. JRA */
+               ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
                if (ret == -1) {
                if (ret == -1) {
+                       /* If we have an fd, this stat should succeed. */
+                       DEBUG(0,("Error doing fstat on open file %s "
+                               "(%s)\n",
+                               smb_fname_str_dbg(smb_fname),
+                               strerror(errno) ));
                        status = map_nt_error_from_unix(errno);
                        fd_close(fsp);
                        return status;
                }
 
                        status = map_nt_error_from_unix(errno);
                        fd_close(fsp);
                        return status;
                }
 
-               if (file_created) {
+               if (*p_file_created) {
+                       /* We created this file. */
+
                        bool need_re_stat = false;
                        /* Do all inheritance work after we've
                        bool need_re_stat = false;
                        /* Do all inheritance work after we've
-                          done a successful stat call and filled
+                          done a successful fstat call and filled
                           in the stat struct in fsp->fsp_name. */
 
                        /* Inherit the ACL if required */
                           in the stat struct in fsp->fsp_name. */
 
                        /* Inherit the ACL if required */
@@ -774,17 +829,13 @@ static NTSTATUS open_file(files_struct *fsp,
                        }
 
                        if (need_re_stat) {
                        }
 
                        if (need_re_stat) {
-                               if (fsp->fh->fd == -1) {
-                                       ret = SMB_VFS_STAT(conn, smb_fname);
-                               } else {
-                                       ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
-                                       /* If we have an fd, this stat should succeed. */
-                                       if (ret == -1) {
-                                               DEBUG(0,("Error doing fstat on open file %s "
-                                                        "(%s)\n",
-                                                        smb_fname_str_dbg(smb_fname),
-                                                        strerror(errno) ));
-                                       }
+                               ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
+                               /* If we have an fd, this stat should succeed. */
+                               if (ret == -1) {
+                                       DEBUG(0,("Error doing fstat on open file %s "
+                                                "(%s)\n",
+                                                smb_fname_str_dbg(smb_fname),
+                                                strerror(errno) ));
                                }
                        }
 
                                }
                        }
 
@@ -792,6 +843,38 @@ static NTSTATUS open_file(files_struct *fsp,
                                     FILE_NOTIFY_CHANGE_FILE_NAME,
                                     smb_fname->base_name);
                }
                                     FILE_NOTIFY_CHANGE_FILE_NAME,
                                     smb_fname->base_name);
                }
+       } else {
+               fsp->fh->fd = -1; /* What we used to call a stat open. */
+               if (!file_existed) {
+                       /* File must exist for a stat open. */
+                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+               }
+
+               status = smbd_check_access_rights(conn,
+                               smb_fname,
+                               false,
+                               access_mask);
+
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
+                               fsp->posix_open &&
+                               S_ISLNK(smb_fname->st.st_ex_mode)) {
+                       /* This is a POSIX stat open for delete
+                        * or rename on a symlink that points
+                        * nowhere. Allow. */
+                       DEBUG(10,("open_file: allowing POSIX "
+                                 "open on bad symlink %s\n",
+                                 smb_fname_str_dbg(smb_fname)));
+                       status = NT_STATUS_OK;
+               }
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(10,("open_file: "
+                               "smbd_check_access_rights on file "
+                               "%s returned %s\n",
+                               smb_fname_str_dbg(smb_fname),
+                               nt_errstr(status) ));
+                       return status;
+               }
        }
 
        /*
        }
 
        /*
@@ -852,6 +935,14 @@ static bool share_conflict(struct share_mode_entry *entry,
                  (unsigned int)entry->share_access,
                  (unsigned int)entry->private_options));
 
                  (unsigned int)entry->share_access,
                  (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;
+       }
+
        DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n",
                  (unsigned int)access_mask, (unsigned int)share_access));
 
        DEBUG(10,("share_conflict: access_mask = 0x%x, share_access = 0x%x\n",
                  (unsigned int)access_mask, (unsigned int)share_access));
 
@@ -933,7 +1024,8 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
        }
 
        if (is_deferred_open_entry(share_entry) &&
        }
 
        if (is_deferred_open_entry(share_entry) &&
-           !open_was_deferred(sconn, share_entry->op_mid)) {
+           !open_was_deferred(sconn, share_entry->op_mid))
+       {
                char *str = talloc_asprintf(talloc_tos(),
                        "Got a deferred entry without a request: "
                        "PANIC: %s\n",
                char *str = talloc_asprintf(talloc_tos(),
                        "Got a deferred entry without a request: "
                        "PANIC: %s\n",
@@ -959,7 +1051,8 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
        }
 
        if ((share_entry->op_type == NO_OPLOCK) &&
        }
 
        if ((share_entry->op_type == NO_OPLOCK) &&
-           (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK)) {
+           (fsp->oplock_type == FAKE_LEVEL_II_OPLOCK))
+       {
                /* Someone has already written to it, but I haven't yet
                 * noticed */
                return;
                /* Someone has already written to it, but I haven't yet
                 * noticed */
                return;
@@ -989,11 +1082,13 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
 
 bool is_stat_open(uint32 access_mask)
 {
 
 bool is_stat_open(uint32 access_mask)
 {
-       return (access_mask &&
-               ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES|
-                                 FILE_WRITE_ATTRIBUTES))==0) &&
-               ((access_mask & (SYNCHRONIZE_ACCESS|FILE_READ_ATTRIBUTES|
-                                FILE_WRITE_ATTRIBUTES)) != 0));
+       const uint32_t stat_open_bits =
+               (SYNCHRONIZE_ACCESS|
+                FILE_READ_ATTRIBUTES|
+                FILE_WRITE_ATTRIBUTES);
+
+       return (((access_mask &  stat_open_bits) != 0) &&
+               ((access_mask & ~stat_open_bits) == 0));
 }
 
 /****************************************************************************
 }
 
 /****************************************************************************
@@ -1081,11 +1176,6 @@ static NTSTATUS open_mode_check(connection_struct *conn,
        return NT_STATUS_OK;
 }
 
        return NT_STATUS_OK;
 }
 
-static bool is_delete_request(files_struct *fsp) {
-       return ((fsp->access_mask == DELETE_ACCESS) &&
-               (fsp->oplock_type == NO_OPLOCK));
-}
-
 /*
  * Send a break message to the oplock holder and delay the open for
  * our client.
 /*
  * Send a break message to the oplock holder and delay the open for
  * our client.
@@ -1233,6 +1323,19 @@ static bool delay_for_batch_oplocks(files_struct *fsp,
                return false;
        }
 
                return false;
        }
 
+       if (server_id_is_disconnected(&batch_entry->pid)) {
+               /*
+                * TODO: clean up.
+                * This could be achieved by sending a break message
+                * to ourselves. Special considerations for files
+                * with delete_on_close flag set!
+                *
+                * For now we keep it simple and do not
+                * allow delete on close for durable handles.
+                */
+               return false;
+       }
+
        /* Found a batch oplock */
        send_break_message(fsp, batch_entry, mid, oplock_request);
        return true;
        /* Found a batch oplock */
        send_break_message(fsp, batch_entry, mid, oplock_request);
        return true;
@@ -1243,8 +1346,6 @@ static bool delay_for_exclusive_oplocks(files_struct *fsp,
                                        int oplock_request,
                                        struct share_mode_entry *ex_entry)
 {
                                        int oplock_request,
                                        struct share_mode_entry *ex_entry)
 {
-       bool delay_it;
-
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
                return false;
        }
        if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
                return false;
        }
@@ -1252,12 +1353,12 @@ static bool delay_for_exclusive_oplocks(files_struct *fsp,
                return false;
        }
 
                return false;
        }
 
-       /* Found an exclusive or batch oplock */
-
-       delay_it = is_delete_request(fsp) ?
-               BATCH_OPLOCK_TYPE(ex_entry->op_type) : true;
-
-       if (!delay_it) {
+       if (server_id_is_disconnected(&ex_entry->pid)) {
+               /*
+                * since only durable handles can get disconnected,
+                * and we can only get durable handles with batch oplocks,
+                * this should actually never be reached...
+                */
                return false;
        }
 
                return false;
        }
 
@@ -1346,8 +1447,8 @@ static void grant_fsp_oplock_type(files_struct *fsp,
                  fsp->oplock_type, fsp_str_dbg(fsp)));
 }
 
                  fsp->oplock_type, fsp_str_dbg(fsp)));
 }
 
-bool request_timed_out(struct timeval request_time,
-                      struct timeval timeout)
+static bool request_timed_out(struct timeval request_time,
+                             struct timeval timeout)
 {
        struct timeval now, end_time;
        GetTimeOfDay(&now);
 {
        struct timeval now, end_time;
        GetTimeOfDay(&now);
@@ -1381,6 +1482,7 @@ static void defer_open(struct share_mode_lock *lck,
                                DEBUG(0, ("Trying to defer an already deferred "
                                        "request: mid=%llu, exiting\n",
                                        (unsigned long long)req->mid));
                                DEBUG(0, ("Trying to defer an already deferred "
                                        "request: mid=%llu, exiting\n",
                                        (unsigned long long)req->mid));
+                               TALLOC_FREE(lck);
                                exit_server("attempt to defer a deferred request");
                        }
                }
                                exit_server("attempt to defer a deferred request");
                        }
                }
@@ -1396,6 +1498,7 @@ static void defer_open(struct share_mode_lock *lck,
 
        if (!push_deferred_open_message_smb(req, request_time, timeout,
                                       state->id, (char *)state, sizeof(*state))) {
 
        if (!push_deferred_open_message_smb(req, request_time, timeout,
                                       state->id, (char *)state, sizeof(*state))) {
+               TALLOC_FREE(lck);
                exit_server("push_deferred_open_message_smb failed");
        }
        if (lck) {
                exit_server("push_deferred_open_message_smb failed");
        }
        if (lck) {
@@ -1408,12 +1511,12 @@ static void defer_open(struct share_mode_lock *lck,
  On overwrite open ensure that the attributes match.
 ****************************************************************************/
 
  On overwrite open ensure that the attributes match.
 ****************************************************************************/
 
-bool open_match_attributes(connection_struct *conn,
-                          uint32 old_dos_attr,
-                          uint32 new_dos_attr,
-                          mode_t existing_unx_mode,
-                          mode_t new_unx_mode,
-                          mode_t *returned_unx_mode)
+static bool open_match_attributes(connection_struct *conn,
+                                 uint32 old_dos_attr,
+                                 uint32 new_dos_attr,
+                                 mode_t existing_unx_mode,
+                                 mode_t new_unx_mode,
+                                 mode_t *returned_unx_mode)
 {
        uint32 noarch_old_dos_attr, noarch_new_dos_attr;
 
 {
        uint32 noarch_old_dos_attr, noarch_new_dos_attr;
 
@@ -1483,7 +1586,8 @@ static NTSTATUS fcb_or_dos_open(struct smb_request *req,
                          (unsigned int)fsp->fh->private_options,
                          (unsigned int)fsp->access_mask ));
 
                          (unsigned int)fsp->fh->private_options,
                          (unsigned int)fsp->access_mask ));
 
-               if (fsp->fh->fd != -1 &&
+               if (fsp != fsp_to_dup_into &&
+                   fsp->fh->fd != -1 &&
                    fsp->vuid == vuid &&
                    fsp->file_pid == file_pid &&
                    (fsp->fh->private_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
                    fsp->vuid == vuid &&
                    fsp->file_pid == file_pid &&
                    (fsp->fh->private_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
@@ -1579,13 +1683,14 @@ static void schedule_async_open(struct timeval request_time,
 static NTSTATUS smbd_calculate_maximum_allowed_access(
        connection_struct *conn,
        const struct smb_filename *smb_fname,
 static NTSTATUS smbd_calculate_maximum_allowed_access(
        connection_struct *conn,
        const struct smb_filename *smb_fname,
+       bool use_privs,
        uint32_t *p_access_mask)
 {
        struct security_descriptor *sd;
        uint32_t access_granted;
        NTSTATUS status;
 
        uint32_t *p_access_mask)
 {
        struct security_descriptor *sd;
        uint32_t access_granted;
        NTSTATUS status;
 
-       if (get_current_uid(conn) == (uid_t)0) {
+       if (!use_privs && (get_current_uid(conn) == (uid_t)0)) {
                *p_access_mask |= FILE_GENERIC_ALL;
                return NT_STATUS_OK;
        }
                *p_access_mask |= FILE_GENERIC_ALL;
                return NT_STATUS_OK;
        }
@@ -1593,7 +1698,8 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
        status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
                                    (SECINFO_OWNER |
                                     SECINFO_GROUP |
        status = SMB_VFS_GET_NT_ACL(conn, smb_fname->base_name,
                                    (SECINFO_OWNER |
                                     SECINFO_GROUP |
-                                    SECINFO_DACL),&sd);
+                                    SECINFO_DACL),
+                                   talloc_tos(), &sd);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
                /*
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
                /*
@@ -1603,37 +1709,50 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
                return NT_STATUS_OK;
        }
        if (!NT_STATUS_IS_OK(status)) {
                return NT_STATUS_OK;
        }
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10,("smbd_calculate_access_mask: "
-                         "Could not get acl on file %s: %s\n",
+               DEBUG(10,("Could not get acl on file %s: %s\n",
                          smb_fname_str_dbg(smb_fname),
                          nt_errstr(status)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
        /*
                          smb_fname_str_dbg(smb_fname),
                          nt_errstr(status)));
                return NT_STATUS_ACCESS_DENIED;
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_access_check()
+        * If we can access the path to this file, by
+        * default we have FILE_READ_ATTRIBUTES from the
+        * containing directory. See the section:
+        * "Algorithm to Check Access to an Existing File"
+        * in MS-FSA.pdf.
+        *
+        * se_file_access_check()
         * also takes care of owner WRITE_DAC and READ_CONTROL.
         */
         * also takes care of owner WRITE_DAC and READ_CONTROL.
         */
-       status = se_access_check(sd,
+       status = se_file_access_check(sd,
                                 get_current_nttok(conn),
                                 get_current_nttok(conn),
+                                use_privs,
                                 (*p_access_mask & ~FILE_READ_ATTRIBUTES),
                                 &access_granted);
 
        TALLOC_FREE(sd);
 
        if (!NT_STATUS_IS_OK(status)) {
                                 (*p_access_mask & ~FILE_READ_ATTRIBUTES),
                                 &access_granted);
 
        TALLOC_FREE(sd);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(10, ("smbd_calculate_access_mask: "
-                          "Access denied on file %s: "
+               DEBUG(10, ("Access denied on file %s: "
                           "when calculating maximum access\n",
                           smb_fname_str_dbg(smb_fname)));
                return NT_STATUS_ACCESS_DENIED;
        }
        *p_access_mask = (access_granted | FILE_READ_ATTRIBUTES);
                           "when calculating maximum access\n",
                           smb_fname_str_dbg(smb_fname)));
                return NT_STATUS_ACCESS_DENIED;
        }
        *p_access_mask = (access_granted | FILE_READ_ATTRIBUTES);
+
+       if (!(access_granted & DELETE_ACCESS)) {
+               if (can_delete_file_in_directory(conn, smb_fname)) {
+                       *p_access_mask |= DELETE_ACCESS;
+               }
+       }
+
        return NT_STATUS_OK;
 }
 
 NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
                                    const struct smb_filename *smb_fname,
        return NT_STATUS_OK;
 }
 
 NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
                                    const struct smb_filename *smb_fname,
+                                   bool use_privs,
                                    uint32_t access_mask,
                                    uint32_t *access_mask_out)
 {
                                    uint32_t access_mask,
                                    uint32_t *access_mask_out)
 {
@@ -1651,7 +1770,7 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
        if (access_mask & MAXIMUM_ALLOWED_ACCESS) {
 
                status = smbd_calculate_maximum_allowed_access(
        if (access_mask & MAXIMUM_ALLOWED_ACCESS) {
 
                status = smbd_calculate_maximum_allowed_access(
-                       conn, smb_fname, &access_mask);
+                       conn, smb_fname, use_privs, &access_mask);
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -1705,6 +1824,109 @@ bool is_deferred_open_async(const void *ptr)
        return state->async_open;
 }
 
        return state->async_open;
 }
 
+static bool clear_ads(uint32_t create_disposition)
+{
+       bool ret = false;
+
+       switch (create_disposition) {
+       case FILE_SUPERSEDE:
+       case FILE_OVERWRITE_IF:
+       case FILE_OVERWRITE:
+               ret = true;
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+static int disposition_to_open_flags(uint32_t create_disposition)
+{
+       int ret = 0;
+
+       /*
+        * Currently we're using FILE_SUPERSEDE as the same as
+        * FILE_OVERWRITE_IF but they really are
+        * different. FILE_SUPERSEDE deletes an existing file
+        * (requiring delete access) then recreates it.
+        */
+
+       switch (create_disposition) {
+       case FILE_SUPERSEDE:
+       case FILE_OVERWRITE_IF:
+               /*
+                * If file exists replace/overwrite. If file doesn't
+                * exist create.
+                */
+               ret = O_CREAT|O_TRUNC;
+               break;
+
+       case FILE_OPEN:
+               /*
+                * If file exists open. If file doesn't exist error.
+                */
+               ret = 0;
+               break;
+
+       case FILE_OVERWRITE:
+               /*
+                * If file exists overwrite. If file doesn't exist
+                * error.
+                */
+               ret = O_TRUNC;
+               break;
+
+       case FILE_CREATE:
+               /*
+                * If file exists error. If file doesn't exist create.
+                */
+               ret = O_CREAT|O_EXCL;
+               break;
+
+       case FILE_OPEN_IF:
+               /*
+                * If file exists open. If file doesn't exist create.
+                */
+               ret = O_CREAT;
+               break;
+       }
+       return ret;
+}
+
+static int calculate_open_access_flags(uint32_t access_mask,
+                                      int oplock_request,
+                                      uint32_t private_flags)
+{
+       bool need_write, need_read;
+
+       /*
+        * Note that we ignore the append flag as append does not
+        * mean the same thing under DOS and Unix.
+        */
+
+       need_write =
+               ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ||
+                (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE));
+
+       if (!need_write) {
+               return O_RDONLY;
+       }
+
+       /* DENY_DOS opens are always underlying read-write on the
+          file handle, no matter what the requested access mask
+          says. */
+
+       need_read =
+               ((private_flags & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) ||
+                access_mask & (FILE_READ_ATTRIBUTES|FILE_READ_DATA|
+                               FILE_READ_EA|FILE_EXECUTE));
+
+       if (!need_read) {
+               return O_WRONLY;
+       }
+       return O_RDWR;
+}
+
 /****************************************************************************
  Open a file with a share mode. Passed in an already created files_struct *.
 ****************************************************************************/
 /****************************************************************************
  Open a file with a share mode. Passed in an already created files_struct *.
 ****************************************************************************/
@@ -1729,7 +1951,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        bool def_acl = False;
        bool posix_open = False;
        bool new_file_created = False;
        bool def_acl = False;
        bool posix_open = False;
        bool new_file_created = False;
-       bool clear_ads = false;
+       bool first_open_attempt = true;
        NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
        mode_t new_unx_mode = (mode_t)0;
        mode_t unx_mode = (mode_t)0;
        NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED;
        mode_t new_unx_mode = (mode_t)0;
        mode_t unx_mode = (mode_t)0;
@@ -1740,6 +1962,13 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        uint32 open_access_mask = access_mask;
        NTSTATUS status;
        char *parent_dir;
        uint32 open_access_mask = access_mask;
        NTSTATUS status;
        char *parent_dir;
+       SMB_STRUCT_STAT saved_stat = smb_fname->st;
+       struct share_mode_entry *batch_entry = NULL;
+       struct share_mode_entry *exclusive_entry = NULL;
+       bool got_level2_oplock = false;
+       bool got_a_none_oplock = false;
+       struct timespec old_write_time;
+       struct file_id id;
 
        if (conn->printer) {
                /*
 
        if (conn->printer) {
                /*
@@ -1829,6 +2058,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                        /* Ensure we don't reprocess this message. */
                        remove_deferred_open_message_smb(req->sconn, req->mid);
 
                        /* Ensure we don't reprocess this message. */
                        remove_deferred_open_message_smb(req->sconn, req->mid);
+
+                       first_open_attempt = false;
                }
        }
 
                }
        }
 
@@ -1859,26 +2090,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        switch( create_disposition ) {
        }
 
        switch( create_disposition ) {
-               /*
-                * Currently we're using FILE_SUPERSEDE as the same as
-                * FILE_OVERWRITE_IF but they really are
-                * different. FILE_SUPERSEDE deletes an existing file
-                * (requiring delete access) then recreates it.
-                */
-               case FILE_SUPERSEDE:
-                       /* If file exists replace/overwrite. If file doesn't
-                        * exist create. */
-                       flags2 |= (O_CREAT | O_TRUNC);
-                       clear_ads = true;
-                       break;
-
-               case FILE_OVERWRITE_IF:
-                       /* If file exists replace/overwrite. If file doesn't
-                        * exist create. */
-                       flags2 |= (O_CREAT | O_TRUNC);
-                       clear_ads = true;
-                       break;
-
                case FILE_OPEN:
                        /* If file exists open. If file doesn't exist error. */
                        if (!file_existed) {
                case FILE_OPEN:
                        /* If file exists open. If file doesn't exist error. */
                        if (!file_existed) {
@@ -1902,8 +2113,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                errno = ENOENT;
                                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                        }
                                errno = ENOENT;
                                return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                        }
-                       flags2 |= O_TRUNC;
-                       clear_ads = true;
                        break;
 
                case FILE_CREATE:
                        break;
 
                case FILE_CREATE:
@@ -1921,24 +2130,24 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                }
                                return map_nt_error_from_unix(errno);
                        }
                                }
                                return map_nt_error_from_unix(errno);
                        }
-                       flags2 |= (O_CREAT|O_EXCL);
                        break;
 
                        break;
 
+               case FILE_SUPERSEDE:
+               case FILE_OVERWRITE_IF:
                case FILE_OPEN_IF:
                case FILE_OPEN_IF:
-                       /* If file exists open. If file doesn't exist
-                        * create. */
-                       flags2 |= O_CREAT;
                        break;
                        break;
-
                default:
                        return NT_STATUS_INVALID_PARAMETER;
        }
 
                default:
                        return NT_STATUS_INVALID_PARAMETER;
        }
 
+       flags2 = disposition_to_open_flags(create_disposition);
+
        /* We only care about matching attributes on file exists and
         * overwrite. */
 
        /* We only care about matching attributes on file exists and
         * overwrite. */
 
-       if (!posix_open && file_existed && ((create_disposition == FILE_OVERWRITE) ||
-                            (create_disposition == FILE_OVERWRITE_IF))) {
+       if (!posix_open && file_existed &&
+           ((create_disposition == FILE_OVERWRITE) ||
+            (create_disposition == FILE_OVERWRITE_IF))) {
                if (!open_match_attributes(conn, existing_dos_attributes,
                                           new_dos_attributes,
                                           smb_fname->st.st_ex_mode,
                if (!open_match_attributes(conn, existing_dos_attributes,
                                           new_dos_attributes,
                                           smb_fname->st.st_ex_mode,
@@ -1956,6 +2165,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        status = smbd_calculate_access_mask(conn, smb_fname,
        }
 
        status = smbd_calculate_access_mask(conn, smb_fname,
+                                       false,
                                        access_mask,
                                        &access_mask); 
        if (!NT_STATUS_IS_OK(status)) {
                                        access_mask,
                                        &access_mask); 
        if (!NT_STATUS_IS_OK(status)) {
@@ -1980,20 +2190,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * mean the same thing under DOS and Unix.
         */
 
         * mean the same thing under DOS and Unix.
         */
 
-       if ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) ||
-                       (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) {
-               /* DENY_DOS opens are always underlying read-write on the
-                  file handle, no matter what the requested access mask
-                   says. */
-               if ((private_flags & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS) ||
-                       access_mask & (FILE_READ_ATTRIBUTES|FILE_READ_DATA|FILE_READ_EA|FILE_EXECUTE)) {
-                       flags = O_RDWR;
-               } else {
-                       flags = O_WRONLY;
-               }
-       } else {
-               flags = O_RDONLY;
-       }
+       flags = calculate_open_access_flags(access_mask, oplock_request,
+                                           private_flags);
 
        /*
         * Currently we only look at FILE_WRITE_THROUGH for create options.
 
        /*
         * Currently we only look at FILE_WRITE_THROUGH for create options.
@@ -2018,6 +2216,24 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                flags2 &= ~(O_CREAT|O_TRUNC);
        }
 
                flags2 &= ~(O_CREAT|O_TRUNC);
        }
 
+       if (first_open_attempt && 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
+                * 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.
+                *
+                * 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.
+                */
+               flags2 |= O_NONBLOCK;
+       }
+
        /*
         * Ensure we can't write on a read-only share or file.
         */
        /*
         * Ensure we can't write on a read-only share or file.
         */
@@ -2047,207 +2263,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                request_time = fsp->open_time;
        }
 
                request_time = fsp->open_time;
        }
 
-       if (file_existed) {
-               struct share_mode_entry *batch_entry = NULL;
-               struct share_mode_entry *exclusive_entry = NULL;
-               bool got_level2_oplock = false;
-               bool got_a_none_oplock = false;
-               struct file_id id;
-
-               struct timespec old_write_time = smb_fname->st.st_ex_mtime;
-               id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
-
-               lck = get_share_mode_lock(talloc_tos(), id,
-                                         conn->connectpath,
-                                         smb_fname, &old_write_time);
-               if (lck == NULL) {
-                       DEBUG(0, ("Could not get share mode lock\n"));
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
-
-               /* Get the types we need to examine. */
-               find_oplock_types(fsp,
-                               oplock_request,
-                               lck,
-                               &batch_entry,
-                               &exclusive_entry,
-                               &got_level2_oplock,
-                               &got_a_none_oplock);
-
-               /* First pass - send break only on batch oplocks. */
-               if ((req != NULL) &&
-                               delay_for_batch_oplocks(fsp,
-                                       req->mid,
-                                       oplock_request,
-                                       batch_entry)) {
-                       schedule_defer_open(lck, request_time, req);
-                       TALLOC_FREE(lck);
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
-
-               /* Use the client requested access mask here, not the one we
-                * open with. */
-               status = open_mode_check(conn, lck, fsp->name_hash,
-                                       access_mask, share_access,
-                                        create_options, &file_existed);
-
-               if (NT_STATUS_IS_OK(status)) {
-                       /* We might be going to allow this open. Check oplock
-                        * status again. */
-                       /* Second pass - send break for both batch or
-                        * exclusive oplocks. */
-                       if ((req != NULL) &&
-                                       delay_for_exclusive_oplocks(
-                                               fsp,
-                                               req->mid,
-                                               oplock_request,
-                                               exclusive_entry)) {
-                               schedule_defer_open(lck, request_time, req);
-                               TALLOC_FREE(lck);
-                               return NT_STATUS_SHARING_VIOLATION;
-                       }
-               }
-
-               if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
-                       /* DELETE_PENDING is not deferred for a second */
-                       TALLOC_FREE(lck);
-                       return status;
-               }
-
-               grant_fsp_oplock_type(fsp,
-                                oplock_request,
-                                got_level2_oplock,
-                                got_a_none_oplock);
-
-               if (!NT_STATUS_IS_OK(status)) {
-                       uint32 can_access_mask;
-                       bool can_access = True;
-
-                       SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION));
-
-                       /* Check if this can be done with the deny_dos and fcb
-                        * calls. */
-                       if (private_flags &
-                           (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS|
-                            NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
-                               if (req == NULL) {
-                                       DEBUG(0, ("DOS open without an SMB "
-                                                 "request!\n"));
-                                       TALLOC_FREE(lck);
-                                       return NT_STATUS_INTERNAL_ERROR;
-                               }
-
-                               /* Use the client requested access mask here,
-                                * not the one we open with. */
-                               status = fcb_or_dos_open(req,
-                                                       conn,
-                                                       fsp,
-                                                       smb_fname,
-                                                       id,
-                                                       req->smbpid,
-                                                       req->vuid,
-                                                       access_mask,
-                                                       share_access,
-                                                       create_options);
-
-                               if (NT_STATUS_IS_OK(status)) {
-                                       TALLOC_FREE(lck);
-                                       if (pinfo) {
-                                               *pinfo = FILE_WAS_OPENED;
-                                       }
-                                       return NT_STATUS_OK;
-                               }
-                       }
-
-                       /*
-                        * This next line is a subtlety we need for
-                        * MS-Access. If a file open will fail due to share
-                        * permissions and also for security (access) reasons,
-                        * we need to return the access failed error, not the
-                        * share error. We can't open the file due to kernel
-                        * oplock deadlock (it's possible we failed above on
-                        * the open_mode_check()) so use a userspace check.
-                        */
-
-                       if (flags & O_RDWR) {
-                               can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA;
-                       } else if (flags & O_WRONLY) {
-                               can_access_mask = FILE_WRITE_DATA;
-                       } else {
-                               can_access_mask = FILE_READ_DATA;
-                       }
-
-                       if (((can_access_mask & FILE_WRITE_DATA) &&
-                               !CAN_WRITE(conn)) ||
-                               !NT_STATUS_IS_OK(smbd_check_access_rights(conn,
-                                               smb_fname, can_access_mask))) {
-                               can_access = False;
-                       }
-
-                       /*
-                        * If we're returning a share violation, ensure we
-                        * cope with the braindead 1 second delay.
-                        */
-
-                       if (!(oplock_request & INTERNAL_OPEN_ONLY) &&
-                           lp_defer_sharing_violations()) {
-                               struct timeval timeout;
-                               struct deferred_open_record state;
-                               int timeout_usecs;
-
-                               /* this is a hack to speed up torture tests
-                                  in 'make test' */
-                               timeout_usecs = lp_parm_int(SNUM(conn),
-                                                           "smbd","sharedelay",
-                                                           SHARING_VIOLATION_USEC_WAIT);
-
-                               /* This is a relative time, added to the absolute
-                                  request_time value to get the absolute timeout time.
-                                  Note that if this is the second or greater time we enter
-                                  this codepath for this particular request mid then
-                                  request_time is left as the absolute time of the *first*
-                                  time this request mid was processed. This is what allows
-                                  the request to eventually time out. */
-
-                               timeout = timeval_set(0, timeout_usecs);
-
-                               /* Nothing actually uses state.delayed_for_oplocks
-                                  but it's handy to differentiate in debug messages
-                                  between a 30 second delay due to oplock break, and
-                                  a 1 second delay for share mode conflicts. */
-
-                               state.delayed_for_oplocks = False;
-                               state.async_open = false;
-                               state.id = id;
-
-                               if ((req != NULL)
-                                   && !request_timed_out(request_time,
-                                                         timeout)) {
-                                       defer_open(lck, request_time, timeout,
-                                                  req, &state);
-                               }
-                       }
-
-                       TALLOC_FREE(lck);
-                       if (can_access) {
-                               /*
-                                * We have detected a sharing violation here
-                                * so return the correct error code
-                                */
-                               status = NT_STATUS_SHARING_VIOLATION;
-                       } else {
-                               status = NT_STATUS_ACCESS_DENIED;
-                       }
-                       return status;
-               }
-
-               /*
-                * We exit this block with the share entry *locked*.....
-                */
-       }
-
-       SMB_ASSERT(!file_existed || (lck != NULL));
-
        /*
         * Ensure we pay attention to default ACLs on directories if required.
         */
        /*
         * Ensure we pay attention to default ACLs on directories if required.
         */
@@ -2263,13 +2278,67 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 (unsigned int)unx_mode, (unsigned int)access_mask,
                 (unsigned int)open_access_mask));
 
                 (unsigned int)unx_mode, (unsigned int)access_mask,
                 (unsigned int)open_access_mask));
 
-       /*
-        * open_file strips any O_TRUNC flags itself.
-        */
-
        fsp_open = open_file(fsp, conn, req, parent_dir,
                             flags|flags2, unx_mode, access_mask,
        fsp_open = open_file(fsp, conn, req, parent_dir,
                             flags|flags2, unx_mode, access_mask,
-                            open_access_mask);
+                            open_access_mask, &new_file_created);
+
+       if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
+               struct deferred_open_record state;
+
+               /*
+                * EWOULDBLOCK/EAGAIN maps to 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;
+               }
+
+               /*
+                * From here on we assume this is an oplock break triggered
+                */
+
+               lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
+               if (lck == NULL) {
+                       state.delayed_for_oplocks = false;
+                       state.async_open = false;
+                       state.id = fsp->file_id;
+                       defer_open(NULL, request_time, timeval_set(0, 0),
+                                  req, &state);
+                       DEBUG(10, ("No share mode lock found after "
+                                  "EWOULDBLOCK, retrying sync\n"));
+                       return NT_STATUS_SHARING_VIOLATION;
+               }
+
+               find_oplock_types(fsp, 0, lck, &batch_entry, &exclusive_entry,
+                                 &got_level2_oplock, &got_a_none_oplock);
+
+               if (delay_for_batch_oplocks(fsp, req->mid, 0, batch_entry) ||
+                   delay_for_exclusive_oplocks(fsp, req->mid, 0,
+                                               exclusive_entry)) {
+                       schedule_defer_open(lck, request_time, req);
+                       TALLOC_FREE(lck);
+                       DEBUG(10, ("Sent oplock break request to kernel "
+                                  "oplock holder\n"));
+                       return NT_STATUS_SHARING_VIOLATION;
+               }
+
+               /*
+                * No oplock from Samba around. Immediately retry with
+                * a blocking open.
+                */
+               state.delayed_for_oplocks = false;
+               state.async_open = false;
+               state.id = lck->data->id;
+               defer_open(lck, request_time, timeval_set(0, 0), req, &state);
+               TALLOC_FREE(lck);
+               DEBUG(10, ("No Samba oplock around after EWOULDBLOCK. "
+                          "Retrying sync\n"));
+               return NT_STATUS_SHARING_VIOLATION;
+       }
 
        if (!NT_STATUS_IS_OK(fsp_open)) {
                if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
 
        if (!NT_STATUS_IS_OK(fsp_open)) {
                if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
@@ -2279,123 +2348,248 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return fsp_open;
        }
 
                return fsp_open;
        }
 
-       if (!file_existed) {
-               struct share_mode_entry *batch_entry = NULL;
-               struct share_mode_entry *exclusive_entry = NULL;
-               bool got_level2_oplock = false;
-               bool got_a_none_oplock = false;
-               struct timespec old_write_time = smb_fname->st.st_ex_mtime;
-               struct file_id id;
+       if (file_existed && !check_same_dev_ino(&saved_stat, &smb_fname->st)) {
                /*
                /*
-                * Deal with the race condition where two smbd's detect the
-                * file doesn't exist and do the create at the same time. One
-                * of them will win and set a share mode, the other (ie. this
-                * one) should check if the requested share mode for this
-                * create is allowed.
+                * The file did exist, but some other (local or NFS)
+                * process either renamed/unlinked and re-created the
+                * file with different dev/ino after we walked the path,
+                * but before we did the open. We could retry the
+                * open but it's a rare enough case it's easier to
+                * just fail the open to prevent creating any problems
+                * in the open file db having the wrong dev/ino key.
                 */
                 */
+               TALLOC_FREE(lck);
+               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));
+               return NT_STATUS_ACCESS_DENIED;
+       }
 
 
-               /*
-                * Now the file exists and fsp is successfully opened,
-                * fsp->dev and fsp->inode are valid and should replace the
-                * dev=0,inode=0 from a non existent file. Spotted by
-                * Nadav Danieli <nadavd@exanet.com>. JRA.
-                */
+       old_write_time = smb_fname->st.st_ex_mtime;
 
 
-               id = fsp->file_id;
+       /*
+        * Deal with the race condition where two smbd's detect the
+        * file doesn't exist and do the create at the same time. One
+        * of them will win and set a share mode, the other (ie. this
+        * one) should check if the requested share mode for this
+        * create is allowed.
+        */
 
 
-               lck = get_share_mode_lock(talloc_tos(), id,
-                                         conn->connectpath,
-                                         smb_fname, &old_write_time);
+       /*
+        * Now the file exists and fsp is successfully opened,
+        * fsp->dev and fsp->inode are valid and should replace the
+        * dev=0,inode=0 from a non existent file. Spotted by
+        * Nadav Danieli <nadavd@exanet.com>. JRA.
+        */
 
 
-               if (lck == NULL) {
-                       DEBUG(0, ("open_file_ntcreate: Could not get share "
-                                 "mode lock for %s\n",
-                                 smb_fname_str_dbg(smb_fname)));
-                       fd_close(fsp);
-                       return NT_STATUS_SHARING_VIOLATION;
-               }
+       id = fsp->file_id;
+
+       lck = get_share_mode_lock(talloc_tos(), id,
+                                 conn->connectpath,
+                                 smb_fname, &old_write_time);
+
+       if (lck == NULL) {
+               DEBUG(0, ("open_file_ntcreate: Could not get share "
+                         "mode lock for %s\n",
+                         smb_fname_str_dbg(smb_fname)));
+               fd_close(fsp);
+               return NT_STATUS_SHARING_VIOLATION;
+       }
+
+       /* Get the types we need to examine. */
+       find_oplock_types(fsp,
+                         oplock_request,
+                         lck,
+                         &batch_entry,
+                         &exclusive_entry,
+                         &got_level2_oplock,
+                         &got_a_none_oplock);
+
+       /* First pass - send break only on batch oplocks. */
+       if ((req != NULL) &&
+           delay_for_batch_oplocks(fsp,
+                                   req->mid,
+                                   oplock_request,
+                                   batch_entry)) {
+               schedule_defer_open(lck, request_time, req);
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               return NT_STATUS_SHARING_VIOLATION;
+       }
 
 
-               /* Get the types we need to examine. */
-               find_oplock_types(fsp,
-                               oplock_request,
-                               lck,
-                               &batch_entry,
-                               &exclusive_entry,
-                               &got_level2_oplock,
-                               &got_a_none_oplock);
+       status = open_mode_check(conn, lck, fsp->name_hash,
+                                access_mask, share_access,
+                                create_options, &file_existed);
 
 
-               /* First pass - send break only on batch oplocks. */
+       if (NT_STATUS_IS_OK(status)) {
+               /* We might be going to allow this open. Check oplock
+                * status again. */
+               /* Second pass - send break for both batch or
+                * exclusive oplocks. */
                if ((req != NULL) &&
                if ((req != NULL) &&
-                               delay_for_batch_oplocks(fsp,
-                                       req->mid,
-                                       oplock_request,
-                                       batch_entry)) {
+                   delay_for_exclusive_oplocks(
+                           fsp,
+                           req->mid,
+                           oplock_request,
+                           exclusive_entry)) {
                        schedule_defer_open(lck, request_time, req);
                        TALLOC_FREE(lck);
                        fd_close(fsp);
                        return NT_STATUS_SHARING_VIOLATION;
                }
                        schedule_defer_open(lck, request_time, req);
                        TALLOC_FREE(lck);
                        fd_close(fsp);
                        return NT_STATUS_SHARING_VIOLATION;
                }
+       }
 
 
-               status = open_mode_check(conn, lck, fsp->name_hash,
-                                       access_mask, share_access,
-                                        create_options, &file_existed);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
+               /* DELETE_PENDING is not deferred for a second */
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               return status;
+       }
 
 
-               if (NT_STATUS_IS_OK(status)) {
-                       /* We might be going to allow this open. Check oplock
-                        * status again. */
-                       /* Second pass - send break for both batch or
-                        * exclusive oplocks. */
-                       if ((req != NULL) &&
-                                       delay_for_exclusive_oplocks(
-                                               fsp,
-                                               req->mid,
-                                               oplock_request,
-                                               exclusive_entry)) {
-                               schedule_defer_open(lck, request_time, req);
+       if (!NT_STATUS_IS_OK(status)) {
+               uint32 can_access_mask;
+               bool can_access = True;
+
+               SMB_ASSERT(NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION));
+
+               /* Check if this can be done with the deny_dos and fcb
+                * calls. */
+               if (private_flags &
+                   (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS|
+                    NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
+                       if (req == NULL) {
+                               DEBUG(0, ("DOS open without an SMB "
+                                         "request!\n"));
                                TALLOC_FREE(lck);
                                fd_close(fsp);
                                TALLOC_FREE(lck);
                                fd_close(fsp);
-                               return NT_STATUS_SHARING_VIOLATION;
+                               return NT_STATUS_INTERNAL_ERROR;
+                       }
+
+                       /* Use the client requested access mask here,
+                        * not the one we open with. */
+                       status = fcb_or_dos_open(req,
+                                                conn,
+                                                fsp,
+                                                smb_fname,
+                                                id,
+                                                req->smbpid,
+                                                req->vuid,
+                                                access_mask,
+                                                share_access,
+                                                create_options);
+
+                       if (NT_STATUS_IS_OK(status)) {
+                               TALLOC_FREE(lck);
+                               if (pinfo) {
+                                       *pinfo = FILE_WAS_OPENED;
+                               }
+                               return NT_STATUS_OK;
                        }
                }
 
                        }
                }
 
-               if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * This next line is a subtlety we need for
+                * MS-Access. If a file open will fail due to share
+                * permissions and also for security (access) reasons,
+                * we need to return the access failed error, not the
+                * share error. We can't open the file due to kernel
+                * oplock deadlock (it's possible we failed above on
+                * the open_mode_check()) so use a userspace check.
+                */
+
+               if (flags & O_RDWR) {
+                       can_access_mask = FILE_READ_DATA|FILE_WRITE_DATA;
+               } else if (flags & O_WRONLY) {
+                       can_access_mask = FILE_WRITE_DATA;
+               } else {
+                       can_access_mask = FILE_READ_DATA;
+               }
+
+               if (((can_access_mask & FILE_WRITE_DATA) &&
+                    !CAN_WRITE(conn)) ||
+                   !NT_STATUS_IS_OK(smbd_check_access_rights(conn,
+                                                             smb_fname,
+                                                             false,
+                                                             can_access_mask))) {
+                       can_access = False;
+               }
+
+               /*
+                * If we're returning a share violation, ensure we
+                * cope with the braindead 1 second delay.
+                */
+
+               if (!(oplock_request & INTERNAL_OPEN_ONLY) &&
+                   lp_defer_sharing_violations()) {
+                       struct timeval timeout;
                        struct deferred_open_record state;
                        struct deferred_open_record state;
+                       int timeout_usecs;
+
+                       /* this is a hack to speed up torture tests
+                          in 'make test' */
+                       timeout_usecs = lp_parm_int(SNUM(conn),
+                                                   "smbd","sharedelay",
+                                                   SHARING_VIOLATION_USEC_WAIT);
+
+                       /* This is a relative time, added to the absolute
+                          request_time value to get the absolute timeout time.
+                          Note that if this is the second or greater time we enter
+                          this codepath for this particular request mid then
+                          request_time is left as the absolute time of the *first*
+                          time this request mid was processed. This is what allows
+                          the request to eventually time out. */
+
+                       timeout = timeval_set(0, timeout_usecs);
+
+                       /* Nothing actually uses state.delayed_for_oplocks
+                          but it's handy to differentiate in debug messages
+                          between a 30 second delay due to oplock break, and
+                          a 1 second delay for share mode conflicts. */
 
                        state.delayed_for_oplocks = False;
                        state.async_open = false;
                        state.id = id;
 
 
                        state.delayed_for_oplocks = False;
                        state.async_open = false;
                        state.id = id;
 
-                       /* Do it all over again immediately. In the second
-                        * round we will find that the file existed and handle
-                        * the DELETE_PENDING and FCB cases correctly. No need
-                        * to duplicate the code here. Essentially this is a
-                        * "goto top of this function", but don't tell
-                        * anybody... */
-
-                       if (req != NULL) {
-                               defer_open(lck, request_time, timeval_zero(),
+                       if ((req != NULL)
+                           && !request_timed_out(request_time,
+                                                 timeout)) {
+                               defer_open(lck, request_time, timeout,
                                           req, &state);
                        }
                                           req, &state);
                        }
-                       TALLOC_FREE(lck);
-                       fd_close(fsp);
-                       return status;
                }
 
                }
 
-               grant_fsp_oplock_type(fsp,
-                                oplock_request,
-                                got_level2_oplock,
-                                got_a_none_oplock);
-
-               /*
-                * We exit this block with the share entry *locked*.....
-                */
-
+               TALLOC_FREE(lck);
+               fd_close(fsp);
+               if (can_access) {
+                       /*
+                        * We have detected a sharing violation here
+                        * so return the correct error code
+                        */
+                       status = NT_STATUS_SHARING_VIOLATION;
+               } else {
+                       status = NT_STATUS_ACCESS_DENIED;
+               }
+               return status;
        }
 
        }
 
-       SMB_ASSERT(lck != NULL);
+       grant_fsp_oplock_type(fsp,
+                             oplock_request,
+                             got_level2_oplock,
+                             got_a_none_oplock);
+
+       /*
+        * We have the share entry *locked*.....
+        */
 
        /* Delete streams if create_disposition requires it */
 
        /* Delete streams if create_disposition requires it */
-       if (file_existed && clear_ads &&
+       if (!new_file_created && clear_ads(create_disposition) &&
            !is_ntfs_stream_smb_fname(smb_fname)) {
                status = delete_all_streams(conn, smb_fname->base_name);
                if (!NT_STATUS_IS_OK(status)) {
            !is_ntfs_stream_smb_fname(smb_fname)) {
                status = delete_all_streams(conn, smb_fname->base_name);
                if (!NT_STATUS_IS_OK(status)) {
@@ -2413,7 +2607,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
            the kernel refuses the operations then the kernel is wrong.
           note that GPFS supports it as well - jmcd */
 
            the kernel refuses the operations then the kernel is wrong.
           note that GPFS supports it as well - jmcd */
 
-       if (fsp->fh->fd != -1) {
+       if (fsp->fh->fd != -1 && lp_kernel_share_modes(SNUM(conn))) {
                int ret_flock;
                ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access, access_mask);
                if(ret_flock == -1 ){
                int ret_flock;
                ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access, access_mask);
                if(ret_flock == -1 ){
@@ -2426,29 +2620,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        /*
        }
 
        /*
-        * At this point onwards, we can guarentee that the share entry
+        * At this point onwards, we can guarantee that the share entry
         * is locked, whether we created the file or not, and that the
         * deny mode is compatible with all current opens.
         */
 
         * is locked, whether we created the file or not, and that the
         * deny mode is compatible with all current opens.
         */
 
-       /*
-        * If requested, truncate the file.
-        */
-
-       if (file_existed && (flags2&O_TRUNC)) {
-               /*
-                * We are modifying the file after open - update the stat
-                * struct..
-                */
-               if ((SMB_VFS_FTRUNCATE(fsp, 0) == -1) ||
-                   (SMB_VFS_FSTAT(fsp, &smb_fname->st)==-1)) {
-                       status = map_nt_error_from_unix(errno);
-                       TALLOC_FREE(lck);
-                       fd_close(fsp);
-                       return status;
-               }
-       }
-
        /*
         * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted,
         * but we don't have to store this - just ignore it on access check.
        /*
         * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted,
         * but we don't have to store this - just ignore it on access check.
@@ -2471,14 +2647,16 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                if (is_stat_open(open_access_mask)) {
                        fsp->oplock_type = NO_OPLOCK;
                }
                if (is_stat_open(open_access_mask)) {
                        fsp->oplock_type = NO_OPLOCK;
                }
+       }
 
 
+       if (new_file_created) {
+               info = FILE_WAS_CREATED;
+       } else {
                if (flags2 & O_TRUNC) {
                        info = FILE_WAS_OVERWRITTEN;
                } else {
                        info = FILE_WAS_OPENED;
                }
                if (flags2 & O_TRUNC) {
                        info = FILE_WAS_OVERWRITTEN;
                } else {
                        info = FILE_WAS_OPENED;
                }
-       } else {
-               info = FILE_WAS_CREATED;
        }
 
        if (pinfo) {
        }
 
        if (pinfo) {
@@ -2520,13 +2698,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                fsp->initial_delete_on_close = True;
        }
 
                fsp->initial_delete_on_close = True;
        }
 
-       if (info == FILE_WAS_OVERWRITTEN
-           || info == FILE_WAS_CREATED
-           || info == FILE_WAS_SUPERSEDED) {
-               new_file_created = True;
-       }
-
-       if (new_file_created) {
+       if (info != FILE_WAS_OPENED) {
                /* Files should be initially set as archive */
                if (lp_map_archive(SNUM(conn)) ||
                    lp_store_dos_attributes(SNUM(conn))) {
                /* Files should be initially set as archive */
                if (lp_map_archive(SNUM(conn)) ||
                    lp_store_dos_attributes(SNUM(conn))) {
@@ -2554,7 +2726,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         * selected.
         */
 
         * selected.
         */
 
-       if (!posix_open && !file_existed && !def_acl) {
+       if (!posix_open && new_file_created && !def_acl) {
 
                int saved_errno = errno; /* We might get ENOSYS in the next
                                          * call.. */
 
                int saved_errno = errno; /* We might get ENOSYS in the next
                                          * call.. */
@@ -2649,9 +2821,9 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        bool need_re_stat = false;
        uint32_t access_mask = SEC_DIR_ADD_SUBDIR;
 
        bool need_re_stat = false;
        uint32_t access_mask = SEC_DIR_ADD_SUBDIR;
 
-       if(access_mask & ~(conn->share_access)) {
+       if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
                DEBUG(5,("mkdir_internal: failing share access "
                DEBUG(5,("mkdir_internal: failing share access "
-                        "%s\n", lp_servicename(SNUM(conn))));
+                        "%s\n", lp_servicename(talloc_tos(), SNUM(conn))));
                return NT_STATUS_ACCESS_DENIED;
        }
 
                return NT_STATUS_ACCESS_DENIED;
        }
 
@@ -2750,21 +2922,6 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        return NT_STATUS_OK;
 }
 
        return NT_STATUS_OK;
 }
 
-/****************************************************************************
- Ensure we didn't get symlink raced on opening a directory.
-****************************************************************************/
-
-bool check_same_stat(const SMB_STRUCT_STAT *sbuf1,
-                       const SMB_STRUCT_STAT *sbuf2)
-{
-       if (sbuf1->st_ex_uid != sbuf2->st_ex_uid ||
-                       sbuf1->st_ex_gid != sbuf2->st_ex_gid ||
-                       !check_same_dev_ino(sbuf1, sbuf2)) {
-               return false;
-       }
-       return true;
-}
-
 /****************************************************************************
  Open a directory from an NT SMB call.
 ****************************************************************************/
 /****************************************************************************
  Open a directory from an NT SMB call.
 ****************************************************************************/
@@ -2808,7 +2965,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                 (unsigned int)create_disposition,
                 (unsigned int)file_attributes));
 
                 (unsigned int)create_disposition,
                 (unsigned int)file_attributes));
 
-       status = smbd_calculate_access_mask(conn, smb_dname,
+       status = smbd_calculate_access_mask(conn, smb_dname, false,
                                            access_mask, &access_mask);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("open_directory: smbd_calculate_access_mask "
                                            access_mask, &access_mask);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("open_directory: smbd_calculate_access_mask "
@@ -2914,7 +3071,10 @@ static NTSTATUS open_directory(connection_struct *conn,
        }
 
        if (info == FILE_WAS_OPENED) {
        }
 
        if (info == FILE_WAS_OPENED) {
-               status = smbd_check_access_rights(conn, smb_dname, access_mask);
+               status = smbd_check_access_rights(conn,
+                                               smb_dname,
+                                               false,
+                                               access_mask);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("open_directory: smbd_check_access_rights on "
                                "file %s failed with %s\n",
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("open_directory: smbd_check_access_rights on "
                                "file %s failed with %s\n",
@@ -3287,7 +3447,7 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
 
 static NTSTATUS inherit_new_acl(files_struct *fsp)
 {
 
 static NTSTATUS inherit_new_acl(files_struct *fsp)
 {
-       TALLOC_CTX *ctx = talloc_tos();
+       TALLOC_CTX *frame = talloc_stackframe();
        char *parent_name = NULL;
        struct security_descriptor *parent_desc = NULL;
        NTSTATUS status = NT_STATUS_OK;
        char *parent_name = NULL;
        struct security_descriptor *parent_desc = NULL;
        NTSTATUS status = NT_STATUS_OK;
@@ -3299,15 +3459,18 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        bool inheritable_components = false;
        size_t size = 0;
 
        bool inheritable_components = false;
        size_t size = 0;
 
-       if (!parent_dirname(ctx, fsp->fsp_name->base_name, &parent_name, NULL)) {
+       if (!parent_dirname(frame, fsp->fsp_name->base_name, &parent_name, NULL)) {
+               TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
 
        status = SMB_VFS_GET_NT_ACL(fsp->conn,
                return NT_STATUS_NO_MEMORY;
        }
 
        status = SMB_VFS_GET_NT_ACL(fsp->conn,
-                               parent_name,
-                               (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
-                               &parent_desc);
+                                   parent_name,
+                                   (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
+                                   frame,
+                                   &parent_desc);
        if (!NT_STATUS_IS_OK(status)) {
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
                return status;
        }
 
@@ -3315,6 +3478,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                                        fsp->is_directory);
 
        if (!inheritable_components && !inherit_owner) {
                                        fsp->is_directory);
 
        if (!inheritable_components && !inherit_owner) {
+               TALLOC_FREE(frame);
                /* Nothing to inherit and not setting owner. */
                return NT_STATUS_OK;
        }
                /* Nothing to inherit and not setting owner. */
                return NT_STATUS_OK;
        }
@@ -3340,7 +3504,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX];
        }
 
                group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX];
        }
 
-       status = se_create_child_secdesc(ctx,
+       status = se_create_child_secdesc(frame,
                        &psd,
                        &size,
                        parent_desc,
                        &psd,
                        &size,
                        parent_desc,
@@ -3348,6 +3512,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                        group_sid,
                        fsp->is_directory);
        if (!NT_STATUS_IS_OK(status)) {
                        group_sid,
                        fsp->is_directory);
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
                return status;
        }
 
@@ -3378,6 +3543,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        if (inherit_owner) {
                unbecome_root();
        }
        if (inherit_owner) {
                unbecome_root();
        }
+       TALLOC_FREE(frame);
        return status;
 }
 
        return status;
 }
 
@@ -3559,14 +3725,20 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                        goto fail;
                }
 
                        goto fail;
                }
 
-               /*
-                * We're opening the stream element of a base_fsp
-                * we already opened. Set up the base_fsp pointer.
-                */
                if (base_fsp) {
                if (base_fsp) {
+                       /*
+                        * We're opening the stream element of a
+                        * base_fsp we already opened. Set up the
+                        * base_fsp pointer.
+                        */
                        fsp->base_fsp = base_fsp;
                }
 
                        fsp->base_fsp = base_fsp;
                }
 
+               if (allocation_size) {
+                       fsp->initial_allocation_size = smb_roundup(fsp->conn,
+                                                       allocation_size);
+               }
+
                status = open_file_ntcreate(conn,
                                            req,
                                            access_mask,
                status = open_file_ntcreate(conn,
                                            req,
                                            access_mask,
@@ -3651,6 +3823,8 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                        fsp->initial_allocation_size = smb_roundup(
                                fsp->conn, (uint64_t)fsp->fsp_name->st.st_ex_size);
                }
                        fsp->initial_allocation_size = smb_roundup(
                                fsp->conn, (uint64_t)fsp->fsp_name->st.st_ex_size);
                }
+       } else {
+               fsp->initial_allocation_size = 0;
        }
 
        if ((info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn)) &&
        }
 
        if ((info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn)) &&
@@ -3674,15 +3848,11 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 
                        fsp->access_mask = FILE_GENERIC_ALL;
 
 
                        fsp->access_mask = FILE_GENERIC_ALL;
 
-                       /* Convert all the generic bits. */
-                       security_acl_map_generic(sd->dacl, &file_generic_mapping);
-                       security_acl_map_generic(sd->sacl, &file_generic_mapping);
-
                        if (sec_info_sent & (SECINFO_OWNER|
                                                SECINFO_GROUP|
                                                SECINFO_DACL|
                                                SECINFO_SACL)) {
                        if (sec_info_sent & (SECINFO_OWNER|
                                                SECINFO_GROUP|
                                                SECINFO_DACL|
                                                SECINFO_SACL)) {
-                               status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd);
+                               status = set_sd(fsp, sd, sec_info_sent);
                        }
 
                        fsp->access_mask = saved_access_mask;
                        }
 
                        fsp->access_mask = saved_access_mask;