]> git.proxmox.com Git - mirror_lxcfs.git/blob - src/sysfs_fuse.c
tree-wide: include own header file first
[mirror_lxcfs.git] / src / sysfs_fuse.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE
5 #endif
6
7 #include "config.h"
8
9 /* Taken over modified from the kernel sources. */
10 #define NBITS 32 /* bits in uint32_t */
11 #define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
12 #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, NBITS)
13
14 #define __STDC_FORMAT_MACROS
15 #include <ctype.h>
16 #include <dirent.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <fuse.h>
20 #include <inttypes.h>
21 #include <libgen.h>
22 #include <pthread.h>
23 #include <sched.h>
24 #include <stdbool.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <wait.h>
32 #include <linux/magic.h>
33 #include <linux/sched.h>
34 #include <sys/epoll.h>
35 #include <sys/mman.h>
36 #include <sys/mount.h>
37 #include <sys/param.h>
38 #include <sys/socket.h>
39 #include <sys/syscall.h>
40 #include <sys/sysinfo.h>
41 #include <sys/vfs.h>
42
43 #include "sysfs_fuse.h"
44
45 #include "bindings.h"
46 #include "memory_utils.h"
47 #include "cgroups/cgroup.h"
48 #include "lxcfs_fuse_compat.h"
49 #include "utils.h"
50
51 static ssize_t get_max_cpus(char *cpulist)
52 {
53 char *c1, *c2;
54 char *maxcpus = cpulist;
55 size_t cpus = 0;
56
57 c1 = strrchr(maxcpus, ',');
58 if (c1)
59 c1++;
60
61 c2 = strrchr(maxcpus, '-');
62 if (c2)
63 c2++;
64
65 if (!c1 && !c2)
66 c1 = maxcpus;
67 else if (c1 > c2)
68 c2 = c1;
69 else if (c1 < c2)
70 c1 = c2;
71 else if (!c1 && c2)
72 c1 = c2;
73
74 errno = 0;
75 cpus = strtoul(c1, NULL, 0);
76 if (errno != 0)
77 return -1;
78
79 return cpus;
80 }
81
82 static void set_bit(unsigned bit, uint32_t *bitarr)
83 {
84 bitarr[bit / NBITS] |= (1 << (bit % NBITS));
85 }
86
87 static bool is_set(unsigned bit, uint32_t *bitarr)
88 {
89 return (bitarr[bit / NBITS] & (1 << (bit % NBITS))) != 0;
90 }
91
92 /* Create cpumask from cpulist aka turn:
93 *
94 * 0,2-3
95 *
96 * into bit array
97 *
98 * 1 0 1 1
99 */
100 static uint32_t *lxc_cpumask(char *buf, size_t nbits)
101 {
102 __do_free uint32_t *bitarr = NULL;
103 char *token;
104 size_t arrlen;
105
106 arrlen = BITS_TO_LONGS(nbits);
107 bitarr = calloc(arrlen, sizeof(uint32_t));
108 if (!bitarr)
109 return ret_set_errno(NULL, ENOMEM);
110
111 lxc_iterate_parts(token, buf, ",") {
112 errno = 0;
113 unsigned end, start;
114 char *range;
115
116 start = strtoul(token, NULL, 0);
117 end = start;
118 range = strchr(token, '-');
119 if (range)
120 end = strtoul(range + 1, NULL, 0);
121
122 if (!(start <= end))
123 return ret_set_errno(NULL, EINVAL);
124
125 if (end >= nbits)
126 return ret_set_errno(NULL, EINVAL);
127
128 while (start <= end)
129 set_bit(start++, bitarr);
130 }
131
132 return move_ptr(bitarr);
133 }
134
135 static int sys_devices_system_cpu_online_read(char *buf, size_t size,
136 off_t offset,
137 struct fuse_file_info *fi)
138 {
139 __do_free char *cg = NULL, *cpuset = NULL;
140 struct fuse_context *fc = fuse_get_context();
141 struct lxcfs_opts *opts = (struct lxcfs_opts *)fc->private_data;
142 struct file_info *d = INTTYPE_TO_PTR(fi->fh);
143 char *cache = d->buf;
144 bool use_view;
145
146 int max_cpus = 0;
147 pid_t initpid;
148 ssize_t total_len = 0;
149
150 if (offset) {
151 int left;
152
153 if (!d->cached)
154 return 0;
155
156 if (offset > d->size)
157 return -EINVAL;
158
159 left = d->size - offset;
160 total_len = left > size ? size : left;
161 memcpy(buf, cache + offset, total_len);
162
163 return total_len;
164 }
165
166 initpid = lookup_initpid_in_store(fc->pid);
167 if (initpid <= 1 || is_shared_pidns(initpid))
168 initpid = fc->pid;
169
170 cg = get_pid_cgroup(initpid, "cpuset");
171 if (!cg)
172 return read_file_fuse("/sys/devices/system/cpu/online", buf, size, d);
173 prune_init_slice(cg);
174
175 cpuset = get_cpuset(cg);
176 if (!cpuset)
177 return 0;
178
179 if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs)
180 use_view = true;
181 else
182 use_view = false;
183
184 if (use_view)
185 max_cpus = max_cpu_count(cg);
186
187 if (use_view) {
188 if (max_cpus > 1)
189 total_len = snprintf(d->buf, d->buflen, "0-%d\n", max_cpus - 1);
190 else
191 total_len = snprintf(d->buf, d->buflen, "0\n");
192 } else {
193 total_len = snprintf(d->buf, d->buflen, "%s\n", cpuset);
194 }
195 if (total_len < 0 || total_len >= d->buflen)
196 return log_error(0, "Failed to write to cache");
197
198 d->size = (int)total_len;
199 d->cached = 1;
200
201 if (total_len > size)
202 total_len = size;
203
204 memcpy(buf, d->buf, total_len);
205
206 return total_len;
207 }
208
209 static int filler_sys_devices_system_cpu(const char *path, void *buf,
210 fuse_fill_dir_t filler)
211 {
212 __do_free uint32_t *cpumask = NULL;
213 __do_free char *cg = NULL, *cpuset = NULL;
214 __do_closedir DIR *dir = NULL;
215 struct dirent *dirent;
216 struct fuse_context *fc = fuse_get_context();
217 pid_t initpid;
218 ssize_t max_cpus;
219
220 initpid = lookup_initpid_in_store(fc->pid);
221 if (initpid <= 1 || is_shared_pidns(initpid))
222 initpid = fc->pid;
223
224 cg = get_pid_cgroup(initpid, "cpuset");
225 if (!cg)
226 return 0;
227 prune_init_slice(cg);
228
229 cpuset = get_cpuset(cg);
230 if (!cpuset)
231 return 0;
232
233 max_cpus = get_max_cpus(cpuset);
234 if (max_cpus < 0 || max_cpus >= (INT_MAX - 1))
235 return -1;
236 max_cpus++;
237
238 cpumask = lxc_cpumask(cpuset, max_cpus);
239 if (!cpumask)
240 return -errno;
241
242 for (size_t i = 0; i < max_cpus; i++) {
243 int ret;
244 char cpu[100];
245
246 if (!is_set(i, cpumask))
247 continue;
248
249 ret = snprintf(cpu, sizeof(cpu), "cpu%ld", i);
250 if (ret < 0 || ret >= sizeof(cpu))
251 continue;
252
253 if (DIR_FILLER(filler, buf, cpu, NULL, 0) != 0)
254 return -ENOENT;
255 }
256
257 dir = opendir(path);
258 if (!dir)
259 return -ENOENT;
260
261 while ((dirent = readdir(dir))) {
262 char *entry = dirent->d_name;
263
264 if (strlen(entry) <= 3)
265 continue;
266 entry += 3;
267
268 /* Don't emit entries we already filtered above. */
269 if (isdigit(*entry))
270 continue;
271
272 if (DIR_FILLER(filler, buf, dirent->d_name, NULL, 0) != 0)
273 return -ENOENT;
274 }
275
276 return 0;
277 }
278
279 static int get_st_mode(const char *path, mode_t *mode)
280 {
281 struct stat sb;
282 int ret;
283
284 ret = lstat(path, &sb);
285 if (ret < 0)
286 return -ENOENT;
287
288 *mode = sb.st_mode;
289 return 0;
290 }
291
292 static off_t get_sysfile_size(const char *which)
293 {
294 __do_fclose FILE *f = NULL;
295 __do_free char *line = NULL;
296 size_t len = 0;
297 ssize_t sz, answer = 0;
298
299 f = fopen(which, "re");
300 if (!f)
301 return 0;
302
303 while ((sz = getline(&line, &len, f)) != -1)
304 answer += sz;
305
306 return answer;
307 }
308
309 static int sys_getattr_legacy(const char *path, struct stat *sb)
310 {
311 struct timespec now;
312
313 memset(sb, 0, sizeof(struct stat));
314 if (clock_gettime(CLOCK_REALTIME, &now) < 0)
315 return -EINVAL;
316
317 sb->st_uid = sb->st_gid = 0;
318 sb->st_atim = sb->st_mtim = sb->st_ctim = now;
319 if (strcmp(path, "/sys") == 0) {
320 sb->st_mode = S_IFDIR | 00555;
321 sb->st_nlink = 2;
322 return 0;
323 }
324
325 if (strcmp(path, "/sys/devices") == 0) {
326 sb->st_mode = S_IFDIR | 00555;
327 sb->st_nlink = 2;
328 return 0;
329 }
330
331 if (strcmp(path, "/sys/devices/system") == 0) {
332 sb->st_mode = S_IFDIR | 00555;
333 sb->st_nlink = 2;
334 return 0;
335 }
336
337 if (strcmp(path, "/sys/devices/system/cpu") == 0) {
338 sb->st_mode = S_IFDIR | 00555;
339 sb->st_nlink = 2;
340 return 0;
341 }
342
343 if (strcmp(path, "/sys/devices/system/cpu/online") == 0) {
344 sb->st_size = get_sysfile_size (path);
345 sb->st_mode = S_IFREG | 00444;
346 sb->st_nlink = 1;
347 return 0;
348 }
349
350 return -ENOENT;
351 }
352
353 __lxcfs_fuse_ops int sys_getattr(const char *path, struct stat *sb)
354 {
355 int ret;
356 struct timespec now;
357 mode_t st_mode;
358
359 if (!liblxcfs_functional())
360 return -EIO;
361
362 if (!liblxcfs_can_use_sys_cpu())
363 return sys_getattr_legacy(path, sb);
364
365 memset(sb, 0, sizeof(struct stat));
366 if (clock_gettime(CLOCK_REALTIME, &now) < 0)
367 return -EINVAL;
368
369 sb->st_uid = sb->st_gid = 0;
370 sb->st_atim = sb->st_mtim = sb->st_ctim = now;
371
372 ret = get_st_mode(path, &st_mode);
373 if (ret)
374 return -ENOENT;
375
376 if (S_ISDIR(st_mode)) {
377 sb->st_mode = st_mode;
378 sb->st_nlink = 2;
379 return 0;
380 }
381
382 if (S_ISREG(st_mode) || S_ISLNK(st_mode)) {
383 sb->st_size = get_sysfile_size(path);
384 sb->st_mode = st_mode;
385 sb->st_nlink = 1;
386 return 0;
387 }
388
389 return -ENOENT;
390 }
391
392 __lxcfs_fuse_ops int sys_release(const char *path, struct fuse_file_info *fi)
393 {
394 do_release_file_info(fi);
395 return 0;
396 }
397
398 __lxcfs_fuse_ops int sys_releasedir(const char *path, struct fuse_file_info *fi)
399 {
400 do_release_file_info(fi);
401 return 0;
402 }
403
404 __lxcfs_fuse_ops int sys_write(const char *path, const char *buf, size_t size,
405 off_t offset, struct fuse_file_info *fi)
406 {
407 __do_close int fd = -EBADF;
408 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
409
410 if (!liblxcfs_functional())
411 return -EIO;
412
413 if (f->type != LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE)
414 return -EINVAL;
415
416 fd = open(path, O_WRONLY | O_CLOEXEC);
417 if (fd == -1)
418 return -errno;
419
420 return pwrite(fd, buf, size, offset);
421 }
422
423 static int sys_readdir_legacy(const char *path, void *buf, fuse_fill_dir_t filler,
424 off_t offset, struct fuse_file_info *fi)
425 {
426 if (strcmp(path, "/sys") == 0) {
427 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
428 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
429 DIR_FILLER(filler, buf, "devices", NULL, 0) != 0)
430 return -ENOENT;
431
432 return 0;
433 }
434 if (strcmp(path, "/sys/devices") == 0) {
435 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
436 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
437 DIR_FILLER(filler, buf, "system", NULL, 0) != 0)
438 return -ENOENT;
439
440 return 0;
441 }
442 if (strcmp(path, "/sys/devices/system") == 0) {
443 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
444 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
445 DIR_FILLER(filler, buf, "cpu", NULL, 0) != 0)
446 return -ENOENT;
447
448 return 0;
449 }
450 if (strcmp(path, "/sys/devices/system/cpu") == 0) {
451 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
452 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
453 DIR_FILLER(filler, buf, "online", NULL, 0) != 0)
454 return -ENOENT;
455
456 return 0;
457 }
458
459 return 0;
460 }
461
462 __lxcfs_fuse_ops int sys_readdir(const char *path, void *buf,
463 fuse_fill_dir_t filler, off_t offset,
464 struct fuse_file_info *fi)
465 {
466 __do_closedir DIR *dir = NULL;
467 struct dirent *dirent;
468 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
469
470 if (!liblxcfs_functional())
471 return -EIO;
472
473 if (!liblxcfs_can_use_sys_cpu())
474 return sys_readdir_legacy(path, buf, filler, offset, fi);
475
476 /*
477 * When we reload LXCFS and we don't load the lxcfs binary itself
478 * changes to such functions as lxcfs_opendir() aren't reflected so
479 * sys_opendir() doesn't run but sys_readdir() does. We need to account
480 * for that here.
481 */
482 if (!f)
483 return -EIO;
484
485 switch (f->type) {
486 case LXC_TYPE_SYS: {
487 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
488 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
489 DIR_FILLER(filler, buf, "devices", NULL, 0) != 0)
490 return -ENOENT;
491
492 return 0;
493 }
494 case LXC_TYPE_SYS_DEVICES: {
495 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
496 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
497 DIR_FILLER(filler, buf, "system", NULL, 0) != 0)
498 return -ENOENT;
499
500 return 0;
501 }
502 case LXC_TYPE_SYS_DEVICES_SYSTEM: {
503 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
504 DIR_FILLER(filler, buf, "..", NULL, 0) != 0 ||
505 DIR_FILLER(filler, buf, "cpu", NULL, 0) != 0)
506 return -ENOENT;
507
508 return 0;
509 }
510 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU:
511 if (DIR_FILLER(filler, buf, ".", NULL, 0) != 0 ||
512 DIR_FILLER(filler, buf, "..", NULL, 0) != 0)
513 return -ENOENT;
514
515 return filler_sys_devices_system_cpu(path, buf, filler);
516 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR: {
517 dir = opendir(path);
518 if (!dir)
519 return -ENOENT;
520
521 while ((dirent = readdir(dir))) {
522 if (DIR_FILLER(filler, buf, dirent->d_name, NULL, 0) != 0)
523 return -ENOENT;
524 }
525
526 return 0;
527 }
528 }
529
530 return -EINVAL;
531 }
532
533 __lxcfs_fuse_ops int sys_readlink(const char *path, char *buf, size_t size)
534 {
535 int ret = readlink(path, buf, size);
536
537 if (!liblxcfs_functional())
538 return -EIO;
539
540 if (ret < 0)
541 return -errno;
542 if (ret > size)
543 return -1;
544
545 buf[ret] = '\0';
546
547 return 0;
548 }
549
550 static int sys_open_legacy(const char *path, struct fuse_file_info *fi)
551 {
552 __do_free struct file_info *info = NULL;
553 int type = -1;
554
555 if (strcmp(path, "/sys/devices") == 0)
556 type = LXC_TYPE_SYS_DEVICES;
557 if (strcmp(path, "/sys/devices/system") == 0)
558 type = LXC_TYPE_SYS_DEVICES_SYSTEM;
559 if (strcmp(path, "/sys/devices/system/cpu") == 0)
560 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU;
561 if (strcmp(path, "/sys/devices/system/cpu/online") == 0)
562 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE;
563 if (type == -1)
564 return -ENOENT;
565
566 info = malloc(sizeof(*info));
567 if (!info)
568 return -ENOMEM;
569
570 memset(info, 0, sizeof(*info));
571 info->type = type;
572
573 info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE;
574
575 info->buf = malloc(info->buflen);
576 if (!info->buf)
577 return -ENOMEM;
578
579 memset(info->buf, 0, info->buflen);
580 /* set actual size to buffer size */
581 info->size = info->buflen;
582
583 fi->fh = PTR_TO_UINT64(move_ptr(info));
584 return 0;
585 }
586
587 __lxcfs_fuse_ops int sys_open(const char *path, struct fuse_file_info *fi)
588 {
589 __do_free struct file_info *info = NULL;
590 int type = -1;
591
592 if (!liblxcfs_functional())
593 return -EIO;
594
595 if (!liblxcfs_can_use_sys_cpu())
596 return sys_open_legacy(path, fi);
597
598 if (strcmp(path, "/sys/devices/system/cpu/online") == 0) {
599 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE;
600 } else if (strncmp(path, "/sys/devices/system/cpu/",
601 STRLITERALLEN("/sys/devices/system/cpu/")) == 0) {
602 int ret;
603 mode_t st_mode;
604
605 ret = get_st_mode(path, &st_mode);
606 if (ret)
607 return ret;
608
609 if (S_ISREG(st_mode))
610 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE;
611 }
612 if (type == -1)
613 return -ENOENT;
614
615 info = malloc(sizeof(*info));
616 if (!info)
617 return -ENOMEM;
618
619 memset(info, 0, sizeof(*info));
620 info->type = type;
621
622 info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE;
623
624 info->buf = malloc(info->buflen);
625 if (!info->buf)
626 return -ENOMEM;
627
628 memset(info->buf, 0, info->buflen);
629 /* set actual size to buffer size */
630 info->size = info->buflen;
631
632 fi->fh = PTR_TO_UINT64(move_ptr(info));
633 return 0;
634 }
635
636 __lxcfs_fuse_ops int sys_opendir(const char *path, struct fuse_file_info *fi)
637 {
638 __do_free struct file_info *dir_info = NULL;
639 int type = -1;
640
641 if (!liblxcfs_functional())
642 return -EIO;
643
644 if (strcmp(path, "/sys") == 0) {
645 type = LXC_TYPE_SYS;
646 } else if (strcmp(path, "/sys/devices") == 0) {
647 type = LXC_TYPE_SYS_DEVICES;
648 } else if (strcmp(path, "/sys/devices/system") == 0) {
649 type = LXC_TYPE_SYS_DEVICES_SYSTEM;
650 } else if (strcmp(path, "/sys/devices/system/cpu") == 0) {
651 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU;
652 } else if (strncmp(path, "/sys/devices/system/cpu/",
653 STRLITERALLEN("/sys/devices/system/cpu/")) == 0) {
654 int ret;
655 mode_t st_mode;
656
657 ret = get_st_mode(path, &st_mode);
658 if (ret)
659 return ret;
660
661 if (S_ISDIR(st_mode))
662 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR;
663 }
664 if (type == -1)
665 return -ENOENT;
666
667 dir_info = malloc(sizeof(*dir_info));
668 if (!dir_info)
669 return -ENOMEM;
670
671 memset(dir_info, 0, sizeof(*dir_info));
672 dir_info->type = type;
673 dir_info->buf = NULL;
674 dir_info->file = NULL;
675 dir_info->buflen = 0;
676
677 fi->fh = PTR_TO_UINT64(move_ptr(dir_info));
678 return 0;
679 }
680
681 static int sys_access_legacy(const char *path, int mask)
682 {
683 if (strcmp(path, "/sys") == 0 && access(path, R_OK) == 0)
684 return 0;
685
686 if (strcmp(path, "/sys/devices") == 0 && access(path, R_OK) == 0)
687 return 0;
688
689 if (strcmp(path, "/sys/devices/system") == 0 && access(path, R_OK) == 0)
690 return 0;
691
692 if (strcmp(path, "/sys/devices/system/cpu") == 0 &&
693 access(path, R_OK) == 0)
694 return 0;
695
696 /* these are all read-only */
697 if ((mask & ~R_OK) != 0)
698 return -EACCES;
699
700 return 0;
701 }
702
703 __lxcfs_fuse_ops int sys_access(const char *path, int mask)
704 {
705 if (!liblxcfs_functional())
706 return -EIO;
707
708 if (!liblxcfs_can_use_sys_cpu())
709 return sys_access_legacy(path, mask);
710
711 return access(path, mask);
712 }
713
714 static int sys_read_legacy(const char *path, char *buf, size_t size,
715 off_t offset, struct fuse_file_info *fi)
716 {
717 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
718
719 switch (f->type) {
720 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE:
721 if (liblxcfs_functional())
722 return sys_devices_system_cpu_online_read(buf, size, offset, fi);
723
724 return read_file_fuse_with_offset(LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE_PATH,
725 buf, size, offset, f);
726 case LXC_TYPE_SYS_DEVICES:
727 break;
728 case LXC_TYPE_SYS_DEVICES_SYSTEM:
729 break;
730 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU:
731 break;
732 }
733
734 return -EINVAL;
735 }
736
737 __lxcfs_fuse_ops int sys_read(const char *path, char *buf, size_t size,
738 off_t offset, struct fuse_file_info *fi)
739 {
740 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
741
742 if (!liblxcfs_functional())
743 return -EIO;
744
745 if (!liblxcfs_can_use_sys_cpu())
746 return sys_read_legacy(path, buf, size, offset, fi);
747
748 switch (f->type) {
749 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE:
750 return sys_devices_system_cpu_online_read(buf, size, offset, fi);
751 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE:
752 return read_file_fuse_with_offset(path, buf, size, offset, f);
753 }
754
755 return -EINVAL;
756 }