s4:kdc: Implement KDC plugin hardware authentication policy
[samba.git] / source3 / modules / vfs_streams_depot.c
index f1c687e9528b41613a77e2e4b47814628a0c37e2..1221b2c2be220d2c28c92d5b9aa9c8dd9dee7104 100644 (file)
@@ -20,6 +20,7 @@
 #include "includes.h"
 #include "smbd/smbd.h"
 #include "system/filesys.h"
+#include "source3/smbd/dir.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -67,23 +68,41 @@ static uint32_t hash_fn(DATA_BLOB key)
  * an option to put in a special ACL entry for a non-existing group.
  */
 
-static bool file_is_valid(vfs_handle_struct *handle, const char *path,
-                         bool check_valid)
+static bool file_is_valid(vfs_handle_struct *handle,
+                       const struct smb_filename *smb_fname)
 {
        char buf;
+       NTSTATUS status;
+       struct smb_filename *pathref = NULL;
+       int ret;
 
-       if (!check_valid) {
-               return true;
-       }
-
-       DEBUG(10, ("file_is_valid (%s) called\n", path));
+       DEBUG(10, ("file_is_valid (%s) called\n", smb_fname->base_name));
 
-       if (SMB_VFS_GETXATTR(handle->conn, path, SAMBA_XATTR_MARKER,
-                                 &buf, sizeof(buf)) != sizeof(buf)) {
-               DEBUG(10, ("GETXATTR failed: %s\n", strerror(errno)));
+       status = synthetic_pathref(talloc_tos(),
+                               handle->conn->cwd_fsp,
+                                smb_fname->base_name,
+                                NULL,
+                                NULL,
+                                smb_fname->twrp,
+                                smb_fname->flags,
+                                &pathref);
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
+       }
+       ret = SMB_VFS_FGETXATTR(pathref->fsp,
+                               SAMBA_XATTR_MARKER,
+                               &buf,
+                               sizeof(buf));
+       if (ret != sizeof(buf)) {
+               int saved_errno = errno;
+               DBG_DEBUG("FGETXATTR failed: %s\n", strerror(saved_errno));
+               TALLOC_FREE(pathref);
+               errno = saved_errno;
                return false;
        }
 
+       TALLOC_FREE(pathref);
+
        if (buf != '1') {
                DEBUG(10, ("got wrong buffer content: '%c'\n", buf));
                return false;
@@ -92,27 +111,37 @@ static bool file_is_valid(vfs_handle_struct *handle, const char *path,
        return true;
 }
 
-static bool mark_file_valid(vfs_handle_struct *handle, const char *path,
-                           bool check_valid)
-{
-       char buf = '1';
-       int ret;
-
-       if (!check_valid) {
-               return true;
-       }
-
-       DEBUG(10, ("marking file %s as valid\n", path));
+/*
+ * Return the root of the stream directory. Can be
+ * external to the share definition but by default
+ * is "handle->conn->connectpath/.streams".
+ *
+ * Note that this is an *absolute* path, starting
+ * with '/', so the dirfsp being used in the
+ * calls below isn't looked at.
+ */
 
-       ret = SMB_VFS_SETXATTR(handle->conn, path, SAMBA_XATTR_MARKER,
-                                   &buf, sizeof(buf), 0);
+static char *stream_rootdir(vfs_handle_struct *handle,
+                           TALLOC_CTX *ctx)
+{
+       const struct loadparm_substitution *lp_sub =
+               loadparm_s3_global_substitution();
+       char *tmp;
 
-       if (ret == -1) {
-               DEBUG(10, ("SETXATTR failed: %s\n", strerror(errno)));
-               return false;
+       tmp = talloc_asprintf(ctx,
+                             "%s/.streams",
+                             handle->conn->connectpath);
+       if (tmp == NULL) {
+               errno = ENOMEM;
+               return NULL;
        }
 
-       return true;
+       return lp_parm_substituted_string(ctx,
+                                         lp_sub,
+                                         SNUM(handle->conn),
+                                         "streams_depot",
+                                         "directory",
+                                         tmp);
 }
 
 /**
@@ -127,39 +156,51 @@ static char *stream_dir(vfs_handle_struct *handle,
        struct smb_filename *smb_fname_hash = NULL;
        char *result = NULL;
        SMB_STRUCT_STAT base_sbuf_tmp;
+       char *tmp = NULL;
        uint8_t first, second;
-       char *tmp;
        char *id_hex;
        struct file_id id;
-       uint8 id_buf[16];
+       uint8_t id_buf[16];
        bool check_valid;
-       const char *rootdir;
-       NTSTATUS status;
+       char *rootdir = NULL;
+       struct smb_filename *rootdir_fname = NULL;
+       struct smb_filename *tmp_fname = NULL;
+       int ret;
 
        check_valid = lp_parm_bool(SNUM(handle->conn),
                      "streams_depot", "check_valid", true);
 
-       tmp = talloc_asprintf(talloc_tos(), "%s/.streams", handle->conn->connectpath);
-
-       if (tmp == NULL) {
+       rootdir = stream_rootdir(handle,
+                                talloc_tos());
+       if (rootdir == NULL) {
                errno = ENOMEM;
                goto fail;
        }
 
-       rootdir = lp_parm_const_string(
-               SNUM(handle->conn), "streams_depot", "directory",
-               tmp);
+       rootdir_fname = synthetic_smb_fname(talloc_tos(),
+                                       rootdir,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+       if (rootdir_fname == NULL) {
+               errno = ENOMEM;
+               goto fail;
+       }
 
        /* Stat the base file if it hasn't already been done. */
        if (base_sbuf == NULL) {
-               struct smb_filename *smb_fname_base = NULL;
-
-               status = create_synthetic_smb_fname(talloc_tos(),
-                                                   smb_fname->base_name,
-                                                   NULL, NULL,
-                                                   &smb_fname_base);
-               if (!NT_STATUS_IS_OK(status)) {
-                       errno = map_errno_from_nt_status(status);
+               struct smb_filename *smb_fname_base;
+
+               smb_fname_base = synthetic_smb_fname(
+                                       talloc_tos(),
+                                       smb_fname->base_name,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+               if (smb_fname_base == NULL) {
+                       errno = ENOMEM;
                        goto fail;
                }
                if (SMB_VFS_NEXT_STAT(handle, smb_fname_base) == -1) {
@@ -174,7 +215,7 @@ static char *stream_dir(vfs_handle_struct *handle,
 
        id = SMB_VFS_FILE_ID_CREATE(handle->conn, &base_sbuf_tmp);
 
-       push_file_id_16((char *)id_buf, &id);
+       push_file_id_16(id_buf, &id);
 
        hash = hash_fn(data_blob_const(id_buf, sizeof(id_buf)));
 
@@ -198,58 +239,91 @@ static char *stream_dir(vfs_handle_struct *handle,
                return NULL;
        }
 
-       status = create_synthetic_smb_fname(talloc_tos(), result, NULL, NULL,
-                                           &smb_fname_hash);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
+       smb_fname_hash = synthetic_smb_fname(talloc_tos(),
+                                       result,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+       if (smb_fname_hash == NULL) {
+               errno = ENOMEM;
                goto fail;
        }
 
        if (SMB_VFS_NEXT_STAT(handle, smb_fname_hash) == 0) {
                struct smb_filename *smb_fname_new = NULL;
                char *newname;
+               bool delete_lost;
 
                if (!S_ISDIR(smb_fname_hash->st.st_ex_mode)) {
                        errno = EINVAL;
                        goto fail;
                }
 
-               if (file_is_valid(handle, smb_fname->base_name, check_valid)) {
+               if (!check_valid ||
+                   file_is_valid(handle, smb_fname)) {
                        return result;
                }
 
                /*
                 * Someone has recreated a file under an existing inode
-                * without deleting the streams directory. For now, just move
-                * it away.
+                * without deleting the streams directory.
+                * Move it away or remove if streams_depot:delete_lost is set.
                 */
 
        again:
-               newname = talloc_asprintf(talloc_tos(), "lost-%lu", random());
-               if (newname == NULL) {
-                       errno = ENOMEM;
-                       goto fail;
-               }
+               delete_lost = lp_parm_bool(SNUM(handle->conn), "streams_depot",
+                                          "delete_lost", false);
+
+               if (delete_lost) {
+                       DEBUG(3, ("Someone has recreated a file under an "
+                             "existing inode. Removing: %s\n",
+                             smb_fname_hash->base_name));
+                       recursive_rmdir(talloc_tos(), handle->conn,
+                                       smb_fname_hash);
+                       SMB_VFS_NEXT_UNLINKAT(handle,
+                                       handle->conn->cwd_fsp,
+                                       smb_fname_hash,
+                                       AT_REMOVEDIR);
+               } else {
+                       newname = talloc_asprintf(talloc_tos(), "lost-%lu",
+                                                 random());
+                       DEBUG(3, ("Someone has recreated a file under an "
+                             "existing inode. Renaming: %s to: %s\n",
+                             smb_fname_hash->base_name,
+                             newname));
+                       if (newname == NULL) {
+                               errno = ENOMEM;
+                               goto fail;
+                       }
 
-               status = create_synthetic_smb_fname(talloc_tos(), newname,
-                                                   NULL, NULL,
-                                                   &smb_fname_new);
-               TALLOC_FREE(newname);
-               if (!NT_STATUS_IS_OK(status)) {
-                       errno = map_errno_from_nt_status(status);
-                       goto fail;
-               }
+                       smb_fname_new = synthetic_smb_fname(
+                                               talloc_tos(),
+                                               newname,
+                                               NULL,
+                                               NULL,
+                                               smb_fname->twrp,
+                                               smb_fname->flags);
+                       TALLOC_FREE(newname);
+                       if (smb_fname_new == NULL) {
+                               errno = ENOMEM;
+                               goto fail;
+                       }
 
-               if (SMB_VFS_NEXT_RENAME(handle, smb_fname_hash,
+                       if (SMB_VFS_NEXT_RENAMEAT(handle,
+                                       handle->conn->cwd_fsp,
+                                       smb_fname_hash,
+                                       handle->conn->cwd_fsp,
                                        smb_fname_new) == -1) {
-                       TALLOC_FREE(smb_fname_new);
-                       if ((errno == EEXIST) || (errno == ENOTEMPTY)) {
-                               goto again;
+                               TALLOC_FREE(smb_fname_new);
+                               if ((errno == EEXIST) || (errno == ENOTEMPTY)) {
+                                       goto again;
+                               }
+                               goto fail;
                        }
-                       goto fail;
-               }
 
-               TALLOC_FREE(smb_fname_new);
+                       TALLOC_FREE(smb_fname_new);
+               }
        }
 
        if (!create_it) {
@@ -257,8 +331,11 @@ static char *stream_dir(vfs_handle_struct *handle,
                goto fail;
        }
 
-       if ((SMB_VFS_NEXT_MKDIR(handle, rootdir, 0755) != 0)
-           && (errno != EEXIST)) {
+       ret = SMB_VFS_NEXT_MKDIRAT(handle,
+                               handle->conn->cwd_fsp,
+                               rootdir_fname,
+                               0755);
+       if ((ret != 0) && (errno != EEXIST)) {
                goto fail;
        }
 
@@ -268,12 +345,27 @@ static char *stream_dir(vfs_handle_struct *handle,
                goto fail;
        }
 
-       if ((SMB_VFS_NEXT_MKDIR(handle, tmp, 0755) != 0)
-           && (errno != EEXIST)) {
+       tmp_fname = synthetic_smb_fname(talloc_tos(),
+                                       tmp,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+       if (tmp_fname == NULL) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       ret = SMB_VFS_NEXT_MKDIRAT(handle,
+                               handle->conn->cwd_fsp,
+                               tmp_fname,
+                               0755);
+       if ((ret != 0) && (errno != EEXIST)) {
                goto fail;
        }
 
        TALLOC_FREE(tmp);
+       TALLOC_FREE(tmp_fname);
 
        tmp = talloc_asprintf(result, "%s/%2.2X/%2.2X", rootdir, first,
                              second);
@@ -282,26 +374,47 @@ static char *stream_dir(vfs_handle_struct *handle,
                goto fail;
        }
 
-       if ((SMB_VFS_NEXT_MKDIR(handle, tmp, 0755) != 0)
-           && (errno != EEXIST)) {
+       tmp_fname = synthetic_smb_fname(talloc_tos(),
+                                       tmp,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+       if (tmp_fname == NULL) {
+               errno = ENOMEM;
                goto fail;
        }
 
-       TALLOC_FREE(tmp);
-
-       if ((SMB_VFS_NEXT_MKDIR(handle, result, 0755) != 0)
-           && (errno != EEXIST)) {
+       ret = SMB_VFS_NEXT_MKDIRAT(handle,
+                       handle->conn->cwd_fsp,
+                       tmp_fname,
+                       0755);
+       if ((ret != 0) && (errno != EEXIST)) {
                goto fail;
        }
 
-       if (!mark_file_valid(handle, smb_fname->base_name, check_valid)) {
+       TALLOC_FREE(tmp);
+       TALLOC_FREE(tmp_fname);
+
+       /* smb_fname_hash is the struct smb_filename version of 'result' */
+       ret = SMB_VFS_NEXT_MKDIRAT(handle,
+                       handle->conn->cwd_fsp,
+                       smb_fname_hash,
+                       0755);
+       if ((ret != 0) && (errno != EEXIST)) {
                goto fail;
        }
 
+       TALLOC_FREE(rootdir_fname);
+       TALLOC_FREE(rootdir);
+       TALLOC_FREE(tmp_fname);
        TALLOC_FREE(smb_fname_hash);
        return result;
 
  fail:
+       TALLOC_FREE(rootdir_fname);
+       TALLOC_FREE(rootdir);
+       TALLOC_FREE(tmp_fname);
        TALLOC_FREE(smb_fname_hash);
        TALLOC_FREE(result);
        return NULL;
@@ -311,6 +424,7 @@ static char *stream_dir(vfs_handle_struct *handle,
  * stream.
  */
 static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
+                                const struct stat_ex *base_sbuf,
                                 const struct smb_filename *smb_fname,
                                 struct smb_filename **smb_fname_out,
                                 bool create_dir)
@@ -329,7 +443,7 @@ static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
                }
        }
 
-       dirname = stream_dir(handle, smb_fname, NULL, create_dir);
+       dirname = stream_dir(handle, smb_fname, base_sbuf, create_dir);
 
        if (dirname == NULL) {
                status = map_nt_error_from_unix(errno);
@@ -353,17 +467,24 @@ static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
                        goto fail;
                }
        } else {
-               /* Normalize the stream type to upercase. */
-               strupper_m(strrchr_m(stream_fname, ':') + 1);
+               /* Normalize the stream type to uppercase. */
+               if (!strupper_m(strrchr_m(stream_fname, ':') + 1)) {
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto fail;
+               }
        }
 
        DEBUG(10, ("stream filename = %s\n", stream_fname));
 
        /* Create an smb_filename with stream_name == NULL. */
-       status = create_synthetic_smb_fname(talloc_tos(), stream_fname, NULL,
-                                           NULL, smb_fname_out);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+       *smb_fname_out = synthetic_smb_fname(talloc_tos(),
+                                       stream_fname,
+                                       NULL,
+                                       NULL,
+                                       smb_fname->twrp,
+                                       smb_fname->flags);
+       if (*smb_fname_out == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        return NT_STATUS_OK;
@@ -377,15 +498,19 @@ static NTSTATUS stream_smb_fname(vfs_handle_struct *handle,
 static NTSTATUS walk_streams(vfs_handle_struct *handle,
                             struct smb_filename *smb_fname_base,
                             char **pdirname,
-                            bool (*fn)(const char *dirname,
+                            bool (*fn)(const struct smb_filename *dirname,
                                        const char *dirent,
                                        void *private_data),
                             void *private_data)
 {
        char *dirname;
-       SMB_STRUCT_DIR *dirhandle = NULL;
-       const char *dirent = NULL;
+       char *rootdir = NULL;
+       char *orig_connectpath = NULL;
+       struct smb_filename *dir_smb_fname = NULL;
+       struct smb_Dir *dir_hnd = NULL;
+       const char *dname = NULL;
        char *talloced = NULL;
+       NTSTATUS status;
 
        dirname = stream_dir(handle, smb_fname_base, &smb_fname_base->st,
                             false);
@@ -402,31 +527,63 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
 
        DEBUG(10, ("walk_streams: dirname=%s\n", dirname));
 
-       dirhandle = SMB_VFS_NEXT_OPENDIR(handle, dirname, NULL, 0);
+       dir_smb_fname = synthetic_smb_fname(talloc_tos(),
+                                       dirname,
+                                       NULL,
+                                       NULL,
+                                       smb_fname_base->twrp,
+                                       smb_fname_base->flags);
+       if (dir_smb_fname == NULL) {
+               TALLOC_FREE(dirname);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       if (dirhandle == NULL) {
+       /*
+        * For OpenDir to succeed if the stream rootdir is outside
+        * the share path, we must temporarily swap out the connect
+        * path for this share. We're dealing with absolute paths
+        * here so we don't care about chdir calls.
+        */
+       rootdir = stream_rootdir(handle, talloc_tos());
+       if (rootdir == NULL) {
+               TALLOC_FREE(dir_smb_fname);
                TALLOC_FREE(dirname);
-               return map_nt_error_from_unix(errno);
+               return NT_STATUS_NO_MEMORY;
        }
 
-       while ((dirent = vfs_readdirname(handle->conn, dirhandle, NULL,
-                                        &talloced)) != NULL) {
+       orig_connectpath = handle->conn->connectpath;
+       handle->conn->connectpath = rootdir;
+
+       status = OpenDir(
+               talloc_tos(), handle->conn, dir_smb_fname, NULL, 0, &dir_hnd);
+       if (!NT_STATUS_IS_OK(status)) {
+               handle->conn->connectpath = orig_connectpath;
+               TALLOC_FREE(rootdir);
+               TALLOC_FREE(dir_smb_fname);
+               TALLOC_FREE(dirname);
+               return status;
+       }
 
-               if (ISDOT(dirent) || ISDOTDOT(dirent)) {
+       while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
+               if (ISDOT(dname) || ISDOTDOT(dname)) {
                        TALLOC_FREE(talloced);
                        continue;
                }
 
-               DEBUG(10, ("walk_streams: dirent=%s\n", dirent));
+               DBG_DEBUG("dirent=%s\n", dname);
 
-               if (!fn(dirname, dirent, private_data)) {
+               if (!fn(dir_smb_fname, dname, private_data)) {
                        TALLOC_FREE(talloced);
                        break;
                }
                TALLOC_FREE(talloced);
        }
 
-       SMB_VFS_NEXT_CLOSEDIR(handle, dirhandle);
+       /* Restore the original connectpath. */
+       handle->conn->connectpath = orig_connectpath;
+       TALLOC_FREE(rootdir);
+       TALLOC_FREE(dir_smb_fname);
+       TALLOC_FREE(dir_hnd);
 
        if (pdirname != NULL) {
                *pdirname = dirname;
@@ -438,28 +595,6 @@ static NTSTATUS walk_streams(vfs_handle_struct *handle,
        return NT_STATUS_OK;
 }
 
-/**
- * Helper to stat/lstat the base file of an smb_fname. This will actually
- * fills in the stat struct in smb_filename.
- */
-static int streams_depot_stat_base(vfs_handle_struct *handle,
-                                  struct smb_filename *smb_fname,
-                                  bool follow_links)
-{
-       char *tmp_stream_name;
-       int result;
-
-       tmp_stream_name = smb_fname->stream_name;
-       smb_fname->stream_name = NULL;
-       if (follow_links) {
-               result = SMB_VFS_NEXT_STAT(handle, smb_fname);
-       } else {
-               result = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
-       }
-       smb_fname->stream_name = tmp_stream_name;
-       return result;
-}
-
 static int streams_depot_stat(vfs_handle_struct *handle,
                              struct smb_filename *smb_fname)
 {
@@ -470,18 +605,13 @@ static int streams_depot_stat(vfs_handle_struct *handle,
        DEBUG(10, ("streams_depot_stat called for [%s]\n",
                   smb_fname_str_dbg(smb_fname)));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
+       if (!is_named_stream(smb_fname)) {
                return SMB_VFS_NEXT_STAT(handle, smb_fname);
        }
 
-       /* If the default stream is requested, just stat the base file. */
-       if (is_ntfs_default_stream_smb_fname(smb_fname)) {
-               return streams_depot_stat_base(handle, smb_fname, true);
-       }
-
        /* Stat the actual stream now. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream,
-                                 false);
+       status = stream_smb_fname(
+               handle, NULL, smb_fname, &smb_fname_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
@@ -509,18 +639,13 @@ static int streams_depot_lstat(vfs_handle_struct *handle,
        DEBUG(10, ("streams_depot_lstat called for [%s]\n",
                   smb_fname_str_dbg(smb_fname)));
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
+       if (!is_named_stream(smb_fname)) {
                return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
        }
 
-       /* If the default stream is requested, just stat the base file. */
-       if (is_ntfs_default_stream_smb_fname(smb_fname)) {
-               return streams_depot_stat_base(handle, smb_fname, false);
-       }
-
        /* Stat the actual stream now. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream,
-                                 false);
+       status = stream_smb_fname(
+               handle, NULL, smb_fname, &smb_fname_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
@@ -534,86 +659,133 @@ static int streams_depot_lstat(vfs_handle_struct *handle,
        return ret;
 }
 
-static int streams_depot_open(vfs_handle_struct *handle,
-                             struct smb_filename *smb_fname,
-                             files_struct *fsp, int flags, mode_t mode)
+static int streams_depot_openat(struct vfs_handle_struct *handle,
+                               const struct files_struct *dirfsp,
+                               const struct smb_filename *smb_fname,
+                               struct files_struct *fsp,
+                               const struct vfs_open_how *how)
 {
        struct smb_filename *smb_fname_stream = NULL;
-       struct smb_filename *smb_fname_base = NULL;
+       struct files_struct *fspcwd = NULL;
        NTSTATUS status;
+       bool create_it;
        int ret = -1;
 
-       if (!is_ntfs_stream_smb_fname(smb_fname)) {
-               return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       if (!is_named_stream(smb_fname)) {
+               return SMB_VFS_NEXT_OPENAT(handle,
+                                          dirfsp,
+                                          smb_fname,
+                                          fsp,
+                                          how);
        }
 
-       /* If the default stream is requested, just open the base file. */
-       if (is_ntfs_default_stream_smb_fname(smb_fname)) {
-               char *tmp_stream_name;
+       if (how->resolve != 0) {
+               errno = ENOSYS;
+               return -1;
+       }
 
-               tmp_stream_name = smb_fname->stream_name;
-               smb_fname->stream_name = NULL;
-               ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
-               smb_fname->stream_name = tmp_stream_name;
+       SMB_ASSERT(fsp_is_alternate_stream(fsp));
+       SMB_ASSERT(dirfsp == NULL);
+       SMB_ASSERT(VALID_STAT(fsp->base_fsp->fsp_name->st));
 
-               return ret;
-       }
+       create_it = (how->flags & O_CREAT);
 
-       /* Ensure the base file still exists. */
-       status = create_synthetic_smb_fname(talloc_tos(),
-                                           smb_fname->base_name,
-                                           NULL, NULL,
-                                           &smb_fname_base);
+       /* Determine the stream name, and then open it. */
+       status = stream_smb_fname(
+               handle,
+               &fsp->base_fsp->fsp_name->st,
+               fsp->fsp_name,
+               &smb_fname_stream,
+               create_it);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
-       ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
-       if (ret == -1) {
-               goto done;
+       if (create_it) {
+               bool check_valid = lp_parm_bool(
+                       SNUM(handle->conn),
+                       "streams_depot",
+                       "check_valid",
+                       true);
+
+               if (check_valid) {
+                       char buf = '1';
+
+                       DBG_DEBUG("marking file %s as valid\n",
+                                 fsp->base_fsp->fsp_name->base_name);
+
+                       ret = SMB_VFS_FSETXATTR(
+                               fsp->base_fsp,
+                               SAMBA_XATTR_MARKER,
+                               &buf,
+                               sizeof(buf),
+                               0);
+
+                       if (ret == -1) {
+                               DBG_DEBUG("FSETXATTR failed: %s\n",
+                                         strerror(errno));
+                               goto done;
+                       }
+               }
        }
 
-       /* Determine the stream name, and then open it. */
-       status = stream_smb_fname(handle, smb_fname, &smb_fname_stream, true);
+       status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
        if (!NT_STATUS_IS_OK(status)) {
                ret = -1;
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
-       ret = SMB_VFS_NEXT_OPEN(handle, smb_fname_stream, fsp, flags, mode);
+       ret = SMB_VFS_NEXT_OPENAT(handle,
+                                 fspcwd,
+                                 smb_fname_stream,
+                                 fsp,
+                                 how);
 
  done:
        TALLOC_FREE(smb_fname_stream);
-       TALLOC_FREE(smb_fname_base);
+       TALLOC_FREE(fspcwd);
        return ret;
 }
 
-static int streams_depot_unlink(vfs_handle_struct *handle,
-                               const struct smb_filename *smb_fname)
+static int streams_depot_unlink_internal(vfs_handle_struct *handle,
+                               struct files_struct *dirfsp,
+                               const struct smb_filename *smb_fname,
+                               int flags)
 {
-       struct smb_filename *smb_fname_base = NULL;
-       NTSTATUS status;
+       struct smb_filename *full_fname = NULL;
+       char *dirname = NULL;
        int ret = -1;
 
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               return -1;
+       }
+
        DEBUG(10, ("streams_depot_unlink called for %s\n",
-                  smb_fname_str_dbg(smb_fname)));
+                  smb_fname_str_dbg(full_fname)));
 
        /* If there is a valid stream, just unlink the stream and return. */
-       if (is_ntfs_stream_smb_fname(smb_fname) &&
-           !is_ntfs_default_stream_smb_fname(smb_fname)) {
+       if (is_named_stream(full_fname)) {
                struct smb_filename *smb_fname_stream = NULL;
+               NTSTATUS status;
 
-               status = stream_smb_fname(handle, smb_fname, &smb_fname_stream,
-                                         false);
+               status = stream_smb_fname(
+                       handle, NULL, full_fname, &smb_fname_stream, false);
+               TALLOC_FREE(full_fname);
                if (!NT_STATUS_IS_OK(status)) {
                        errno = map_errno_from_nt_status(status);
                        return -1;
                }
 
-               ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_stream);
+               ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp->conn->cwd_fsp,
+                               smb_fname_stream,
+                               0);
 
                TALLOC_FREE(smb_fname_stream);
                return ret;
@@ -623,14 +795,100 @@ static int streams_depot_unlink(vfs_handle_struct *handle,
         * We potentially need to delete the per-inode streams directory
         */
 
-       status = create_synthetic_smb_fname(talloc_tos(), smb_fname->base_name,
-                                           NULL, NULL, &smb_fname_base);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
+       if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
+               ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+       } else {
+               ret = SMB_VFS_NEXT_STAT(handle, full_fname);
+               if (ret == -1 && (errno == ENOENT || errno == ELOOP)) {
+                       if (VALID_STAT(smb_fname->st) &&
+                                       S_ISLNK(smb_fname->st.st_ex_mode)) {
+                               /*
+                                * Original name was a link - Could be
+                                * trying to remove a dangling symlink.
+                                */
+                               ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
+                       }
+               }
+       }
+       if (ret == -1) {
+               TALLOC_FREE(full_fname);
                return -1;
        }
 
-       if (lp_posix_pathnames()) {
+       /*
+        * We know the unlink should succeed as the ACL
+        * check is already done in the caller. Remove the
+        * file *after* the streams.
+        */
+       dirname = stream_dir(handle,
+                            full_fname,
+                            &full_fname->st,
+                            false);
+       TALLOC_FREE(full_fname);
+       if (dirname != NULL) {
+               struct smb_filename *smb_fname_dir = NULL;
+
+               smb_fname_dir = synthetic_smb_fname(talloc_tos(),
+                                                   dirname,
+                                                   NULL,
+                                                   NULL,
+                                                   smb_fname->twrp,
+                                                   smb_fname->flags);
+               if (smb_fname_dir == NULL) {
+                       TALLOC_FREE(dirname);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               SMB_VFS_NEXT_UNLINKAT(handle,
+                                     dirfsp->conn->cwd_fsp,
+                                     smb_fname_dir,
+                                     AT_REMOVEDIR);
+               TALLOC_FREE(smb_fname_dir);
+               TALLOC_FREE(dirname);
+       }
+
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp,
+                               smb_fname,
+                               flags);
+       return ret;
+}
+
+static int streams_depot_rmdir_internal(vfs_handle_struct *handle,
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname)
+{
+       struct smb_filename *full_fname = NULL;
+       struct smb_filename *smb_fname_base = NULL;
+       int ret = -1;
+
+       full_fname = full_path_from_dirfsp_atname(talloc_tos(),
+                                                 dirfsp,
+                                                 smb_fname);
+       if (full_fname == NULL) {
+               return -1;
+       }
+
+       DBG_DEBUG("called for %s\n", full_fname->base_name);
+
+       /*
+        * We potentially need to delete the per-inode streams directory
+        */
+
+       smb_fname_base = synthetic_smb_fname(talloc_tos(),
+                               full_fname->base_name,
+                               NULL,
+                               NULL,
+                               full_fname->twrp,
+                               full_fname->flags);
+       TALLOC_FREE(full_fname);
+       if (smb_fname_base == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       if (smb_fname_base->flags & SMB_FILENAME_POSIX_PATH) {
                ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
        } else {
                ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
@@ -641,33 +899,80 @@ static int streams_depot_unlink(vfs_handle_struct *handle,
                return -1;
        }
 
-       if (smb_fname_base->st.st_ex_nlink == 1) {
+       /*
+        * We know the rmdir should succeed as the ACL
+        * check is already done in the caller. Remove the
+        * directory *after* the streams.
+        */
+       {
                char *dirname = stream_dir(handle, smb_fname_base,
                                           &smb_fname_base->st, false);
 
                if (dirname != NULL) {
-                       SMB_VFS_NEXT_RMDIR(handle, dirname);
+                       struct smb_filename *smb_fname_dir =
+                               synthetic_smb_fname(talloc_tos(),
+                                               dirname,
+                                               NULL,
+                                               NULL,
+                                               smb_fname->twrp,
+                                               smb_fname->flags);
+                       if (smb_fname_dir == NULL) {
+                               TALLOC_FREE(smb_fname_base);
+                               TALLOC_FREE(dirname);
+                               errno = ENOMEM;
+                               return -1;
+                       }
+                       SMB_VFS_NEXT_UNLINKAT(handle,
+                                       dirfsp->conn->cwd_fsp,
+                                       smb_fname_dir,
+                                       AT_REMOVEDIR);
+                       TALLOC_FREE(smb_fname_dir);
                }
                TALLOC_FREE(dirname);
        }
 
-       ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname);
-
+       ret = SMB_VFS_NEXT_UNLINKAT(handle,
+                               dirfsp,
+                               smb_fname,
+                               AT_REMOVEDIR);
        TALLOC_FREE(smb_fname_base);
        return ret;
 }
 
-static int streams_depot_rename(vfs_handle_struct *handle,
+static int streams_depot_unlinkat(vfs_handle_struct *handle,
+                       struct files_struct *dirfsp,
+                       const struct smb_filename *smb_fname,
+                       int flags)
+{
+       int ret;
+       if (flags & AT_REMOVEDIR) {
+               ret = streams_depot_rmdir_internal(handle,
+                               dirfsp,
+                               smb_fname);
+       } else {
+               ret = streams_depot_unlink_internal(handle,
+                               dirfsp,
+                               smb_fname,
+                               flags);
+       }
+       return ret;
+}
+
+static int streams_depot_renameat(vfs_handle_struct *handle,
+                               files_struct *srcfsp,
                                const struct smb_filename *smb_fname_src,
+                               files_struct *dstfsp,
                                const struct smb_filename *smb_fname_dst)
 {
        struct smb_filename *smb_fname_src_stream = NULL;
        struct smb_filename *smb_fname_dst_stream = NULL;
+       struct smb_filename *full_src = NULL;
+       struct smb_filename *full_dst = NULL;
        bool src_is_stream, dst_is_stream;
        NTSTATUS status;
        int ret = -1;
 
-       DEBUG(10, ("streams_depot_rename called for %s => %s\n",
+       DEBUG(10, ("streams_depot_renameat called for %s => %s\n",
                   smb_fname_str_dbg(smb_fname_src),
                   smb_fname_str_dbg(smb_fname_dst)));
 
@@ -675,8 +980,11 @@ static int streams_depot_rename(vfs_handle_struct *handle,
        dst_is_stream = is_ntfs_stream_smb_fname(smb_fname_dst);
 
        if (!src_is_stream && !dst_is_stream) {
-               return SMB_VFS_NEXT_RENAME(handle, smb_fname_src,
-                                          smb_fname_dst);
+               return SMB_VFS_NEXT_RENAMEAT(handle,
+                                       srcfsp,
+                                       smb_fname_src,
+                                       dstfsp,
+                                       smb_fname_dst);
        }
 
        /* for now don't allow renames from or to the default stream */
@@ -686,22 +994,48 @@ static int streams_depot_rename(vfs_handle_struct *handle,
                goto done;
        }
 
-       status = stream_smb_fname(handle, smb_fname_src, &smb_fname_src_stream,
-                                 false);
+       full_src = full_path_from_dirfsp_atname(talloc_tos(),
+                                               srcfsp,
+                                               smb_fname_src);
+       if (full_src == NULL) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       full_dst = full_path_from_dirfsp_atname(talloc_tos(),
+                                               dstfsp,
+                                               smb_fname_dst);
+       if (full_dst == NULL) {
+               errno = ENOMEM;
+               goto done;
+       }
+
+       status = stream_smb_fname(
+               handle, NULL, full_src, &smb_fname_src_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
-       status = stream_smb_fname(handle, smb_fname_dst,
-                                 &smb_fname_dst_stream, false);
+       status = stream_smb_fname(
+               handle, NULL, full_dst, &smb_fname_dst_stream, false);
        if (!NT_STATUS_IS_OK(status)) {
                errno = map_errno_from_nt_status(status);
                goto done;
        }
 
-       ret = SMB_VFS_NEXT_RENAME(handle, smb_fname_src_stream,
-                                 smb_fname_dst_stream);
+       /*
+        * We must use handle->conn->cwd_fsp as
+        * srcfsp and dstfsp directory handles here
+        * as we used the full pathname from the cwd dir
+        * to calculate the streams directory and filename
+        * within.
+        */
+       ret = SMB_VFS_NEXT_RENAMEAT(handle,
+                               handle->conn->cwd_fsp,
+                               smb_fname_src_stream,
+                               handle->conn->cwd_fsp,
+                               smb_fname_dst_stream);
 
 done:
        TALLOC_FREE(smb_fname_src_stream);
@@ -711,12 +1045,12 @@ done:
 
 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
                           struct stream_struct **streams,
-                          const char *name, SMB_OFF_T size,
-                          SMB_OFF_T alloc_size)
+                          const char *name, off_t size,
+                          off_t alloc_size)
 {
        struct stream_struct *tmp;
 
-       tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
+       tmp = talloc_realloc(mem_ctx, *streams, struct stream_struct,
                                   (*num_streams)+1);
        if (tmp == NULL) {
                return false;
@@ -743,15 +1077,15 @@ struct streaminfo_state {
        NTSTATUS status;
 };
 
-static bool collect_one_stream(const char *dirname,
+static bool collect_one_stream(const struct smb_filename *dirfname,
                               const char *dirent,
                               void *private_data)
 {
+       const char *dirname = dirfname->base_name;
        struct streaminfo_state *state =
                (struct streaminfo_state *)private_data;
        struct smb_filename *smb_fname = NULL;
        char *sname = NULL;
-       NTSTATUS status;
        bool ret;
 
        sname = talloc_asprintf(talloc_tos(), "%s/%s", dirname, dirent);
@@ -761,10 +1095,14 @@ static bool collect_one_stream(const char *dirname,
                goto out;
        }
 
-       status = create_synthetic_smb_fname(talloc_tos(), sname, NULL,
-                                           NULL, &smb_fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               state->status = status;
+       smb_fname = synthetic_smb_fname(talloc_tos(),
+                                       sname,
+                                       NULL,
+                                       NULL,
+                                       dirfname->twrp,
+                                       0);
+       if (smb_fname == NULL) {
+               state->status = NT_STATUS_NO_MEMORY;
                ret = false;
                goto out;
        }
@@ -793,9 +1131,8 @@ static bool collect_one_stream(const char *dirname,
        return ret;
 }
 
-static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
+static NTSTATUS streams_depot_fstreaminfo(vfs_handle_struct *handle,
                                         struct files_struct *fsp,
-                                        const char *fname,
                                         TALLOC_CTX *mem_ctx,
                                         unsigned int *pnum_streams,
                                         struct stream_struct **pstreams)
@@ -805,48 +1142,33 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
        NTSTATUS status;
        struct streaminfo_state state;
 
-       status = create_synthetic_smb_fname(talloc_tos(), fname, NULL, NULL,
-                                           &smb_fname_base);
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
-       }
-
-       if ((fsp != NULL) && (fsp->fh->fd != -1)) {
-               ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
-       }
-       else {
-               if (lp_posix_pathnames()) {
-                       ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
-               } else {
-                       ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
-               }
+       smb_fname_base = synthetic_smb_fname(talloc_tos(),
+                                       fsp->fsp_name->base_name,
+                                       NULL,
+                                       NULL,
+                                       fsp->fsp_name->twrp,
+                                       fsp->fsp_name->flags);
+       if (smb_fname_base == NULL) {
+               return NT_STATUS_NO_MEMORY;
        }
 
+       ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
        if (ret == -1) {
                status = map_nt_error_from_unix(errno);
                goto out;
        }
 
-       state.streams = NULL;
-       state.num_streams = 0;
-
-       if (!S_ISDIR(smb_fname_base->st.st_ex_mode)) {
-               if (!add_one_stream(mem_ctx,
-                                   &state.num_streams, &state.streams,
-                                   "::$DATA", smb_fname_base->st.st_ex_size,
-                                   SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
-                                                      &smb_fname_base->st))) {
-                       status = NT_STATUS_NO_MEMORY;
-                       goto out;
-               }
-       }
-
+       state.streams = *pstreams;
+       state.num_streams = *pnum_streams;
        state.mem_ctx = mem_ctx;
        state.handle = handle;
        state.status = NT_STATUS_OK;
 
-       status = walk_streams(handle, smb_fname_base, NULL, collect_one_stream,
-                             &state);
+       status = walk_streams(handle,
+                               smb_fname_base,
+                               NULL,
+                               collect_one_stream,
+                               &state);
 
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(state.streams);
@@ -861,7 +1183,11 @@ static NTSTATUS streams_depot_streaminfo(vfs_handle_struct *handle,
 
        *pnum_streams = state.num_streams;
        *pstreams = state.streams;
-       status = NT_STATUS_OK;
+       status = SMB_VFS_NEXT_FSTREAMINFO(handle,
+                               fsp->base_fsp ? fsp->base_fsp : fsp,
+                               mem_ctx,
+                               pnum_streams,
+                               pstreams);
 
  out:
        TALLOC_FREE(smb_fname_base);
@@ -875,17 +1201,17 @@ static uint32_t streams_depot_fs_capabilities(struct vfs_handle_struct *handle,
 }
 
 static struct vfs_fn_pointers vfs_streams_depot_fns = {
-       .fs_capabilities = streams_depot_fs_capabilities,
-       .open_fn = streams_depot_open,
-       .stat = streams_depot_stat,
-       .lstat = streams_depot_lstat,
-       .unlink = streams_depot_unlink,
-       .rename = streams_depot_rename,
-       .streaminfo = streams_depot_streaminfo,
+       .fs_capabilities_fn = streams_depot_fs_capabilities,
+       .openat_fn = streams_depot_openat,
+       .stat_fn = streams_depot_stat,
+       .lstat_fn = streams_depot_lstat,
+       .unlinkat_fn = streams_depot_unlinkat,
+       .renameat_fn = streams_depot_renameat,
+       .fstreaminfo_fn = streams_depot_fstreaminfo,
 };
 
-NTSTATUS vfs_streams_depot_init(void);
-NTSTATUS vfs_streams_depot_init(void)
+static_decl_vfs;
+NTSTATUS vfs_streams_depot_init(TALLOC_CTX *ctx)
 {
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_depot",
                                &vfs_streams_depot_fns);