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