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