1 /*
2 * Copyright (C) 2019 Red Hat, Inc.
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 * Helpers for dealing with the many variants of stat(). This
19 * C file should be included from any file that wants to mock
20 * stat() correctly.
21 */
22
23 #include "virmock.h"
24 #include "viralloc.h"
25
26 #include <sys/stat.h>
27 #include <unistd.h>
28
29 /*
30 * The POSIX stat() function might resolve to any number of different
31 * symbols in the C library.
32 *
33 * The may be an additional stat64() function exposed by the headers
34 * too.
35 *
36 * On 64-bit hosts the stat & stat64 functions are identical, always
37 * referring to the 64-bit ABI.
38 *
39 * On 32-bit hosts they refer to the 32-bit & 64-bit ABIs respectively.
40 *
41 * With meson libvirt will have _FILE_OFFSET_BITS=64 always defined.
42 * On 32-bit hosts it causes the C library to transparently rewrite
43 * stat() calls to be stat64() calls. Libvirt will never see the 32-bit
44 * ABI from the traditional stat() call. We cannot assume this rewriting
45 * is done using a macro. It might be, but on GLibC it is done with a
46 * magic __asm__ statement to apply the rewrite at link time instead of
47 * at preprocessing.
48 *
49 * In GLibC there may be two additional functions exposed by the headers,
50 * __xstat() and __xstat64(). When these exist, stat() and stat64() are
51 * transparently rewritten to call __xstat() and __xstat64() respectively.
52 * The former symbols will not actually exist in the library at all, only
53 * the header. The leading "__" indicates the symbols are a private impl
54 * detail of the C library that applications should not care about.
55 * Unfortunately, because we are trying to mock replace the C library,
56 * we need to know about this internal impl detail.
57 *
58 * On macOS stat() and lstat() are resolved to _stat$INODE64 and
59 * _lstat$INODE64, respectively. stat(2) man page also declares that
60 * stat64(), lstat64() and fstat64() are deprecated, and when
61 * building on Apple Silicon (aarch64) those functions are missing
62 * from the header altogether and should not be mocked.
63 *
64 * With all this in mind the list of functions we have to mock will depend
65 * on several factors
66 *
67 * - If the stat or __xstat but there is no 64-bit version.
68 *
69 * - If __xstat & __xstat64 exist, then stat & stat64 will not exist
70 * as symbols in the library, so the latter should not be mocked.
71 *
72 * - If __xstat exists in the library, but not the header than it
73 * it is just there for binary back compat and should not be
74 * mocked
75 *
76 * The same all applies to lstat()
77 */
78
79 #if !defined(__APPLE__)
80 # if !defined(WITH___XSTAT_DECL)
81 # if defined(WITH_STAT)
82 # if !defined(WITH___XSTAT) && !defined(WITH_STAT64)
83 # define MOCK_STAT
84 # endif
85 # endif
86 # if defined(WITH_STAT64)
87 # define MOCK_STAT64
88 # endif
89 # else /* WITH___XSTAT_DECL */
90 # if defined(WITH___XSTAT) && !defined(WITH___XSTAT64)
91 # define MOCK___XSTAT
92 # endif
93 # if defined(WITH___XSTAT64)
94 # define MOCK___XSTAT64
95 # endif
96 # endif /* WITH___XSTAT_DECL */
97 # if !defined(WITH___LXSTAT_DECL)
98 # if defined(WITH_LSTAT)
99 # if !defined(WITH___LXSTAT) && !defined(WITH_LSTAT64)
100 # define MOCK_LSTAT
101 # endif
102 # endif
103 # if defined(WITH_LSTAT64)
104 # define MOCK_LSTAT64
105 # endif
106 # else /* WITH___LXSTAT_DECL */
107 # if defined(WITH___LXSTAT) && !defined(WITH___LXSTAT64)
108 # define MOCK___LXSTAT
109 # endif
110 # if defined(WITH___LXSTAT64)
111 # define MOCK___LXSTAT64
112 # endif
113 # endif /* WITH___LXSTAT_DECL */
114 #else /* __APPLE__ */
115 # define MOCK_STAT
116 # if defined(WITH_STAT64_DECL)
117 # define MOCK_STAT64
118 # endif
119 # define MOCK_LSTAT
120 # if defined(WITH_LSTAT64_DECL)
121 # define MOCK_LSTAT64
122 # endif
123 #endif
124
125 #ifdef MOCK_STAT
126 static int (*real_stat)(const char *path, struct stat *sb);
127 #endif
128 #ifdef MOCK_STAT64
129 static int (*real_stat64)(const char *path, struct stat64 *sb);
130 #endif
131 #ifdef MOCK___XSTAT
132 static int (*real___xstat)(int ver, const char *path, struct stat *sb);
133 #endif
134 #ifdef MOCK___XSTAT64
135 static int (*real___xstat64)(int ver, const char *path, struct stat64 *sb);
136 #endif
137 #ifdef MOCK_LSTAT
138 static int (*real_lstat)(const char *path, struct stat *sb);
139 #endif
140 #ifdef MOCK_LSTAT64
141 static int (*real_lstat64)(const char *path, struct stat64 *sb);
142 #endif
143 #ifdef MOCK___LXSTAT
144 static int (*real___lxstat)(int ver, const char *path, struct stat *sb);
145 #endif
146 #ifdef MOCK___LXSTAT64
147 static int (*real___lxstat64)(int ver, const char *path, struct stat64 *sb);
148 #endif
149
150 static bool init;
151 static bool debug;
152
153 #define fdebug(msg, ...) do { if (debug) fprintf(stderr, msg, __VA_ARGS__); } while (0)
154
virMockStatInit(void)155 static void virMockStatInit(void)
156 {
157 if (init)
158 return;
159
160 init = true;
161 debug = getenv("VIR_MOCK_STAT_DEBUG");
162
163 #ifdef MOCK_STAT
164 # if defined(__APPLE__) && defined(__x86_64__)
165 VIR_MOCK_REAL_INIT_ALIASED(stat, "stat$INODE64");
166 # else
167 VIR_MOCK_REAL_INIT(stat);
168 # endif
169 fdebug("real stat %p\n", real_stat);
170 #endif
171 #ifdef MOCK_STAT64
172 VIR_MOCK_REAL_INIT(stat64);
173 fdebug("real stat64 %p\n", real_stat64);
174 #endif
175 #ifdef MOCK___XSTAT
176 VIR_MOCK_REAL_INIT(__xstat);
177 fdebug("real __xstat %p\n", real___xstat);
178 #endif
179 #ifdef MOCK___XSTAT64
180 VIR_MOCK_REAL_INIT(__xstat64);
181 fdebug("real __xstat64 %p\n", real___xstat64);
182 #endif
183 #ifdef MOCK_LSTAT
184 # if defined(__APPLE__) && defined(__x86_64__)
185 VIR_MOCK_REAL_INIT_ALIASED(lstat, "lstat$INODE64");
186 # else
187 VIR_MOCK_REAL_INIT(lstat);
188 # endif
189 fdebug("real lstat %p\n", real_lstat);
190 #endif
191 #ifdef MOCK_LSTAT64
192 VIR_MOCK_REAL_INIT(lstat64);
193 fdebug("real lstat64 %p\n", real_lstat64);
194 #endif
195 #ifdef MOCK___LXSTAT
196 VIR_MOCK_REAL_INIT(__lxstat);
197 fdebug("real __lxstat %p\n", real___lxstat);
198 #endif
199 #ifdef MOCK___LXSTAT64
200 VIR_MOCK_REAL_INIT(__lxstat64);
201 fdebug("real __lxstat64 %p\n", real___lxstat64);
202 #endif
203 }
204
205 /*
206 * @stat: the path being queried
207 * @newpath: fill with redirected path, or leave NULL to use orig path
208 *
209 * Return 0 on success, -1 on allocation error
210 */
211 static int virMockStatRedirect(const char *path, char **newpath);
212
213 #ifndef VIR_MOCK_STAT_HOOK
214 # define VIR_MOCK_STAT_HOOK do { } while (0)
215 #endif
216
217 #ifdef MOCK_STAT
stat(const char * path,struct stat * sb)218 int stat(const char *path, struct stat *sb)
219 {
220 g_autofree char *newpath = NULL;
221
222 virMockStatInit();
223
224 if (virMockStatRedirect(path, &newpath) < 0)
225 abort();
226 fdebug("stat redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
227
228 VIR_MOCK_STAT_HOOK;
229
230 return real_stat(newpath ? newpath : path, sb);
231 }
232 #endif
233
234 #ifdef MOCK_STAT64
stat64(const char * path,struct stat64 * sb)235 int stat64(const char *path, struct stat64 *sb)
236 {
237 g_autofree char *newpath = NULL;
238
239 virMockStatInit();
240
241 if (virMockStatRedirect(path, &newpath) < 0)
242 abort();
243 fdebug("stat64 redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
244
245 VIR_MOCK_STAT_HOOK;
246
247 return real_stat64(newpath ? newpath : path, sb);
248 }
249 #endif
250
251 #ifdef MOCK___XSTAT
252 int
__xstat(int ver,const char * path,struct stat * sb)253 __xstat(int ver, const char *path, struct stat *sb)
254 {
255 g_autofree char *newpath = NULL;
256
257 virMockStatInit();
258
259 if (virMockStatRedirect(path, &newpath) < 0)
260 abort();
261 fdebug("__xstat redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
262
263 VIR_MOCK_STAT_HOOK;
264
265 return real___xstat(ver, newpath ? newpath : path, sb);
266 }
267 #endif
268
269 #ifdef MOCK___XSTAT64
270 int
__xstat64(int ver,const char * path,struct stat64 * sb)271 __xstat64(int ver, const char *path, struct stat64 *sb)
272 {
273 g_autofree char *newpath = NULL;
274
275 virMockStatInit();
276
277 if (virMockStatRedirect(path, &newpath) < 0)
278 abort();
279 fdebug("__xstat64 redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
280
281 VIR_MOCK_STAT_HOOK;
282
283 return real___xstat64(ver, newpath ? newpath : path, sb);
284 }
285 #endif
286
287 #ifdef MOCK_LSTAT
288 int
lstat(const char * path,struct stat * sb)289 lstat(const char *path, struct stat *sb)
290 {
291 g_autofree char *newpath = NULL;
292
293 virMockStatInit();
294
295 if (virMockStatRedirect(path, &newpath) < 0)
296 abort();
297 fdebug("lstat redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
298
299 VIR_MOCK_STAT_HOOK;
300
301 return real_lstat(newpath ? newpath : path, sb);
302 }
303 #endif
304
305 #ifdef MOCK_LSTAT64
306 int
lstat64(const char * path,struct stat64 * sb)307 lstat64(const char *path, struct stat64 *sb)
308 {
309 g_autofree char *newpath = NULL;
310
311 virMockStatInit();
312
313 if (virMockStatRedirect(path, &newpath) < 0)
314 abort();
315 fdebug("lstat64 redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
316
317 VIR_MOCK_STAT_HOOK;
318
319 return real_lstat64(newpath ? newpath : path, sb);
320 }
321 #endif
322
323 #ifdef MOCK___LXSTAT
324 int
__lxstat(int ver,const char * path,struct stat * sb)325 __lxstat(int ver, const char *path, struct stat *sb)
326 {
327 g_autofree char *newpath = NULL;
328
329 virMockStatInit();
330
331 if (virMockStatRedirect(path, &newpath) < 0)
332 abort();
333 fdebug("__lxstat redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
334
335 VIR_MOCK_STAT_HOOK;
336
337 return real___lxstat(ver, newpath ? newpath : path, sb);
338 }
339 #endif
340
341 #ifdef MOCK___LXSTAT64
342 int
__lxstat64(int ver,const char * path,struct stat64 * sb)343 __lxstat64(int ver, const char *path, struct stat64 *sb)
344 {
345 g_autofree char *newpath = NULL;
346
347 virMockStatInit();
348
349 if (virMockStatRedirect(path, &newpath) < 0)
350 abort();
351 fdebug("__lxstat64 redirect %s to %s sb=%p\n", path, newpath ? newpath : path, sb);
352
353 VIR_MOCK_STAT_HOOK;
354
355 return real___lxstat64(ver, newpath ? newpath : path, sb);
356 }
357 #endif
358