#include "includes.h"
#include "smbd/smbd.h"
#include "system/filesys.h"
+#include "source3/smbd/dir.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_VFS
* 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)
+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;
- 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;
return true;
}
-static bool mark_file_valid(vfs_handle_struct *handle, const char *path)
-{
- char buf = '1';
- int ret;
-
- 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);
}
/**
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;
+ 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->cwd);
-
- 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;
smb_fname_base = synthetic_smb_fname(
- talloc_tos(), smb_fname->base_name, NULL, NULL);
+ talloc_tos(),
+ smb_fname->base_name,
+ NULL,
+ NULL,
+ smb_fname->twrp,
+ smb_fname->flags);
if (smb_fname_base == NULL) {
errno = ENOMEM;
goto fail;
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)));
return NULL;
}
- smb_fname_hash = synthetic_smb_fname(talloc_tos(), result, NULL, NULL);
+ 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 (!check_valid ||
- file_is_valid(handle, smb_fname->base_name)) {
+ file_is_valid(handle, smb_fname)) {
return result;
}
smb_fname_hash->base_name));
recursive_rmdir(talloc_tos(), handle->conn,
smb_fname_hash);
- SMB_VFS_NEXT_RMDIR(handle, smb_fname_hash->base_name);
+ SMB_VFS_NEXT_UNLINKAT(handle,
+ handle->conn->cwd_fsp,
+ smb_fname_hash,
+ AT_REMOVEDIR);
} else {
newname = talloc_asprintf(talloc_tos(), "lost-%lu",
random());
}
smb_fname_new = synthetic_smb_fname(
- talloc_tos(), newname, NULL, NULL);
+ 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,
- smb_fname_new) == -1) {
+ 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;
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;
}
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);
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 (check_valid && !mark_file_valid(handle, smb_fname->base_name)) {
+ 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;
* 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)
}
}
- 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);
goto fail;
}
} else {
- /* Normalize the stream type to upercase. */
+ /* 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. */
- *smb_fname_out = synthetic_smb_fname(
- talloc_tos(), stream_fname, NULL, NULL);
+ *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;
}
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;
- 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);
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;
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)
{
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);
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);
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. */
- smb_fname_base = synthetic_smb_fname(
- talloc_tos(), smb_fname->base_name, NULL, NULL);
- if (smb_fname_base == NULL) {
+ /* 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 = ENOMEM;
+ 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;
+ 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;
* We potentially need to delete the per-inode streams directory
*/
- smb_fname_base = synthetic_smb_fname(
- talloc_tos(), smb_fname->base_name, NULL, NULL);
- if (smb_fname_base == NULL) {
- errno = ENOMEM;
- return -1;
- }
-
- if (lp_posix_pathnames()) {
- ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname_base);
+ if (full_fname->flags & SMB_FILENAME_POSIX_PATH) {
+ ret = SMB_VFS_NEXT_LSTAT(handle, full_fname);
} else {
- ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
+ 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(smb_fname_base);
+ TALLOC_FREE(full_fname);
return -1;
}
- if (smb_fname_base->st.st_ex_nlink == 1) {
- char *dirname = stream_dir(handle, smb_fname_base,
- &smb_fname_base->st, false);
-
- if (dirname != NULL) {
- SMB_VFS_NEXT_RMDIR(handle, dirname);
+ /*
+ * 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_UNLINK(handle, smb_fname);
-
- TALLOC_FREE(smb_fname_base);
+ ret = SMB_VFS_NEXT_UNLINKAT(handle,
+ dirfsp,
+ smb_fname,
+ flags);
return ret;
}
-static int streams_depot_rmdir(vfs_handle_struct *handle, const char *path)
+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;
- DEBUG(10, ("streams_depot_rmdir called for %s\n", path));
+ 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(), path, NULL, NULL);
+ 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 (lp_posix_pathnames()) {
+ 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);
return -1;
}
- if (smb_fname_base->st.st_ex_nlink == 2) {
+ /*
+ * 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_RMDIR(handle, path);
-
+ 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)));
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 */
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);
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);
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;
}
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)
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.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);
*pnum_streams = state.num_streams;
*pstreams = state.streams;
- status = SMB_VFS_NEXT_STREAMINFO(handle, fsp, fname, mem_ctx, pnum_streams, pstreams);
+ status = SMB_VFS_NEXT_FSTREAMINFO(handle,
+ fsp->base_fsp ? fsp->base_fsp : fsp,
+ mem_ctx,
+ pnum_streams,
+ pstreams);
out:
TALLOC_FREE(smb_fname_base);
static struct vfs_fn_pointers vfs_streams_depot_fns = {
.fs_capabilities_fn = streams_depot_fs_capabilities,
- .open_fn = streams_depot_open,
+ .openat_fn = streams_depot_openat,
.stat_fn = streams_depot_stat,
.lstat_fn = streams_depot_lstat,
- .unlink_fn = streams_depot_unlink,
- .rmdir_fn = streams_depot_rmdir,
- .rename_fn = streams_depot_rename,
- .streaminfo_fn = streams_depot_streaminfo,
+ .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);