ctdb: Use str_list_add_printf() in lock_helper_args()
[samba.git] / source3 / smbd / dosmode.c
index bcd680b82d6270a99e70934c5814e7cf65e07534..32de2a4a0182181a07166c01c2a51b0e1e00ffdd 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
    dos mode handling functions
    Copyright (C) Andrew Tridgell 1992-1998
 #include "lib/param/loadparm.h"
 #include "lib/util/tevent_ntstatus.h"
 #include "lib/util/string_wrappers.h"
-
-static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
-                               const struct smb_filename *smb_fname,
-                               files_struct **ret_fsp,
-                               bool *need_close);
+#include "fake_file.h"
 
 static void dos_mode_debug_print(const char *func, uint32_t mode)
 {
@@ -68,14 +64,20 @@ static void dos_mode_debug_print(const char *func, uint32_t mode)
        if (mode & FILE_ATTRIBUTE_COMPRESSED) {
                fstrcat(modestr, "[compressed]");
        }
+       if (mode & FILE_ATTRIBUTE_REPARSE_POINT) {
+               fstrcat(modestr, "[reparse_point]");
+       }
 
-       DBG_INFO("%s returning (0x%x): \"%s\"\n", func, (unsigned)mode,
+       DBG_INFO("%s returning (0x%" PRIx32 "): \"%s\"\n",
+                func,
+                mode,
                 modestr);
 }
 
-static uint32_t filter_mode_by_protocol(uint32_t mode)
+static uint32_t filter_mode_by_protocol(enum protocol_types protocol,
+                                       uint32_t mode)
 {
-       if (get_Protocol() <= PROTOCOL_LANMAN2) {
+       if (protocol <= PROTOCOL_LANMAN2) {
                DEBUG(10,("filter_mode_by_protocol: "
                        "filtering result 0x%x to 0x%x\n",
                        (unsigned int)mode,
@@ -90,7 +92,7 @@ static uint32_t filter_mode_by_protocol(uint32_t mode)
     Base permission for files:
          if creating file and inheriting (i.e. parent_dir != NULL)
            apply read/write bits from parent directory.
-         else   
+         else
            everybody gets read bit set
          dos readonly is represented in unix by removing everyone's write bit
          dos archive is represented in unix by the user's execute bit
@@ -110,37 +112,42 @@ static uint32_t filter_mode_by_protocol(uint32_t mode)
 
 mode_t unix_mode(connection_struct *conn, int dosmode,
                 const struct smb_filename *smb_fname,
-                struct smb_filename *smb_fname_parent)
+                struct files_struct *parent_dirfsp)
 {
        mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
        mode_t dir_mode = 0; /* Mode of the inherit_from directory if
                              * inheriting. */
 
-       if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
+       if ((dosmode & FILE_ATTRIBUTE_READONLY) &&
+           !lp_store_dos_attributes(SNUM(conn))) {
                result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
        }
 
-       if ((smb_fname_parent != NULL) && lp_inherit_permissions(SNUM(conn))) {
+       if ((parent_dirfsp != NULL) && lp_inherit_permissions(SNUM(conn))) {
+               struct stat_ex sbuf = { .st_ex_nlink = 0, };
+               int ret;
+
                DBG_DEBUG("[%s] inheriting from [%s]\n",
                          smb_fname_str_dbg(smb_fname),
-                         smb_fname_str_dbg(smb_fname_parent));
+                         smb_fname_str_dbg(parent_dirfsp->fsp_name));
 
-               if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
-                       DBG_ERR("stat failed [%s]: %s\n",
-                               smb_fname_str_dbg(smb_fname_parent),
+               ret = SMB_VFS_FSTAT(parent_dirfsp, &sbuf);
+               if (ret != 0) {
+                       DBG_ERR("fstat failed [%s]: %s\n",
+                               smb_fname_str_dbg(parent_dirfsp->fsp_name),
                                strerror(errno));
                        return(0);      /* *** shouldn't happen! *** */
                }
 
                /* Save for later - but explicitly remove setuid bit for safety. */
-               dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
+               dir_mode = sbuf.st_ex_mode & ~S_ISUID;
                DEBUG(2,("unix_mode(%s) inherit mode %o\n",
                         smb_fname_str_dbg(smb_fname), (int)dir_mode));
                /* Clear "result" */
                result = 0;
-       } 
+       }
 
-       if (IS_DOS_DIR(dosmode)) {
+       if (dosmode & FILE_ATTRIBUTE_DIRECTORY) {
                /* We never make directories read only for the owner as under DOS a user
                can always create a file in a read-only directory. */
                result |= (S_IFDIR | S_IWUSR);
@@ -150,22 +157,28 @@ mode_t unix_mode(connection_struct *conn, int dosmode,
                        result |= dir_mode;
                } else {
                        /* Provisionally add all 'x' bits */
-                       result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
+                       result |= (S_IXUSR | S_IXGRP | S_IXOTH);
 
                        /* Apply directory mask */
                        result &= lp_directory_mask(SNUM(conn));
                        /* Add in force bits */
                        result |= lp_force_directory_mode(SNUM(conn));
                }
-       } else { 
-               if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
+       } else {
+               if ((dosmode & FILE_ATTRIBUTE_ARCHIVE) &&
+                   lp_map_archive(SNUM(conn))) {
                        result |= S_IXUSR;
+               }
 
-               if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
+               if ((dosmode & FILE_ATTRIBUTE_SYSTEM) &&
+                   lp_map_system(SNUM(conn))) {
                        result |= S_IXGRP;
+               }
 
-               if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
-                       result |= S_IXOTH;  
+               if ((dosmode & FILE_ATTRIBUTE_HIDDEN) &&
+                   lp_map_hidden(SNUM(conn))) {
+                       result |= S_IXOTH;
+               }
 
                if (dir_mode) {
                        /* Inherit 666 component of parent directory mode */
@@ -189,43 +202,49 @@ mode_t unix_mode(connection_struct *conn, int dosmode,
 ****************************************************************************/
 
 static uint32_t dos_mode_from_sbuf(connection_struct *conn,
-                                const struct smb_filename *smb_fname)
+                                  const struct stat_ex *st,
+                                  struct files_struct *fsp)
 {
        int result = 0;
-       enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
+       enum mapreadonly_options ro_opts =
+               (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
 
 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
        /* if we can find out if a file is immutable we should report it r/o */
-       if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
+       if (st->st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
                result |= FILE_ATTRIBUTE_READONLY;
        }
 #endif
        if (ro_opts == MAP_READONLY_YES) {
                /* Original Samba method - map inverse of user "w" bit. */
-               if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
+               if ((st->st_ex_mode & S_IWUSR) == 0) {
                        result |= FILE_ATTRIBUTE_READONLY;
                }
-       } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
+       }
+       if (ro_opts == MAP_READONLY_PERMISSIONS) {
+               /* smb_fname->fsp can be NULL for an MS-DFS link. */
                /* Check actual permissions for read-only. */
-               if (!can_write_to_file(conn,
-                               conn->cwd_fsp,
-                               smb_fname))
-               {
+               if ((fsp != NULL) && !can_write_to_fsp(fsp)) {
                        result |= FILE_ATTRIBUTE_READONLY;
                }
-       } /* Else never set the readonly bit. */
+       }
 
-       if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
+       if (MAP_ARCHIVE(conn) && ((st->st_ex_mode & S_IXUSR) != 0)) {
                result |= FILE_ATTRIBUTE_ARCHIVE;
+       }
 
-       if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
+       if (MAP_SYSTEM(conn) && ((st->st_ex_mode & S_IXGRP) != 0)) {
                result |= FILE_ATTRIBUTE_SYSTEM;
+       }
 
-       if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
+       if (MAP_HIDDEN(conn) && ((st->st_ex_mode & S_IXOTH) != 0)) {
                result |= FILE_ATTRIBUTE_HIDDEN;
+       }
 
-       if (S_ISDIR(smb_fname->st.st_ex_mode))
-               result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
+       if (S_ISDIR(st->st_ex_mode)) {
+               result = FILE_ATTRIBUTE_DIRECTORY |
+                        (result & FILE_ATTRIBUTE_READONLY);
+       }
 
        dos_mode_debug_print(__func__, result);
 
@@ -273,7 +292,7 @@ NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
                        update_stat_ex_create_time(&smb_fname->st,
                                                   create_time);
 
-                       DBG_DEBUG("file %s case 1 set btime %s\n",
+                       DBG_DEBUG("file %s case 1 set btime %s",
                                  smb_fname_str_dbg(smb_fname),
                                  time_to_asc(convert_timespec_to_time_t(
                                                      create_time)));
@@ -294,54 +313,41 @@ NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
                        update_stat_ex_create_time(&smb_fname->st,
                                                   create_time);
 
-                       DBG_DEBUG("file %s case 3 set btime %s\n",
+                       DBG_DEBUG("file %s case 3 set btime %s",
                                  smb_fname_str_dbg(smb_fname),
                                  time_to_asc(convert_timespec_to_time_t(
                                                      create_time)));
                }
                break;
        case 4:
+       case 5:
        {
-               struct xattr_DosInfo4 *info = &dosattrib.info.info4;
+               uint32_t info_valid_flags;
+               NTTIME info_create_time;
 
-               dosattr = info->attrib;
+               if (dosattrib.version == 4) {
+                       info_valid_flags = dosattrib.info.info4.valid_flags;
+                       info_create_time = dosattrib.info.info4.create_time;
+                       dosattr = dosattrib.info.info4.attrib;
+               } else {
+                       info_valid_flags = dosattrib.info.info5.valid_flags;
+                       info_create_time = dosattrib.info.info5.create_time;
+                       dosattr = dosattrib.info.info5.attrib;
+               }
 
-               if ((info->valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
-                   !null_nttime(info->create_time))
+               if ((info_valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
+                   !null_nttime(info_create_time))
                {
                        struct timespec creat_time;
 
-                       creat_time = nt_time_to_full_timespec(info->create_time);
+                       creat_time = nt_time_to_full_timespec(info_create_time);
                        update_stat_ex_create_time(&smb_fname->st, creat_time);
 
                        DBG_DEBUG("file [%s] creation time [%s]\n",
                                smb_fname_str_dbg(smb_fname),
-                               nt_time_string(talloc_tos(), info->create_time));
+                               nt_time_string(talloc_tos(), info_create_time));
                }
 
-               if (info->valid_flags & XATTR_DOSINFO_ITIME) {
-                       struct timespec itime;
-                       uint64_t file_id;
-
-                       itime = nt_time_to_unix_timespec(info->itime);
-                       if (smb_fname->st.st_ex_iflags &
-                           ST_EX_IFLAG_CALCULATED_ITIME)
-                       {
-                               update_stat_ex_itime(&smb_fname->st, itime);
-                       }
-
-                       file_id = make_file_id_from_itime(&smb_fname->st);
-                       if (smb_fname->st.st_ex_iflags &
-                           ST_EX_IFLAG_CALCULATED_FILE_ID)
-                       {
-                               update_stat_ex_file_id(&smb_fname->st, file_id);
-                       }
-
-                       DBG_DEBUG("file [%s] itime [%s] fileid [%"PRIx64"]\n",
-                               smb_fname_str_dbg(smb_fname),
-                               nt_time_string(talloc_tos(), info->itime),
-                               file_id);
-               }
                break;
        }
        default:
@@ -355,8 +361,16 @@ NTSTATUS parse_dos_attribute_blob(struct smb_filename *smb_fname,
                dosattr |= FILE_ATTRIBUTE_DIRECTORY;
        }
 
-       /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
-       *pattr |= (uint32_t)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
+       /*
+        * _SPARSE and _REPARSE_POINT are valid on get but not on
+        * set. Both are created via special fcntls.
+        */
+
+       dosattr &= (SAMBA_ATTRIBUTES_MASK|
+                   FILE_ATTRIBUTE_SPARSE|
+                   FILE_ATTRIBUTE_REPARSE_POINT);
+
+       *pattr |= dosattr;
 
        dos_mode_debug_print(__func__, *pattr);
 
@@ -378,10 +392,23 @@ NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
        /* Don't reset pattr to zero as we may already have filename-based attributes we
           need to preserve. */
 
-       sizeret = SMB_VFS_FGETXATTR(fsp->base_fsp ? fsp->base_fsp : fsp,
+       sizeret = SMB_VFS_FGETXATTR(fsp,
                                    SAMBA_XATTR_DOS_ATTRIB,
                                    attrstr,
                                    sizeof(attrstr));
+       if (sizeret == -1 && ( errno == EPERM || errno == EACCES )) {
+               /* we may also retrieve dos attribs for unreadable files, this
+                  is why we'll retry as root. We don't use root in the first
+                  run because in cases like NFS, root might have even less
+                  rights than the real user
+               */
+               become_root();
+               sizeret = SMB_VFS_FGETXATTR(fsp,
+                                           SAMBA_XATTR_DOS_ATTRIB,
+                                           attrstr,
+                                           sizeof(attrstr));
+               unbecome_root();
+       }
        if (sizeret == -1) {
                DBG_INFO("Cannot get attribute "
                         "from EA on file %s: Error = %s\n",
@@ -406,44 +433,41 @@ NTSTATUS fget_ea_dos_attribute(struct files_struct *fsp,
 ****************************************************************************/
 
 NTSTATUS set_ea_dos_attribute(connection_struct *conn,
-                             const struct smb_filename *smb_fname,
+                             struct smb_filename *smb_fname,
                              uint32_t dosmode)
 {
-       struct xattr_DOSATTRIB dosattrib;
+       struct xattr_DOSATTRIB dosattrib = { .version = 0, };
        enum ndr_err_code ndr_err;
-       DATA_BLOB blob;
+       DATA_BLOB blob = { .data = NULL, };
+       struct timespec btime;
        int ret;
 
        if (!lp_store_dos_attributes(SNUM(conn))) {
                return NT_STATUS_NOT_IMPLEMENTED;
        }
 
+       if (smb_fname->fsp == NULL) {
+               /* symlink */
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       }
        /*
         * Don't store FILE_ATTRIBUTE_OFFLINE, it's dealt with in
         * vfs_default via DMAPI if that is enabled.
         */
        dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
 
-       ZERO_STRUCT(dosattrib);
-       ZERO_STRUCT(blob);
-
-       dosattrib.version = 4;
-       dosattrib.info.info4.valid_flags = XATTR_DOSINFO_ATTRIB |
+       dosattrib.version = 5;
+       dosattrib.info.info5.valid_flags = XATTR_DOSINFO_ATTRIB |
                                        XATTR_DOSINFO_CREATE_TIME;
-       dosattrib.info.info4.attrib = dosmode;
-       dosattrib.info.info4.create_time = full_timespec_to_nt_time(
+       dosattrib.info.info5.attrib = dosmode;
+       dosattrib.info.info5.create_time = full_timespec_to_nt_time(
                &smb_fname->st.st_ex_btime);
 
-       if (!(smb_fname->st.st_ex_iflags & ST_EX_IFLAG_CALCULATED_ITIME)) {
-               dosattrib.info.info4.valid_flags |= XATTR_DOSINFO_ITIME;
-               dosattrib.info.info4.itime = full_timespec_to_nt_time(
-                       &smb_fname->st.st_ex_itime);
-       }
-
-       DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
-               (unsigned int)dosmode,
-               time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
-               smb_fname_str_dbg(smb_fname) ));
+       DBG_DEBUG("set attribute 0x%" PRIx32 ", btime = %s on file %s\n",
+                 dosmode,
+                 time_to_asc(convert_timespec_to_time_t(
+                         smb_fname->st.st_ex_btime)),
+                 smb_fname_str_dbg(smb_fname));
 
        ndr_err = ndr_push_struct_blob(
                        &blob, talloc_tos(), &dosattrib,
@@ -465,8 +489,6 @@ NTSTATUS set_ea_dos_attribute(connection_struct *conn,
                               blob.data, blob.length, 0);
        if (ret != 0) {
                NTSTATUS status = NT_STATUS_OK;
-               bool need_close = false;
-               files_struct *fsp = NULL;
                bool set_dosmode_ok = false;
 
                if ((errno != EPERM) && (errno != EACCES)) {
@@ -485,9 +507,8 @@ NTSTATUS set_ea_dos_attribute(connection_struct *conn,
                        return NT_STATUS_ACCESS_DENIED;
                }
 
-               status = smbd_check_access_rights(conn,
-                                       conn->cwd_fsp,
-                                       smb_fname,
+               status = smbd_check_access_rights_fsp(conn->cwd_fsp,
+                                       smb_fname->fsp,
                                        false,
                                        FILE_WRITE_ATTRIBUTES);
                if (NT_STATUS_IS_OK(status)) {
@@ -495,93 +516,95 @@ NTSTATUS set_ea_dos_attribute(connection_struct *conn,
                }
 
                if (!set_dosmode_ok && lp_dos_filemode(SNUM(conn))) {
-                       set_dosmode_ok = can_write_to_file(conn,
-                                               conn->cwd_fsp,
-                                               smb_fname);
+                       set_dosmode_ok = can_write_to_fsp(smb_fname->fsp);
                }
 
                if (!set_dosmode_ok) {
                        return NT_STATUS_ACCESS_DENIED;
                }
 
-               /*
-                * We need to get an open file handle to do the
-                * metadata operation under root.
-                */
-
-               status = get_file_handle_for_metadata(conn,
-                                               smb_fname,
-                                               &fsp,
-                                               &need_close);
-               if (!NT_STATUS_IS_OK(status)) {
-                       return status;
-               }
-
                become_root();
-               ret = SMB_VFS_FSETXATTR(fsp,
+               ret = SMB_VFS_FSETXATTR(smb_fname->fsp,
                                        SAMBA_XATTR_DOS_ATTRIB,
                                        blob.data, blob.length, 0);
                if (ret == 0) {
                        status = NT_STATUS_OK;
                }
                unbecome_root();
-               if (need_close) {
-                       close_file(NULL, fsp, NORMAL_CLOSE);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
                }
-               return status;
        }
-       DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
-               (unsigned int)dosmode,
-               smb_fname_str_dbg(smb_fname)));
+
+       /*
+        * We correctly stored the create time.
+        * We *always* set XATTR_DOSINFO_CREATE_TIME,
+        * so now it can no longer be considered
+        * calculated. Make sure to use the value rounded
+        * to NTTIME granularity we've stored in the xattr.
+        */
+       btime = nt_time_to_full_timespec(dosattrib.info.info5.create_time);
+       update_stat_ex_create_time(&smb_fname->st, btime);
+
+       DBG_DEBUG("set EA 0x%" PRIx32 " on file %s\n",
+                 dosmode,
+                 smb_fname_str_dbg(smb_fname));
        return NT_STATUS_OK;
 }
 
-/****************************************************************************
- Change a unix mode to a dos mode for an ms dfs link.
-****************************************************************************/
-
-uint32_t dos_mode_msdfs(connection_struct *conn,
-                     const struct smb_filename *smb_fname)
+static uint32_t
+dos_mode_from_name(connection_struct *conn, const char *name, uint32_t dosmode)
 {
-       uint32_t result = 0;
-
-       DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
-
-       if (!VALID_STAT(smb_fname->st)) {
-               return 0;
-       }
+       const char *p = NULL;
+       uint32_t result = dosmode;
 
-       /* First do any modifications that depend on the path name. */
-       /* hide files with a name starting with a . */
-       if (lp_hide_dot_files(SNUM(conn))) {
-               const char *p = strrchr_m(smb_fname->base_name, '/');
+       if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
+           lp_hide_dot_files(SNUM(conn)))
+       {
+               p = strrchr_m(name, '/');
                if (p) {
                        p++;
                } else {
-                       p = smb_fname->base_name;
+                       p = name;
                }
 
                /* Only . and .. are not hidden. */
-               if (p[0] == '.' && !((p[1] == '\0') ||
-                               (p[1] == '.' && p[2] == '\0'))) {
+               if ((p[0] == '.') && !(ISDOT(p) || ISDOTDOT(p))) {
                        result |= FILE_ATTRIBUTE_HIDDEN;
                }
        }
 
-       result |= dos_mode_from_sbuf(conn, smb_fname);
-
-       /* Optimization : Only call is_hidden_path if it's not already
-          hidden. */
-       if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
-           IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
+       if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, name)) {
                result |= FILE_ATTRIBUTE_HIDDEN;
        }
 
+       return result;
+}
+
+/****************************************************************************
+ Change a unix mode to a dos mode for an ms dfs link.
+****************************************************************************/
+
+uint32_t dos_mode_msdfs(connection_struct *conn,
+                       const char *name,
+                       const struct stat_ex *st)
+{
+       uint32_t result = 0;
+
+       DEBUG(8, ("dos_mode_msdfs: %s\n", name));
+
+       if (!VALID_STAT(*st)) {
+               return 0;
+       }
+
+       result = dos_mode_from_name(conn, name, result);
+       result |= dos_mode_from_sbuf(conn, st, NULL);
+
        if (result == 0) {
                result = FILE_ATTRIBUTE_NORMAL;
        }
 
-       result = filter_mode_by_protocol(result);
+       result = filter_mode_by_protocol(conn_protocol(conn->sconn), result);
 
        /*
         * Add in that it is a reparse point
@@ -596,23 +619,16 @@ uint32_t dos_mode_msdfs(connection_struct *conn,
 /*
  * check whether a file or directory is flagged as compressed.
  */
-static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
-                                         struct files_struct *fsp,
-                                         struct smb_filename *smb_fname,
+static NTSTATUS dos_mode_check_compressed(struct files_struct *fsp,
                                          bool *is_compressed)
 {
        NTSTATUS status;
        uint16_t compression_fmt;
-       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-       if (tmp_ctx == NULL) {
-               status = NT_STATUS_NO_MEMORY;
-               goto err_out;
-       }
 
-       status = SMB_VFS_FGET_COMPRESSION(conn, tmp_ctx, fsp,
-                                        &compression_fmt);
+       status = SMB_VFS_FGET_COMPRESSION(
+               fsp->conn, talloc_tos(), fsp, &compression_fmt);
        if (!NT_STATUS_IS_OK(status)) {
-               goto err_ctx_free;
+               return status;
        }
 
        if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
@@ -620,59 +636,20 @@ static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
        } else {
                *is_compressed = false;
        }
-       status = NT_STATUS_OK;
-
-err_ctx_free:
-       talloc_free(tmp_ctx);
-err_out:
-       return status;
-}
-
-static uint32_t dos_mode_from_name(connection_struct *conn,
-                                  const struct smb_filename *smb_fname,
-                                  uint32_t dosmode)
-{
-       const char *p = NULL;
-       uint32_t result = dosmode;
-
-       if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
-           lp_hide_dot_files(SNUM(conn)))
-       {
-               p = strrchr_m(smb_fname->base_name, '/');
-               if (p) {
-                       p++;
-               } else {
-                       p = smb_fname->base_name;
-               }
-
-               /* Only . and .. are not hidden. */
-               if ((p[0] == '.') &&
-                   !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0')))
-               {
-                       result |= FILE_ATTRIBUTE_HIDDEN;
-               }
-       }
-
-       if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
-           IS_HIDDEN_PATH(conn, smb_fname->base_name))
-       {
-               result |= FILE_ATTRIBUTE_HIDDEN;
-       }
-
-       return result;
+       return NT_STATUS_OK;
 }
 
 static uint32_t dos_mode_post(uint32_t dosmode,
-                             connection_struct *conn,
                              struct files_struct *fsp,
-                             struct smb_filename *smb_fname,
                              const char *func)
 {
+       struct smb_filename *smb_fname = NULL;
        NTSTATUS status;
 
        if (fsp != NULL) {
                smb_fname = fsp->fsp_name;
        }
+       SMB_ASSERT(smb_fname != NULL);
 
        /*
         * According to MS-FSA a stream name does not have
@@ -691,17 +668,16 @@ static uint32_t dos_mode_post(uint32_t dosmode,
                dosmode &= ~(FILE_ATTRIBUTE_DIRECTORY);
        }
 
-       if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
+       if (fsp->conn->fs_capabilities & FILE_FILE_COMPRESSION) {
                bool compressed = false;
 
-               status = dos_mode_check_compressed(conn, fsp, smb_fname,
-                                                  &compressed);
+               status = dos_mode_check_compressed(fsp, &compressed);
                if (NT_STATUS_IS_OK(status) && compressed) {
                        dosmode |= FILE_ATTRIBUTE_COMPRESSED;
                }
        }
 
-       dosmode |= dos_mode_from_name(conn, smb_fname, dosmode);
+       dosmode |= dos_mode_from_name(fsp->conn, smb_fname->base_name, dosmode);
 
        if (S_ISDIR(smb_fname->st.st_ex_mode)) {
                dosmode |= FILE_ATTRIBUTE_DIRECTORY;
@@ -709,7 +685,8 @@ static uint32_t dos_mode_post(uint32_t dosmode,
                dosmode = FILE_ATTRIBUTE_NORMAL;
        }
 
-       dosmode = filter_mode_by_protocol(dosmode);
+       dosmode = filter_mode_by_protocol(conn_protocol(fsp->conn->sconn),
+                                         dosmode);
 
        dos_mode_debug_print(func, dosmode);
        return dosmode;
@@ -726,39 +703,47 @@ uint32_t fdos_mode(struct files_struct *fsp)
        uint32_t result = 0;
        NTSTATUS status = NT_STATUS_OK;
 
-       if (fsp == NULL) {
-               /*
-                * The pathological case where a callers does
-                * fdos_mode(smb_fname->fsp) passing a pathref fsp. But as
-                * smb_fname points at a symlink in POSIX context smb_fname->fsp
-                * is NULL.
-                */
-               return FILE_ATTRIBUTE_NORMAL;
-       }
-
        DBG_DEBUG("%s\n", fsp_str_dbg(fsp));
 
+       if (fsp->fake_file_handle != NULL) {
+               return dosmode_from_fake_filehandle(fsp->fake_file_handle);
+       }
+
        if (!VALID_STAT(fsp->fsp_name->st)) {
                return 0;
        }
 
-       if (S_ISLNK(fsp->fsp_name->st.st_ex_mode)) {
+       switch (fsp->fsp_name->st.st_ex_mode & S_IFMT) {
+       case S_IFLNK:
                return FILE_ATTRIBUTE_NORMAL;
+               break;
+       case S_IFIFO:
+       case S_IFSOCK:
+       case S_IFBLK:
+       case S_IFCHR:
+               return FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT;
+               break;
+       default:
+               break;
+       }
+
+       if (fsp->fsp_name->st.cached_dos_attributes != FILE_ATTRIBUTE_INVALID) {
+               return fsp->fsp_name->st.cached_dos_attributes;
        }
 
        /* Get the DOS attributes via the VFS if we can */
-       status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn, fsp, &result);
-       if (!NT_STATUS_IS_OK(status)) {
-               /*
-                * Only fall back to using UNIX modes if we get NOT_IMPLEMENTED.
-                */
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
-                       result |= dos_mode_from_sbuf(fsp->conn, fsp->fsp_name);
-               }
+       status = SMB_VFS_FGET_DOS_ATTRIBUTES(fsp->conn,
+                                            metadata_fsp(fsp),
+                                            &result);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+               result |= dos_mode_from_sbuf(fsp->conn,
+                                            &fsp->fsp_name->st,
+                                            fsp);
        }
 
-       result = dos_mode_post(result, fsp->conn, fsp, NULL, __func__);
-       return result;
+       fsp->fsp_name->st.cached_dos_attributes = dos_mode_post(result, fsp, __func__);
+       return fsp->fsp_name->st.cached_dos_attributes;
 }
 
 struct dos_mode_at_state {
@@ -796,6 +781,25 @@ struct tevent_req *dos_mode_at_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       if (smb_fname->fsp == NULL) {
+               if (ISDOTDOT(smb_fname->base_name)) {
+                       /*
+                        * smb_fname->fsp is explicitly closed
+                        * for ".." to prevent meta-data leakage.
+                        */
+                       state->dosmode = FILE_ATTRIBUTE_DIRECTORY;
+               } else {
+                       /*
+                        * This is a symlink in POSIX context.
+                        * FIXME ? Should we move to returning
+                        * FILE_ATTRIBUTE_REPARSE_POINT here ?
+                        */
+                       state->dosmode = FILE_ATTRIBUTE_NORMAL;
+               }
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
        subreq = SMB_VFS_GET_DOS_ATTRIBUTES_SEND(state,
                                                 ev,
                                                 dir_fsp,
@@ -816,8 +820,6 @@ static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
        struct dos_mode_at_state *state =
                tevent_req_data(req,
                struct dos_mode_at_state);
-       char *path = NULL;
-       struct smb_filename *smb_path = NULL;
        struct vfs_aio_state aio_state;
        NTSTATUS status;
        bool ok;
@@ -840,13 +842,16 @@ static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
                 * valid (cf the checks in dos_mode() and dos_mode_at_send().
                 *
                 * If SMB_VFS_GET_DOS_ATTRIBUTES[_SEND|_RECV] fails we must call
-                * dos_mode_post() which also does the mapping of a last ressort
+                * dos_mode_post() which also does the mapping of a last resort
                 * from S_IFMT(st_mode).
                 *
-                * Only if we get NT_STATUS_NOT_IMPLEMENTED from a stacked VFS
-                * module we must fallback to sync processing.
+                * Only if we get NT_STATUS_NOT_IMPLEMENTED or
+                * NT_STATUS_NOT_SUPPORTED from a stacked VFS module we must
+                * fallback to sync processing.
                 */
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+               if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) &&
+                   !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED))
+               {
                        /*
                         * state->dosmode should still be 0, but reset
                         * it to be sure.
@@ -857,9 +862,7 @@ static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
        }
        if (NT_STATUS_IS_OK(status)) {
                state->dosmode = dos_mode_post(state->dosmode,
-                                              state->dir_fsp->conn,
-                                              NULL,
-                                              state->smb_fname,
+                                              state->smb_fname->fsp,
                                               __func__);
                tevent_req_done(req);
                return;
@@ -869,24 +872,6 @@ static void dos_mode_at_vfs_get_dosmode_done(struct tevent_req *subreq)
         * Fall back to sync dos_mode() if we got NOT_IMPLEMENTED.
         */
 
-       path = talloc_asprintf(state,
-                              "%s/%s",
-                              state->dir_fsp->fsp_name->base_name,
-                              state->smb_fname->base_name);
-       if (tevent_req_nomem(path, req)) {
-               return;
-       }
-
-       smb_path = synthetic_smb_fname(state,
-                                      path,
-                                      NULL,
-                                      &state->smb_fname->st,
-                                      state->smb_fname->twrp,
-                                      0);
-       if (tevent_req_nomem(smb_path, req)) {
-               return;
-       }
-
        state->dosmode = fdos_mode(state->smb_fname->fsp);
        tevent_req_done(req);
        return;
@@ -925,9 +910,7 @@ int file_set_dosmode(connection_struct *conn,
        int mask=0;
        mode_t tmp;
        mode_t unixmode;
-       int ret = -1, lret = -1;
-       files_struct *fsp = NULL;
-       bool need_close = false;
+       int ret = -1;
        NTSTATUS status;
 
        if (!CAN_WRITE(conn)) {
@@ -935,15 +918,37 @@ int file_set_dosmode(connection_struct *conn,
                return -1;
        }
 
+       if (S_ISLNK(smb_fname->st.st_ex_mode)) {
+               /* A symlink in POSIX context, ignore */
+               return 0;
+       }
+
+       if ((S_ISDIR(smb_fname->st.st_ex_mode)) &&
+           (dosmode & FILE_ATTRIBUTE_TEMPORARY))
+       {
+               errno = EINVAL;
+               return -1;
+       }
+
        dosmode &= SAMBA_ATTRIBUTES_MASK;
 
        DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
                  dosmode, smb_fname_str_dbg(smb_fname)));
 
+       if (smb_fname->fsp == NULL) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if ((smb_fname->fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) &&
+           !lp_store_dos_attributes(SNUM(conn)))
+       {
+               return 0;
+       }
+
        unixmode = smb_fname->st.st_ex_mode;
 
-       get_acl_group_bits(conn, smb_fname,
-                       &smb_fname->st.st_ex_mode);
+       get_acl_group_bits(conn, smb_fname->fsp, &smb_fname->st.st_ex_mode);
 
        if (S_ISDIR(smb_fname->st.st_ex_mode))
                dosmode |= FILE_ATTRIBUTE_DIRECTORY;
@@ -951,28 +956,30 @@ int file_set_dosmode(connection_struct *conn,
                dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
 
        /* Store the DOS attributes in an EA by preference. */
-       status = SMB_VFS_SET_DOS_ATTRIBUTES(conn, smb_fname, dosmode);
+       status = SMB_VFS_FSET_DOS_ATTRIBUTES(conn,
+                                            metadata_fsp(smb_fname->fsp),
+                                            dosmode);
        if (NT_STATUS_IS_OK(status)) {
-               if (!newfile) {
-                       notify_fname(conn, NOTIFY_ACTION_MODIFIED,
-                               FILE_NOTIFY_CHANGE_ATTRIBUTES,
-                               smb_fname->base_name);
-               }
-               smb_fname->st.st_ex_mode = unixmode;
-               return 0;
-       } else {
-               /*
-                * Only fall back to using UNIX modes if
-                * we get NOT_IMPLEMENTED.
-                */
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
-                       errno = map_errno_from_nt_status(status);
-                       return -1;
-               }
+               smb_fname->st.cached_dos_attributes = dosmode;
+               ret = 0;
+               goto done;
+       }
+
+       /*
+        * Only fall back to using UNIX modes if
+        * we get NOT_IMPLEMENTED.
+        */
+       if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+               errno = map_errno_from_nt_status(status);
+               return -1;
        }
 
        /* Fall back to UNIX modes. */
-       unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
+       unixmode = unix_mode(
+               conn,
+               dosmode,
+               smb_fname,
+               parent_dir != NULL ? parent_dir->fsp : NULL);
 
        /* preserve the file type bits */
        mask |= S_IFMT;
@@ -1001,9 +1008,9 @@ int file_set_dosmode(connection_struct *conn,
                unixmode |= tmp;
        }
 
-       /* if we previously had any w bits set then leave them alone 
+       /* if we previously had any w bits set then leave them alone
                whilst adding in the new w bits, if the new mode is not rdonly */
-       if (!IS_DOS_READONLY(dosmode)) {
+       if (!(dosmode & FILE_ATTRIBUTE_READONLY)) {
                unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
        }
 
@@ -1018,9 +1025,11 @@ int file_set_dosmode(connection_struct *conn,
         * Simply refuse to do the chmod in this case.
         */
 
-       if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
-                       geteuid() != sec_initial_uid() &&
-                       !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
+       if (S_ISDIR(smb_fname->st.st_ex_mode) &&
+           (unixmode & S_ISGID) &&
+           geteuid() != sec_initial_uid() &&
+           !current_user_in_group(conn, smb_fname->st.st_ex_gid))
+       {
                DEBUG(3,("file_set_dosmode: setgid bit cannot be "
                        "set for directory %s\n",
                        smb_fname_str_dbg(smb_fname)));
@@ -1028,15 +1037,9 @@ int file_set_dosmode(connection_struct *conn,
                return -1;
        }
 
-       ret = SMB_VFS_CHMOD(conn, smb_fname, unixmode);
+       ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
        if (ret == 0) {
-               if(!newfile || (lret != -1)) {
-                       notify_fname(conn, NOTIFY_ACTION_MODIFIED,
-                                    FILE_NOTIFY_CHANGE_ATTRIBUTES,
-                                    smb_fname->base_name);
-               }
-               smb_fname->st.st_ex_mode = unixmode;
-               return 0;
+               goto done;
        }
 
        if((errno != EPERM) && (errno != EACCES))
@@ -1049,34 +1052,17 @@ int file_set_dosmode(connection_struct *conn,
                bits on a file. Just like file_ntimes below.
        */
 
-       if (!can_write_to_file(conn,
-                       conn->cwd_fsp,
-                       smb_fname))
+       if (!can_write_to_fsp(smb_fname->fsp))
        {
                errno = EACCES;
                return -1;
        }
 
-       /*
-        * We need to get an open file handle to do the
-        * metadata operation under root.
-        */
-
-       status = get_file_handle_for_metadata(conn,
-                                             smb_fname,
-                                             &fsp,
-                                             &need_close);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
-               return -1;
-       }
-
        become_root();
-       ret = SMB_VFS_FCHMOD(fsp, unixmode);
+       ret = SMB_VFS_FCHMOD(smb_fname->fsp, unixmode);
        unbecome_root();
-       if (need_close) {
-               close_file(NULL, fsp, NORMAL_CLOSE);
-       }
+
+done:
        if (!newfile) {
                notify_fname(conn, NOTIFY_ACTION_MODIFIED,
                             FILE_NOTIFY_CHANGE_ATTRIBUTES,
@@ -1113,15 +1099,17 @@ NTSTATUS file_set_sparse(connection_struct *conn,
         * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
         * following access flags are granted.
         */
-       if ((fsp->access_mask & (FILE_WRITE_DATA
-                               | FILE_WRITE_ATTRIBUTES
-                               | SEC_FILE_APPEND_DATA)) == 0) {
-               DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
-                       "access_mask[0x%08X] - access denied\n",
-                       smb_fname_str_dbg(fsp->fsp_name),
-                       sparse,
-                       fsp->access_mask));
-               return NT_STATUS_ACCESS_DENIED;
+       status = check_any_access_fsp(fsp,
+                                 FILE_WRITE_DATA
+                                 | FILE_WRITE_ATTRIBUTES
+                                 | SEC_FILE_APPEND_DATA);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_DEBUG("fname[%s] set[%u] "
+                         "access_mask[0x%08X] - access denied\n",
+                         smb_fname_str_dbg(fsp->fsp_name),
+                         sparse,
+                         fsp->access_mask);
+               return status;
        }
 
        if (fsp->fsp_flags.is_directory) {
@@ -1137,6 +1125,19 @@ NTSTATUS file_set_sparse(connection_struct *conn,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       if (fsp_is_alternate_stream(fsp)) {
+               /*
+                * MS-FSA 2.1.1.5 IsSparse
+                *
+                * This is a per stream attribute, but our backends don't
+                * support it a consistent way, therefore just pretend
+                * success and ignore the request.
+                */
+               DBG_DEBUG("Ignoring request to set FILE_ATTRIBUTE_SPARSE on "
+                         "[%s]\n", fsp_str_dbg(fsp));
+               return NT_STATUS_OK;
+       }
+
        DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
                  sparse, smb_fname_str_dbg(fsp->fsp_name)));
 
@@ -1169,6 +1170,7 @@ NTSTATUS file_set_sparse(connection_struct *conn,
                     FILE_NOTIFY_CHANGE_ATTRIBUTES,
                     fsp->fsp_name->base_name);
 
+       fsp->fsp_name->st.cached_dos_attributes = new_dosmode;
        fsp->fsp_flags.is_sparse = sparse;
 
        return NT_STATUS_OK;
@@ -1179,26 +1181,27 @@ NTSTATUS file_set_sparse(connection_struct *conn,
  than POSIX.
 *******************************************************************/
 
-int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
+int file_ntimes(connection_struct *conn,
+               files_struct *fsp,
                struct smb_file_time *ft)
 {
        int ret = -1;
 
        errno = 0;
 
-       DEBUG(6, ("file_ntime: actime: %s",
-                 time_to_asc(convert_timespec_to_time_t(ft->atime))));
-       DEBUG(6, ("file_ntime: modtime: %s",
-                 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
-       DEBUG(6, ("file_ntime: ctime: %s",
-                 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
-       DEBUG(6, ("file_ntime: createtime: %s",
-                 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
+       DBG_INFO("actime: %s",
+                time_to_asc(convert_timespec_to_time_t(ft->atime)));
+       DBG_INFO("modtime: %s",
+                time_to_asc(convert_timespec_to_time_t(ft->mtime)));
+       DBG_INFO("ctime: %s",
+                time_to_asc(convert_timespec_to_time_t(ft->ctime)));
+       DBG_INFO("createtime: %s",
+                time_to_asc(convert_timespec_to_time_t(ft->create_time)));
 
        /* Don't update the time on read-only shares */
        /* We need this as set_filetime (which can be called on
           close and other paths) can end up calling this function
-          without the NEED_WRITE protection. Found by : 
+          without the NEED_WRITE protection. Found by :
           Leo Weppelman <leo@wau.mis.ah.nl>
        */
 
@@ -1206,8 +1209,9 @@ int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
                return 0;
        }
 
-       if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
-               return 0;
+       if (SMB_VFS_FNTIMES(fsp, ft) == 0) {
+               ret = 0;
+               goto done;
        }
 
        if((errno != EPERM) && (errno != EACCES)) {
@@ -1225,35 +1229,19 @@ int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
         */
 
        /* Check if we have write access. */
-       if (can_write_to_file(conn,
-                       conn->cwd_fsp,
-                       smb_fname))
-       {
+       if (can_write_to_fsp(fsp)) {
                /* We are allowed to become root and change the filetime. */
                become_root();
-               ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
+               ret = SMB_VFS_FNTIMES(fsp, ft);
                unbecome_root();
        }
 
-       return ret;
-}
-
-/******************************************************************
- Force a "sticky" write time on a pathname. This will always be
- returned on all future write time queries and set on close.
-******************************************************************/
-
-bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
-{
-       if (is_omit_timespec(&mtime)) {
-               return true;
-       }
-
-       if (!set_sticky_write_time(fileid, mtime)) {
-               return false;
+done:
+       if (ret == 0) {
+               copy_stat_ex_timestamps(&fsp->fsp_name->st, ft);
        }
 
-       return true;
+       return ret;
 }
 
 /******************************************************************
@@ -1263,6 +1251,8 @@ bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
 
 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
 {
+       bool ok;
+
        if (is_omit_timespec(&mtime)) {
                return true;
        }
@@ -1270,53 +1260,35 @@ bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
        fsp->fsp_flags.write_time_forced = true;
        TALLOC_FREE(fsp->update_write_time_event);
 
-       return set_sticky_write_time_path(fsp->file_id, mtime);
+       ok = set_sticky_write_time(fsp->file_id, mtime);
+       return ok;
 }
 
 /******************************************************************
  Set a create time EA.
 ******************************************************************/
 
-NTSTATUS set_create_timespec_ea(connection_struct *conn,
-                               const struct smb_filename *psmb_fname,
+NTSTATUS set_create_timespec_ea(struct files_struct *fsp,
                                struct timespec create_time)
 {
-       struct smb_filename *smb_fname;
        uint32_t dosmode;
        int ret;
-       NTSTATUS status;
 
-       if (!lp_store_dos_attributes(SNUM(conn))) {
+       if (!lp_store_dos_attributes(SNUM(fsp->conn))) {
                return NT_STATUS_OK;
        }
 
-       status = synthetic_pathref(talloc_tos(),
-                                       conn->cwd_fsp,
-                                       psmb_fname->base_name,
-                                       NULL,
-                                       NULL,
-                                       psmb_fname->twrp,
-                                       psmb_fname->flags,
-                                       &smb_fname);
+       dosmode = fdos_mode(fsp);
 
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       dosmode = fdos_mode(psmb_fname->fsp);
-
-       smb_fname->st.st_ex_btime = create_time;
-
-       ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
+       fsp->fsp_name->st.st_ex_btime = create_time;
+       ret = file_set_dosmode(fsp->conn, fsp->fsp_name, dosmode, NULL, false);
        if (ret == -1) {
-               TALLOC_FREE(smb_fname);
                return map_nt_error_from_unix(errno);
        }
 
-       DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
-               smb_fname_str_dbg(smb_fname)));
+       DBG_DEBUG("wrote create time EA for file %s\n",
+               smb_fname_str_dbg(fsp->fsp_name));
 
-       TALLOC_FREE(smb_fname);
        return NT_STATUS_OK;
 }
 
@@ -1328,6 +1300,10 @@ struct timespec get_create_timespec(connection_struct *conn,
                                struct files_struct *fsp,
                                const struct smb_filename *smb_fname)
 {
+       if (fsp != NULL) {
+               struct files_struct *meta_fsp = metadata_fsp(fsp);
+               return meta_fsp->fsp_name->st.st_ex_btime;
+       }
        return smb_fname->st.st_ex_btime;
 }
 
@@ -1341,78 +1317,3 @@ struct timespec get_change_timespec(connection_struct *conn,
 {
        return smb_fname->st.st_ex_mtime;
 }
-
-/****************************************************************************
- Get a real open file handle we can do meta-data operations on. As it's
- going to be used under root access only on meta-data we should look for
- any existing open file handle first, and use that in preference (also to
- avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
-****************************************************************************/
-
-static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
-                               const struct smb_filename *smb_fname,
-                               files_struct **ret_fsp,
-                               bool *need_close)
-{
-       NTSTATUS status;
-       files_struct *fsp;
-       struct file_id file_id;
-       struct smb_filename *smb_fname_cp = NULL;
-
-       *need_close = false;
-
-       if (!VALID_STAT(smb_fname->st)) {
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
-
-       for(fsp = file_find_di_first(conn->sconn, file_id, true);
-                       fsp;
-                       fsp = file_find_di_next(fsp, true)) {
-               if (fsp_get_io_fd(fsp) != -1) {
-                       *ret_fsp = fsp;
-                       return NT_STATUS_OK;
-               }
-       }
-
-       smb_fname_cp = cp_smb_filename(talloc_tos(),
-                                       smb_fname);
-       if (smb_fname_cp == NULL) {
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       status = openat_pathref_fsp(conn->cwd_fsp, smb_fname_cp);
-       if (!NT_STATUS_IS_OK(status)) {
-               TALLOC_FREE(smb_fname_cp);
-               return status;
-       }
-
-       /* Opens an INTERNAL_OPEN_ONLY write handle. */
-       status = SMB_VFS_CREATE_FILE(
-               conn,                                   /* conn */
-               NULL,                                   /* req */
-               smb_fname_cp,                           /* fname */
-               FILE_WRITE_ATTRIBUTES,                  /* access_mask */
-               (FILE_SHARE_READ | FILE_SHARE_WRITE |   /* share_access */
-                       FILE_SHARE_DELETE),
-               FILE_OPEN,                              /* create_disposition*/
-               0,                                      /* create_options */
-               0,                                      /* file_attributes */
-               INTERNAL_OPEN_ONLY,                     /* oplock_request */
-               NULL,                                   /* lease */
-                0,                                      /* allocation_size */
-               0,                                      /* private_flags */
-               NULL,                                   /* sd */
-               NULL,                                   /* ea_list */
-               ret_fsp,                                /* result */
-               NULL,                                   /* pinfo */
-               NULL, NULL);                            /* create context */
-
-       TALLOC_FREE(smb_fname_cp);
-
-       if (NT_STATUS_IS_OK(status)) {
-               *need_close = true;
-       }
-       return status;
-}