smbd: convert parent_dir arg of open_file() to struct smb_filename
[samba.git] / source3 / smbd / open.c
index 9493021c48dc1147fb29a8f6a24c42b392473ed5..ad913f723a5a878ac45de152b06fdaef27ee3632 100644 (file)
@@ -262,31 +262,19 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                                uint32_t access_mask)
 {
        NTSTATUS status;
-       char *parent_dir = NULL;
        struct security_descriptor *parent_sd = NULL;
        uint32_t access_granted = 0;
-       struct smb_filename *parent_smb_fname = NULL;
+       struct smb_filename *parent_dir = NULL;
        struct share_mode_lock *lck = NULL;
        struct file_id id = {0};
        uint32_t name_hash;
        bool delete_on_close_set;
        int ret;
        TALLOC_CTX *frame = talloc_stackframe();
+       bool ok;
 
-       if (!parent_dirname(frame,
-                               smb_fname->base_name,
-                               &parent_dir,
-                               NULL)) {
-               status = NT_STATUS_NO_MEMORY;
-               goto out;
-       }
-
-       parent_smb_fname = synthetic_smb_fname(frame,
-                               parent_dir,
-                               NULL,
-                               NULL,
-                               smb_fname->flags);
-       if (parent_smb_fname == NULL) {
+       ok = parent_smb_fname(frame, smb_fname, &parent_dir, NULL);
+       if (!ok) {
                status = NT_STATUS_NO_MEMORY;
                goto out;
        }
@@ -302,7 +290,7 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        }
 
        status = SMB_VFS_GET_NT_ACL(conn,
-                               parent_smb_fname,
+                               parent_dir,
                                SECINFO_DACL,
                                frame,
                                &parent_sd);
@@ -310,7 +298,7 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5,("check_parent_access: SMB_VFS_GET_NT_ACL failed for "
                        "%s with error %s\n",
-                       parent_dir,
+                       smb_fname_str_dbg(parent_dir),
                        nt_errstr(status)));
                goto out;
        }
@@ -334,7 +322,7 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                DEBUG(5,("check_parent_access: access check "
                        "on directory %s for "
                        "path %s for mask 0x%x returned (0x%x) %s\n",
-                       parent_dir,
+                       smb_fname_str_dbg(parent_dir),
                        smb_fname->base_name,
                        access_mask,
                        access_granted,
@@ -352,15 +340,15 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        }
 
        /* Check if the directory has delete-on-close set */
-       ret = SMB_VFS_STAT(conn, parent_smb_fname);
+       ret = SMB_VFS_STAT(conn, parent_dir);
        if (ret != 0) {
                status = map_nt_error_from_unix(errno);
                goto out;
        }
 
-       id = SMB_VFS_FILE_ID_CREATE(conn, &parent_smb_fname->st);
+       id = SMB_VFS_FILE_ID_CREATE(conn, &parent_dir->st);
 
-       status = file_name_hash(conn, parent_smb_fname->base_name, &name_hash);
+       status = file_name_hash(conn, parent_dir->base_name, &name_hash);
        if (!NT_STATUS_IS_OK(status)) {
                goto out;
        }
@@ -609,61 +597,48 @@ static int non_widelink_open(struct connection_struct *conn,
        struct smb_filename *smb_fname_rel = NULL;
        int saved_errno = 0;
        struct smb_filename *oldwd_fname = NULL;
-       char *parent_dir = NULL;
-       struct smb_filename parent_dir_fname = {0};
-       const char *final_component = NULL;
-       bool is_directory = false;
+       struct smb_filename *parent_dir_fname = NULL;
        bool ok;
 
-#ifdef O_DIRECTORY
-       if (flags & O_DIRECTORY) {
-               is_directory = true;
-       }
-#endif
-
-       if (is_directory) {
-               parent_dir = talloc_strdup(talloc_tos(), smb_fname->base_name);
-               if (parent_dir == NULL) {
+       if (fsp->fsp_flags.is_directory) {
+               parent_dir_fname = cp_smb_filename(talloc_tos(), smb_fname);
+               if (parent_dir_fname == NULL) {
                        saved_errno = errno;
                        goto out;
                }
 
-               final_component = ".";
+               smb_fname_rel = synthetic_smb_fname(parent_dir_fname,
+                                                   ".",
+                                                   smb_fname->stream_name,
+                                                   &smb_fname->st,
+                                                   smb_fname->flags);
+               if (smb_fname_rel == NULL) {
+                       saved_errno = errno;
+                       goto out;
+               }
        } else {
-               ok = parent_dirname(talloc_tos(),
-                                   smb_fname->base_name,
-                                   &parent_dir,
-                                   &final_component);
+               ok = parent_smb_fname(talloc_tos(),
+                                     smb_fname,
+                                     &parent_dir_fname,
+                                     &smb_fname_rel);
                if (!ok) {
                        saved_errno = errno;
                        goto out;
                }
        }
 
-       parent_dir_fname = (struct smb_filename) { .base_name = parent_dir };
-
        oldwd_fname = vfs_GetWd(talloc_tos(), conn);
        if (oldwd_fname == NULL) {
                goto out;
        }
 
        /* Pin parent directory in place. */
-       if (vfs_ChDir(conn, &parent_dir_fname) == -1) {
-               goto out;
-       }
-
-       smb_fname_rel = synthetic_smb_fname(talloc_tos(),
-                               final_component,
-                               smb_fname->stream_name,
-                               &smb_fname->st,
-                               smb_fname->flags);
-       if (smb_fname_rel == NULL) {
-               saved_errno = ENOMEM;
+       if (vfs_ChDir(conn, parent_dir_fname) == -1) {
                goto out;
        }
 
        /* Ensure the relative path is below the share. */
-       status = check_reduced_name(conn, &parent_dir_fname, smb_fname_rel);
+       status = check_reduced_name(conn, parent_dir_fname, smb_fname_rel);
        if (!NT_STATUS_IS_OK(status)) {
                saved_errno = map_errno_from_nt_status(status);
                goto out;
@@ -729,8 +704,7 @@ static int non_widelink_open(struct connection_struct *conn,
 
   out:
 
-       TALLOC_FREE(parent_dir);
-       TALLOC_FREE(smb_fname_rel);
+       TALLOC_FREE(parent_dir_fname);
 
        if (oldwd_fname != NULL) {
                int ret = vfs_ChDir(conn, oldwd_fname);
@@ -756,6 +730,9 @@ NTSTATUS fd_open(struct connection_struct *conn,
 {
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
+       struct smb_filename *conn_rootdir_fname = NULL;
+       const char *conn_rootdir;
+       int saved_errno = 0;
 
        /*
         * Never follow symlinks on a POSIX client. The
@@ -767,44 +744,38 @@ NTSTATUS fd_open(struct connection_struct *conn,
        }
 
        /* Ensure path is below share definition. */
-       if (!lp_widelinks(SNUM(conn))) {
-               struct smb_filename *conn_rootdir_fname = NULL;
-               const char *conn_rootdir = SMB_VFS_CONNECTPATH(conn,
-                                               smb_fname);
-               int saved_errno = 0;
+       conn_rootdir = SMB_VFS_CONNECTPATH(conn,
+                                       smb_fname);
 
-               if (conn_rootdir == NULL) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-               conn_rootdir_fname = synthetic_smb_fname(talloc_tos(),
-                                               conn_rootdir,
-                                               NULL,
-                                               NULL,
-                                               0);
-               if (conn_rootdir_fname == NULL) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-
-               /*
-                * Only follow symlinks within a share
-                * definition.
-                */
-               fsp->fh->fd = non_widelink_open(conn,
-                                       conn_rootdir_fname,
-                                       fsp,
-                                       smb_fname,
-                                       flags,
-                                       mode,
+       if (conn_rootdir == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+       conn_rootdir_fname = synthetic_smb_fname(talloc_tos(),
+                                       conn_rootdir,
+                                       NULL,
+                                       NULL,
                                        0);
-               if (fsp->fh->fd == -1) {
-                       saved_errno = errno;
-               }
-               TALLOC_FREE(conn_rootdir_fname);
-               if (saved_errno != 0) {
-                       errno = saved_errno;
-               }
-       } else {
-               fsp->fh->fd = SMB_VFS_OPEN(conn, smb_fname, fsp, flags, mode);
+       if (conn_rootdir_fname == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /*
+        * Only follow symlinks within a share
+        * definition.
+        */
+       fsp->fh->fd = non_widelink_open(conn,
+                               conn_rootdir_fname,
+                               fsp,
+                               smb_fname,
+                               flags,
+                               mode,
+                               0);
+       if (fsp->fh->fd == -1) {
+               saved_errno = errno;
+       }
+       TALLOC_FREE(conn_rootdir_fname);
+       if (saved_errno != 0) {
+               errno = saved_errno;
        }
 
        if (fsp->fh->fd == -1) {
@@ -1148,7 +1119,7 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
 static NTSTATUS open_file(files_struct *fsp,
                          connection_struct *conn,
                          struct smb_request *req,
-                         const char *parent_dir,
+                         struct smb_filename *parent_dir,
                          int flags,
                          mode_t unx_mode,
                          uint32_t access_mask, /* client requested access mask. */
@@ -1160,6 +1131,17 @@ static NTSTATUS open_file(files_struct *fsp,
        int accmode = (flags & O_ACCMODE);
        int local_flags = flags;
        bool file_existed = VALID_STAT(fsp->fsp_name->st);
+       uint32_t need_fd_mask =
+               FILE_READ_DATA |
+               FILE_WRITE_DATA |
+               FILE_APPEND_DATA |
+               FILE_EXECUTE |
+               WRITE_DAC_ACCESS |
+               WRITE_OWNER_ACCESS |
+               SEC_FLAG_SYSTEM_SECURITY |
+               READ_CONTROL_ACCESS;
+       bool creating = !file_existed && (flags & O_CREAT);
+       bool truncating = (flags & O_TRUNC);
 
        fsp->fh->fd = -1;
        errno = EPERM;
@@ -1211,12 +1193,7 @@ static NTSTATUS open_file(files_struct *fsp,
                local_flags = (flags & ~O_ACCMODE)|O_RDWR;
        }
 
-       if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|
-                                FILE_APPEND_DATA|FILE_EXECUTE|
-                                WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|
-                                READ_CONTROL_ACCESS))||
-           (!file_existed && (local_flags & O_CREAT)) ||
-           ((local_flags & O_TRUNC) == O_TRUNC) ) {
+       if ((open_access_mask & need_fd_mask) || creating || truncating) {
                const char *wild;
                int ret;
 
@@ -1230,6 +1207,7 @@ static NTSTATUS open_file(files_struct *fsp,
                if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) {
                        local_flags &= ~O_TRUNC; /* Can't truncate a FIFO. */
                        local_flags |= O_NONBLOCK;
+                       truncating = false;
                }
 #endif
 
@@ -1359,7 +1337,8 @@ static NTSTATUS open_file(files_struct *fsp,
 
                        /* Inherit the ACL if required */
                        if (lp_inherit_permissions(SNUM(conn))) {
-                               inherit_access_posix_acl(conn, parent_dir,
+                               inherit_access_posix_acl(conn,
+                                                        parent_dir->base_name,
                                                         smb_fname,
                                                         unx_mode);
                                need_re_stat = true;
@@ -1367,7 +1346,8 @@ static NTSTATUS open_file(files_struct *fsp,
 
                        /* Change the owner if required. */
                        if (lp_inherit_owner(SNUM(conn)) != INHERIT_OWNER_NO) {
-                               change_file_owner_to_parent(conn, parent_dir,
+                               change_file_owner_to_parent(conn,
+                                                           parent_dir->base_name,
                                                            fsp);
                                need_re_stat = true;
                        }
@@ -1436,27 +1416,26 @@ static NTSTATUS open_file(files_struct *fsp,
        fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
        fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
        fsp->file_pid = req ? req->smbpid : 0;
-       fsp->can_lock = True;
-       fsp->can_read = ((access_mask & FILE_READ_DATA) != 0);
-       fsp->can_write =
+       fsp->fsp_flags.can_lock = true;
+       fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
+       fsp->fsp_flags.can_write =
                CAN_WRITE(conn) &&
                ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
        fsp->print_file = NULL;
-       fsp->modified = False;
+       fsp->fsp_flags.modified = false;
        fsp->sent_oplock_break = NO_BREAK_SENT;
-       fsp->is_directory = False;
+       fsp->fsp_flags.is_directory = false;
        if (conn->aio_write_behind_list &&
            is_in_path(smb_fname->base_name, conn->aio_write_behind_list,
                       conn->case_sensitive)) {
-               fsp->aio_write_behind = True;
+               fsp->fsp_flags.aio_write_behind = true;
        }
 
-       fsp->wcp = NULL; /* Write cache pointer. */
-
        DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n",
                 conn->session_info->unix_info->unix_name,
                 smb_fname_str_dbg(smb_fname),
-                BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write),
+                BOOLSTR(fsp->fsp_flags.can_read),
+                BOOLSTR(fsp->fsp_flags.can_write),
                 conn->num_files_open));
 
        errno = 0;
@@ -1566,7 +1545,7 @@ static bool validate_my_share_entries_fn(
        struct validate_my_share_entries_state *state = private_data;
        files_struct *fsp;
 
-       if (!serverid_equal(&state->self, &e->pid)) {
+       if (!server_id_equal(&state->self, &e->pid)) {
                return false;
        }
 
@@ -1813,7 +1792,7 @@ static NTSTATUS open_mode_check(connection_struct *conn,
        struct share_mode_data *d = lck->data;
        struct open_mode_check_state state;
        uint16_t new_flags;
-       bool ok, conflict;
+       bool ok, conflict, have_share_entries;
 
        if (is_stat_open(access_mask)) {
                /* Stat open that doesn't trigger oplock breaks or share mode
@@ -1838,7 +1817,12 @@ static NTSTATUS open_mode_check(connection_struct *conn,
        }
 #endif
 
-       if (d->num_share_modes == 0) {
+       have_share_entries = share_mode_have_entries(lck);
+       if (!have_share_entries) {
+               /*
+                * This is a fresh share mode lock where no conflicts
+                * can happen.
+                */
                return NT_STATUS_OK;
        }
 
@@ -2577,8 +2561,10 @@ grant:
                }
        }
 
-       if (granted & SMB2_LEASE_READ) {
+       if ((granted & SMB2_LEASE_READ) &&
+           ((lck->data->flags & SHARE_MODE_LEASE_READ) == 0)) {
                lck->data->flags |= SHARE_MODE_LEASE_READ;
+               lck->data->modified = true;
        }
 
        DBG_DEBUG("oplock type 0x%x on file %s\n",
@@ -2668,7 +2654,6 @@ static void defer_open_done(struct tevent_req *req);
 static void defer_open(struct share_mode_lock *lck,
                       struct timeval timeout,
                       struct smb_request *req,
-                      bool delayed_for_oplocks,
                       struct file_id id)
 {
        struct deferred_open_record *open_rec = NULL;
@@ -2682,11 +2667,10 @@ static void defer_open(struct share_mode_lock *lck,
        abs_timeout = timeval_sum(&req->request_time, &timeout);
 
        DBG_DEBUG("request time [%s] timeout [%s] mid [%" PRIu64 "] "
-                 "delayed_for_oplocks [%s] file_id [%s]\n",
+                 "file_id [%s]\n",
                  timeval_str_buf(&req->request_time, false, true, &tvbuf1),
                  timeval_str_buf(&abs_timeout, false, true, &tvbuf2),
                  req->mid,
-                 delayed_for_oplocks ? "yes" : "no",
                  file_id_str_buf(id, &fbuf));
 
        open_rec = talloc_zero(NULL, struct deferred_open_record);
@@ -2704,10 +2688,11 @@ static void defer_open(struct share_mode_lock *lck,
 
        DBG_DEBUG("defering mid %" PRIu64 "\n", req->mid);
 
-       watch_req = dbwrap_watched_watch_send(watch_state,
-                                             req->sconn->ev_ctx,
-                                             lck->data->record,
-                                             (struct server_id){0});
+       watch_req = share_mode_watch_send(
+               watch_state,
+               req->sconn->ev_ctx,
+               lck->data->id,
+               (struct server_id){0});
        if (watch_req == NULL) {
                exit_server("Could not watch share mode record");
        }
@@ -2732,7 +2717,7 @@ static void defer_open_done(struct tevent_req *req)
        NTSTATUS status;
        bool ret;
 
-       status = dbwrap_watched_watch_recv(req, NULL, NULL);
+       status = share_mode_watch_recv(req, NULL, NULL);
        TALLOC_FREE(req);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(5, ("dbwrap_watched_watch_recv returned %s\n",
@@ -2824,13 +2809,13 @@ static bool setup_poll_open(
        }
 
        if (lck != NULL) {
-               open_rec->watch_req = dbwrap_watched_watch_send(
+               open_rec->watch_req = share_mode_watch_send(
                        open_rec,
                        req->sconn->ev_ctx,
-                       lck->data->record,
+                       lck->data->id,
                        (struct server_id) {0});
                if (open_rec->watch_req == NULL) {
-                       DBG_WARNING("dbwrap_watched_watch_send failed\n");
+                       DBG_WARNING("share_mode_watch_send failed\n");
                        TALLOC_FREE(open_rec);
                        return false;
                }
@@ -2860,7 +2845,7 @@ static void poll_open_done(struct tevent_req *subreq)
        NTSTATUS status;
        bool ok;
 
-       status = dbwrap_watched_watch_recv(subreq, NULL, NULL);
+       status = share_mode_watch_recv(subreq, NULL, NULL);
        TALLOC_FREE(subreq);
        DBG_DEBUG("dbwrap_watched_watch_recv returned %s\n",
                  nt_errstr(status));
@@ -2974,7 +2959,7 @@ static void schedule_defer_open(struct share_mode_lock *lck,
                return;
        }
 
-       defer_open(lck, timeout, req, true, id);
+       defer_open(lck, timeout, req, id);
 }
 
 /****************************************************************************
@@ -3293,10 +3278,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        struct share_mode_lock *lck = NULL;
        uint32_t open_access_mask = access_mask;
        NTSTATUS status;
-       char *parent_dir;
+       struct smb_filename *parent_dir_fname = NULL;
        SMB_STRUCT_STAT saved_stat = smb_fname->st;
        struct timespec old_write_time;
        struct file_id id;
+       bool setup_poll = false;
        bool ok;
 
        if (conn->printer) {
@@ -3322,8 +3308,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                                        req->vuid);
        }
 
-       if (!parent_dirname(talloc_tos(), smb_fname->base_name, &parent_dir,
-                           NULL)) {
+       ok = parent_smb_fname(talloc_tos(),
+                             smb_fname,
+                             &parent_dir_fname,
+                             NULL);
+       if (!ok) {
                return NT_STATUS_NO_MEMORY;
        }
 
@@ -3341,7 +3330,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                /* We add FILE_ATTRIBUTE_ARCHIVE to this as this mode is only used if the file is
                 * created new. */
                unx_mode = unix_mode(conn, new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
-                                    smb_fname, parent_dir);
+                                    smb_fname, parent_dir_fname);
        }
 
        DEBUG(10, ("open_file_ntcreate: fname=%s, dos_attrs=0x%x "
@@ -3592,7 +3581,15 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+       if (VALID_STAT(smb_fname->st)) {
+               /*
+                * Only try and create a file id before open
+                * for an existing file. For a file being created
+                * this won't do anything useful until the file
+                * exists and has a valid stat struct.
+                */
+               fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+       }
        fsp->fh->private_options = private_flags;
        fsp->access_mask = open_access_mask; /* We change this to the
                                              * requested access_mask after
@@ -3618,7 +3615,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
         */
 
         if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) &&
-           (def_acl = directory_has_default_acl(conn, parent_dir))) {
+           (def_acl = directory_has_default_acl(conn, parent_dir_fname))) {
                unx_mode = (0777 & lp_create_mask(SNUM(conn)));
        }
 
@@ -3628,11 +3625,19 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 (unsigned int)unx_mode, (unsigned int)access_mask,
                 (unsigned int)open_access_mask));
 
-       fsp_open = open_file(fsp, conn, req, parent_dir,
+       fsp_open = open_file(fsp, conn, req, parent_dir_fname,
                             flags|flags2, unx_mode, access_mask,
                             open_access_mask, &new_file_created);
 
        if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_NETWORK_BUSY)) {
+               if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
+                       DEBUG(10, ("FIFO busy\n"));
+                       return NT_STATUS_NETWORK_BUSY;
+               }
+               if (req == NULL) {
+                       DEBUG(10, ("Internal open busy\n"));
+                       return NT_STATUS_NETWORK_BUSY;
+               }
                /*
                 * This handles the kernel oplock case:
                 *
@@ -3642,15 +3647,20 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 * "Samba locking.tdb oplocks" are handled below after acquiring
                 * the sharemode lock with get_share_mode_lock().
                 */
-               if (file_existed && S_ISFIFO(fsp->fsp_name->st.st_ex_mode)) {
-                       DEBUG(10, ("FIFO busy\n"));
-                       return NT_STATUS_NETWORK_BUSY;
-               }
-               if (req == NULL) {
-                       DEBUG(10, ("Internal open busy\n"));
-                       return NT_STATUS_NETWORK_BUSY;
-               }
+               setup_poll = true;
+       }
 
+       if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+               /*
+                * EINTR from the open(2) syscall. Just setup a retry
+                * in a bit. We can't use the sys_write() tight retry
+                * loop here, as we might have to actually deal with
+                * lease-break signals to avoid a deadlock.
+                */
+               setup_poll = true;
+       }
+
+       if (setup_poll) {
                /*
                 * From here on we assume this is an oplock break triggered
                 */
@@ -3681,7 +3691,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        }
 
        if (!NT_STATUS_IS_OK(fsp_open)) {
-               if (NT_STATUS_EQUAL(fsp_open, NT_STATUS_RETRY)) {
+               bool wait_for_aio = NT_STATUS_EQUAL(
+                       fsp_open, NT_STATUS_MORE_PROCESSING_REQUIRED);
+               if (wait_for_aio) {
                        schedule_async_open(req);
                }
                return fsp_open;
@@ -3877,7 +3889,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        return NT_STATUS_SHARING_VIOLATION;
                }
 
-               fsp->kernel_share_modes_taken = true;
+               fsp->fsp_flags.kernel_share_modes_taken = true;
        }
 
        /*
@@ -3933,7 +3945,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                }
                /* Note that here we set the *initial* delete on close flag,
                   not the regular one. The magic gets handled in close. */
-               fsp->initial_delete_on_close = True;
+               fsp->fsp_flags.initial_delete_on_close = true;
        }
 
        /*
@@ -3966,7 +3978,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                        if (!posix_open) {
                                if (file_set_dosmode(conn, smb_fname,
                                            new_dos_attributes | FILE_ATTRIBUTE_ARCHIVE,
-                                           parent_dir, true) == 0) {
+                                           parent_dir_fname, true) == 0) {
                                        unx_mode = smb_fname->st.st_ex_mode;
                                }
                        }
@@ -3976,9 +3988,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        /* Determine sparse flag. */
        if (posix_open) {
                /* POSIX opens are sparse by default. */
-               fsp->is_sparse = true;
+               fsp->fsp_flags.is_sparse = true;
        } else {
-               fsp->is_sparse =
+               fsp->fsp_flags.is_sparse =
                        (existing_dos_attributes & FILE_ATTRIBUTE_SPARSE);
        }
 
@@ -4036,7 +4048,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                 */
                struct timespec write_time = get_share_mode_write_time(lck);
 
-               if (!null_timespec(write_time)) {
+               if (!is_omit_timespec(&write_time)) {
                        update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
                }
        }
@@ -4050,30 +4062,41 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                               struct smb_filename *smb_dname,
                               uint32_t file_attributes)
 {
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
        mode_t mode;
+       struct smb_filename *parent_dir_fname = NULL;
        char *parent_dir = NULL;
        NTSTATUS status;
        bool posix_open = false;
        bool need_re_stat = false;
        uint32_t access_mask = SEC_DIR_ADD_SUBDIR;
        int ret;
+       bool ok;
 
        if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
                DEBUG(5,("mkdir_internal: failing share access "
-                        "%s\n", lp_servicename(talloc_tos(), SNUM(conn))));
+                        "%s\n", lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
                return NT_STATUS_ACCESS_DENIED;
        }
 
-       if (!parent_dirname(talloc_tos(), smb_dname->base_name, &parent_dir,
-                           NULL)) {
+       ok = parent_smb_fname(talloc_tos(),
+                             smb_dname,
+                             &parent_dir_fname,
+                             NULL);
+       if (!ok) {
                return NT_STATUS_NO_MEMORY;
        }
+       parent_dir = parent_dir_fname->base_name;
 
        if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
                posix_open = true;
                mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
        } else {
-               mode = unix_mode(conn, FILE_ATTRIBUTE_DIRECTORY, smb_dname, parent_dir);
+               mode = unix_mode(conn,
+                                FILE_ATTRIBUTE_DIRECTORY,
+                                smb_dname,
+                                parent_dir_fname);
        }
 
        status = check_parent_access(conn,
@@ -4125,7 +4148,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
                if (!posix_open) {
                        file_set_dosmode(conn, smb_dname,
                                         file_attributes | FILE_ATTRIBUTE_DIRECTORY,
-                                        parent_dir, true);
+                                        parent_dir_fname, true);
                }
        }
 
@@ -4189,7 +4212,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                               files_struct **result)
 {
        files_struct *fsp = NULL;
-       bool dir_existed = VALID_STAT(smb_dname->st) ? True : False;
+       bool dir_existed = VALID_STAT(smb_dname->st);
        struct share_mode_lock *lck = NULL;
        NTSTATUS status;
        struct timespec mtimespec;
@@ -4207,15 +4230,16 @@ static NTSTATUS open_directory(connection_struct *conn,
                file_attributes |= FILE_ATTRIBUTE_DIRECTORY;
        }
 
-       DEBUG(5,("open_directory: opening directory %s, access_mask = 0x%x, "
-                "share_access = 0x%x create_options = 0x%x, "
-                "create_disposition = 0x%x, file_attributes = 0x%x\n",
+       DBG_INFO("opening directory %s, access_mask = 0x%"PRIx32", "
+                "share_access = 0x%"PRIx32" create_options = 0x%"PRIx32", "
+                "create_disposition = 0x%"PRIx32", "
+                "file_attributes = 0x%"PRIx32"\n",
                 smb_fname_str_dbg(smb_dname),
-                (unsigned int)access_mask,
-                (unsigned int)share_access,
-                (unsigned int)create_options,
-                (unsigned int)create_disposition,
-                (unsigned int)file_attributes));
+                access_mask,
+                share_access,
+                create_options,
+                create_disposition,
+                file_attributes);
 
        status = smbd_calculate_access_mask(conn, smb_dname, false,
                                            access_mask, &access_mask);
@@ -4367,9 +4391,9 @@ static NTSTATUS open_directory(connection_struct *conn,
        fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_dname->st);
        fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
        fsp->file_pid = req ? req->smbpid : 0;
-       fsp->can_lock = False;
-       fsp->can_read = False;
-       fsp->can_write = False;
+       fsp->fsp_flags.can_lock = false;
+       fsp->fsp_flags.can_read = false;
+       fsp->fsp_flags.can_write = false;
 
        fsp->fh->private_options = 0;
        /*
@@ -4377,10 +4401,10 @@ static NTSTATUS open_directory(connection_struct *conn,
         */
        fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
        fsp->print_file = NULL;
-       fsp->modified = False;
+       fsp->fsp_flags.modified = false;
        fsp->oplock_type = NO_OPLOCK;
        fsp->sent_oplock_break = NO_BREAK_SENT;
-       fsp->is_directory = True;
+       fsp->fsp_flags.is_directory = true;
        if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) {
                fsp->posix_flags |= FSP_POSIX_FLAGS_ALL;
        }
@@ -4393,11 +4417,11 @@ static NTSTATUS open_directory(connection_struct *conn,
        /* Don't store old timestamps for directory
           handles in the internal database. We don't
           update them in there if new objects
-          are creaded in the directory. Currently
+          are created in the directory. Currently
           we only update timestamps on file writes.
           See bug #9870.
        */
-       ZERO_STRUCT(mtimespec);
+       mtimespec = make_omit_timespec();
 
 #ifdef O_DIRECTORY
        status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
@@ -4511,7 +4535,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                if (NT_STATUS_IS_OK(status)) {
                        /* Note that here we set the *initial* delete on close flag,
                           not the regular one. The magic gets handled in close. */
-                       fsp->initial_delete_on_close = True;
+                       fsp->fsp_flags.initial_delete_on_close = true;
                }
        }
 
@@ -4522,7 +4546,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                 */
                struct timespec write_time = get_share_mode_write_time(lck);
 
-               if (!null_timespec(write_time)) {
+               if (!is_omit_timespec(&write_time)) {
                        update_stat_ex_mtime(&fsp->fsp_name->st, write_time);
                }
        }
@@ -4792,7 +4816,6 @@ static NTSTATUS open_streams_for_delete(connection_struct *conn,
 static NTSTATUS inherit_new_acl(files_struct *fsp)
 {
        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;
@@ -4810,25 +4833,20 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        const struct dom_sid *SY_U_sid = NULL;
        const struct dom_sid *SY_G_sid = NULL;
        size_t size = 0;
-       struct smb_filename *parent_smb_fname = NULL;
-
-       if (!parent_dirname(frame, fsp->fsp_name->base_name, &parent_name, NULL)) {
-               TALLOC_FREE(frame);
-               return NT_STATUS_NO_MEMORY;
-       }
-       parent_smb_fname = synthetic_smb_fname(talloc_tos(),
-                                               parent_name,
-                                               NULL,
-                                               NULL,
-                                               fsp->fsp_name->flags);
+       struct smb_filename *parent_dir = NULL;
+       bool ok;
 
-       if (parent_smb_fname == NULL) {
+       ok = parent_smb_fname(frame,
+                             fsp->fsp_name,
+                             &parent_dir,
+                             NULL);
+       if (!ok) {
                TALLOC_FREE(frame);
                return NT_STATUS_NO_MEMORY;
        }
 
        status = SMB_VFS_GET_NT_ACL(fsp->conn,
-                                   parent_smb_fname,
+                                   parent_dir,
                                    (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
                                    frame,
                                    &parent_desc);
@@ -4838,7 +4856,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
        }
 
        inheritable_components = sd_has_inheritable_components(parent_desc,
-                                       fsp->is_directory);
+                                       fsp->fsp_flags.is_directory);
 
        if (!inheritable_components && !inherit_owner) {
                TALLOC_FREE(frame);
@@ -4880,7 +4898,6 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 
        if (try_builtin_administrators) {
                struct unixid ids;
-               bool ok;
 
                ZERO_STRUCT(ids);
                ok = sids_to_unixids(&global_sid_Builtin_Administrators, 1, &ids);
@@ -4904,7 +4921,6 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 
        if (try_system) {
                struct unixid ids;
-               bool ok;
 
                ZERO_STRUCT(ids);
                ok = sids_to_unixids(&global_sid_System, 1, &ids);
@@ -4959,7 +4975,7 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                        parent_desc,
                        owner_sid,
                        group_sid,
-                       fsp->is_directory);
+                       fsp->fsp_flags.is_directory);
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
                return status;
@@ -5391,14 +5407,33 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
-       if ((access_mask & SEC_FLAG_SYSTEM_SECURITY) &&
-                       !security_token_has_privilege(get_current_nttok(conn),
-                                       SEC_PRIV_SECURITY)) {
-               DEBUG(10, ("create_file_unixpath: open on %s "
-                       "failed - SEC_FLAG_SYSTEM_SECURITY denied.\n",
-                       smb_fname_str_dbg(smb_fname)));
-               status = NT_STATUS_PRIVILEGE_NOT_HELD;
-               goto fail;
+       if (access_mask & SEC_FLAG_SYSTEM_SECURITY) {
+               bool ok;
+
+               ok = security_token_has_privilege(get_current_nttok(conn),
+                                                 SEC_PRIV_SECURITY);
+               if (!ok) {
+                       DBG_DEBUG("open on %s failed - "
+                               "SEC_FLAG_SYSTEM_SECURITY denied.\n",
+                               smb_fname_str_dbg(smb_fname));
+                       status = NT_STATUS_PRIVILEGE_NOT_HELD;
+                       goto fail;
+               }
+
+               if (conn->sconn->using_smb2 &&
+                   (access_mask == SEC_FLAG_SYSTEM_SECURITY))
+               {
+                       /*
+                        * No other bits set. Windows SMB2 refuses this.
+                        * See smbtorture3 SMB2-SACL test.
+                        *
+                        * Note this is an SMB2-only behavior,
+                        * smbtorture3 SMB1-SYSTEM-SECURITY already tests
+                        * that SMB1 allows this.
+                        */
+                       status = NT_STATUS_ACCESS_DENIED;
+                       goto fail;
+               }
        }
 
        /*
@@ -5612,7 +5647,9 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                }
        }
 
-       if (!fsp->is_directory && S_ISDIR(fsp->fsp_name->st.st_ex_mode)) {
+       if (!fsp->fsp_flags.is_directory &&
+           S_ISDIR(fsp->fsp_name->st.st_ex_mode))
+       {
                status = NT_STATUS_ACCESS_DENIED;
                goto fail;
        }
@@ -5620,7 +5657,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        /* Save the requested allocation size. */
        if ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN)) {
                if ((allocation_size > (uint64_t)fsp->fsp_name->st.st_ex_size)
-                   && !(fsp->is_directory))
+                   && !(fsp->fsp_flags.is_directory))
                {
                        fsp->initial_allocation_size = smb_roundup(
                                fsp->conn, allocation_size);
@@ -5757,7 +5794,7 @@ static NTSTATUS get_relative_fid_filename(
                goto out;
        }
 
-       if (!dir_fsp->is_directory) {
+       if (!dir_fsp->fsp_flags.is_directory) {
 
                /*
                 * Check to see if this is a mac fork of some kind.
@@ -5869,6 +5906,7 @@ NTSTATUS create_file_default(connection_struct *conn,
        files_struct *fsp = NULL;
        NTSTATUS status;
        bool stream_name = false;
+       struct smb2_create_blob *posx = NULL;
 
        DBG_DEBUG("create_file: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
@@ -5970,6 +6008,36 @@ NTSTATUS create_file_default(connection_struct *conn,
                }
        }
 
+       posx = smb2_create_blob_find(
+               in_context_blobs, SMB2_CREATE_TAG_POSIX);
+       if (posx != NULL) {
+               uint32_t wire_mode_bits = 0;
+               mode_t mode_bits = 0;
+               SMB_STRUCT_STAT sbuf = { 0 };
+               enum perm_type ptype =
+                       (create_options & FILE_DIRECTORY_FILE) ?
+                       PERM_NEW_DIR : PERM_NEW_FILE;
+
+               if (posx->data.length != 4) {
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto fail;
+               }
+
+               wire_mode_bits = IVAL(posx->data.data, 0);
+               status = unix_perms_from_wire(
+                       conn, &sbuf, wire_mode_bits, ptype, &mode_bits);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+               /*
+                * Remove type info from mode, leaving only the
+                * permissions and setuid/gid bits.
+                */
+               mode_bits &= ~S_IFMT;
+
+               file_attributes = (FILE_FLAG_POSIX_SEMANTICS | mode_bits);
+       }
+
        status = create_file_unixpath(
                conn, req, smb_fname, access_mask, share_access,
                create_disposition, create_options, file_attributes,