nfs4_acls: Make fstat_with_cap_dac_override static
[samba.git] / source3 / modules / vfs_error_inject.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *  Samba VFS module for error injection in VFS calls
4  *  Copyright (C) Christof Schmitt 2017
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "smbd/smbd.h"
22
23 #undef DBGC_CLASS
24 #define DBGC_CLASS DBGC_VFS
25
26 struct unix_error_map {
27         const char *err_str;
28         int error;
29 } unix_error_map_array[] = {
30         {       "ESTALE",       ESTALE  },
31         {       "EBADF",        EBADF   },
32         {       "EINTR",        EINTR   },
33         {       "EACCES",       EACCES  },
34         {       "EROFS",        EROFS   },
35 };
36
37 static int find_unix_error_from_string(const char *err_str)
38 {
39         size_t i;
40
41         for (i = 0; i < ARRAY_SIZE(unix_error_map_array); i++) {
42                 struct unix_error_map *m = &unix_error_map_array[i];
43
44                 if (strequal(err_str, m->err_str)) {
45                         return m->error;
46                 }
47         }
48
49         return 0;
50 }
51
52 static int inject_unix_error(const char *vfs_func, vfs_handle_struct *handle)
53 {
54         const char *err_str;
55         int error;
56
57         err_str = lp_parm_const_string(SNUM(handle->conn),
58                                        "error_inject", vfs_func, NULL);
59         if (err_str == NULL) {
60                 return 0;
61         }
62
63         error = find_unix_error_from_string(err_str);
64         if (error != 0) {
65                 DBG_WARNING("Returning error %s for VFS function %s\n",
66                             err_str, vfs_func);
67                 return error;
68         }
69
70         if (strequal(err_str, "panic")) {
71                 DBG_ERR("Panic in VFS function %s\n", vfs_func);
72                 smb_panic("error_inject");
73         }
74
75         DBG_ERR("Unknown error inject %s requested "
76                 "for vfs function %s\n", err_str, vfs_func);
77
78         return 0;
79 }
80
81 static int vfs_error_inject_chdir(vfs_handle_struct *handle,
82                                   const struct smb_filename *smb_fname)
83 {
84         int error;
85
86         error = inject_unix_error("chdir", handle);
87         if (error != 0) {
88                 errno = error;
89                 return -1;
90         }
91
92         return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
93 }
94
95 static ssize_t vfs_error_inject_pwrite(vfs_handle_struct *handle,
96                                        files_struct *fsp,
97                                        const void *data,
98                                        size_t n,
99                                        off_t offset)
100 {
101         int error;
102
103         error = inject_unix_error("pwrite", handle);
104         if (error != 0) {
105                 errno = error;
106                 return -1;
107         }
108
109         return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
110 }
111
112 static int vfs_error_inject_openat(struct vfs_handle_struct *handle,
113                                    const struct files_struct *dirfsp,
114                                    const struct smb_filename *smb_fname,
115                                    files_struct *fsp,
116                                    const struct vfs_open_how *how)
117 {
118         int error = inject_unix_error("openat", handle);
119         int create_error = inject_unix_error("openat_create", handle);
120         int dirfsp_flags = (O_NOFOLLOW|O_DIRECTORY);
121         bool return_error;
122
123 #ifdef O_PATH
124         dirfsp_flags |= O_PATH;
125 #else
126 #ifdef O_SEARCH
127         dirfsp_flags |= O_SEARCH;
128 #endif
129 #endif
130
131         if ((create_error != 0) && (how->flags & O_CREAT)) {
132                 struct stat_ex st = {
133                         .st_ex_nlink = 0,
134                 };
135                 int ret;
136
137                 ret = SMB_VFS_FSTATAT(handle->conn,
138                                       dirfsp,
139                                       smb_fname,
140                                       &st,
141                                       AT_SYMLINK_NOFOLLOW);
142
143                 if ((ret == -1) && (errno == ENOENT)) {
144                         errno = create_error;
145                         return -1;
146                 }
147         }
148
149         return_error = (error != 0);
150         return_error &= !fsp->fsp_flags.is_pathref;
151         return_error &= ((how->flags & dirfsp_flags) != dirfsp_flags);
152
153         if (return_error) {
154                 errno = error;
155                 return -1;
156         }
157         return SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, how);
158 }
159
160 static int vfs_error_inject_unlinkat(struct vfs_handle_struct *handle,
161                                      struct files_struct *dirfsp,
162                                      const struct smb_filename *smb_fname,
163                                      int flags)
164 {
165         struct smb_filename *full_fname = NULL;
166         struct smb_filename *parent_fname = NULL;
167         int error = inject_unix_error("unlinkat", handle);
168         int ret;
169         NTSTATUS status;
170
171         if (error == 0) {
172                 return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
173         }
174
175         full_fname = full_path_from_dirfsp_atname(talloc_tos(),
176                                                   dirfsp,
177                                                   smb_fname);
178         if (full_fname == NULL) {
179                 return -1;
180         }
181
182         status = SMB_VFS_PARENT_PATHNAME(handle->conn,
183                                          full_fname, /* TALLOC_CTX. */
184                                          full_fname,
185                                          &parent_fname,
186                                          NULL);
187         if (!NT_STATUS_IS_OK(status)) {
188                 TALLOC_FREE(full_fname);
189                 errno = map_errno_from_nt_status(status);
190                 return -1;
191         }
192
193         ret = SMB_VFS_STAT(handle->conn, parent_fname);
194         if (ret != 0) {
195                 TALLOC_FREE(full_fname);
196                 return -1;
197         }
198
199         if (parent_fname->st.st_ex_uid == get_current_uid(dirfsp->conn)) {
200                 return SMB_VFS_NEXT_UNLINKAT(handle, dirfsp, smb_fname, flags);
201         }
202
203         errno = error;
204         return -1;
205 }
206
207 static struct vfs_fn_pointers vfs_error_inject_fns = {
208         .chdir_fn = vfs_error_inject_chdir,
209         .pwrite_fn = vfs_error_inject_pwrite,
210         .openat_fn = vfs_error_inject_openat,
211         .unlinkat_fn = vfs_error_inject_unlinkat,
212 };
213
214 static_decl_vfs;
215 NTSTATUS vfs_error_inject_init(TALLOC_CTX *ctx)
216 {
217         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "error_inject",
218                                 &vfs_error_inject_fns);
219 }