1 /* SPDX-License-Identifier: LGPL-2.1+ */
21 #include <linux/magic.h>
22 #include <linux/sched.h>
23 #include <sys/epoll.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>
32 #include "sysfs_fuse.h"
35 #include "memory_utils.h"
36 #include "cgroups/cgroup.h"
37 #include "lxcfs_fuse_compat.h"
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)
45 static ssize_t
get_max_cpus(char *cpulist
)
48 char *maxcpus
= cpulist
;
51 c1
= strrchr(maxcpus
, ',');
55 c2
= strrchr(maxcpus
, '-');
69 cpus
= strtoul(c1
, NULL
, 0);
76 static void set_bit(unsigned bit
, uint32_t *bitarr
)
78 bitarr
[bit
/ NBITS
] |= (1 << (bit
% NBITS
));
81 static bool is_set(unsigned bit
, uint32_t *bitarr
)
83 return (bitarr
[bit
/ NBITS
] & (1 << (bit
% NBITS
))) != 0;
86 /* Create cpumask from cpulist aka turn:
94 static uint32_t *lxc_cpumask(char *buf
, size_t nbits
)
96 __do_free
uint32_t *bitarr
= NULL
;
100 arrlen
= BITS_TO_LONGS(nbits
);
101 bitarr
= calloc(arrlen
, sizeof(uint32_t));
103 return ret_set_errno(NULL
, ENOMEM
);
105 lxc_iterate_parts(token
, buf
, ",") {
110 start
= strtoul(token
, NULL
, 0);
112 range
= strchr(token
, '-');
114 end
= strtoul(range
+ 1, NULL
, 0);
117 return ret_set_errno(NULL
, EINVAL
);
120 return ret_set_errno(NULL
, EINVAL
);
123 set_bit(start
++, bitarr
);
126 return move_ptr(bitarr
);
129 static int sys_devices_system_cpu_online_read(char *buf
, size_t size
,
131 struct fuse_file_info
*fi
)
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
;
142 ssize_t total_len
= 0;
150 if (offset
> d
->size
)
153 left
= d
->size
- offset
;
154 total_len
= left
> size
? size
: left
;
155 memcpy(buf
, cache
+ offset
, total_len
);
160 initpid
= lookup_initpid_in_store(fc
->pid
);
161 if (initpid
<= 1 || is_shared_pidns(initpid
))
164 cg
= get_pid_cgroup(initpid
, "cpuset");
166 return read_file_fuse("/sys/devices/system/cpu/online", buf
, size
, d
);
167 prune_init_slice(cg
);
169 cpuset
= get_cpuset(cg
);
173 if (cgroup_ops
->can_use_cpuview(cgroup_ops
) && opts
&& opts
->use_cfs
)
179 max_cpus
= max_cpu_count(cg
);
183 total_len
= snprintf(d
->buf
, d
->buflen
, "0-%d\n", max_cpus
- 1);
185 total_len
= snprintf(d
->buf
, d
->buflen
, "0\n");
187 total_len
= snprintf(d
->buf
, d
->buflen
, "%s\n", cpuset
);
189 if (total_len
< 0 || total_len
>= d
->buflen
)
190 return log_error(0, "Failed to write to cache");
192 d
->size
= (int)total_len
;
195 if ((size_t)total_len
> size
)
198 memcpy(buf
, d
->buf
, total_len
);
203 static int filler_sys_devices_system_cpu(const char *path
, void *buf
,
204 fuse_fill_dir_t filler
)
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();
214 initpid
= lookup_initpid_in_store(fc
->pid
);
215 if (initpid
<= 1 || is_shared_pidns(initpid
))
218 cg
= get_pid_cgroup(initpid
, "cpuset");
221 prune_init_slice(cg
);
223 cpuset
= get_cpuset(cg
);
227 max_cpus
= get_max_cpus(cpuset
);
228 if (max_cpus
< 0 || max_cpus
>= (INT_MAX
- 1))
232 cpumask
= lxc_cpumask(cpuset
, max_cpus
);
236 for (ssize_t i
= 0; i
< max_cpus
; i
++) {
240 if (!is_set(i
, cpumask
))
243 ret
= snprintf(cpu
, sizeof(cpu
), "cpu%ld", i
);
244 if (ret
< 0 || (size_t)ret
>= sizeof(cpu
))
247 if (DIR_FILLER(filler
, buf
, cpu
, NULL
, 0) != 0)
255 while ((dirent
= readdir(dir
))) {
256 char *entry
= dirent
->d_name
;
258 if (strlen(entry
) <= 3)
262 /* Don't emit entries we already filtered above. */
266 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
273 static int get_st_mode(const char *path
, mode_t
*mode
)
278 ret
= lstat(path
, &sb
);
286 static off_t
get_sysfile_size(const char *which
)
288 __do_fclose
FILE *f
= NULL
;
289 __do_free
char *line
= NULL
;
291 ssize_t sz
, answer
= 0;
293 f
= fopen(which
, "re");
297 while ((sz
= getline(&line
, &len
, f
)) != -1)
303 static int sys_getattr_legacy(const char *path
, struct stat
*sb
)
307 memset(sb
, 0, sizeof(struct stat
));
308 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
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;
319 if (strcmp(path
, "/sys/devices") == 0) {
320 sb
->st_mode
= S_IFDIR
| 00555;
325 if (strcmp(path
, "/sys/devices/system") == 0) {
326 sb
->st_mode
= S_IFDIR
| 00555;
331 if (strcmp(path
, "/sys/devices/system/cpu") == 0) {
332 sb
->st_mode
= S_IFDIR
| 00555;
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;
347 __lxcfs_fuse_ops
int sys_getattr(const char *path
, struct stat
*sb
)
353 if (!liblxcfs_functional())
356 if (!liblxcfs_can_use_sys_cpu())
357 return sys_getattr_legacy(path
, sb
);
359 memset(sb
, 0, sizeof(struct stat
));
360 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
363 sb
->st_uid
= sb
->st_gid
= 0;
364 sb
->st_atim
= sb
->st_mtim
= sb
->st_ctim
= now
;
366 ret
= get_st_mode(path
, &st_mode
);
370 if (S_ISDIR(st_mode
)) {
371 sb
->st_mode
= st_mode
;
376 if (S_ISREG(st_mode
) || S_ISLNK(st_mode
)) {
377 sb
->st_size
= get_sysfile_size(path
);
378 sb
->st_mode
= st_mode
;
386 __lxcfs_fuse_ops
int sys_release(const char *path
, struct fuse_file_info
*fi
)
388 do_release_file_info(fi
);
392 __lxcfs_fuse_ops
int sys_releasedir(const char *path
, struct fuse_file_info
*fi
)
394 do_release_file_info(fi
);
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
)
401 __do_close
int fd
= -EBADF
;
402 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
404 if (!liblxcfs_functional())
407 if (f
->type
!= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
)
410 fd
= open(path
, O_WRONLY
| O_CLOEXEC
);
414 return pwrite(fd
, buf
, size
, offset
);
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
)
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)
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)
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)
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)
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
)
460 __do_closedir
DIR *dir
= NULL
;
461 struct dirent
*dirent
;
462 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
464 if (!liblxcfs_functional())
467 if (!liblxcfs_can_use_sys_cpu())
468 return sys_readdir_legacy(path
, buf
, filler
, offset
, fi
);
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
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)
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)
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)
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)
509 return filler_sys_devices_system_cpu(path
, buf
, filler
);
510 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
: {
515 while ((dirent
= readdir(dir
))) {
516 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
527 __lxcfs_fuse_ops
int sys_readlink(const char *path
, char *buf
, size_t size
)
531 if (!liblxcfs_functional())
534 ret
= readlink(path
, buf
, size
);
538 if ((size_t)ret
> size
)
546 static int sys_open_legacy(const char *path
, struct fuse_file_info
*fi
)
548 __do_free
struct file_info
*info
= NULL
;
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
;
562 info
= malloc(sizeof(*info
));
566 memset(info
, 0, sizeof(*info
));
569 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
571 info
->buf
= malloc(info
->buflen
);
575 memset(info
->buf
, 0, info
->buflen
);
576 /* set actual size to buffer size */
577 info
->size
= info
->buflen
;
579 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
583 __lxcfs_fuse_ops
int sys_open(const char *path
, struct fuse_file_info
*fi
)
585 __do_free
struct file_info
*info
= NULL
;
588 if (!liblxcfs_functional())
591 if (!liblxcfs_can_use_sys_cpu())
592 return sys_open_legacy(path
, fi
);
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) {
601 ret
= get_st_mode(path
, &st_mode
);
605 if (S_ISREG(st_mode
))
606 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
;
611 info
= malloc(sizeof(*info
));
615 memset(info
, 0, sizeof(*info
));
618 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
620 info
->buf
= malloc(info
->buflen
);
624 memset(info
->buf
, 0, info
->buflen
);
625 /* set actual size to buffer size */
626 info
->size
= info
->buflen
;
628 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
632 __lxcfs_fuse_ops
int sys_opendir(const char *path
, struct fuse_file_info
*fi
)
634 __do_free
struct file_info
*dir_info
= NULL
;
637 if (!liblxcfs_functional())
640 if (strcmp(path
, "/sys") == 0) {
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) {
653 ret
= get_st_mode(path
, &st_mode
);
657 if (S_ISDIR(st_mode
))
658 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
;
663 dir_info
= malloc(sizeof(*dir_info
));
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;
673 fi
->fh
= PTR_TO_UINT64(move_ptr(dir_info
));
677 static int sys_access_legacy(const char *path
, int mask
)
679 if (strcmp(path
, "/sys") == 0 && access(path
, R_OK
) == 0)
682 if (strcmp(path
, "/sys/devices") == 0 && access(path
, R_OK
) == 0)
685 if (strcmp(path
, "/sys/devices/system") == 0 && access(path
, R_OK
) == 0)
688 if (strcmp(path
, "/sys/devices/system/cpu") == 0 &&
689 access(path
, R_OK
) == 0)
692 /* these are all read-only */
693 if ((mask
& ~R_OK
) != 0)
699 __lxcfs_fuse_ops
int sys_access(const char *path
, int mask
)
701 if (!liblxcfs_functional())
704 if (!liblxcfs_can_use_sys_cpu())
705 return sys_access_legacy(path
, mask
);
707 return access(path
, mask
);
710 static int sys_read_legacy(const char *path
, char *buf
, size_t size
,
711 off_t offset
, struct fuse_file_info
*fi
)
713 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
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
);
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
:
724 case LXC_TYPE_SYS_DEVICES_SYSTEM
:
726 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
:
733 __lxcfs_fuse_ops
int sys_read(const char *path
, char *buf
, size_t size
,
734 off_t offset
, struct fuse_file_info
*fi
)
736 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
738 if (!liblxcfs_functional())
741 if (!liblxcfs_can_use_sys_cpu())
742 return sys_read_legacy(path
, buf
, size
, offset
, fi
);
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
);