1 /*
2  * virfilewrapper.c: Wrapper for universal file access
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <config.h>
20 
21 #ifndef WIN32
22 
23 # include <fcntl.h>
24 
25 # include "viralloc.h"
26 # include "virfile.h"
27 # include "virfilewrapper.h"
28 # include "virmock.h"
29 # include "virstring.h"
30 
31 
32 /* Mapping for prefix overrides */
33 static size_t noverrides;
34 static const char **overrides;
35 
36 /* nprefixes == noverrides, but two variables make it easier to use
37  * VIR_*_ELEMENT macros */
38 static size_t nprefixes;
39 static const char **prefixes;
40 
41 /* TODO: callbacks */
42 static int (*real_open)(const char *path, int flags, ...);
43 static FILE *(*real_fopen)(const char *path, const char *mode);
44 static int (*real_access)(const char *path, int mode);
45 static int (*real_mkdir)(const char *path, mode_t mode);
46 static DIR *(*real_opendir)(const char *path);
47 static int (*real_execv)(const char *path, char *const argv[]);
48 static int (*real_execve)(const char *path, char *const argv[], char *const envp[]);
49 
init_syms(void)50 static void init_syms(void)
51 {
52     if (real_fopen)
53         return;
54 
55     VIR_MOCK_REAL_INIT(fopen);
56     VIR_MOCK_REAL_INIT(access);
57     VIR_MOCK_REAL_INIT(mkdir);
58     VIR_MOCK_REAL_INIT(open);
59 # if defined(__APPLE__) && defined(__x86_64__)
60     VIR_MOCK_REAL_INIT_ALIASED(opendir, "opendir$INODE64");
61 # else
62     VIR_MOCK_REAL_INIT(opendir);
63 # endif
64     VIR_MOCK_REAL_INIT(execv);
65     VIR_MOCK_REAL_INIT(execve);
66 }
67 
68 
69 void
virFileWrapperAddPrefix(const char * prefix,const char * override)70 virFileWrapperAddPrefix(const char *prefix,
71                         const char *override)
72 {
73     /* Both parameters are mandatory */
74     if (!prefix || !override) {
75         fprintf(stderr, "Attempt to add invalid path override\n");
76         abort();
77     }
78 
79     init_syms();
80 
81     VIR_APPEND_ELEMENT(prefixes, nprefixes, prefix);
82     VIR_APPEND_ELEMENT(overrides, noverrides, override);
83 }
84 
85 
86 void
virFileWrapperRemovePrefix(const char * prefix)87 virFileWrapperRemovePrefix(const char *prefix)
88 {
89     size_t i = 0;
90 
91     for (i = 0; i < noverrides; i++) {
92         if (STREQ(prefixes[i], prefix))
93             break;
94     }
95 
96     if (i == noverrides)
97         return;
98 
99     VIR_DELETE_ELEMENT(overrides, i, noverrides);
100     VIR_DELETE_ELEMENT(prefixes, i, nprefixes);
101 }
102 
103 void
virFileWrapperClearPrefixes(void)104 virFileWrapperClearPrefixes(void)
105 {
106     nprefixes = 0;
107     noverrides = 0;
108 
109     VIR_FREE(prefixes);
110     VIR_FREE(overrides);
111 }
112 
113 # include "virmockstathelpers.c"
114 
115 int
virMockStatRedirect(const char * path,char ** newpath)116 virMockStatRedirect(const char *path, char **newpath)
117 {
118     size_t i = 0;
119 
120     for (i = 0; i < noverrides; i++) {
121         const char *tmp = STRSKIP(path, prefixes[i]);
122 
123         if (!tmp)
124             continue;
125 
126         *newpath = g_strdup_printf("%s%s", overrides[i], tmp);
127         break;
128     }
129 
130     return 0;
131 }
132 
133 
134 # define PATH_OVERRIDE(newpath, path) \
135     do { \
136         init_syms(); \
137  \
138         if (virMockStatRedirect(path, &newpath) < 0) \
139             abort(); \
140     } while (0)
141 
142 
fopen(const char * path,const char * mode)143 FILE *fopen(const char *path, const char *mode)
144 {
145     g_autofree char *newpath = NULL;
146 
147     PATH_OVERRIDE(newpath, path);
148 
149     return real_fopen(newpath ? newpath : path, mode);
150 }
151 
access(const char * path,int mode)152 int access(const char *path, int mode)
153 {
154     g_autofree char *newpath = NULL;
155 
156     PATH_OVERRIDE(newpath, path);
157 
158     return real_access(newpath ? newpath : path, mode);
159 }
160 
open(const char * path,int flags,...)161 int open(const char *path, int flags, ...)
162 {
163     g_autofree char *newpath = NULL;
164     va_list ap;
165     mode_t mode = 0;
166 
167     PATH_OVERRIDE(newpath, path);
168 
169     /* The mode argument is mandatory when O_CREAT is set in flags,
170      * otherwise the argument is ignored.
171      */
172     if (flags & O_CREAT) {
173         va_start(ap, flags);
174         mode = (mode_t) va_arg(ap, int);
175         va_end(ap);
176     }
177 
178     return real_open(newpath ? newpath : path, flags, mode);
179 }
180 
opendir(const char * path)181 DIR *opendir(const char *path)
182 {
183     g_autofree char *newpath = NULL;
184 
185     PATH_OVERRIDE(newpath, path);
186 
187     return real_opendir(newpath ? newpath : path);
188 }
189 
execv(const char * path,char * const argv[])190 int execv(const char *path, char *const argv[])
191 {
192     g_autofree char *newpath = NULL;
193 
194     PATH_OVERRIDE(newpath, path);
195 
196     return real_execv(newpath ? newpath : path, argv);
197 }
198 
execve(const char * path,char * const argv[],char * const envp[])199 int execve(const char *path, char *const argv[], char *const envp[])
200 {
201     g_autofree char *newpath = NULL;
202 
203     PATH_OVERRIDE(newpath, path);
204 
205     return real_execve(newpath ? newpath : path, argv, envp);
206 }
207 
208 #endif
209