smbd: Add brl_num_locks access function
[mat/samba.git] / source3 / smbd / open.c
index 0f34992454bcad73bd06f931488ce336ed5acfb9..1a86233fd9ff2faaf553a86bfa3f79bee5c4b204 100644 (file)
 #include "../libcli/security/security.h"
 #include "../librpc/gen_ndr/ndr_security.h"
 #include "../librpc/gen_ndr/open_files.h"
+#include "../librpc/gen_ndr/idmap.h"
+#include "passdb/lookup_sid.h"
 #include "auth.h"
 #include "serverid.h"
 #include "messages.h"
+#include "source3/lib/dbwrap/dbwrap_watch.h"
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -73,6 +76,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        struct security_descriptor *sd = NULL;
        uint32_t rejected_share_access;
        uint32_t rejected_mask = access_mask;
+       uint32_t do_not_check_mask = 0;
 
        rejected_share_access = access_mask & ~(conn->share_access);
 
@@ -115,7 +119,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 |
-                       SECINFO_DACL),&sd);
+                        SECINFO_DACL), talloc_tos(), &sd);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("smbd_check_access_rights: Could not get acl "
@@ -131,13 +135,32 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_file_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.
         */
+       do_not_check_mask = FILE_READ_ATTRIBUTES;
+
+       /*
+        * Samba 3.6 and earlier granted execute access even
+        * if the ACL did not contain execute rights.
+        * Samba 4.0 is more correct and checks it.
+        * The compatibilty mode allows to skip this check
+        * to smoothen upgrades.
+        */
+       if (lp_acl_allow_execute_always(SNUM(conn))) {
+               do_not_check_mask |= FILE_EXECUTE;
+       }
+
        status = se_file_access_check(sd,
                                get_current_nttok(conn),
                                use_privs,
-                               (access_mask & ~FILE_READ_ATTRIBUTES),
+                               (access_mask & ~do_not_check_mask),
                                &rejected_mask);
 
        DEBUG(10,("smbd_check_access_rights: file %s requesting "
@@ -237,6 +260,7 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        status = SMB_VFS_GET_NT_ACL(conn,
                                parent_dir,
                                SECINFO_DACL,
+                                   talloc_tos(),
                                &parent_sd);
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -248,7 +272,13 @@ static NTSTATUS check_parent_access(struct connection_struct *conn,
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_file_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.
         */
        status = se_file_access_check(parent_sd,
@@ -372,13 +402,12 @@ void change_file_owner_to_parent(connection_struct *conn,
                                        const char *inherit_from_dir,
                                        files_struct *fsp)
 {
-       struct smb_filename *smb_fname_parent = NULL;
-       NTSTATUS status;
+       struct smb_filename *smb_fname_parent;
        int ret;
 
-       status = create_synthetic_smb_fname(talloc_tos(), inherit_from_dir,
-                                           NULL, NULL, &smb_fname_parent);
-       if (!NT_STATUS_IS_OK(status)) {
+       smb_fname_parent = synthetic_smb_fname(talloc_tos(), inherit_from_dir,
+                                              NULL, NULL);
+       if (smb_fname_parent == NULL) {
                return;
        }
 
@@ -427,17 +456,17 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                                       const char *fname,
                                       SMB_STRUCT_STAT *psbuf)
 {
-       struct smb_filename *smb_fname_parent = NULL;
+       struct smb_filename *smb_fname_parent;
        struct smb_filename *smb_fname_cwd = NULL;
        char *saved_dir = NULL;
        TALLOC_CTX *ctx = talloc_tos();
        NTSTATUS status = NT_STATUS_OK;
        int ret;
 
-       status = create_synthetic_smb_fname(ctx, inherit_from_dir, NULL, NULL,
-                                           &smb_fname_parent);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname_parent = synthetic_smb_fname(ctx, inherit_from_dir,
+                                              NULL, NULL);
+       if (smb_fname_parent == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        ret = SMB_VFS_STAT(conn, smb_fname_parent);
@@ -475,10 +504,10 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                goto chdir;
        }
 
-       status = create_synthetic_smb_fname(ctx, ".", NULL, NULL,
-                                           &smb_fname_cwd);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       smb_fname_cwd = synthetic_smb_fname(ctx, ".", NULL, NULL);
+       if (smb_fname_cwd == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto chdir;
        }
 
        ret = SMB_VFS_STAT(conn, smb_fname_cwd);
@@ -1010,15 +1039,6 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                return;
        }
 
-       if (is_deferred_open_entry(share_entry) &&
-           !open_was_deferred(sconn, share_entry->op_mid)) {
-               char *str = talloc_asprintf(talloc_tos(),
-                       "Got a deferred entry without a request: "
-                       "PANIC: %s\n",
-                       share_mode_str(talloc_tos(), num, share_entry));
-               smb_panic(str);
-       }
-
        if (!is_valid_share_mode_entry(share_entry)) {
                return;
        }
@@ -1032,12 +1052,9 @@ static void validate_my_share_entries(struct smbd_server_connection *sconn,
                          "share entry with an open file\n");
        }
 
-       if (is_deferred_open_entry(share_entry)) {
-               goto panic;
-       }
-
        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;
@@ -1181,18 +1198,9 @@ static NTSTATUS send_break_message(files_struct *fsp,
        /* Create the message. */
        share_mode_entry_to_message(msg, exclusive);
 
-       /* Add in the FORCE_OPLOCK_BREAK_TO_NONE bit in the message if set. We
-          don't want this set in the share mode struct pointed to by lck. */
-
-       if (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE) {
-               SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET,
-                       exclusive->op_type | FORCE_OPLOCK_BREAK_TO_NONE);
-       }
-
        status = messaging_send_buf(fsp->conn->sconn->msg_ctx, exclusive->pid,
                                    MSG_SMB_BREAK_REQUEST,
-                                   (uint8 *)msg,
-                                   MSG_SMB_SHARE_MODE_ENTRY_SIZE);
+                                   (uint8 *)msg, sizeof(msg));
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(3, ("Could not send oplock break message: %s\n",
                          nt_errstr(status)));
@@ -1234,19 +1242,20 @@ static void find_oplock_types(files_struct *fsp,
        }
 
        for (i=0; i<lck->data->num_share_modes; i++) {
-               if (!is_valid_share_mode_entry(&lck->data->share_modes[i])) {
+               struct share_mode_entry *e = &lck->data->share_modes[i];
+
+               if (!is_valid_share_mode_entry(e)) {
                        continue;
                }
 
-               if (lck->data->share_modes[i].op_type == NO_OPLOCK &&
-                               is_stat_open(lck->data->share_modes[i].access_mask)) {
+               if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
                        /* We ignore stat opens in the table - they
                           always have NO_OPLOCK and never get or
                           cause breaks. JRA. */
                        continue;
                }
 
-               if (BATCH_OPLOCK_TYPE(lck->data->share_modes[i].op_type)) {
+               if (BATCH_OPLOCK_TYPE(e->op_type)) {
                        /* batch - can only be one. */
                        if (share_mode_stale_pid(lck->data, i)) {
                                DEBUG(10, ("Found stale batch oplock\n"));
@@ -1255,10 +1264,10 @@ static void find_oplock_types(files_struct *fsp,
                        if (*pp_ex_or_batch || *pp_batch || *got_level2 || *got_no_oplock) {
                                smb_panic("Bad batch oplock entry.");
                        }
-                       *pp_batch = &lck->data->share_modes[i];
+                       *pp_batch = e;
                }
 
-               if (EXCLUSIVE_OPLOCK_TYPE(lck->data->share_modes[i].op_type)) {
+               if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
                        if (share_mode_stale_pid(lck->data, i)) {
                                DEBUG(10, ("Found stale duplicate oplock\n"));
                                continue;
@@ -1267,10 +1276,10 @@ static void find_oplock_types(files_struct *fsp,
                        if (*pp_ex_or_batch || *got_level2 || *got_no_oplock) {
                                smb_panic("Bad exclusive or batch oplock entry.");
                        }
-                       *pp_ex_or_batch = &lck->data->share_modes[i];
+                       *pp_ex_or_batch = e;
                }
 
-               if (LEVEL_II_OPLOCK_TYPE(lck->data->share_modes[i].op_type)) {
+               if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
                        if (*pp_batch || *pp_ex_or_batch) {
                                if (share_mode_stale_pid(lck->data, i)) {
                                        DEBUG(10, ("Found stale LevelII "
@@ -1282,7 +1291,7 @@ static void find_oplock_types(files_struct *fsp,
                        *got_level2 = true;
                }
 
-               if (lck->data->share_modes[i].op_type == NO_OPLOCK) {
+               if (e->op_type == NO_OPLOCK) {
                        if (*pp_batch || *pp_ex_or_batch) {
                                if (share_mode_stale_pid(lck->data, i)) {
                                        DEBUG(10, ("Found stale NO_OPLOCK "
@@ -1359,7 +1368,7 @@ static bool file_has_brlocks(files_struct *fsp)
        if (!br_lck)
                return false;
 
-       return br_lck->num_locks > 0 ? true : false;
+       return (brl_num_locks(br_lck) > 0);
 }
 
 static void grant_fsp_oplock_type(files_struct *fsp,
@@ -1441,6 +1450,13 @@ static bool request_timed_out(struct timeval request_time,
        return (timeval_compare(&end_time, &now) < 0);
 }
 
+struct defer_open_state {
+       struct smbd_server_connection *sconn;
+       uint64_t mid;
+};
+
+static void defer_open_done(struct tevent_req *req);
+
 /****************************************************************************
  Handle the 1 second delay in returning a SHARING_VIOLATION error.
 ****************************************************************************/
@@ -1451,30 +1467,6 @@ static void defer_open(struct share_mode_lock *lck,
                       struct smb_request *req,
                       struct deferred_open_record *state)
 {
-       struct server_id self = messaging_server_id(req->sconn->msg_ctx);
-
-       /* Paranoia check */
-
-       if (lck) {
-               int i;
-
-               for (i=0; i<lck->data->num_share_modes; i++) {
-                       struct share_mode_entry *e = &lck->data->share_modes[i];
-
-                       if (is_deferred_open_entry(e) &&
-                           serverid_equal(&self, &e->pid) &&
-                           (e->op_mid == 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");
-                       }
-               }
-       }
-
-       /* End paranoia check */
-
        DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred "
                  "open entry for mid %llu\n",
                  (unsigned int)request_time.tv_sec,
@@ -1487,8 +1479,59 @@ static void defer_open(struct share_mode_lock *lck,
                exit_server("push_deferred_open_message_smb failed");
        }
        if (lck) {
-               add_deferred_open(lck, req->mid, request_time, self, state->id);
+               struct defer_open_state *watch_state;
+               struct tevent_req *watch_req;
+               bool ret;
+
+               watch_state = talloc(req->sconn, struct defer_open_state);
+               if (watch_state == NULL) {
+                       exit_server("talloc failed");
+               }
+               watch_state->sconn = req->sconn;
+               watch_state->mid = req->mid;
+
+               DEBUG(10, ("defering mid %llu\n",
+                          (unsigned long long)req->mid));
+
+               watch_req = dbwrap_record_watch_send(
+                       watch_state, req->sconn->ev_ctx, lck->data->record,
+                       req->sconn->msg_ctx);
+               if (watch_req == NULL) {
+                       exit_server("Could not watch share mode record");
+               }
+               tevent_req_set_callback(watch_req, defer_open_done,
+                                       watch_state);
+
+               ret = tevent_req_set_endtime(
+                       watch_req, req->sconn->ev_ctx,
+                       timeval_sum(&request_time, &timeout));
+               SMB_ASSERT(ret);
+       }
+}
+
+static void defer_open_done(struct tevent_req *req)
+{
+       struct defer_open_state *state = tevent_req_callback_data(
+               req, struct defer_open_state);
+       NTSTATUS status;
+       bool ret;
+
+       status = dbwrap_record_watch_recv(req, talloc_tos(), NULL);
+       TALLOC_FREE(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(5, ("dbwrap_record_watch_recv returned %s\n",
+                         nt_errstr(status)));
+               /*
+                * Even if it failed, retry anyway. TODO: We need a way to
+                * tell a re-scheduled open about that error.
+                */
        }
+
+       DEBUG(10, ("scheduling mid %llu\n", (unsigned long long)state->mid));
+
+       ret = schedule_deferred_open_message_smb(state->sconn, state->mid);
+       SMB_ASSERT(ret);
+       TALLOC_FREE(state);
 }
 
 
@@ -1683,7 +1726,8 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
        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)) {
                /*
@@ -1700,7 +1744,13 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
        }
 
        /*
-        * Never test FILE_READ_ATTRIBUTES. se_file_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.
         */
        status = se_file_access_check(sd,
@@ -1778,19 +1828,6 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
  Remove the deferred open entry under lock.
 ****************************************************************************/
 
-void remove_deferred_open_entry(struct file_id id, uint64_t mid,
-                               struct server_id pid)
-{
-       struct share_mode_lock *lck = get_existing_share_mode_lock(
-               talloc_tos(), id);
-       if (lck == NULL) {
-               DEBUG(0, ("could not get share mode lock\n"));
-               return;
-       }
-       del_deferred_open_entry(lck, mid, pid);
-       TALLOC_FREE(lck);
-}
-
 /****************************************************************************
  Return true if this is a state pointer to an asynchronous create.
 ****************************************************************************/
@@ -1882,10 +1919,7 @@ static int calculate_open_access_flags(uint32_t access_mask,
         * 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));
-
+       need_write = (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA));
        if (!need_write) {
                return O_RDONLY;
        }
@@ -1929,6 +1963,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        bool def_acl = False;
        bool posix_open = False;
        bool new_file_created = 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;
@@ -2025,16 +2060,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        if (is_deferred_open_async(ptr)) {
                                SET_STAT_INVALID(smb_fname->st);
                                file_existed = false;
-                       } else {
-                               struct deferred_open_record *state = (struct deferred_open_record *)ptr;
-                               /* Remove the deferred open entry under lock. */
-                               remove_deferred_open_entry(
-                                       state->id, req->mid,
-                                       messaging_server_id(req->sconn->msg_ctx));
                        }
 
                        /* Ensure we don't reprocess this message. */
                        remove_deferred_open_message_smb(req->sconn, req->mid);
+
+                       first_open_attempt = false;
                }
        }
 
@@ -2152,7 +2183,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
        open_access_mask = access_mask;
 
-       if ((flags2 & O_TRUNC) || (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) {
+       if (flags2 & O_TRUNC) {
                open_access_mask |= FILE_WRITE_DATA; /* This will cause oplock breaks. */
        }
 
@@ -2191,6 +2222,24 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                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.
         */
@@ -2239,11 +2288,68 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                             flags|flags2, unx_mode, 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)) {
                        schedule_async_open(request_time, req);
                }
-               TALLOC_FREE(lck);
                return fsp_open;
        }
 
@@ -2257,7 +2363,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 * 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). "
@@ -2422,10 +2527,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
                /*
                 * If we're returning a share violation, ensure we
-                * cope with the braindead 1 second delay.
+                * cope with the braindead 1 second delay (SMB1 only).
                 */
 
                if (!(oplock_request & INTERNAL_OPEN_ONLY) &&
+                   !conn->sconn->using_smb2 &&
                    lp_defer_sharing_violations()) {
                        struct timeval timeout;
                        struct deferred_open_record state;
@@ -2665,12 +2771,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                  (unsigned int)new_unx_mode));
        }
 
-       /* If this is a successful open, we must remove any deferred open
-        * records. */
-       if (req != NULL) {
-               del_deferred_open_entry(lck, req->mid,
-                                       messaging_server_id(req->sconn->msg_ctx));
-       }
        TALLOC_FREE(lck);
 
        return NT_STATUS_OK;
@@ -3180,9 +3280,9 @@ void msg_file_was_renamed(struct messaging_context *msg,
                stream_name = NULL;
        }
 
-       status = create_synthetic_smb_fname(talloc_tos(), base_name,
-                                           stream_name, NULL, &smb_fname);
-       if (!NT_STATUS_IS_OK(status)) {
+       smb_fname = synthetic_smb_fname(talloc_tos(), base_name,
+                                       stream_name, NULL);
+       if (smb_fname == NULL) {
                return;
        }
 
@@ -3269,17 +3369,17 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
        }
 
        for (i=0; i<num_streams; i++) {
-               struct smb_filename *smb_fname = NULL;
+               struct smb_filename *smb_fname;
 
                if (strequal(stream_info[i].name, "::$DATA")) {
                        streams[i] = NULL;
                        continue;
                }
 
-               status = create_synthetic_smb_fname(talloc_tos(), fname,
-                                                   stream_info[i].name,
-                                                   NULL, &smb_fname);
-               if (!NT_STATUS_IS_OK(status)) {
+               smb_fname = synthetic_smb_fname(
+                       talloc_tos(), fname, stream_info[i].name, NULL);
+               if (smb_fname == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto fail;
                }
 
@@ -3346,27 +3446,37 @@ NTSTATUS open_streams_for_delete(connection_struct *conn,
 
 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;
        struct security_descriptor *psd = NULL;
-       struct dom_sid *owner_sid = NULL;
-       struct dom_sid *group_sid = NULL;
+       const struct dom_sid *owner_sid = NULL;
+       const struct dom_sid *group_sid = NULL;
        uint32_t security_info_sent = (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL);
+       struct security_token *token = fsp->conn->session_info->security_token;
        bool inherit_owner = lp_inherit_owner(SNUM(fsp->conn));
        bool inheritable_components = false;
+       bool try_builtin_administrators = false;
+       const struct dom_sid *BA_U_sid = NULL;
+       const struct dom_sid *BA_G_sid = NULL;
+       bool try_system = false;
+       const struct dom_sid *SY_U_sid = NULL;
+       const struct dom_sid *SY_G_sid = NULL;
        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,
-                               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)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
@@ -3374,6 +3484,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                                        fsp->is_directory);
 
        if (!inheritable_components && !inherit_owner) {
+               TALLOC_FREE(frame);
                /* Nothing to inherit and not setting owner. */
                return NT_STATUS_OK;
        }
@@ -3393,13 +3504,99 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        }
 
        if (owner_sid == NULL) {
-               owner_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_USER_SID_INDEX];
+               if (security_token_has_builtin_administrators(token)) {
+                       try_builtin_administrators = true;
+               } else if (security_token_is_system(token)) {
+                       try_builtin_administrators = true;
+                       try_system = true;
+               }
+       }
+
+       if (group_sid == NULL &&
+           token->num_sids == PRIMARY_GROUP_SID_INDEX)
+       {
+               if (security_token_is_system(token)) {
+                       try_builtin_administrators = true;
+                       try_system = true;
+               }
+       }
+
+       if (try_builtin_administrators) {
+               struct unixid ids;
+               bool ok;
+
+               ZERO_STRUCT(ids);
+               ok = sids_to_unixids(&global_sid_Builtin_Administrators, 1, &ids);
+               if (ok) {
+                       switch (ids.type) {
+                       case ID_TYPE_BOTH:
+                               BA_U_sid = &global_sid_Builtin_Administrators;
+                               BA_G_sid = &global_sid_Builtin_Administrators;
+                               break;
+                       case ID_TYPE_UID:
+                               BA_U_sid = &global_sid_Builtin_Administrators;
+                               break;
+                       case ID_TYPE_GID:
+                               BA_G_sid = &global_sid_Builtin_Administrators;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       if (try_system) {
+               struct unixid ids;
+               bool ok;
+
+               ZERO_STRUCT(ids);
+               ok = sids_to_unixids(&global_sid_System, 1, &ids);
+               if (ok) {
+                       switch (ids.type) {
+                       case ID_TYPE_BOTH:
+                               SY_U_sid = &global_sid_System;
+                               SY_G_sid = &global_sid_System;
+                               break;
+                       case ID_TYPE_UID:
+                               SY_U_sid = &global_sid_System;
+                               break;
+                       case ID_TYPE_GID:
+                               SY_G_sid = &global_sid_System;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       if (owner_sid == NULL) {
+               owner_sid = BA_U_sid;
        }
+
+       if (owner_sid == NULL) {
+               owner_sid = SY_U_sid;
+       }
+
        if (group_sid == NULL) {
-               group_sid = &fsp->conn->session_info->security_token->sids[PRIMARY_GROUP_SID_INDEX];
+               group_sid = SY_G_sid;
        }
 
-       status = se_create_child_secdesc(ctx,
+       if (try_system && group_sid == NULL) {
+               group_sid = BA_G_sid;
+       }
+
+       if (owner_sid == NULL) {
+               owner_sid = &token->sids[PRIMARY_USER_SID_INDEX];
+       }
+       if (group_sid == NULL) {
+               if (token->num_sids == PRIMARY_GROUP_SID_INDEX) {
+                       group_sid = &token->sids[PRIMARY_USER_SID_INDEX];
+               } else {
+                       group_sid = &token->sids[PRIMARY_GROUP_SID_INDEX];
+               }
+       }
+
+       status = se_create_child_secdesc(frame,
                        &psd,
                        &size,
                        parent_desc,
@@ -3407,6 +3604,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                        group_sid,
                        fsp->is_directory);
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
                return status;
        }
 
@@ -3437,6 +3635,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        if (inherit_owner) {
                unbecome_root();
        }
+       TALLOC_FREE(frame);
        return status;
 }
 
@@ -3540,11 +3739,11 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
 
                /* Create an smb_filename with stream_name == NULL. */
-               status = create_synthetic_smb_fname(talloc_tos(),
-                                                   smb_fname->base_name,
-                                                   NULL, NULL,
-                                                   &smb_fname_base);
-               if (!NT_STATUS_IS_OK(status)) {
+               smb_fname_base = synthetic_smb_fname(talloc_tos(),
+                                                    smb_fname->base_name,
+                                                    NULL, NULL);
+               if (smb_fname_base == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
                        goto fail;
                }