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
43 #include <linux/magic.h>
44 #include <linux/sched.h>
45 #include <sys/epoll.h>
47 #include <sys/mount.h>
48 #include <sys/param.h>
49 #include <sys/socket.h>
50 #include <sys/syscall.h>
51 #include <sys/sysinfo.h>
55 #include "memory_utils.h"
56 #include "cgroups/cgroup.h"
57 #include "lxcfs_fuse_compat.h"
58 #include "sysfs_fuse.h"
61 static ssize_t
get_max_cpus(char *cpulist
)
64 char *maxcpus
= cpulist
;
67 c1
= strrchr(maxcpus
, ',');
71 c2
= strrchr(maxcpus
, '-');
85 cpus
= strtoul(c1
, NULL
, 0);
92 static void set_bit(unsigned bit
, uint32_t *bitarr
)
94 bitarr
[bit
/ NBITS
] |= (1 << (bit
% NBITS
));
97 static bool is_set(unsigned bit
, uint32_t *bitarr
)
99 return (bitarr
[bit
/ NBITS
] & (1 << (bit
% NBITS
))) != 0;
102 /* Create cpumask from cpulist aka turn:
110 static uint32_t *lxc_cpumask(char *buf
, size_t nbits
)
112 __do_free
uint32_t *bitarr
= NULL
;
116 arrlen
= BITS_TO_LONGS(nbits
);
117 bitarr
= calloc(arrlen
, sizeof(uint32_t));
119 return ret_set_errno(NULL
, ENOMEM
);
121 lxc_iterate_parts(token
, buf
, ",") {
126 start
= strtoul(token
, NULL
, 0);
128 range
= strchr(token
, '-');
130 end
= strtoul(range
+ 1, NULL
, 0);
133 return ret_set_errno(NULL
, EINVAL
);
136 return ret_set_errno(NULL
, EINVAL
);
139 set_bit(start
++, bitarr
);
142 return move_ptr(bitarr
);
145 static int sys_devices_system_cpu_online_read(char *buf
, size_t size
,
147 struct fuse_file_info
*fi
)
149 __do_free
char *cg
= NULL
, *cpuset
= NULL
;
150 struct fuse_context
*fc
= fuse_get_context();
151 struct lxcfs_opts
*opts
= (struct lxcfs_opts
*)fc
->private_data
;
152 struct file_info
*d
= INTTYPE_TO_PTR(fi
->fh
);
153 char *cache
= d
->buf
;
158 ssize_t total_len
= 0;
166 if (offset
> d
->size
)
169 left
= d
->size
- offset
;
170 total_len
= left
> size
? size
: left
;
171 memcpy(buf
, cache
+ offset
, total_len
);
176 initpid
= lookup_initpid_in_store(fc
->pid
);
177 if (initpid
<= 1 || is_shared_pidns(initpid
))
180 cg
= get_pid_cgroup(initpid
, "cpuset");
182 return read_file_fuse("/sys/devices/system/cpu/online", buf
, size
, d
);
183 prune_init_slice(cg
);
185 cpuset
= get_cpuset(cg
);
189 if (cgroup_ops
->can_use_cpuview(cgroup_ops
) && opts
&& opts
->use_cfs
)
195 max_cpus
= max_cpu_count(cg
);
199 total_len
= snprintf(d
->buf
, d
->buflen
, "0-%d\n", max_cpus
- 1);
201 total_len
= snprintf(d
->buf
, d
->buflen
, "0\n");
203 total_len
= snprintf(d
->buf
, d
->buflen
, "%s\n", cpuset
);
205 if (total_len
< 0 || total_len
>= d
->buflen
)
206 return log_error(0, "Failed to write to cache");
208 d
->size
= (int)total_len
;
211 if (total_len
> size
)
214 memcpy(buf
, d
->buf
, total_len
);
219 static int filler_sys_devices_system_cpu(const char *path
, void *buf
,
220 fuse_fill_dir_t filler
)
222 __do_free
uint32_t *cpumask
= NULL
;
223 __do_free
char *cg
= NULL
, *cpuset
= NULL
;
224 __do_closedir
DIR *dir
= NULL
;
225 struct dirent
*dirent
;
226 struct fuse_context
*fc
= fuse_get_context();
232 initpid
= lookup_initpid_in_store(fc
->pid
);
233 if (initpid
<= 1 || is_shared_pidns(initpid
))
236 cg
= get_pid_cgroup(initpid
, "cpuset");
239 prune_init_slice(cg
);
241 cpuset
= get_cpuset(cg
);
245 max_cpus
= get_max_cpus(cpuset
);
246 if (max_cpus
< 0 || max_cpus
>= (INT_MAX
- 1))
250 cpumask
= lxc_cpumask(cpuset
, max_cpus
);
254 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 len
= strlen(dirent
->d_name
);
274 if (strncmp(dirent
->d_name
, "cpu", 3) == 0 &&
275 dirent
->d_name
[len
- 1] >= '0' &&
276 dirent
->d_name
[len
- 1] <= '9')
279 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
286 static mode_t
get_st_mode(const char *path
)
291 ret
= lstat(path
, &sb
);
298 static off_t
get_sysfile_size(const char *which
)
300 __do_fclose
FILE *f
= NULL
;
301 __do_free
char *line
= NULL
;
303 ssize_t sz
, answer
= 0;
305 f
= fopen(which
, "re");
309 while ((sz
= getline(&line
, &len
, f
)) != -1)
315 __lxcfs_fuse_ops
int sys_getattr(const char *path
, struct stat
*sb
)
320 if (!liblxcfs_functional())
323 memset(sb
, 0, sizeof(struct stat
));
324 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0)
327 sb
->st_uid
= sb
->st_gid
= 0;
328 sb
->st_atim
= sb
->st_mtim
= sb
->st_ctim
= now
;
330 st_mode
= get_st_mode(path
);
334 if (S_ISDIR(st_mode
)) {
335 sb
->st_mode
= st_mode
;
340 if (S_ISREG(st_mode
) || S_ISLNK(st_mode
)) {
341 sb
->st_size
= get_sysfile_size(path
);
342 sb
->st_mode
= st_mode
;
350 __lxcfs_fuse_ops
int sys_write(const char *path
, const char *buf
,
351 size_t size
, off_t offset
,
352 struct fuse_file_info
*fi
)
354 __do_close
int fd
= -EBADF
;
355 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
357 if (!liblxcfs_functional())
360 if (f
->type
!= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
)
363 fd
= open(path
, O_WRONLY
| O_CLOEXEC
);
367 return pwrite(fd
, buf
, size
, offset
);
370 __lxcfs_fuse_ops
int sys_readdir(const char *path
, void *buf
,
371 fuse_fill_dir_t filler
, off_t offset
,
372 struct fuse_file_info
*fi
)
374 __do_closedir
DIR *dir
= NULL
;
375 struct dirent
*dirent
;
376 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
378 if (!liblxcfs_functional())
383 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
384 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
385 DIR_FILLER(filler
, buf
, "devices", NULL
, 0) != 0)
390 case LXC_TYPE_SYS_DEVICES
: {
391 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
392 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
393 DIR_FILLER(filler
, buf
, "system", NULL
, 0) != 0)
398 case LXC_TYPE_SYS_DEVICES_SYSTEM
: {
399 if (DIR_FILLER(filler
, buf
, ".", NULL
, 0) != 0 ||
400 DIR_FILLER(filler
, buf
, "..", NULL
, 0) != 0 ||
401 DIR_FILLER(filler
, buf
, "cpu", NULL
, 0) != 0)
406 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
:
407 return filler_sys_devices_system_cpu(path
, buf
, filler
);
408 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
: {
413 while ((dirent
= readdir(dir
))) {
414 if (DIR_FILLER(filler
, buf
, dirent
->d_name
, NULL
, 0) != 0)
425 __lxcfs_fuse_ops
int sys_readlink(const char *path
, char *buf
, size_t size
)
427 int ret
= readlink(path
, buf
, size
);
429 if (!liblxcfs_functional())
441 __lxcfs_fuse_ops
int sys_open(const char *path
, struct fuse_file_info
*fi
)
443 __do_free
struct file_info
*info
= NULL
;
446 if (!liblxcfs_functional())
449 if (strcmp(path
, "/sys/devices/system/cpu/online") == 0)
450 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE
;
451 else if (strncmp(path
, "/sys/devices/system/cpu/",
452 STRLITERALLEN("/sys/devices/system/cpu/")) == 0 &&
453 S_ISREG(get_st_mode(path
)))
454 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
;
458 info
= malloc(sizeof(*info
));
462 memset(info
, 0, sizeof(*info
));
465 info
->buflen
= get_sysfile_size(path
) + BUF_RESERVE_SIZE
;
467 info
->buf
= malloc(info
->buflen
);
471 memset(info
->buf
, 0, info
->buflen
);
472 /* set actual size to buffer size */
473 info
->size
= info
->buflen
;
475 fi
->fh
= PTR_TO_UINT64(move_ptr(info
));
479 __lxcfs_fuse_ops
int sys_opendir(const char *path
, struct fuse_file_info
*fi
)
481 __do_free
struct file_info
*dir_info
= NULL
;
484 if (!liblxcfs_functional())
487 if (strcmp(path
, "/sys") == 0)
489 if (strcmp(path
, "/sys/devices") == 0)
490 type
= LXC_TYPE_SYS_DEVICES
;
491 if (strcmp(path
, "/sys/devices/system") == 0)
492 type
= LXC_TYPE_SYS_DEVICES_SYSTEM
;
493 if (strcmp(path
, "/sys/devices/system/cpu") == 0)
494 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU
;
495 if (strncmp(path
, "/sys/devices/system/cpu/",
496 STRLITERALLEN("/sys/devices/system/cpu/")) == 0 &&
497 S_ISDIR(get_st_mode(path
)))
498 type
= LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR
;
502 dir_info
= malloc(sizeof(*dir_info
));
506 memset(dir_info
, 0, sizeof(*dir_info
));
507 dir_info
->type
= type
;
508 dir_info
->buf
= NULL
;
509 dir_info
->file
= NULL
;
510 dir_info
->buflen
= 0;
512 fi
->fh
= PTR_TO_UINT64(move_ptr(dir_info
));
516 __lxcfs_fuse_ops
int sys_access(const char *path
, int mask
)
518 if (!liblxcfs_functional())
521 return access(path
, mask
);
524 __lxcfs_fuse_ops
int sys_release(const char *path
, struct fuse_file_info
*fi
)
526 do_release_file_info(fi
);
530 __lxcfs_fuse_ops
int sys_releasedir(const char *path
, struct fuse_file_info
*fi
)
532 do_release_file_info(fi
);
536 __lxcfs_fuse_ops
int sys_read(const char *path
, char *buf
, size_t size
,
537 off_t offset
, struct fuse_file_info
*fi
)
539 struct file_info
*f
= INTTYPE_TO_PTR(fi
->fh
);
541 if (!liblxcfs_functional())
545 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE
:
546 return sys_devices_system_cpu_online_read(buf
, size
, offset
, fi
);
547 case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE
:
548 return read_file_fuse_with_offset(path
, buf
, size
, offset
, f
);