X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fmodules%2Fvfs_streams_depot.c;h=9870d0d571b12b647db02562b00be4924903bd69;hb=HEAD;hp=f1c687e9528b41613a77e2e4b47814628a0c37e2;hpb=c615ebed6e3d273a682806b952d543e834e5630d;p=samba.git diff --git a/source3/modules/vfs_streams_depot.c b/source3/modules/vfs_streams_depot.c index f1c687e9528..1221b2c2be2 100644 --- a/source3/modules/vfs_streams_depot.c +++ b/source3/modules/vfs_streams_depot.c @@ -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);