Add missing libxml2-tools dependency
[archive/platform/upstream/libvirt.git] / tests / vircgroupmock.c
1 /*
2  * Copyright (C) 2013 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  * Author: Daniel P. Berrange <berrange@redhat.com>
19  */
20
21 #include <config.h>
22
23 #ifdef __linux__
24 # include "internal.h"
25
26 # include <stdio.h>
27 # include <dlfcn.h>
28 # include <stdlib.h>
29 # include <unistd.h>
30 # include <fcntl.h>
31 # include <sys/stat.h>
32 # include <stdarg.h>
33 # include "testutilslxc.h"
34
35 static int (*realopen)(const char *path, int flags, ...);
36 static FILE *(*realfopen)(const char *path, const char *mode);
37 static int (*realaccess)(const char *path, int mode);
38 static int (*realstat)(const char *path, struct stat *sb);
39 static int (*real__xstat)(int ver, const char *path, struct stat *sb);
40 static int (*reallstat)(const char *path, struct stat *sb);
41 static int (*real__lxstat)(int ver, const char *path, struct stat *sb);
42 static int (*realmkdir)(const char *path, mode_t mode);
43
44 /* Don't make static, since it causes problems with clang
45  * when passed as an arg to asprintf()
46  * vircgroupmock.c:462:22: error: static variable 'fakesysfsdir' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
47  */
48 char *fakesysfsdir;
49 const char *fakedevicedir0 = FAKEDEVDIR0;
50 const char *fakedevicedir1 = FAKEDEVDIR1;
51
52
53 # define SYSFS_PREFIX "/not/really/sys/fs/cgroup/"
54
55 /*
56  * The plan:
57  *
58  * We fake out /proc/mounts, so make it look as is cgroups
59  * are mounted on /not/really/sys/fs/cgroup. We don't
60  * use /sys/fs/cgroup, because we want to make it easy to
61  * detect places where we've not mocked enough syscalls.
62  *
63  * In any open/acces/mkdir calls we look at path and if
64  * it starts with /not/really/sys/fs/cgroup, we rewrite
65  * the path to point at a temporary directory referred
66  * to by LIBVIRT_FAKE_SYSFS_DIR env variable that is
67  * set by the main test suite
68  *
69  * In mkdir() calls, we simulate the cgroups behaviour
70  * whereby creating the directory auto-creates a bunch
71  * of files beneath it
72  */
73
74 /*
75  * Intentionally missing the 'devices' mount.
76  * Co-mounting cpu & cpuacct controllers
77  * An anonymous controller for systemd
78  */
79 const char *procmounts =
80     "rootfs / rootfs rw 0 0\n"
81     "tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n"
82     "tmpfs /not/really/sys/fs/cgroup tmpfs rw,seclabel,nosuid,nodev,noexec,mode=755 0 0\n"
83     "cgroup /not/really/sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd 0 0\n"
84     "cgroup /not/really/sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n"
85     "cgroup /not/really/sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpuacct,cpu 0 0\n"
86     "cgroup /not/really/sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n"
87     "cgroup /not/really/sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n"
88     "cgroup /not/really/sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n"
89     "/dev/sda1 /boot ext4 rw,seclabel,relatime,data=ordered 0 0\n"
90     "tmpfs /tmp tmpfs rw,seclabel,relatime,size=1024000k 0 0\n";
91
92 const char *procselfcgroups =
93     "115:memory:/\n"
94     "8:blkio:/\n"
95     "6:freezer:/\n"
96     "3:cpuacct,cpu:/system\n"
97     "2:cpuset:/\n"
98     "1:name=systemd:/user/berrange/123\n";
99
100 const char *proccgroups =
101     "#subsys_name    hierarchy       num_cgroups     enabled\n"
102     "cpuset  2       4       1\n"
103     "cpu     3       48      1\n"
104     "cpuacct 3       48      1\n"
105     "memory  4       4       1\n"
106     "devices 5       4       1\n"
107     "freezer 6       4       1\n"
108     "blkio   8       4       1\n";
109
110
111 const char *procmountsallinone =
112     "rootfs / rootfs rw 0 0\n"
113     "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0\n"
114     "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n"
115     "udev /dev devtmpfs rw,relatime,size=16458560k,nr_inodes=4114640,mode=755 0 0\n"
116     "devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n"
117     "nfsd /proc/fs/nfsd nfsd rw,relatime 0 0\n"
118     "cgroup /not/really/sys/fs/cgroup cgroup rw,relatime,blkio,devices,memory,cpuacct,cpu,cpuset 0 0\n";
119
120 const char *procselfcgroupsallinone =
121     "6:blkio,devices,memory,cpuacct,cpu,cpuset:/";
122
123 const char *proccgroupsallinone =
124     "#subsys_name    hierarchy       num_cgroups     enabled\n"
125     "cpuset   6   1  1\n"
126     "cpu      6   1  1\n"
127     "cpuacct  6   1  1\n"
128     "memory   6   1  1\n"
129     "devices  6   1  1\n"
130     "blkio    6   1  1\n";
131
132 const char *procmountslogind =
133     "none /not/really/sys/fs/cgroup tmpfs rw,rootcontext=system_u:object_r:sysfs_t:s0,seclabel,relatime,size=4k,mode=755 0 0\n"
134     "systemd /not/really/sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,name=systemd 0 0\n";
135
136 const char *procselfcgroupslogind =
137     "1:name=systemd:/\n";
138
139 const char *proccgroupslogind =
140     "#subsys_name    hierarchy       num_cgroups     enabled\n"
141     "cpuset    0  1  1\n"
142     "cpu       0  1  1\n"
143     "cpuacct   0  1  1\n"
144     "memory    0  1  0\n"
145     "devices   0  1  1\n"
146     "freezer   0  1  1\n"
147     "net_cls   0  1  1\n"
148     "blkio     0  1  1\n"
149     "perf_event  0  1  1\n";
150
151
152
153 static int make_file(const char *path,
154                      const char *name,
155                      const char *value)
156 {
157     int fd = -1;
158     int ret = -1;
159     char *filepath = NULL;
160
161     if (asprintf(&filepath, "%s/%s", path, name) < 0)
162         return -1;
163
164     if ((fd = realopen(filepath, O_CREAT|O_WRONLY, 0600)) < 0)
165         goto cleanup;
166
167     if (write(fd, value, strlen(value)) != strlen(value))
168         goto cleanup;
169
170     ret = 0;
171  cleanup:
172     if (fd != -1 && close(fd) < 0)
173         ret = -1;
174     free(filepath);
175
176     return ret;
177 }
178
179 static int make_controller(const char *path, mode_t mode)
180 {
181     int ret = -1;
182     const char *controller;
183
184     if (!STRPREFIX(path, fakesysfsdir)) {
185         errno = EINVAL;
186         return -1;
187     }
188     controller = path + strlen(fakesysfsdir) + 1;
189
190     if (STREQ(controller, "cpu"))
191         return symlink("cpu,cpuacct", path);
192     if (STREQ(controller, "cpuacct"))
193         return symlink("cpu,cpuacct", path);
194
195     if (realmkdir(path, mode) < 0)
196         goto cleanup;
197
198 # define MAKE_FILE(name, value)                 \
199     do {                                        \
200         if (make_file(path, name, value) < 0)   \
201             goto cleanup;                       \
202     } while (0)
203
204     if (STRPREFIX(controller, "cpu,cpuacct")) {
205         MAKE_FILE("cpu.cfs_period_us", "100000\n");
206         MAKE_FILE("cpu.cfs_quota_us", "-1\n");
207         MAKE_FILE("cpu.rt_period_us", "1000000\n");
208         MAKE_FILE("cpu.rt_runtime_us", "950000\n");
209         MAKE_FILE("cpu.shares", "1024\n");
210         MAKE_FILE("cpu.stat",
211                   "nr_periods 0\n"
212                   "nr_throttled 0\n"
213                   "throttled_time 0\n");
214         MAKE_FILE("cpuacct.stat",
215                   "user 216687025\n"
216                   "system 43421396\n");
217         MAKE_FILE("cpuacct.usage", "2787788855799582\n");
218         MAKE_FILE("cpuacct.usage_percpu", "1413142688153030 1374646168910542\n");
219     } else if (STRPREFIX(controller, "cpuset")) {
220         MAKE_FILE("cpuset.cpu_exclusive", "1\n");
221         if (STREQ(controller, "cpuset"))
222             MAKE_FILE("cpuset.cpus", "0-1");
223         else
224             MAKE_FILE("cpuset.cpus", ""); /* Values don't inherit */
225         MAKE_FILE("cpuset.mem_exclusive", "1\n");
226         MAKE_FILE("cpuset.mem_hardwall", "0\n");
227         MAKE_FILE("cpuset.memory_migrate", "0\n");
228         MAKE_FILE("cpuset.memory_pressure", "0\n");
229         MAKE_FILE("cpuset.memory_pressure_enabled", "0\n");
230         MAKE_FILE("cpuset.memory_spread_page", "0\n");
231         MAKE_FILE("cpuset.memory_spread_slab", "0\n");
232         if (STREQ(controller, "cpuset"))
233             MAKE_FILE("cpuset.mems", "0");
234         else
235             MAKE_FILE("cpuset.mems", ""); /* Values don't inherit */
236         MAKE_FILE("cpuset.sched_load_balance", "1\n");
237         MAKE_FILE("cpuset.sched_relax_domain_level", "-1\n");
238     } else if (STRPREFIX(controller, "memory")) {
239         MAKE_FILE("memory.failcnt", "0\n");
240         MAKE_FILE("memory.force_empty", ""); /* Write only */
241         MAKE_FILE("memory.kmem.tcp.failcnt", "0\n");
242         MAKE_FILE("memory.kmem.tcp.limit_in_bytes", "9223372036854775807\n");
243         MAKE_FILE("memory.kmem.tcp.max_usage_in_bytes", "0\n");
244         MAKE_FILE("memory.kmem.tcp.usage_in_bytes", "16384\n");
245         MAKE_FILE("memory.limit_in_bytes", "9223372036854775807\n");
246         MAKE_FILE("memory.max_usage_in_bytes", "0\n");
247         MAKE_FILE("memory.memsw.failcnt", ""); /* Not supported */
248         MAKE_FILE("memory.memsw.limit_in_bytes", ""); /* Not supported */
249         MAKE_FILE("memory.memsw.max_usage_in_bytes", ""); /* Not supported */
250         MAKE_FILE("memory.memsw.usage_in_bytes", ""); /* Not supported */
251         MAKE_FILE("memory.move_charge_at_immigrate", "0\n");
252         MAKE_FILE("memory.numa_stat",
253                   "total=367664 N0=367664\n"
254                   "file=314764 N0=314764\n"
255                   "anon=51999 N0=51999\n"
256                   "unevictable=901 N0=901\n");
257         MAKE_FILE("memory.oom_control",
258                   "oom_kill_disable 0\n"
259                   "under_oom 0\n");
260         MAKE_FILE("memory.soft_limit_in_bytes", "9223372036854775807\n");
261         MAKE_FILE("memory.stat",
262                   "cache 1336619008\n"
263                   "rss 97792000\n"
264                   "mapped_file 42090496\n"
265                   "pgpgin 13022605027\n"
266                   "pgpgout 13023820533\n"
267                   "pgfault 54429417056\n"
268                   "pgmajfault 315715\n"
269                   "inactive_anon 145887232\n"
270                   "active_anon 67100672\n"
271                   "inactive_file 627400704\n"
272                   "active_file 661872640\n"
273                   "unevictable 3690496\n"
274                   "hierarchical_memory_limit 9223372036854775807\n"
275                   "total_cache 1336635392\n"
276                   "total_rss 118689792\n"
277                   "total_mapped_file 42106880\n"
278                   "total_pgpgin 13022606816\n"
279                   "total_pgpgout 13023820793\n"
280                   "total_pgfault 54429422313\n"
281                   "total_pgmajfault 315715\n"
282                   "total_inactive_anon 145891328\n"
283                   "total_active_anon 88010752\n"
284                   "total_inactive_file 627400704\n"
285                   "total_active_file 661872640\n"
286                   "total_unevictable 3690496\n"
287                   "recent_rotated_anon 112807028\n"
288                   "recent_rotated_file 2547948\n"
289                   "recent_scanned_anon 113796164\n"
290                   "recent_scanned_file 8199863\n");
291         MAKE_FILE("memory.swappiness", "60\n");
292         MAKE_FILE("memory.usage_in_bytes", "1455321088\n");
293         MAKE_FILE("memory.use_hierarchy", "0\n");
294     } else if (STRPREFIX(controller, "freezer")) {
295         MAKE_FILE("freezer.state", "THAWED");
296     } else if (STRPREFIX(controller, "blkio")) {
297         MAKE_FILE("blkio.io_merged",
298                   "8:0 Read 1100949\n"
299                   "8:0 Write 2248076\n"
300                   "8:0 Sync 63063\n"
301                   "8:0 Async 3285962\n"
302                   "8:0 Total 3349025\n");
303         MAKE_FILE("blkio.io_queued",
304                   "8:0 Read 0\n"
305                   "8:0 Write 0\n"
306                   "8:0 Sync 0\n"
307                   "8:0 Async 0\n"
308                   "8:0 Total 0\n");
309         MAKE_FILE("blkio.io_service_bytes",
310                   "8:0 Read 59542078464\n"
311                   "8:0 Write 397369182208\n"
312                   "8:0 Sync 234080922624\n"
313                   "8:0 Async 222830338048\n"
314                   "8:0 Total 456911260672\n");
315         MAKE_FILE("blkio.io_serviced",
316                   "8:0 Read 3402504\n"
317                   "8:0 Write 14966516\n"
318                   "8:0 Sync 12064031\n"
319                   "8:0 Async 6304989\n"
320                   "8:0 Total 18369020\n");
321         MAKE_FILE("blkio.io_service_time",
322                   "8:0 Read 10747537542349\n"
323                   "8:0 Write 9200028590575\n"
324                   "8:0 Sync 6449319855381\n"
325                   "8:0 Async 13498246277543\n"
326                   "8:0 Total 19947566132924\n");
327         MAKE_FILE("blkio.io_wait_time",
328                   "8:0 Read 14687514824889\n"
329                   "8:0 Write 357748452187691\n"
330                   "8:0 Sync 55296974349413\n"
331                   "8:0 Async 317138992663167\n"
332                   "8:0 Total 372435967012580\n");
333         MAKE_FILE("blkio.reset_stats", ""); /* Write only */
334         MAKE_FILE("blkio.sectors", "8:0 892404806\n");
335         MAKE_FILE("blkio.throttle.io_service_bytes",
336                   "8:0 Read 59542107136\n"
337                   "8:0 Write 411440480256\n"
338                   "8:0 Sync 248486822912\n"
339                   "8:0 Async 222495764480\n"
340                   "8:0 Total 470982587392\n"
341                   "9:0 Read 59542107137\n"
342                   "9:0 Write 411440480257\n"
343                   "9:0 Sync 248486822912\n"
344                   "9:0 Async 222495764480\n"
345                   "9:0 Total 470982587392\n");
346         MAKE_FILE("blkio.throttle.io_serviced",
347                   "8:0 Read 4832583\n"
348                   "8:0 Write 36641903\n"
349                   "8:0 Sync 30723171\n"
350                   "8:0 Async 10751315\n"
351                   "8:0 Total 41474486\n"
352                   "9:0 Read 4832584\n"
353                   "9:0 Write 36641904\n"
354                   "9:0 Sync 30723171\n"
355                   "9:0 Async 10751315\n"
356                   "9:0 Total 41474486\n");
357         MAKE_FILE("blkio.throttle.read_bps_device", "");
358         MAKE_FILE("blkio.throttle.read_iops_device", "");
359         MAKE_FILE("blkio.throttle.write_bps_device", "");
360         MAKE_FILE("blkio.throttle.write_iops_device", "");
361         MAKE_FILE("blkio.time", "8:0 61019089\n");
362         MAKE_FILE("blkio.weight", "1000\n");
363         MAKE_FILE("blkio.weight_device", "");
364
365     } else {
366         errno = EINVAL;
367         goto cleanup;
368     }
369
370     ret = 0;
371  cleanup:
372     return ret;
373 }
374
375 static void init_syms(void)
376 {
377     if (realfopen)
378         return;
379
380 # define LOAD_SYM(name)                                                 \
381     do {                                                                \
382         if (!(real ## name = dlsym(RTLD_NEXT, #name))) {                \
383             fprintf(stderr, "Cannot find real '%s' symbol\n", #name);   \
384             abort();                                                    \
385         }                                                               \
386     } while (0)
387
388 # define LOAD_SYM_ALT(name1, name2)                                     \
389     do {                                                                \
390         if (!(real ## name1 = dlsym(RTLD_NEXT, #name1)) &&              \
391             !(real ## name2 = dlsym(RTLD_NEXT, #name2))) {              \
392             fprintf(stderr, "Cannot find real '%s' or '%s' symbol\n", #name1, #name2); \
393             abort();                                                    \
394         }                                                               \
395     } while (0)
396
397     LOAD_SYM(fopen);
398     LOAD_SYM(access);
399     LOAD_SYM_ALT(lstat, __lxstat);
400     LOAD_SYM_ALT(stat, __xstat);
401     LOAD_SYM(mkdir);
402     LOAD_SYM(open);
403 }
404
405 static void init_sysfs(void)
406 {
407     if (fakesysfsdir)
408         return;
409
410     if (!(fakesysfsdir = getenv("LIBVIRT_FAKE_SYSFS_DIR"))) {
411         fprintf(stderr, "Missing LIBVIRT_FAKE_SYSFS_DIR env variable\n");
412         abort();
413     }
414
415 # define MAKE_CONTROLLER(subpath)                               \
416     do {                                                        \
417         char *path;                                             \
418         if (asprintf(&path, "%s/%s", fakesysfsdir, subpath) < 0)\
419             abort();                                            \
420         if (make_controller(path, 0755) < 0) {                  \
421             fprintf(stderr, "Cannot initialize %s\n", path);    \
422             abort();                                            \
423         }                                                       \
424     } while (0)
425
426     MAKE_CONTROLLER("cpu");
427     MAKE_CONTROLLER("cpuacct");
428     MAKE_CONTROLLER("cpu,cpuacct");
429     MAKE_CONTROLLER("cpu,cpuacct/system");
430     MAKE_CONTROLLER("cpuset");
431     MAKE_CONTROLLER("blkio");
432     MAKE_CONTROLLER("memory");
433     MAKE_CONTROLLER("freezer");
434 }
435
436
437 FILE *fopen(const char *path, const char *mode)
438 {
439     const char *mock;
440     bool allinone = false, logind = false;
441     init_syms();
442
443     mock = getenv("VIR_CGROUP_MOCK_MODE");
444     if (mock) {
445         if (STREQ(mock, "allinone"))
446             allinone = true;
447         else if (STREQ(mock, "logind"))
448             logind = true;
449     }
450
451     if (STREQ(path, "/proc/mounts")) {
452         if (STREQ(mode, "r")) {
453             if (allinone)
454                 return fmemopen((void *)procmountsallinone,
455                                 strlen(procmountsallinone), mode);
456             else if (logind)
457                 return fmemopen((void *)procmountslogind,
458                                 strlen(procmountslogind), mode);
459             else
460                 return fmemopen((void *)procmounts, strlen(procmounts), mode);
461         } else {
462             errno = EACCES;
463             return NULL;
464         }
465     }
466     if (STREQ(path, "/proc/cgroups")) {
467         if (STREQ(mode, "r")) {
468             if (allinone)
469                 return fmemopen((void *)proccgroupsallinone,
470                                 strlen(proccgroupsallinone), mode);
471             else if (logind)
472                 return fmemopen((void *)proccgroupslogind,
473                                 strlen(proccgroupslogind), mode);
474             else
475                 return fmemopen((void *)proccgroups, strlen(proccgroups), mode);
476         } else {
477             errno = EACCES;
478             return NULL;
479         }
480     }
481     if (STREQ(path, "/proc/self/cgroup")) {
482         if (STREQ(mode, "r")) {
483             if (allinone)
484                 return fmemopen((void *)procselfcgroupsallinone,
485                                 strlen(procselfcgroupsallinone), mode);
486             else if (logind)
487                 return fmemopen((void *)procselfcgroupslogind,
488                                 strlen(procselfcgroupslogind), mode);
489             else
490                 return fmemopen((void *)procselfcgroups, strlen(procselfcgroups), mode);
491         } else {
492             errno = EACCES;
493             return NULL;
494         }
495     }
496
497     return realfopen(path, mode);
498 }
499
500 int access(const char *path, int mode)
501 {
502     int ret;
503
504     init_syms();
505
506     if (STRPREFIX(path, SYSFS_PREFIX)) {
507         init_sysfs();
508         char *newpath;
509         if (asprintf(&newpath, "%s/%s",
510                      fakesysfsdir,
511                      path + strlen(SYSFS_PREFIX)) < 0) {
512             errno = ENOMEM;
513             return -1;
514         }
515         ret = realaccess(newpath, mode);
516         free(newpath);
517     } else if (STREQ(path, "/proc/cgroups") ||
518                STREQ(path, "/proc/self/cgroup")) {
519         /* These files are readable for all. */
520         ret = (mode == F_OK || mode == R_OK) ? 0 : -1;
521     } else if (STREQ(path, "/proc/mounts")) {
522         /* This one is accessible anytime for anybody. In fact, it's just
523          * a symlink to /proc/self/mounts. */
524         ret = 0;
525     } else {
526         ret = realaccess(path, mode);
527     }
528     return ret;
529 }
530
531 int __lxstat(int ver, const char *path, struct stat *sb)
532 {
533     int ret;
534
535     init_syms();
536
537     if (STRPREFIX(path, SYSFS_PREFIX)) {
538         init_sysfs();
539         char *newpath;
540         if (asprintf(&newpath, "%s/%s",
541                      fakesysfsdir,
542                      path + strlen(SYSFS_PREFIX)) < 0) {
543             errno = ENOMEM;
544             return -1;
545         }
546         ret = real__lxstat(ver, newpath, sb);
547         free(newpath);
548     } else if (STRPREFIX(path, fakedevicedir0)) {
549         sb->st_mode = S_IFBLK;
550         sb->st_rdev = makedev(8, 0);
551         return 0;
552     } else if (STRPREFIX(path, fakedevicedir1)) {
553         sb->st_mode = S_IFBLK;
554         sb->st_rdev = makedev(9, 0);
555         return 0;
556     } else {
557         ret = real__lxstat(ver, path, sb);
558     }
559     return ret;
560 }
561
562 int lstat(const char *path, struct stat *sb)
563 {
564     int ret;
565
566     init_syms();
567
568     if (STRPREFIX(path, SYSFS_PREFIX)) {
569         init_sysfs();
570         char *newpath;
571         if (asprintf(&newpath, "%s/%s",
572                      fakesysfsdir,
573                      path + strlen(SYSFS_PREFIX)) < 0) {
574             errno = ENOMEM;
575             return -1;
576         }
577         ret = reallstat(newpath, sb);
578         free(newpath);
579     } else if (STRPREFIX(path, fakedevicedir0)) {
580         sb->st_mode = S_IFBLK;
581         sb->st_rdev = makedev(8, 0);
582         return 0;
583     } else if (STRPREFIX(path, fakedevicedir1)) {
584         sb->st_mode = S_IFBLK;
585         sb->st_rdev = makedev(9, 0);
586         return 0;
587     } else {
588         ret = reallstat(path, sb);
589     }
590     return ret;
591 }
592
593 int __xstat(int ver, const char *path, struct stat *sb)
594 {
595     int ret;
596
597     init_syms();
598
599     if (STRPREFIX(path, SYSFS_PREFIX)) {
600         init_sysfs();
601         char *newpath;
602         if (asprintf(&newpath, "%s/%s",
603                      fakesysfsdir,
604                      path + strlen(SYSFS_PREFIX)) < 0) {
605             errno = ENOMEM;
606             return -1;
607         }
608         ret = real__xstat(ver, newpath, sb);
609         free(newpath);
610     } else if (STRPREFIX(path, fakedevicedir0)) {
611         sb->st_mode = S_IFBLK;
612         sb->st_rdev = makedev(8, 0);
613         return 0;
614     } else if (STRPREFIX(path, fakedevicedir1)) {
615         sb->st_mode = S_IFBLK;
616         sb->st_rdev = makedev(9, 0);
617         return 0;
618     } else {
619         ret = real__xstat(ver, path, sb);
620     }
621     return ret;
622 }
623
624 int stat(const char *path, struct stat *sb)
625 {
626     int ret;
627
628     init_syms();
629
630     if (STRPREFIX(path, SYSFS_PREFIX)) {
631         init_sysfs();
632         char *newpath;
633         if (asprintf(&newpath, "%s/%s",
634                      fakesysfsdir,
635                      path + strlen(SYSFS_PREFIX)) < 0) {
636             errno = ENOMEM;
637             return -1;
638         }
639         ret = realstat(newpath, sb);
640         free(newpath);
641     } else if (STRPREFIX(path, fakedevicedir0)) {
642         sb->st_mode = S_IFBLK;
643         sb->st_rdev = makedev(8, 0);
644         return 0;
645     } else if (STRPREFIX(path, fakedevicedir1)) {
646         sb->st_mode = S_IFBLK;
647         sb->st_rdev = makedev(9, 0);
648         return 0;
649     } else {
650         ret = realstat(path, sb);
651     }
652     return ret;
653 }
654
655 int mkdir(const char *path, mode_t mode)
656 {
657     int ret;
658
659     init_syms();
660
661     if (STRPREFIX(path, SYSFS_PREFIX)) {
662         init_sysfs();
663         char *newpath;
664         if (asprintf(&newpath, "%s/%s",
665                      fakesysfsdir,
666                      path + strlen(SYSFS_PREFIX)) < 0) {
667             errno = ENOMEM;
668             return -1;
669         }
670         ret = make_controller(newpath, mode);
671         free(newpath);
672     } else {
673         ret = realmkdir(path, mode);
674     }
675     return ret;
676 }
677
678 int open(const char *path, int flags, ...)
679 {
680     int ret;
681     char *newpath = NULL;
682
683     init_syms();
684
685     if (STRPREFIX(path, SYSFS_PREFIX)) {
686         init_sysfs();
687         if (asprintf(&newpath, "%s/%s",
688                      fakesysfsdir,
689                      path + strlen(SYSFS_PREFIX)) < 0) {
690             errno = ENOMEM;
691             return -1;
692         }
693     }
694     if (flags & O_CREAT) {
695         va_list ap;
696         mode_t mode;
697         va_start(ap, flags);
698         mode = va_arg(ap, mode_t);
699         va_end(ap);
700         ret = realopen(newpath ? newpath : path, flags, mode);
701     } else {
702         ret = realopen(newpath ? newpath : path, flags);
703     }
704     free(newpath);
705     return ret;
706 }
707 #else
708 /* Nothing to override on non-__linux__ platforms */
709 #endif