diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/modules/vfs_error_inject.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/modules/vfs_error_inject.c')
-rw-r--r-- | source3/modules/vfs_error_inject.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/source3/modules/vfs_error_inject.c b/source3/modules/vfs_error_inject.c new file mode 100644 index 0000000..529504f --- /dev/null +++ b/source3/modules/vfs_error_inject.c @@ -0,0 +1,219 @@ +/* + * Unix SMB/CIFS implementation. + * Samba VFS module for error injection in VFS calls + * Copyright (C) Christof Schmitt 2017 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbd/smbd.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +struct unix_error_map { + const char *err_str; + int error; +} unix_error_map_array[] = { + { "ESTALE", ESTALE }, + { "EBADF", EBADF }, + { "EINTR", EINTR }, + { "EACCES", EACCES }, + { "EROFS", EROFS }, +}; + +static int find_unix_error_from_string(const char *err_str) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(unix_error_map_array); i++) { + struct unix_error_map *m = &unix_error_map_array[i]; + + if (strequal(err_str, m->err_str)) { + return m->error; + } + } + + return 0; +} + +static int inject_unix_error(const char *vfs_func, vfs_handle_struct *handle) +{ + const char *err_str; + int error; + + err_str = lp_parm_const_string(SNUM(handle->conn), + "error_inject", vfs_func, NULL); + if (err_str == NULL) { + return 0; + } + + error = find_unix_error_from_string(err_str); + if (error != 0) { + DBG_WARNING("Returning error %s for VFS function %s\n", + err_str, vfs_func); + return error; + } + + if (strequal(err_str, "panic")) { + DBG_ERR("Panic in VFS function %s\n", vfs_func); + smb_panic("error_inject"); + } + + DBG_ERR("Unknown error inject %s requested " + "for vfs function %s\n", err_str, vfs_func); + + return 0; +} + +static int vfs_error_inject_chdir(vfs_handle_struct *handle, + const struct smb_filename *smb_fname) +{ + int error; + + error = inject_unix_error("chdir", handle); + if (error != 0) { + errno = error; + return -1; + } + + return SMB_VFS_NEXT_CHDIR(handle, smb_fname); +} + +static ssize_t vfs_error_inject_pwrite(vfs_handle_struct *handle, + files_struct *fsp, + const void *data, + size_t n, + off_t offset) +{ + int error; + + error = inject_unix_error("pwrite", handle); + if (error != 0) { + errno = error; + return -1; + } + + return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset); +} + +static int vfs_error_inject_openat(struct vfs_handle_struct *handle, + const struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + files_struct *fsp, + const struct vfs_open_how *how) +{ + int error = inject_unix_error("openat", handle); + int create_error = inject_unix_error("openat_create", handle); + int dirfsp_flags = (O_NOFOLLOW|O_DIRECTORY); + bool return_error; + +#ifdef O_PATH + dirfsp_flags |= O_PATH; +#else +#ifdef O_SEARCH + dirfsp_flags |= O_SEARCH; +#endif +#endif + + if ((create_error != 0) && (how->flags & O_CREAT)) { + struct stat_ex st = { + .st_ex_nlink = 0, + }; + int ret; + + ret = SMB_VFS_FSTATAT(handle->conn, + dirfsp, + smb_fname, + &st, + AT_SYMLINK_NOFOLLOW); + + if ((ret == -1) && (errno == ENOENT)) { + errno = create_error; + return -1; + } + } + + return_error = (error != 0); + return_error &= !fsp->fsp_flags.is_pathref; + return_error &= ((how->flags & dirfsp_flags) != dirfsp_flags); + + if (return_error) { + errno = error; + return -1; + } + return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how); +} + +static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle, + struct files_struct *dirfsp, + const struct smb_filename *smb_fname, + int flags) +{ + struct smb_filename *full_fname = NULL; + struct smb_filename *parent_fname = NULL; + int error = inject_unix_error("unlinkat", handle); + int ret; + NTSTATUS status; + + if (error == 0) { + return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags); + } + + full_fname = full_path_from_dirfsp_atname(talloc_tos(), + dirfsp, + smb_fname); + if (full_fname == NULL) { + return -1; + } + + status = SMB_VFS_PARENT_PATHNAME(handle->conn, + full_fname, /* TALLOC_CTX. */ + full_fname, + &parent_fname, + NULL); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(full_fname); + errno = map_errno_from_nt_status(status); + return -1; + } + + ret = SMB_VFS_STAT(handle->conn, parent_fname); + if (ret != 0) { + TALLOC_FREE(full_fname); + return -1; + } + + if (parent_fname->st.st_ex_uid == get_current_uid(dirfsp->conn)) { + return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags); + } + + errno = error; + return -1; +} + +static struct vfs_fn_pointers vfs_error_inject_fns = { + .chdir_fn = vfs_error_inject_chdir, + .pwrite_fn = vfs_error_inject_pwrite, + .openat_fn = vfs_error_inject_openat, + .unlinkat_fn = vfs_error_inject_unlinkat, +}; + +static_decl_vfs; +NTSTATUS vfs_error_inject_init(TALLOC_CTX *ctx) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "error_inject", + &vfs_error_inject_fns); +} |