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