#include "includes.h"
#include "smbd/smbd.h"
#include "system/filesys.h"
+#include "source3/smbd/dir.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_VFS
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", smb_fname->base_name));
- if (SMB_VFS_GETXATTR(handle->conn, smb_fname, 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 true;
}
-static bool mark_file_valid(vfs_handle_struct *handle,
- const struct smb_filename *smb_fname)
-{
- char buf = '1';
- int ret;
-
- DEBUG(10, ("marking file %s as valid\n", smb_fname->base_name));
+/*
+ * 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, smb_fname, 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);
}
/**
const struct smb_filename *smb_fname,
const SMB_STRUCT_STAT *base_sbuf, bool create_it)
{
- const struct loadparm_substitution *lp_sub =
- loadparm_s3_global_substitution();
uint32_t hash;
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_t id_buf[16];
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) {
- errno = ENOMEM;
- goto fail;
- }
-
- rootdir = lp_parm_substituted_string(talloc_tos(), lp_sub,
- SNUM(handle->conn), "streams_depot", "directory",
- tmp);
+ rootdir = stream_rootdir(handle,
+ talloc_tos());
if (rootdir == 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)));
goto fail;
}
- if (check_valid && !mark_file_valid(handle, smb_fname)) {
- goto fail;
- }
-
TALLOC_FREE(rootdir_fname);
TALLOC_FREE(rootdir);
TALLOC_FREE(tmp_fname);
* 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;
void *private_data)
{
char *dirname;
+ char *rootdir = NULL;
+ char *orig_connectpath = NULL;
struct smb_filename *dir_smb_fname = NULL;
struct smb_Dir *dir_hnd = NULL;
const char *dname = NULL;
- long offset = 0;
char *talloced = NULL;
+ NTSTATUS status;
dirname = stream_dir(handle, smb_fname_base, &smb_fname_base->st,
false);
return NT_STATUS_NO_MEMORY;
}
- dir_hnd = OpenDir(talloc_tos(), handle->conn, dir_smb_fname, NULL, 0);
- if (dir_hnd == 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 ((dname = ReadDirName(dir_hnd, &offset, 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;
+ }
+
+ while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
if (ISDOT(dname) || ISDOTDOT(dname)) {
TALLOC_FREE(talloced);
continue;
TALLOC_FREE(talloced);
}
+ /* Restore the original connectpath. */
+ handle->conn->connectpath = orig_connectpath;
+ TALLOC_FREE(rootdir);
TALLOC_FREE(dir_smb_fname);
TALLOC_FREE(dir_hnd);
}
/* 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);
}
/* 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);
const struct files_struct *dirfsp,
const struct smb_filename *smb_fname,
struct files_struct *fsp,
- int flags,
- mode_t mode)
+ 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_named_stream(smb_fname)) {
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ how);
}
- /*
- * For now assert this so the below SMB_VFS_STAT() is ok.
- */
- SMB_ASSERT(fsp_get_pathref_fd(dirfsp) == AT_FDCWD);
-
- /* Ensure the base file still exists. */
- 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) {
- ret = -1;
- errno = ENOMEM;
- goto done;
+ if (how->resolve != 0) {
+ errno = ENOSYS;
+ return -1;
}
- ret = SMB_VFS_NEXT_STAT(handle, smb_fname_base);
- if (ret == -1) {
- goto done;
- }
+ SMB_ASSERT(fsp_is_alternate_stream(fsp));
+ SMB_ASSERT(dirfsp == NULL);
+ SMB_ASSERT(VALID_STAT(fsp->base_fsp->fsp_name->st));
+
+ create_it = (how->flags & O_CREAT);
/* Determine the stream name, and then open it. */
- status = stream_smb_fname(handle, smb_fname, &smb_fname_stream, true);
+ 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;
}
+ 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;
+ }
+ }
+ }
+
status = vfs_at_fspcwd(talloc_tos(), handle->conn, &fspcwd);
if (!NT_STATUS_IS_OK(status)) {
ret = -1;
fspcwd,
smb_fname_stream,
fsp,
- flags,
- mode);
+ how);
done:
TALLOC_FREE(smb_fname_stream);
- TALLOC_FREE(smb_fname_base);
TALLOC_FREE(fspcwd);
return ret;
}
struct smb_filename *smb_fname_stream = NULL;
NTSTATUS status;
- status = stream_smb_fname(handle, full_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);
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);
int flags)
{
int ret;
- SMB_ASSERT(dirfsp == dirfsp->conn->cwd_fsp);
if (flags & AT_REMOVEDIR) {
ret = streams_depot_rmdir_internal(handle,
dirfsp,
{
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;
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;
}
+ /*
+ * 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,
- srcfsp,
+ handle->conn->cwd_fsp,
smb_fname_src_stream,
- dstfsp,
+ handle->conn->cwd_fsp,
smb_fname_dst_stream);
done:
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 struct smb_filename *smb_fname,
TALLOC_CTX *mem_ctx,
unsigned int *pnum_streams,
struct stream_struct **pstreams)
struct streaminfo_state state;
smb_fname_base = synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
+ fsp->fsp_name->base_name,
NULL,
NULL,
- smb_fname->twrp,
- smb_fname->flags);
+ fsp->fsp_name->twrp,
+ fsp->fsp_name->flags);
if (smb_fname_base == NULL) {
return NT_STATUS_NO_MEMORY;
}
- if ((fsp != NULL) && (fsp_get_pathref_fd(fsp) != -1)) {
- ret = SMB_VFS_NEXT_FSTAT(handle, fsp, &smb_fname_base->st);
- }
- else {
- 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);
- }
- }
-
+ 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;
- if (S_ISLNK(smb_fname_base->st.st_ex_mode)) {
- /*
- * Currently we do't have SMB_VFS_LLISTXATTR
- * inside the VFS which means there's no way
- * to cope with a symlink when lp_posix_pathnames().
- * returns true. For now ignore links.
- * FIXME - by adding SMB_VFS_LLISTXATTR. JRA.
- */
- status = NT_STATUS_OK;
- } else {
- 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,
- smb_fname_base,
+ status = SMB_VFS_NEXT_FSTREAMINFO(handle,
+ fsp->base_fsp ? fsp->base_fsp : fsp,
mem_ctx,
pnum_streams,
pstreams);
.lstat_fn = streams_depot_lstat,
.unlinkat_fn = streams_depot_unlinkat,
.renameat_fn = streams_depot_renameat,
- .streaminfo_fn = streams_depot_streaminfo,
+ .fstreaminfo_fn = streams_depot_fstreaminfo,
};
static_decl_vfs;