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 __lxcfs_fuse_ops
int sys_getattr(const char *path
, struct stat
*sb
)
326 if (!liblxcfs_functional())
329 memset(sb
, 0, sizeof(struct stat
));
330 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
333 sb
->st_uid
= sb
->st_gid
= 0;
334 sb
->st_atim
= sb
->st_mtim
= sb
->st_ctim
= now
;
336 ret
= get_st_mode(path
, &st_mode
);
340 if (S_ISDIR(st_mode
)) {
341 sb
->st_mode
= st_mode
;
346 if (S_ISREG(st_mode
) || S_ISLNK(st_mode
)) {
347 sb
->st_size
= get_sysfile_size(path
);
348 sb
->st_mode
= st_mode
;
356 __lxcfs_fuse_ops
int sys_write(const char *path
, const char *buf
,
357 size_t size
, off_t offset
,
358 struct fuse_file_info
*fi
)
360 __do_close
int fd
= -EBADF
;
361 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
363 if (!liblxcfs_functional())
366 if (f
->type
!= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
)
369 fd
= open(path
, O_WRONLY
| O_CLOEXEC
);
373 return pwrite(fd
, buf
, size
, offset
);
376 __lxcfs_fuse_ops
int sys_readdir(const char *path
, void *buf
,
377 fuse_fill_dir_t filler
, off_t offset
,
378 struct fuse_file_info
*fi
)
380 __do_closedir
DIR *dir
= NULL
;
381 struct dirent
*dirent
;
382 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
384 if (!liblxcfs_functional())
389 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
390 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
391 DIR_FILLER(filler
, buf
, "devices", NULL
, 0) != 0)
396 case LXC_TYPE_SYS_DEVICES
: {
397 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
398 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
399 DIR_FILLER(filler
, buf
, "system", NULL
, 0) != 0)
404 case LXC_TYPE_SYS_DEVICES_SYSTEM
: {
405 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
406 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
407 DIR_FILLER(filler
, buf
, "cpu", NULL
, 0) != 0)
412 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
:
413 return filler_sys_devices_system_cpu(path
, buf
, filler
);
414 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
: {
419 while ((dirent
= readdir(dir
))) {
420 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
431 __lxcfs_fuse_ops
int sys_readlink(const char *path
, char *buf
, size_t size
)
433 int ret
= readlink(path
, buf
, size
);
435 if (!liblxcfs_functional())
447 __lxcfs_fuse_ops
int sys_open(const char *path
, struct fuse_file_info
*fi
)
449 __do_free
struct file_info
*info
= NULL
;
452 if (!liblxcfs_functional())
455 if (strcmp(path
, "/sys/devices/system/cpu/online") == 0) {
456 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE
;
457 } else if (strncmp(path
, "/sys/devices/system/cpu/",
458 STRLITERALLEN("/sys/devices/system/cpu/")) == 0) {
462 ret
= get_st_mode(path
, &st_mode
);
466 if (S_ISREG(st_mode
))
467 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
;
472 info
= malloc(sizeof(*info
));
476 memset(info
, 0, sizeof(*info
));
479 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
481 info
->buf
= malloc(info
->buflen
);
485 memset(info
->buf
, 0, info
->buflen
);
486 /* set actual size to buffer size */
487 info
->size
= info
->buflen
;
489 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
493 __lxcfs_fuse_ops
int sys_opendir(const char *path
, struct fuse_file_info
*fi
)
495 __do_free
struct file_info
*dir_info
= NULL
;
498 if (!liblxcfs_functional())
501 if (strcmp(path
, "/sys") == 0) {
503 } else if (strcmp(path
, "/sys/devices") == 0) {
504 type
= LXC_TYPE_SYS_DEVICES
;
505 } else if (strcmp(path
, "/sys/devices/system") == 0) {
506 type
= LXC_TYPE_SYS_DEVICES_SYSTEM
;
507 } else if (strcmp(path
, "/sys/devices/system/cpu") == 0) {
508 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
;
509 } else if (strncmp(path
, "/sys/devices/system/cpu/",
510 STRLITERALLEN("/sys/devices/system/cpu/")) == 0) {
514 ret
= get_st_mode(path
, &st_mode
);
518 if (S_ISDIR(st_mode
))
519 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
;
524 dir_info
= malloc(sizeof(*dir_info
));
528 memset(dir_info
, 0, sizeof(*dir_info
));
529 dir_info
->type
= type
;
530 dir_info
->buf
= NULL
;
531 dir_info
->file
= NULL
;
532 dir_info
->buflen
= 0;
534 fi
->fh
= PTR_TO_UINT64(move_ptr(dir_info
));
538 __lxcfs_fuse_ops
int sys_access(const char *path
, int mask
)
540 if (!liblxcfs_functional())
543 return access(path
, mask
);
546 __lxcfs_fuse_ops
int sys_release(const char *path
, struct fuse_file_info
*fi
)
548 do_release_file_info(fi
);
552 __lxcfs_fuse_ops
int sys_releasedir(const char *path
, struct fuse_file_info
*fi
)
554 do_release_file_info(fi
);
558 __lxcfs_fuse_ops
int sys_read(const char *path
, char *buf
, size_t size
,
559 off_t offset
, struct fuse_file_info
*fi
)
561 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
563 if (!liblxcfs_functional())
567 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE
:
568 return sys_devices_system_cpu_online_read(buf
, size
, offset
, fi
);
569 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
:
570 return read_file_fuse_with_offset(path
, buf
, size
, offset
, f
);