]> git.proxmox.com Git - mirror_lxcfs.git/blob - src/sysfs_fuse.c
sys: rework get_st_mode()
[mirror_lxcfs.git] / src / sysfs_fuse.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE
5 #endif
6
7 #include "config.h"
8
9 #ifdef HAVE_FUSE3
10 #ifndef FUSE_USE_VERSION
11 #define FUSE_USE_VERSION 30
12 #endif
13 #else
14 #ifndef FUSE_USE_VERSION
15 #define FUSE_USE_VERSION 26
16 #endif
17 #endif
18
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)
23
24 #define _FILE_OFFSET_BITS 64
25
26 #define __STDC_FORMAT_MACROS
27 #include <ctype.h>
28 #include <dirent.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <fuse.h>
32 #include <inttypes.h>
33 #include <libgen.h>
34 #include <pthread.h>
35 #include <sched.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <wait.h>
44 #include <linux/magic.h>
45 #include <linux/sched.h>
46 #include <sys/epoll.h>
47 #include <sys/mman.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>
53 #include <sys/vfs.h>
54
55 #include "bindings.h"
56 #include "memory_utils.h"
57 #include "cgroups/cgroup.h"
58 #include "lxcfs_fuse_compat.h"
59 #include "sysfs_fuse.h"
60 #include "utils.h"
61
62 static ssize_t get_max_cpus(char *cpulist)
63 {
64 char *c1, *c2;
65 char *maxcpus = cpulist;
66 size_t cpus = 0;
67
68 c1 = strrchr(maxcpus, ',');
69 if (c1)
70 c1++;
71
72 c2 = strrchr(maxcpus, '-');
73 if (c2)
74 c2++;
75
76 if (!c1 && !c2)
77 c1 = maxcpus;
78 else if (c1 > c2)
79 c2 = c1;
80 else if (c1 < c2)
81 c1 = c2;
82 else if (!c1 && c2)
83 c1 = c2;
84
85 errno = 0;
86 cpus = strtoul(c1, NULL, 0);
87 if (errno != 0)
88 return -1;
89
90 return cpus;
91 }
92
93 static void set_bit(unsigned bit, uint32_t *bitarr)
94 {
95 bitarr[bit / NBITS] |= (1 << (bit % NBITS));
96 }
97
98 static bool is_set(unsigned bit, uint32_t *bitarr)
99 {
100 return (bitarr[bit / NBITS] & (1 << (bit % NBITS))) != 0;
101 }
102
103 /* Create cpumask from cpulist aka turn:
104 *
105 * 0,2-3
106 *
107 * into bit array
108 *
109 * 1 0 1 1
110 */
111 static uint32_t *lxc_cpumask(char *buf, size_t nbits)
112 {
113 __do_free uint32_t *bitarr = NULL;
114 char *token;
115 size_t arrlen;
116
117 arrlen = BITS_TO_LONGS(nbits);
118 bitarr = calloc(arrlen, sizeof(uint32_t));
119 if (!bitarr)
120 return ret_set_errno(NULL, ENOMEM);
121
122 lxc_iterate_parts(token, buf, ",") {
123 errno = 0;
124 unsigned end, start;
125 char *range;
126
127 start = strtoul(token, NULL, 0);
128 end = start;
129 range = strchr(token, '-');
130 if (range)
131 end = strtoul(range + 1, NULL, 0);
132
133 if (!(start <= end))
134 return ret_set_errno(NULL, EINVAL);
135
136 if (end >= nbits)
137 return ret_set_errno(NULL, EINVAL);
138
139 while (start <= end)
140 set_bit(start++, bitarr);
141 }
142
143 return move_ptr(bitarr);
144 }
145
146 static int sys_devices_system_cpu_online_read(char *buf, size_t size,
147 off_t offset,
148 struct fuse_file_info *fi)
149 {
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;
155 bool use_view;
156
157 int max_cpus = 0;
158 pid_t initpid;
159 ssize_t total_len = 0;
160
161 if (offset) {
162 int left;
163
164 if (!d->cached)
165 return 0;
166
167 if (offset > d->size)
168 return -EINVAL;
169
170 left = d->size - offset;
171 total_len = left > size ? size : left;
172 memcpy(buf, cache + offset, total_len);
173
174 return total_len;
175 }
176
177 initpid = lookup_initpid_in_store(fc->pid);
178 if (initpid <= 1 || is_shared_pidns(initpid))
179 initpid = fc->pid;
180
181 cg = get_pid_cgroup(initpid, "cpuset");
182 if (!cg)
183 return read_file_fuse("/sys/devices/system/cpu/online", buf, size, d);
184 prune_init_slice(cg);
185
186 cpuset = get_cpuset(cg);
187 if (!cpuset)
188 return 0;
189
190 if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs)
191 use_view = true;
192 else
193 use_view = false;
194
195 if (use_view)
196 max_cpus = max_cpu_count(cg);
197
198 if (use_view) {
199 if (max_cpus > 1)
200 total_len = snprintf(d->buf, d->buflen, "0-%d\n", max_cpus - 1);
201 else
202 total_len = snprintf(d->buf, d->buflen, "0\n");
203 } else {
204 total_len = snprintf(d->buf, d->buflen, "%s\n", cpuset);
205 }
206 if (total_len < 0 || total_len >= d->buflen)
207 return log_error(0, "Failed to write to cache");
208
209 d->size = (int)total_len;
210 d->cached = 1;
211
212 if (total_len > size)
213 total_len = size;
214
215 memcpy(buf, d->buf, total_len);
216
217 return total_len;
218 }
219
220 static int filler_sys_devices_system_cpu(const char *path, void *buf,
221 fuse_fill_dir_t filler)
222 {
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();
228 pid_t initpid;
229 ssize_t max_cpus;
230
231 initpid = lookup_initpid_in_store(fc->pid);
232 if (initpid <= 1 || is_shared_pidns(initpid))
233 initpid = fc->pid;
234
235 cg = get_pid_cgroup(initpid, "cpuset");
236 if (!cg)
237 return 0;
238 prune_init_slice(cg);
239
240 cpuset = get_cpuset(cg);
241 if (!cpuset)
242 return 0;
243
244 max_cpus = get_max_cpus(cpuset);
245 if (max_cpus < 0 || max_cpus >= (INT_MAX - 1))
246 return -1;
247 max_cpus++;
248
249 cpumask = lxc_cpumask(cpuset, max_cpus);
250 if (!cpumask)
251 return -errno;
252
253 for (size_t i = 0; i < max_cpus; i++) {
254 int ret;
255 char cpu[100];
256
257 if (!is_set(i, cpumask))
258 continue;
259
260 ret = snprintf(cpu, sizeof(cpu), "cpu%ld", i);
261 if (ret < 0 || ret >= sizeof(cpu))
262 continue;
263
264 if (DIR_FILLER(filler, buf, cpu, NULL, 0) != 0)
265 return -ENOENT;
266 }
267
268 dir = opendir(path);
269 if (!dir)
270 return -ENOENT;
271
272 while ((dirent = readdir(dir))) {
273 char *entry = dirent->d_name;
274
275 if (strlen(entry) <= 3)
276 continue;
277 entry += 3;
278
279 /* Don't emit entries we already filtered above. */
280 if (isdigit(*entry))
281 continue;
282
283 if (DIR_FILLER(filler, buf, dirent->d_name, NULL, 0) != 0)
284 return -ENOENT;
285 }
286
287 return 0;
288 }
289
290 static int get_st_mode(const char *path, mode_t *mode)
291 {
292 struct stat sb;
293 int ret;
294
295 ret = lstat(path, &sb);
296 if (ret < 0)
297 return -ENOENT;
298
299 *mode = sb.st_mode;
300 return 0;
301 }
302
303 static off_t get_sysfile_size(const char *which)
304 {
305 __do_fclose FILE *f = NULL;
306 __do_free char *line = NULL;
307 size_t len = 0;
308 ssize_t sz, answer = 0;
309
310 f = fopen(which, "re");
311 if (!f)
312 return 0;
313
314 while ((sz = getline(&line, &len, f)) != -1)
315 answer += sz;
316
317 return answer;
318 }
319
320 __lxcfs_fuse_ops int sys_getattr(const char *path, struct stat *sb)
321 {
322 int ret;
323 struct timespec now;
324 mode_t st_mode;
325
326 if (!liblxcfs_functional())
327 return -EIO;
328
329 memset(sb, 0, sizeof(struct stat));
330 if (clock_gettime(CLOCK_REALTIME, &now) < 0)
331 return -EINVAL;
332
333 sb->st_uid = sb->st_gid = 0;
334 sb->st_atim = sb->st_mtim = sb->st_ctim = now;
335
336 ret = get_st_mode(path, &st_mode);
337 if (ret)
338 return -ENOENT;
339
340 if (S_ISDIR(st_mode)) {
341 sb->st_mode = st_mode;
342 sb->st_nlink = 2;
343 return 0;
344 }
345
346 if (S_ISREG(st_mode) || S_ISLNK(st_mode)) {
347 sb->st_size = get_sysfile_size(path);
348 sb->st_mode = st_mode;
349 sb->st_nlink = 1;
350 return 0;
351 }
352
353 return -ENOENT;
354 }
355
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)
359 {
360 __do_close int fd = -EBADF;
361 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
362
363 if (!liblxcfs_functional())
364 return -EIO;
365
366 if (f->type != LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE)
367 return -EINVAL;
368
369 fd = open(path, O_WRONLY | O_CLOEXEC);
370 if (fd == -1)
371 return -errno;
372
373 return pwrite(fd, buf, size, offset);
374 }
375
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)
379 {
380 __do_closedir DIR *dir = NULL;
381 struct dirent *dirent;
382 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
383
384 if (!liblxcfs_functional())
385 return -EIO;
386
387 switch (f->type) {
388 case LXC_TYPE_SYS: {
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)
392 return -ENOENT;
393
394 return 0;
395 }
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)
400 return -ENOENT;
401
402 return 0;
403 }
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)
408 return -ENOENT;
409
410 return 0;
411 }
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: {
415 dir = opendir(path);
416 if (!dir)
417 return -ENOENT;
418
419 while ((dirent = readdir(dir))) {
420 if (DIR_FILLER(filler, buf, dirent->d_name, NULL, 0) != 0)
421 return -ENOENT;
422 }
423
424 return 0;
425 }
426 }
427
428 return -EINVAL;
429 }
430
431 __lxcfs_fuse_ops int sys_readlink(const char *path, char *buf, size_t size)
432 {
433 int ret = readlink(path, buf, size);
434
435 if (!liblxcfs_functional())
436 return -EIO;
437
438 if (ret < 0)
439 return -errno;
440 if (ret > size)
441 return -1;
442
443 buf[ret] = '\0';
444
445 return 0;
446 }
447 __lxcfs_fuse_ops int sys_open(const char *path, struct fuse_file_info *fi)
448 {
449 __do_free struct file_info *info = NULL;
450 int type = -1;
451
452 if (!liblxcfs_functional())
453 return -EIO;
454
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) {
459 int ret;
460 mode_t st_mode;
461
462 ret = get_st_mode(path, &st_mode);
463 if (ret)
464 return ret;
465
466 if (S_ISREG(st_mode))
467 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE;
468 }
469 if (type == -1)
470 return -ENOENT;
471
472 info = malloc(sizeof(*info));
473 if (!info)
474 return -ENOMEM;
475
476 memset(info, 0, sizeof(*info));
477 info->type = type;
478
479 info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE;
480
481 info->buf = malloc(info->buflen);
482 if (!info->buf)
483 return -ENOMEM;
484
485 memset(info->buf, 0, info->buflen);
486 /* set actual size to buffer size */
487 info->size = info->buflen;
488
489 fi->fh = PTR_TO_UINT64(move_ptr(info));
490 return 0;
491 }
492
493 __lxcfs_fuse_ops int sys_opendir(const char *path, struct fuse_file_info *fi)
494 {
495 __do_free struct file_info *dir_info = NULL;
496 int type = -1;
497
498 if (!liblxcfs_functional())
499 return -EIO;
500
501 if (strcmp(path, "/sys") == 0) {
502 type = LXC_TYPE_SYS;
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) {
511 int ret;
512 mode_t st_mode;
513
514 ret = get_st_mode(path, &st_mode);
515 if (ret)
516 return ret;
517
518 if (S_ISDIR(st_mode))
519 type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR;
520 }
521 if (type == -1)
522 return -ENOENT;
523
524 dir_info = malloc(sizeof(*dir_info));
525 if (!dir_info)
526 return -ENOMEM;
527
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;
533
534 fi->fh = PTR_TO_UINT64(move_ptr(dir_info));
535 return 0;
536 }
537
538 __lxcfs_fuse_ops int sys_access(const char *path, int mask)
539 {
540 if (!liblxcfs_functional())
541 return -EIO;
542
543 return access(path, mask);
544 }
545
546 __lxcfs_fuse_ops int sys_release(const char *path, struct fuse_file_info *fi)
547 {
548 do_release_file_info(fi);
549 return 0;
550 }
551
552 __lxcfs_fuse_ops int sys_releasedir(const char *path, struct fuse_file_info *fi)
553 {
554 do_release_file_info(fi);
555 return 0;
556 }
557
558 __lxcfs_fuse_ops int sys_read(const char *path, char *buf, size_t size,
559 off_t offset, struct fuse_file_info *fi)
560 {
561 struct file_info *f = INTTYPE_TO_PTR(fi->fh);
562
563 if (!liblxcfs_functional())
564 return -EIO;
565
566 switch (f->type) {
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);
571 }
572
573 return -EINVAL;
574 }