smbd: remove redundant conn arg from fd_open()
[samba.git] / source3 / smbd / open.c
index c2a14da44743c978733dcc1e49c655be42a86cb9..bd4d8c3616514697e85b9228d83abb5adf1d9d19 100644 (file)
@@ -147,10 +147,14 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
                return NT_STATUS_OK;
        }
 
-       status = SMB_VFS_GET_NT_ACL(conn, smb_fname,
+       status = SMB_VFS_GET_NT_ACL_AT(conn,
+                       dirfsp,
+                       smb_fname,
                        (SECINFO_OWNER |
-                       SECINFO_GROUP |
-                        SECINFO_DACL), talloc_tos(), &sd);
+                               SECINFO_GROUP |
+                               SECINFO_DACL),
+                       talloc_tos(),
+                       &sd);
 
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("smbd_check_access_rights: Could not get acl "
@@ -264,6 +268,7 @@ NTSTATUS smbd_check_access_rights(struct connection_struct *conn,
 }
 
 NTSTATUS check_parent_access(struct connection_struct *conn,
+                               struct files_struct *dirfsp,
                                struct smb_filename *smb_fname,
                                uint32_t access_mask)
 {
@@ -279,6 +284,13 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
        TALLOC_CTX *frame = talloc_stackframe();
        bool ok;
 
+       /*
+        * NB. When dirfsp != conn->cwd_fsp, we must
+        * change parent_dir to be "." for the name here.
+        */
+
+       SMB_ASSERT(dirfsp == conn->cwd_fsp);
+
        ok = parent_smb_fname(frame, smb_fname, &parent_dir, NULL);
        if (!ok) {
                status = NT_STATUS_NO_MEMORY;
@@ -295,17 +307,18 @@ NTSTATUS check_parent_access(struct connection_struct *conn,
                goto out;
        }
 
-       status = SMB_VFS_GET_NT_ACL(conn,
+       status = SMB_VFS_GET_NT_ACL_AT(conn,
+                               dirfsp,
                                parent_dir,
                                SECINFO_DACL,
                                frame,
                                &parent_sd);
 
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(5,("check_parent_access: SMB_VFS_GET_NT_ACL failed for "
+               DBG_INFO("SMB_VFS_GET_NT_ACL_AT failed for "
                        "%s with error %s\n",
                        smb_fname_str_dbg(parent_dir),
-                       nt_errstr(status)));
+                       nt_errstr(status));
                goto out;
        }
 
@@ -389,7 +402,9 @@ static NTSTATUS check_base_file_access(struct connection_struct *conn,
 {
        NTSTATUS status;
 
-       status = smbd_calculate_access_mask(conn, smb_fname,
+       status = smbd_calculate_access_mask(conn,
+                                       conn->cwd_fsp,
+                                       smb_fname,
                                        false,
                                        access_mask,
                                        &access_mask);
@@ -445,7 +460,6 @@ static int link_errno_convert(int err)
 }
 
 static int non_widelink_open(struct connection_struct *conn,
-                       const struct smb_filename *conn_rootdir_fname,
                        files_struct *fsp,
                        struct smb_filename *smb_fname,
                        int flags,
@@ -457,16 +471,16 @@ static int non_widelink_open(struct connection_struct *conn,
 ****************************************************************************/
 
 static int process_symlink_open(struct connection_struct *conn,
-                       const struct smb_filename *conn_rootdir_fname,
                        files_struct *fsp,
                        struct smb_filename *smb_fname,
                        int flags,
                        mode_t mode,
                        unsigned int link_depth)
 {
+       const char *conn_rootdir = NULL;
+       struct smb_filename conn_rootdir_fname;
        int fd = -1;
        char *link_target = NULL;
-       struct smb_filename target_fname = {0};
        int link_len = -1;
        struct smb_filename *oldwd_fname = NULL;
        size_t rootdir_len = 0;
@@ -475,6 +489,15 @@ static int process_symlink_open(struct connection_struct *conn,
        bool matched = false;
        int saved_errno = 0;
 
+       conn_rootdir = SMB_VFS_CONNECTPATH(conn, smb_fname);
+       if (conn_rootdir == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+       conn_rootdir_fname = (struct smb_filename) {
+               .base_name = discard_const_p(char, conn_rootdir),
+       };
+
        /*
         * Ensure we don't get stuck in a symlink loop.
         */
@@ -491,7 +514,19 @@ static int process_symlink_open(struct connection_struct *conn,
                goto out;
        }
 
-       /* Read the link target. */
+       /*
+        * Read the link target. We do this just to verify that smb_fname indeed
+        * points at a symbolic link and return the SMB_VFS_READLINKAT() errno
+        * and failure in case smb_fname is NOT a symlink.
+        *
+        * The caller needs this piece of information to distinguish two cases
+        * where open() fails with errno=ENOTDIR, cf the comment in
+        * non_widelink_open().
+        *
+        * We rely on SMB_VFS_REALPATH() to resolve the path including the
+        * symlink. Once we have SMB_VFS_STATX() or something similar in our VFS
+        * we may want to use that instead of SMB_VFS_READLINKAT().
+        */
        link_len = SMB_VFS_READLINKAT(conn,
                                conn->cwd_fsp,
                                smb_fname,
@@ -501,12 +536,8 @@ static int process_symlink_open(struct connection_struct *conn,
                goto out;
        }
 
-       /* Ensure it's at least null terminated. */
-       link_target[link_len] = '\0';
-       target_fname = (struct smb_filename){ .base_name = link_target };
-
        /* Convert to an absolute path. */
-       resolved_fname = SMB_VFS_REALPATH(conn, talloc_tos(), &target_fname);
+       resolved_fname = SMB_VFS_REALPATH(conn, talloc_tos(), smb_fname);
        if (resolved_fname == NULL) {
                goto out;
        }
@@ -517,9 +548,9 @@ static int process_symlink_open(struct connection_struct *conn,
         * does not end in '/'. FIXME ! Should we
         * smb_assert this ?
         */
-       rootdir_len = strlen(conn_rootdir_fname->base_name);
+       rootdir_len = strlen(conn_rootdir_fname.base_name);
 
-       matched = (strncmp(conn_rootdir_fname->base_name,
+       matched = (strncmp(conn_rootdir_fname.base_name,
                                resolved_name,
                                rootdir_len) == 0);
        if (!matched) {
@@ -554,13 +585,12 @@ static int process_symlink_open(struct connection_struct *conn,
        }
 
        /* Ensure we operate from the root of the share. */
-       if (vfs_ChDir(conn, conn_rootdir_fname) == -1) {
+       if (vfs_ChDir(conn, &conn_rootdir_fname) == -1) {
                goto out;
        }
 
        /* And do it all again.. */
        fd = non_widelink_open(conn,
-                               conn_rootdir_fname,
                                fsp,
                                smb_fname,
                                flags,
@@ -592,7 +622,6 @@ static int process_symlink_open(struct connection_struct *conn,
 ****************************************************************************/
 
 static int non_widelink_open(struct connection_struct *conn,
-                       const struct smb_filename *conn_rootdir_fname,
                        files_struct *fsp,
                        struct smb_filename *smb_fname,
                        int flags,
@@ -618,6 +647,7 @@ static int non_widelink_open(struct connection_struct *conn,
                                                    ".",
                                                    smb_fname->stream_name,
                                                    &smb_fname->st,
+                                                   smb_fname->twrp,
                                                    smb_fname->flags);
                if (smb_fname_rel == NULL) {
                        saved_errno = errno;
@@ -687,7 +717,6 @@ static int non_widelink_open(struct connection_struct *conn,
                         * to ensure it's under the share definition.
                         */
                        fd = process_symlink_open(conn,
-                                       conn_rootdir_fname,
                                        fsp,
                                        smb_fname_rel,
                                        flags,
@@ -730,15 +759,13 @@ static int non_widelink_open(struct connection_struct *conn,
  fd support routines - attempt to do a dos_open.
 ****************************************************************************/
 
-NTSTATUS fd_open(struct connection_struct *conn,
-                files_struct *fsp,
+NTSTATUS fd_open(files_struct *fsp,
                 int flags,
                 mode_t mode)
 {
+       struct connection_struct *conn = fsp->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;
 
        /*
@@ -750,28 +777,11 @@ NTSTATUS fd_open(struct connection_struct *conn,
                flags |= O_NOFOLLOW;
        }
 
-       /* Ensure path is below share definition. */
-       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,
@@ -780,7 +790,6 @@ NTSTATUS fd_open(struct connection_struct *conn,
        if (fsp->fh->fd == -1) {
                saved_errno = errno;
        }
-       TALLOC_FREE(conn_rootdir_fname);
        if (saved_errno != 0) {
                errno = saved_errno;
        }
@@ -800,11 +809,14 @@ NTSTATUS fd_open(struct connection_struct *conn,
                        }
                }
 
+               DBG_DEBUG("name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
+                         smb_fname_str_dbg(smb_fname), flags, (int)mode,
+                         fsp->fh->fd, strerror(errno));
+               return status;
        }
 
-       DEBUG(10,("fd_open: name %s, flags = 0%o mode = 0%o, fd = %d. %s\n",
-                 smb_fname_str_dbg(smb_fname), flags, (int)mode, fsp->fh->fd,
-               (fsp->fh->fd == -1) ? strerror(errno) : "" ));
+       DBG_DEBUG("name %s, flags = 0%o mode = 0%o, fd = %d\n",
+                 smb_fname_str_dbg(smb_fname), flags, (int)mode, fsp->fh->fd);
 
        return status;
 }
@@ -934,7 +946,12 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
                goto chdir;
        }
 
-       smb_fname_cwd = synthetic_smb_fname(ctx, ".", NULL, NULL, 0);
+       smb_fname_cwd = synthetic_smb_fname(ctx,
+                                           ".",
+                                           NULL,
+                                           NULL,
+                                           smb_dname->twrp,
+                                           0);
        if (smb_fname_cwd == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto chdir;
@@ -1006,11 +1023,10 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
  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)
+static NTSTATUS fd_open_atomic(files_struct *fsp,
+                              int flags,
+                              mode_t mode,
+                              bool *file_created)
 {
        NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
        NTSTATUS retry_status;
@@ -1021,7 +1037,7 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                /*
                 * We're not creating the file, just pass through.
                 */
-               status = fd_open(conn, fsp, flags, mode);
+               status = fd_open(fsp, flags, mode);
                *file_created = false;
                return status;
        }
@@ -1030,7 +1046,7 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                /*
                 * Fail if already exists, just pass through.
                 */
-               status = fd_open(conn, fsp, flags, mode);
+               status = fd_open(fsp, flags, mode);
 
                /*
                 * Here we've opened with O_CREAT|O_EXCL. If that went
@@ -1070,7 +1086,7 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                retry_status = NT_STATUS_OBJECT_NAME_COLLISION;
        }
 
-       status = fd_open(conn, fsp, curr_flags, mode);
+       status = fd_open(fsp, curr_flags, mode);
        if (NT_STATUS_IS_OK(status)) {
                *file_created = !file_existed;
                return NT_STATUS_OK;
@@ -1089,7 +1105,7 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
                        curr_flags = flags | O_EXCL;
                }
 
-               status = fd_open(conn, fsp, curr_flags, mode);
+               status = fd_open(fsp, curr_flags, mode);
        }
 
        *file_created = (NT_STATUS_IS_OK(status) && !file_existed);
@@ -1101,7 +1117,6 @@ static NTSTATUS fd_open_atomic(struct connection_struct *conn,
 ****************************************************************************/
 
 static NTSTATUS open_file(files_struct *fsp,
-                         connection_struct *conn,
                          struct smb_request *req,
                          struct smb_filename *parent_dir,
                          int flags,
@@ -1110,6 +1125,7 @@ static NTSTATUS open_file(files_struct *fsp,
                          uint32_t open_access_mask, /* what we're actually using in the open. */
                          bool *p_file_created)
 {
+       connection_struct *conn = fsp->conn;
        struct smb_filename *smb_fname = fsp->fsp_name;
        NTSTATUS status = NT_STATUS_OK;
        int accmode = (flags & O_ACCMODE);
@@ -1255,6 +1271,7 @@ static NTSTATUS open_file(files_struct *fsp,
                                }
 
                                status = check_parent_access(conn,
+                                                       conn->cwd_fsp,
                                                        smb_fname,
                                                        SEC_DIR_ADD_FILE);
                                if (!NT_STATUS_IS_OK(status)) {
@@ -1272,8 +1289,10 @@ static NTSTATUS open_file(files_struct *fsp,
                 * Actually do the open - if O_TRUNC is needed handle it
                 * below under the share mode lock.
                 */
-               status = fd_open_atomic(conn, fsp, local_flags & ~O_TRUNC,
-                               unx_mode, p_file_created);
+               status = fd_open_atomic(fsp,
+                                       local_flags & ~O_TRUNC,
+                                       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),
@@ -1467,17 +1486,18 @@ static bool mask_conflict(
  Returns True if conflict, False if not.
 ****************************************************************************/
 
+static const uint32_t conflicting_access =
+       FILE_WRITE_DATA|
+       FILE_APPEND_DATA|
+       FILE_READ_DATA|
+       FILE_EXECUTE|
+       DELETE_ACCESS;
+
 static bool share_conflict(uint32_t e_access_mask,
                           uint32_t e_share_access,
                           uint32_t access_mask,
                           uint32_t share_access)
 {
-       const uint32_t conflicting_access =
-               FILE_WRITE_DATA|
-               FILE_APPEND_DATA|
-               FILE_READ_DATA|
-               FILE_EXECUTE|
-               DELETE_ACCESS;
        bool conflict;
 
        DBG_DEBUG("existing access_mask = 0x%"PRIx32", "
@@ -1728,7 +1748,9 @@ static uint16_t share_mode_flags_restrict(
                &existing_lease_type);
 
        existing_access_mask |= access_mask;
-       existing_share_mode &= share_mode;
+       if (access_mask & conflicting_access) {
+               existing_share_mode &= share_mode;
+       }
        existing_lease_type |= lease_type;
 
        ret = share_mode_flags_set(
@@ -1767,7 +1789,10 @@ static bool open_mode_check_fn(
        }
 
        access_mask = state->access_mask | e->access_mask;
-       share_access = state->share_access & e->share_access;
+       share_access = state->share_access;
+       if (e->access_mask & conflicting_access) {
+               share_access &= e->share_access;
+       }
        lease_type = state->lease_type | get_lease_type(e, state->fid);
 
        if ((access_mask == state->access_mask) &&
@@ -3020,6 +3045,7 @@ static void schedule_async_open(struct smb_request *req)
 
 static NTSTATUS smbd_calculate_maximum_allowed_access(
        connection_struct *conn,
+       struct files_struct *dirfsp,
        const struct smb_filename *smb_fname,
        bool use_privs,
        uint32_t *p_access_mask)
@@ -3028,16 +3054,21 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
        uint32_t access_granted;
        NTSTATUS status;
 
+       SMB_ASSERT(dirfsp == conn->cwd_fsp);
+
        if (!use_privs && (get_current_uid(conn) == (uid_t)0)) {
                *p_access_mask |= FILE_GENERIC_ALL;
                return NT_STATUS_OK;
        }
 
-       status = SMB_VFS_GET_NT_ACL(conn, smb_fname,
-                                   (SECINFO_OWNER |
-                                    SECINFO_GROUP |
-                                    SECINFO_DACL),
-                                   talloc_tos(), &sd);
+       status = SMB_VFS_GET_NT_ACL_AT(conn,
+                               dirfsp,
+                               smb_fname,
+                               (SECINFO_OWNER |
+                                       SECINFO_GROUP |
+                                       SECINFO_DACL),
+                               talloc_tos(),
+                               &sd);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
                /*
@@ -3092,15 +3123,18 @@ static NTSTATUS smbd_calculate_maximum_allowed_access(
 }
 
 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)
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname,
+                       bool use_privs,
+                       uint32_t access_mask,
+                       uint32_t *access_mask_out)
 {
        NTSTATUS status;
        uint32_t orig_access_mask = access_mask;
        uint32_t rejected_share_access;
 
+       SMB_ASSERT(dirfsp == conn->cwd_fsp);
+
        if (access_mask & SEC_MASK_INVALID) {
                DBG_DEBUG("access_mask [%8x] contains invalid bits\n",
                          access_mask);
@@ -3116,8 +3150,11 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn,
        /* Calculate MAXIMUM_ALLOWED_ACCESS if requested. */
        if (access_mask & MAXIMUM_ALLOWED_ACCESS) {
 
-               status = smbd_calculate_maximum_allowed_access(
-                       conn, smb_fname, use_privs, &access_mask);
+               status = smbd_calculate_maximum_allowed_access(conn,
+                                       dirfsp,
+                                       smb_fname,
+                                       use_privs,
+                                       &access_mask);
 
                if (!NT_STATUS_IS_OK(status)) {
                        return status;
@@ -3296,6 +3333,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
        bool setup_poll = false;
        bool ok;
 
+       SMB_ASSERT(fsp->dirfsp == conn->cwd_fsp);
+
        if (conn->printer) {
                /*
                 * Printers are handled completely differently.
@@ -3496,10 +3535,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
                }
        }
 
-       status = smbd_calculate_access_mask(conn, smb_fname,
+       status = smbd_calculate_access_mask(conn,
+                                       conn->cwd_fsp,
+                                       smb_fname,
                                        false,
                                        access_mask,
-                                       &access_mask); 
+                                       &access_mask);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("open_file_ntcreate: smbd_calculate_access_mask "
                        "on file %s returned %s\n",
@@ -3639,10 +3680,14 @@ 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_fname,
-                            flags|flags2, unx_mode, access_mask,
-                            open_access_mask, &new_file_created);
-
+       fsp_open = open_file(fsp,
+                            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"));
@@ -4073,6 +4118,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 }
 
 static NTSTATUS mkdir_internal(connection_struct *conn,
+                              struct files_struct **dirfsp,
                               struct smb_filename *smb_dname,
                               uint32_t file_attributes)
 {
@@ -4087,6 +4133,8 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        int ret;
        bool ok;
 
+       SMB_ASSERT(*dirfsp == conn->cwd_fsp);
+
        if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) {
                DEBUG(5,("mkdir_internal: failing share access "
                         "%s\n", lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
@@ -4112,8 +4160,9 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        }
 
        status = check_parent_access(conn,
-                                       smb_dname,
-                                       access_mask);
+                                    *dirfsp,
+                                    smb_dname,
+                                    access_mask);
        if(!NT_STATUS_IS_OK(status)) {
                DEBUG(5,("mkdir_internal: check_parent_access "
                        "on directory %s for path %s returned %s\n",
@@ -4124,9 +4173,9 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
        }
 
        ret = SMB_VFS_MKDIRAT(conn,
-                       conn->cwd_fsp,
-                       smb_dname,
-                       mode);
+                             *dirfsp,
+                             smb_dname,
+                             mode);
        if (ret != 0) {
                return map_nt_error_from_unix(errno);
        }
@@ -4214,6 +4263,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn,
 
 static NTSTATUS open_directory(connection_struct *conn,
                               struct smb_request *req,
+                              struct files_struct **dirfsp,
                               struct smb_filename *smb_dname,
                               uint32_t access_mask,
                               uint32_t share_access,
@@ -4229,8 +4279,11 @@ static NTSTATUS open_directory(connection_struct *conn,
        NTSTATUS status;
        struct timespec mtimespec;
        int info = 0;
+       int flags;
        bool ok;
 
+       SMB_ASSERT(*dirfsp == conn->cwd_fsp);
+
        if (is_ntfs_stream_smb_fname(smb_dname)) {
                DEBUG(2, ("open_directory: %s is a stream name!\n",
                          smb_fname_str_dbg(smb_dname)));
@@ -4253,8 +4306,12 @@ static NTSTATUS open_directory(connection_struct *conn,
                 create_disposition,
                 file_attributes);
 
-       status = smbd_calculate_access_mask(conn, smb_dname, false,
-                                           access_mask, &access_mask);
+       status = smbd_calculate_access_mask(conn,
+                                       *dirfsp,
+                                       smb_dname,
+                                       false,
+                                       access_mask,
+                                       &access_mask);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("open_directory: smbd_calculate_access_mask "
                        "on file %s returned %s\n",
@@ -4296,7 +4353,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                                return status;
                        }
 
-                       status = mkdir_internal(conn, smb_dname,
+                       status = mkdir_internal(conn, dirfsp, smb_dname,
                                                file_attributes);
 
                        if (!NT_STATUS_IS_OK(status)) {
@@ -4320,7 +4377,7 @@ static NTSTATUS open_directory(connection_struct *conn,
                                status = NT_STATUS_OK;
                                info = FILE_WAS_OPENED;
                        } else {
-                               status = mkdir_internal(conn, smb_dname,
+                               status = mkdir_internal(conn, dirfsp, smb_dname,
                                                file_attributes);
 
                                if (NT_STATUS_IS_OK(status)) {
@@ -4379,7 +4436,7 @@ static NTSTATUS open_directory(connection_struct *conn,
 
        if (info == FILE_WAS_OPENED) {
                status = smbd_check_access_rights(conn,
-                                               conn->cwd_fsp,
+                                               *dirfsp,
                                                smb_dname,
                                                false,
                                                access_mask);
@@ -4427,6 +4484,12 @@ static NTSTATUS open_directory(connection_struct *conn,
                return status;
        }
 
+       if (*dirfsp == fsp->conn->cwd_fsp) {
+               fsp->dirfsp = fsp->conn->cwd_fsp;
+       } else {
+               fsp->dirfsp = talloc_move(fsp, dirfsp);
+       }
+
        /* Don't store old timestamps for directory
           handles in the internal database. We don't
           update them in there if new objects
@@ -4436,12 +4499,13 @@ static NTSTATUS open_directory(connection_struct *conn,
        */
        mtimespec = make_omit_timespec();
 
-#ifdef O_DIRECTORY
-       status = fd_open(conn, fsp, O_RDONLY|O_DIRECTORY, 0);
-#else
        /* POSIX allows us to open a directory with O_RDONLY. */
-       status = fd_open(conn, fsp, O_RDONLY, 0);
+       flags = O_RDONLY;
+#ifdef O_DIRECTORY
+       flags |= O_DIRECTORY;
 #endif
+
+       status = fd_open(fsp, flags, 0);
        if (!NT_STATUS_IS_OK(status)) {
                DBG_INFO("Could not open fd for "
                        "%s (%s)\n",
@@ -4583,7 +4647,7 @@ NTSTATUS create_directory(connection_struct *conn, struct smb_request *req,
        status = SMB_VFS_CREATE_FILE(
                conn,                                   /* conn */
                req,                                    /* req */
-               0,                                      /* root_dir_fid */
+               &conn->cwd_fsp,                         /* dirfsp */
                smb_dname,                              /* fname */
                FILE_READ_ATTRIBUTES,                   /* access_mask */
                FILE_SHARE_NONE,                        /* share_access */
@@ -4654,8 +4718,12 @@ void msg_file_was_renamed(struct messaging_context *msg_ctx,
                msg->stream_name = NULL;
        }
 
-       smb_fname = synthetic_smb_fname(
-               msg, msg->base_name, msg->stream_name, NULL, 0);
+       smb_fname = synthetic_smb_fname(msg,
+                                       msg->base_name,
+                                       msg->stream_name,
+                                       NULL,
+                                       0,
+                                       0);
        if (smb_fname == NULL) {
                DBG_DEBUG("synthetic_smb_fname failed\n");
                goto out;
@@ -4756,6 +4824,7 @@ static NTSTATUS open_streams_for_delete(connection_struct *conn,
                                        smb_fname->base_name,
                                        stream_info[i].name,
                                        NULL,
+                                       smb_fname->twrp,
                                        (smb_fname->flags &
                                                ~SMB_FILENAME_POSIX_PATH));
                if (smb_fname_cp == NULL) {
@@ -4771,7 +4840,7 @@ static NTSTATUS open_streams_for_delete(connection_struct *conn,
                status = SMB_VFS_CREATE_FILE(
                         conn,                  /* conn */
                         NULL,                  /* req */
-                        0,                     /* root_dir_fid */
+                        &conn->cwd_fsp,        /* dirfsp */
                         smb_fname_cp,          /* fname */
                         DELETE_ACCESS,         /* access_mask */
                         (FILE_SHARE_READ |     /* share_access */
@@ -4858,11 +4927,12 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = SMB_VFS_GET_NT_ACL(fsp->conn,
-                                   parent_dir,
-                                   (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
-                                   frame,
-                                   &parent_desc);
+       status = SMB_VFS_GET_NT_ACL_AT(fsp->conn,
+                               fsp->conn->cwd_fsp,
+                               parent_dir,
+                               (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL),
+                               frame,
+                               &parent_desc);
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(frame);
                return status;
@@ -5326,6 +5396,7 @@ static NTSTATUS lease_match(connection_struct *conn,
 
 static NTSTATUS create_file_unixpath(connection_struct *conn,
                                     struct smb_request *req,
+                                    struct files_struct **dirfsp,
                                     struct smb_filename *smb_fname,
                                     uint32_t access_mask,
                                     uint32_t share_access,
@@ -5348,6 +5419,8 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        files_struct *fsp = NULL;
        NTSTATUS status;
 
+       SMB_ASSERT(*dirfsp == conn->cwd_fsp);
+
        DBG_DEBUG("create_file_unixpath: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
                  "create_disposition = 0x%x create_options = 0x%x "
@@ -5487,6 +5560,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                                                smb_fname->base_name,
                                                NULL,
                                                NULL,
+                                               smb_fname->twrp,
                                                smb_fname->flags);
                if (smb_fname_base == NULL) {
                        status = NT_STATUS_NO_MEMORY;
@@ -5520,15 +5594,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                base_privflags = NTCREATEX_OPTIONS_PRIVATE_STREAM_BASEOPEN;
 
                /* Open the base file. */
-               status = create_file_unixpath(conn, NULL, smb_fname_base, 0,
+               status = create_file_unixpath(conn,
+                                             NULL,
+                                             dirfsp,
+                                             smb_fname_base,
+                                             0,
                                              FILE_SHARE_READ
                                              | FILE_SHARE_WRITE
                                              | FILE_SHARE_DELETE,
                                              base_create_disposition,
-                                             0, 0, 0, NULL, 0,
+                                             0,
+                                             0,
+                                             0,
+                                             NULL,
+                                             0,
                                              base_privflags,
-                                             NULL, NULL,
-                                             &base_fsp, NULL);
+                                             NULL,
+                                             NULL,
+                                             &base_fsp,
+                                             NULL);
                TALLOC_FREE(smb_fname_base);
 
                if (!NT_STATUS_IS_OK(status)) {
@@ -5566,10 +5650,17 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                 */
 
                oplock_request = 0;
-               status = open_directory(
-                       conn, req, smb_fname, access_mask, share_access,
-                       create_disposition, create_options, file_attributes,
-                       &info, &fsp);
+               status = open_directory(conn,
+                                       req,
+                                       dirfsp,
+                                       smb_fname,
+                                       access_mask,
+                                       share_access,
+                                       create_disposition,
+                                       create_options,
+                                       file_attributes,
+                                       &info,
+                                       &fsp);
        } else {
 
                /*
@@ -5586,6 +5677,12 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                        goto fail;
                }
 
+               if (*dirfsp == fsp->conn->cwd_fsp) {
+                       fsp->dirfsp = fsp->conn->cwd_fsp;
+               } else {
+                       fsp->dirfsp = talloc_move(fsp, dirfsp);
+               }
+
                if (base_fsp) {
                        /*
                         * We're opening the stream element of a
@@ -5638,11 +5735,17 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
                        }
 
                        oplock_request = 0;
-                       status = open_directory(
-                               conn, req, smb_fname, access_mask,
-                               share_access, create_disposition,
-                               create_options, file_attributes,
-                               &info, &fsp);
+                       status = open_directory(conn,
+                                               req,
+                                               dirfsp,
+                                               smb_fname,
+                                               access_mask,
+                                               share_access,
+                                               create_disposition,
+                                               create_options,
+                                               file_attributes,
+                                               &info,
+                                               &fsp);
                }
        }
 
@@ -5774,130 +5877,9 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
        return status;
 }
 
-/*
- * Calculate the full path name given a relative fid.
- */
-static NTSTATUS get_relative_fid_filename(
-       connection_struct *conn,
-       struct smb_request *req,
-       uint16_t root_dir_fid,
-       const struct smb_filename *smb_fname,
-       struct smb_filename **smb_fname_out)
-{
-       files_struct *dir_fsp;
-       char *parent_fname = NULL;
-       char *new_base_name = NULL;
-       uint32_t ucf_flags = ucf_flags_from_smb_request(req);
-       NTSTATUS status;
-
-       if (root_dir_fid == 0 || !smb_fname) {
-               status = NT_STATUS_INTERNAL_ERROR;
-               goto out;
-       }
-
-       dir_fsp = file_fsp(req, root_dir_fid);
-
-       if (dir_fsp == NULL) {
-               status = NT_STATUS_INVALID_HANDLE;
-               goto out;
-       }
-
-       if (is_ntfs_stream_smb_fname(dir_fsp->fsp_name)) {
-               status = NT_STATUS_INVALID_HANDLE;
-               goto out;
-       }
-
-       if (!dir_fsp->fsp_flags.is_directory) {
-
-               /*
-                * Check to see if this is a mac fork of some kind.
-                */
-
-               if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
-                   is_ntfs_stream_smb_fname(smb_fname)) {
-                       status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
-                       goto out;
-               }
-
-               /*
-                 we need to handle the case when we get a
-                 relative open relative to a file and the
-                 pathname is blank - this is a reopen!
-                 (hint from demyn plantenberg)
-               */
-
-               status = NT_STATUS_INVALID_HANDLE;
-               goto out;
-       }
-
-       if (ISDOT(dir_fsp->fsp_name->base_name)) {
-               /*
-                * We're at the toplevel dir, the final file name
-                * must not contain ./, as this is filtered out
-                * normally by srvstr_get_path and unix_convert
-                * explicitly rejects paths containing ./.
-                */
-               parent_fname = talloc_strdup(talloc_tos(), "");
-               if (parent_fname == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto out;
-               }
-       } else {
-               size_t dir_name_len = strlen(dir_fsp->fsp_name->base_name);
-
-               /*
-                * Copy in the base directory name.
-                */
-
-               parent_fname = talloc_array(talloc_tos(), char,
-                   dir_name_len+2);
-               if (parent_fname == NULL) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto out;
-               }
-               memcpy(parent_fname, dir_fsp->fsp_name->base_name,
-                   dir_name_len+1);
-
-               /*
-                * Ensure it ends in a '/'.
-                * We used TALLOC_SIZE +2 to add space for the '/'.
-                */
-
-               if(dir_name_len
-                   && (parent_fname[dir_name_len-1] != '\\')
-                   && (parent_fname[dir_name_len-1] != '/')) {
-                       parent_fname[dir_name_len] = '/';
-                       parent_fname[dir_name_len+1] = '\0';
-               }
-       }
-
-       new_base_name = talloc_asprintf(talloc_tos(), "%s%s", parent_fname,
-                                       smb_fname->base_name);
-       if (new_base_name == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto out;
-       }
-
-       status = filename_convert(req,
-                               conn,
-                               new_base_name,
-                               ucf_flags,
-                               NULL,
-                               NULL,
-                               smb_fname_out);
-       if (!NT_STATUS_IS_OK(status)) {
-               goto out;
-       }
-
- out:
-       TALLOC_FREE(parent_fname);
-       TALLOC_FREE(new_base_name);
-       return status;
-}
-
 NTSTATUS create_file_default(connection_struct *conn,
                             struct smb_request *req,
-                            uint16_t root_dir_fid,
+                            struct files_struct **_dirfsp,
                             struct smb_filename *smb_fname,
                             uint32_t access_mask,
                             uint32_t share_access,
@@ -5920,13 +5902,17 @@ NTSTATUS create_file_default(connection_struct *conn,
        NTSTATUS status;
        bool stream_name = false;
        struct smb2_create_blob *posx = NULL;
+       struct files_struct *dirfsp = *_dirfsp;
+
+       SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
 
        DBG_DEBUG("create_file: access_mask = 0x%x "
                  "file_attributes = 0x%x, share_access = 0x%x, "
                  "create_disposition = 0x%x create_options = 0x%x "
                  "oplock_request = 0x%x "
                  "private_flags = 0x%x "
-                 "root_dir_fid = 0x%x, ea_list = %p, sd = %p, "
+                 "ea_list = %p, sd = %p, "
+                 "dirfsp = %s, "
                  "fname = %s\n",
                  (unsigned int)access_mask,
                  (unsigned int)file_attributes,
@@ -5935,8 +5921,10 @@ NTSTATUS create_file_default(connection_struct *conn,
                  (unsigned int)create_options,
                  (unsigned int)oplock_request,
                  (unsigned int)private_flags,
-                 (unsigned int)root_dir_fid,
-                 ea_list, sd, smb_fname_str_dbg(smb_fname));
+                 ea_list,
+                 sd,
+                 fsp_str_dbg(dirfsp),
+                 smb_fname_str_dbg(smb_fname));
 
        if (req != NULL) {
                /*
@@ -5947,20 +5935,6 @@ NTSTATUS create_file_default(connection_struct *conn,
                get_deferred_open_message_state(req, &req->request_time, NULL);
        }
 
-       /*
-        * Calculate the filename from the root_dir_if if necessary.
-        */
-
-       if (root_dir_fid != 0) {
-               struct smb_filename *smb_fname_out = NULL;
-               status = get_relative_fid_filename(conn, req, root_dir_fid,
-                                                  smb_fname, &smb_fname_out);
-               if (!NT_STATUS_IS_OK(status)) {
-                       goto fail;
-               }
-               smb_fname = smb_fname_out;
-       }
-
        /*
         * Check to see if this is a mac fork of some kind.
         */
@@ -6051,13 +6025,23 @@ NTSTATUS create_file_default(connection_struct *conn,
                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,
-               oplock_request, lease, allocation_size, private_flags,
-               sd, ea_list,
-               &fsp, &info);
-
+       status = create_file_unixpath(conn,
+                                     req,
+                                     _dirfsp,
+                                     smb_fname,
+                                     access_mask,
+                                     share_access,
+                                     create_disposition,
+                                     create_options,
+                                     file_attributes,
+                                     oplock_request,
+                                     lease,
+                                     allocation_size,
+                                     private_flags,
+                                     sd,
+                                     ea_list,
+                                     &fsp,
+                                     &info);
        if (!NT_STATUS_IS_OK(status)) {
                goto fail;
        }