1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #ifndef FUSE_USE_VERSION
11 #define FUSE_USE_VERSION 30
14 #ifndef FUSE_USE_VERSION
15 #define FUSE_USE_VERSION 26
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)
24 #define _FILE_OFFSET_BITS 64
26 #define __STDC_FORMAT_MACROS
44 #include <linux/magic.h>
45 #include <linux/sched.h>
46 #include <sys/epoll.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>
56 #include "memory_utils.h"
57 #include "cgroups/cgroup.h"
58 #include "lxcfs_fuse_compat.h"
59 #include "sysfs_fuse.h"
62 static ssize_t
get_max_cpus(char *cpulist
)
65 char *maxcpus
= cpulist
;
68 c1
= strrchr(maxcpus
, ',');
72 c2
= strrchr(maxcpus
, '-');
86 cpus
= strtoul(c1
, NULL
, 0);
93 static void set_bit(unsigned bit
, uint32_t *bitarr
)
95 bitarr
[bit
/ NBITS
] |= (1 << (bit
% NBITS
));
98 static bool is_set(unsigned bit
, uint32_t *bitarr
)
100 return (bitarr
[bit
/ NBITS
] & (1 << (bit
% NBITS
))) != 0;
103 /* Create cpumask from cpulist aka turn:
111 static uint32_t *lxc_cpumask(char *buf
, size_t nbits
)
113 __do_free
uint32_t *bitarr
= NULL
;
117 arrlen
= BITS_TO_LONGS(nbits
);
118 bitarr
= calloc(arrlen
, sizeof(uint32_t));
120 return ret_set_errno(NULL
, ENOMEM
);
122 lxc_iterate_parts(token
, buf
, ",") {
127 start
= strtoul(token
, NULL
, 0);
129 range
= strchr(token
, '-');
131 end
= strtoul(range
+ 1, NULL
, 0);
134 return ret_set_errno(NULL
, EINVAL
);
137 return ret_set_errno(NULL
, EINVAL
);
140 set_bit(start
++, bitarr
);
143 return move_ptr(bitarr
);
146 static int sys_devices_system_cpu_online_read(char *buf
, size_t size
,
148 struct fuse_file_info
*fi
)
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
;
159 ssize_t total_len
= 0;
167 if (offset
> d
->size
)
170 left
= d
->size
- offset
;
171 total_len
= left
> size
? size
: left
;
172 memcpy(buf
, cache
+ offset
, total_len
);
177 initpid
= lookup_initpid_in_store(fc
->pid
);
178 if (initpid
<= 1 || is_shared_pidns(initpid
))
181 cg
= get_pid_cgroup(initpid
, "cpuset");
183 return read_file_fuse("/sys/devices/system/cpu/online", buf
, size
, d
);
184 prune_init_slice(cg
);
186 cpuset
= get_cpuset(cg
);
190 if (cgroup_ops
->can_use_cpuview(cgroup_ops
) && opts
&& opts
->use_cfs
)
196 max_cpus
= max_cpu_count(cg
);
200 total_len
= snprintf(d
->buf
, d
->buflen
, "0-%d\n", max_cpus
- 1);
202 total_len
= snprintf(d
->buf
, d
->buflen
, "0\n");
204 total_len
= snprintf(d
->buf
, d
->buflen
, "%s\n", cpuset
);
206 if (total_len
< 0 || total_len
>= d
->buflen
)
207 return log_error(0, "Failed to write to cache");
209 d
->size
= (int)total_len
;
212 if (total_len
> size
)
215 memcpy(buf
, d
->buf
, total_len
);
220 static int filler_sys_devices_system_cpu(const char *path
, void *buf
,
221 fuse_fill_dir_t filler
)
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();
231 initpid
= lookup_initpid_in_store(fc
->pid
);
232 if (initpid
<= 1 || is_shared_pidns(initpid
))
235 cg
= get_pid_cgroup(initpid
, "cpuset");
238 prune_init_slice(cg
);
240 cpuset
= get_cpuset(cg
);
244 max_cpus
= get_max_cpus(cpuset
);
245 if (max_cpus
< 0 || max_cpus
>= (INT_MAX
- 1))
249 cpumask
= lxc_cpumask(cpuset
, max_cpus
);
253 for (size_t i
= 0; i
< max_cpus
; i
++) {
257 if (!is_set(i
, cpumask
))
260 ret
= snprintf(cpu
, sizeof(cpu
), "cpu%ld", i
);
261 if (ret
< 0 || ret
>= sizeof(cpu
))
264 if (DIR_FILLER(filler
, buf
, cpu
, NULL
, 0) != 0)
272 while ((dirent
= readdir(dir
))) {
273 char *entry
= dirent
->d_name
;
275 if (strlen(entry
) <= 3)
279 /* Don't emit entries we already filtered above. */
283 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
290 static int get_st_mode(const char *path
, mode_t
*mode
)
295 ret
= lstat(path
, &sb
);
303 static off_t
get_sysfile_size(const char *which
)
305 __do_fclose
FILE *f
= NULL
;
306 __do_free
char *line
= NULL
;
308 ssize_t sz
, answer
= 0;
310 f
= fopen(which
, "re");
314 while ((sz
= getline(&line
, &len
, f
)) != -1)
320 static int sys_getattr_legacy(const char *path
, struct stat
*sb
)
324 memset(sb
, 0, sizeof(struct stat
));
325 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
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;
336 if (strcmp(path
, "/sys/devices") == 0) {
337 sb
->st_mode
= S_IFDIR
| 00555;
342 if (strcmp(path
, "/sys/devices/system") == 0) {
343 sb
->st_mode
= S_IFDIR
| 00555;
348 if (strcmp(path
, "/sys/devices/system/cpu") == 0) {
349 sb
->st_mode
= S_IFDIR
| 00555;
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;
364 __lxcfs_fuse_ops
int sys_getattr(const char *path
, struct stat
*sb
)
370 if (!liblxcfs_functional())
373 if (!liblxcfs_can_use_sys_cpu())
374 return sys_getattr_legacy(path
, sb
);
376 memset(sb
, 0, sizeof(struct stat
));
377 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
380 sb
->st_uid
= sb
->st_gid
= 0;
381 sb
->st_atim
= sb
->st_mtim
= sb
->st_ctim
= now
;
383 ret
= get_st_mode(path
, &st_mode
);
387 if (S_ISDIR(st_mode
)) {
388 sb
->st_mode
= st_mode
;
393 if (S_ISREG(st_mode
) || S_ISLNK(st_mode
)) {
394 sb
->st_size
= get_sysfile_size(path
);
395 sb
->st_mode
= st_mode
;
403 __lxcfs_fuse_ops
int sys_release(const char *path
, struct fuse_file_info
*fi
)
405 do_release_file_info(fi
);
409 __lxcfs_fuse_ops
int sys_releasedir(const char *path
, struct fuse_file_info
*fi
)
411 do_release_file_info(fi
);
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
)
418 __do_close
int fd
= -EBADF
;
419 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
421 if (!liblxcfs_functional())
424 if (f
->type
!= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
)
427 fd
= open(path
, O_WRONLY
| O_CLOEXEC
);
431 return pwrite(fd
, buf
, size
, offset
);
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
)
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)
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)
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)
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)
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
)
477 __do_closedir
DIR *dir
= NULL
;
478 struct dirent
*dirent
;
479 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
481 if (!liblxcfs_functional())
484 if (!liblxcfs_can_use_sys_cpu())
485 return sys_readdir_legacy(path
, buf
, filler
, offset
, fi
);
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
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)
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)
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)
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)
526 return filler_sys_devices_system_cpu(path
, buf
, filler
);
527 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
: {
532 while ((dirent
= readdir(dir
))) {
533 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
544 __lxcfs_fuse_ops
int sys_readlink(const char *path
, char *buf
, size_t size
)
546 int ret
= readlink(path
, buf
, size
);
548 if (!liblxcfs_functional())
561 static int sys_open_legacy(const char *path
, struct fuse_file_info
*fi
)
563 __do_free
struct file_info
*info
= NULL
;
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
;
577 info
= malloc(sizeof(*info
));
581 memset(info
, 0, sizeof(*info
));
584 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
586 info
->buf
= malloc(info
->buflen
);
590 memset(info
->buf
, 0, info
->buflen
);
591 /* set actual size to buffer size */
592 info
->size
= info
->buflen
;
594 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
598 __lxcfs_fuse_ops
int sys_open(const char *path
, struct fuse_file_info
*fi
)
600 __do_free
struct file_info
*info
= NULL
;
603 if (!liblxcfs_functional())
606 if (!liblxcfs_can_use_sys_cpu())
607 return sys_open_legacy(path
, fi
);
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) {
616 ret
= get_st_mode(path
, &st_mode
);
620 if (S_ISREG(st_mode
))
621 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
;
626 info
= malloc(sizeof(*info
));
630 memset(info
, 0, sizeof(*info
));
633 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
635 info
->buf
= malloc(info
->buflen
);
639 memset(info
->buf
, 0, info
->buflen
);
640 /* set actual size to buffer size */
641 info
->size
= info
->buflen
;
643 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
647 __lxcfs_fuse_ops
int sys_opendir(const char *path
, struct fuse_file_info
*fi
)
649 __do_free
struct file_info
*dir_info
= NULL
;
652 if (!liblxcfs_functional())
655 if (strcmp(path
, "/sys") == 0) {
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) {
668 ret
= get_st_mode(path
, &st_mode
);
672 if (S_ISDIR(st_mode
))
673 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
;
678 dir_info
= malloc(sizeof(*dir_info
));
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;
688 fi
->fh
= PTR_TO_UINT64(move_ptr(dir_info
));
692 static int sys_access_legacy(const char *path
, int mask
)
694 if (strcmp(path
, "/sys") == 0 && access(path
, R_OK
) == 0)
697 if (strcmp(path
, "/sys/devices") == 0 && access(path
, R_OK
) == 0)
700 if (strcmp(path
, "/sys/devices/system") == 0 && access(path
, R_OK
) == 0)
703 if (strcmp(path
, "/sys/devices/system/cpu") == 0 &&
704 access(path
, R_OK
) == 0)
707 /* these are all read-only */
708 if ((mask
& ~R_OK
) != 0)
714 __lxcfs_fuse_ops
int sys_access(const char *path
, int mask
)
716 if (!liblxcfs_functional())
719 if (!liblxcfs_can_use_sys_cpu())
720 return sys_access_legacy(path
, mask
);
722 return access(path
, mask
);
725 static int sys_read_legacy(const char *path
, char *buf
, size_t size
,
726 off_t offset
, struct fuse_file_info
*fi
)
728 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
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
);
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
:
739 case LXC_TYPE_SYS_DEVICES_SYSTEM
:
741 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
:
748 __lxcfs_fuse_ops
int sys_read(const char *path
, char *buf
, size_t size
,
749 off_t offset
, struct fuse_file_info
*fi
)
751 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
753 if (!liblxcfs_functional())
756 if (!liblxcfs_can_use_sys_cpu())
757 return sys_read_legacy(path
, buf
, size
, offset
, fi
);
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
);