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