1 /* SPDX-License-Identifier: LGPL-2.1+ */
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)
14 #define __STDC_FORMAT_MACROS
32 #include <linux/magic.h>
33 #include <linux/sched.h>
34 #include <sys/epoll.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>
43 #include "sysfs_fuse.h"
46 #include "memory_utils.h"
47 #include "cgroups/cgroup.h"
48 #include "lxcfs_fuse_compat.h"
51 static ssize_t
get_max_cpus(char *cpulist
)
54 char *maxcpus
= cpulist
;
57 c1
= strrchr(maxcpus
, ',');
61 c2
= strrchr(maxcpus
, '-');
75 cpus
= strtoul(c1
, NULL
, 0);
82 static void set_bit(unsigned bit
, uint32_t *bitarr
)
84 bitarr
[bit
/ NBITS
] |= (1 << (bit
% NBITS
));
87 static bool is_set(unsigned bit
, uint32_t *bitarr
)
89 return (bitarr
[bit
/ NBITS
] & (1 << (bit
% NBITS
))) != 0;
92 /* Create cpumask from cpulist aka turn:
100 static uint32_t *lxc_cpumask(char *buf
, size_t nbits
)
102 __do_free
uint32_t *bitarr
= NULL
;
106 arrlen
= BITS_TO_LONGS(nbits
);
107 bitarr
= calloc(arrlen
, sizeof(uint32_t));
109 return ret_set_errno(NULL
, ENOMEM
);
111 lxc_iterate_parts(token
, buf
, ",") {
116 start
= strtoul(token
, NULL
, 0);
118 range
= strchr(token
, '-');
120 end
= strtoul(range
+ 1, NULL
, 0);
123 return ret_set_errno(NULL
, EINVAL
);
126 return ret_set_errno(NULL
, EINVAL
);
129 set_bit(start
++, bitarr
);
132 return move_ptr(bitarr
);
135 static int sys_devices_system_cpu_online_read(char *buf
, size_t size
,
137 struct fuse_file_info
*fi
)
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
;
148 ssize_t total_len
= 0;
156 if (offset
> d
->size
)
159 left
= d
->size
- offset
;
160 total_len
= left
> size
? size
: left
;
161 memcpy(buf
, cache
+ offset
, total_len
);
166 initpid
= lookup_initpid_in_store(fc
->pid
);
167 if (initpid
<= 1 || is_shared_pidns(initpid
))
170 cg
= get_pid_cgroup(initpid
, "cpuset");
172 return read_file_fuse("/sys/devices/system/cpu/online", buf
, size
, d
);
173 prune_init_slice(cg
);
175 cpuset
= get_cpuset(cg
);
179 if (cgroup_ops
->can_use_cpuview(cgroup_ops
) && opts
&& opts
->use_cfs
)
185 max_cpus
= max_cpu_count(cg
);
189 total_len
= snprintf(d
->buf
, d
->buflen
, "0-%d\n", max_cpus
- 1);
191 total_len
= snprintf(d
->buf
, d
->buflen
, "0\n");
193 total_len
= snprintf(d
->buf
, d
->buflen
, "%s\n", cpuset
);
195 if (total_len
< 0 || total_len
>= d
->buflen
)
196 return log_error(0, "Failed to write to cache");
198 d
->size
= (int)total_len
;
201 if (total_len
> size
)
204 memcpy(buf
, d
->buf
, total_len
);
209 static int filler_sys_devices_system_cpu(const char *path
, void *buf
,
210 fuse_fill_dir_t filler
)
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();
220 initpid
= lookup_initpid_in_store(fc
->pid
);
221 if (initpid
<= 1 || is_shared_pidns(initpid
))
224 cg
= get_pid_cgroup(initpid
, "cpuset");
227 prune_init_slice(cg
);
229 cpuset
= get_cpuset(cg
);
233 max_cpus
= get_max_cpus(cpuset
);
234 if (max_cpus
< 0 || max_cpus
>= (INT_MAX
- 1))
238 cpumask
= lxc_cpumask(cpuset
, max_cpus
);
242 for (size_t i
= 0; i
< max_cpus
; i
++) {
246 if (!is_set(i
, cpumask
))
249 ret
= snprintf(cpu
, sizeof(cpu
), "cpu%ld", i
);
250 if (ret
< 0 || ret
>= sizeof(cpu
))
253 if (DIR_FILLER(filler
, buf
, cpu
, NULL
, 0) != 0)
261 while ((dirent
= readdir(dir
))) {
262 char *entry
= dirent
->d_name
;
264 if (strlen(entry
) <= 3)
268 /* Don't emit entries we already filtered above. */
272 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
279 static int get_st_mode(const char *path
, mode_t
*mode
)
284 ret
= lstat(path
, &sb
);
292 static off_t
get_sysfile_size(const char *which
)
294 __do_fclose
FILE *f
= NULL
;
295 __do_free
char *line
= NULL
;
297 ssize_t sz
, answer
= 0;
299 f
= fopen(which
, "re");
303 while ((sz
= getline(&line
, &len
, f
)) != -1)
309 static int sys_getattr_legacy(const char *path
, struct stat
*sb
)
313 memset(sb
, 0, sizeof(struct stat
));
314 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
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;
325 if (strcmp(path
, "/sys/devices") == 0) {
326 sb
->st_mode
= S_IFDIR
| 00555;
331 if (strcmp(path
, "/sys/devices/system") == 0) {
332 sb
->st_mode
= S_IFDIR
| 00555;
337 if (strcmp(path
, "/sys/devices/system/cpu") == 0) {
338 sb
->st_mode
= S_IFDIR
| 00555;
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;
353 __lxcfs_fuse_ops
int sys_getattr(const char *path
, struct stat
*sb
)
359 if (!liblxcfs_functional())
362 if (!liblxcfs_can_use_sys_cpu())
363 return sys_getattr_legacy(path
, sb
);
365 memset(sb
, 0, sizeof(struct stat
));
366 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
369 sb
->st_uid
= sb
->st_gid
= 0;
370 sb
->st_atim
= sb
->st_mtim
= sb
->st_ctim
= now
;
372 ret
= get_st_mode(path
, &st_mode
);
376 if (S_ISDIR(st_mode
)) {
377 sb
->st_mode
= st_mode
;
382 if (S_ISREG(st_mode
) || S_ISLNK(st_mode
)) {
383 sb
->st_size
= get_sysfile_size(path
);
384 sb
->st_mode
= st_mode
;
392 __lxcfs_fuse_ops
int sys_release(const char *path
, struct fuse_file_info
*fi
)
394 do_release_file_info(fi
);
398 __lxcfs_fuse_ops
int sys_releasedir(const char *path
, struct fuse_file_info
*fi
)
400 do_release_file_info(fi
);
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
)
407 __do_close
int fd
= -EBADF
;
408 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
410 if (!liblxcfs_functional())
413 if (f
->type
!= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
)
416 fd
= open(path
, O_WRONLY
| O_CLOEXEC
);
420 return pwrite(fd
, buf
, size
, offset
);
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
)
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)
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)
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)
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)
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
)
466 __do_closedir
DIR *dir
= NULL
;
467 struct dirent
*dirent
;
468 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
470 if (!liblxcfs_functional())
473 if (!liblxcfs_can_use_sys_cpu())
474 return sys_readdir_legacy(path
, buf
, filler
, offset
, fi
);
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
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)
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)
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)
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)
515 return filler_sys_devices_system_cpu(path
, buf
, filler
);
516 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
: {
521 while ((dirent
= readdir(dir
))) {
522 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
533 __lxcfs_fuse_ops
int sys_readlink(const char *path
, char *buf
, size_t size
)
535 int ret
= readlink(path
, buf
, size
);
537 if (!liblxcfs_functional())
550 static int sys_open_legacy(const char *path
, struct fuse_file_info
*fi
)
552 __do_free
struct file_info
*info
= NULL
;
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
;
566 info
= malloc(sizeof(*info
));
570 memset(info
, 0, sizeof(*info
));
573 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
575 info
->buf
= malloc(info
->buflen
);
579 memset(info
->buf
, 0, info
->buflen
);
580 /* set actual size to buffer size */
581 info
->size
= info
->buflen
;
583 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
587 __lxcfs_fuse_ops
int sys_open(const char *path
, struct fuse_file_info
*fi
)
589 __do_free
struct file_info
*info
= NULL
;
592 if (!liblxcfs_functional())
595 if (!liblxcfs_can_use_sys_cpu())
596 return sys_open_legacy(path
, fi
);
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) {
605 ret
= get_st_mode(path
, &st_mode
);
609 if (S_ISREG(st_mode
))
610 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
;
615 info
= malloc(sizeof(*info
));
619 memset(info
, 0, sizeof(*info
));
622 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
624 info
->buf
= malloc(info
->buflen
);
628 memset(info
->buf
, 0, info
->buflen
);
629 /* set actual size to buffer size */
630 info
->size
= info
->buflen
;
632 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
636 __lxcfs_fuse_ops
int sys_opendir(const char *path
, struct fuse_file_info
*fi
)
638 __do_free
struct file_info
*dir_info
= NULL
;
641 if (!liblxcfs_functional())
644 if (strcmp(path
, "/sys") == 0) {
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) {
657 ret
= get_st_mode(path
, &st_mode
);
661 if (S_ISDIR(st_mode
))
662 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
;
667 dir_info
= malloc(sizeof(*dir_info
));
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;
677 fi
->fh
= PTR_TO_UINT64(move_ptr(dir_info
));
681 static int sys_access_legacy(const char *path
, int mask
)
683 if (strcmp(path
, "/sys") == 0 && access(path
, R_OK
) == 0)
686 if (strcmp(path
, "/sys/devices") == 0 && access(path
, R_OK
) == 0)
689 if (strcmp(path
, "/sys/devices/system") == 0 && access(path
, R_OK
) == 0)
692 if (strcmp(path
, "/sys/devices/system/cpu") == 0 &&
693 access(path
, R_OK
) == 0)
696 /* these are all read-only */
697 if ((mask
& ~R_OK
) != 0)
703 __lxcfs_fuse_ops
int sys_access(const char *path
, int mask
)
705 if (!liblxcfs_functional())
708 if (!liblxcfs_can_use_sys_cpu())
709 return sys_access_legacy(path
, mask
);
711 return access(path
, mask
);
714 static int sys_read_legacy(const char *path
, char *buf
, size_t size
,
715 off_t offset
, struct fuse_file_info
*fi
)
717 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
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
);
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
:
728 case LXC_TYPE_SYS_DEVICES_SYSTEM
:
730 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
:
737 __lxcfs_fuse_ops
int sys_read(const char *path
, char *buf
, size_t size
,
738 off_t offset
, struct fuse_file_info
*fi
)
740 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
742 if (!liblxcfs_functional())
745 if (!liblxcfs_can_use_sys_cpu())
746 return sys_read_legacy(path
, buf
, size
, offset
, fi
);
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
);