]>
Commit | Line | Data |
---|---|---|
db0463bf | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
71f17cd2 | 2 | |
f834b6bf SP |
3 | #include "config.h" |
4 | ||
d9c820ee | 5 | #include <ctype.h> |
71f17cd2 YB |
6 | #include <dirent.h> |
7 | #include <errno.h> | |
8 | #include <fcntl.h> | |
71f17cd2 YB |
9 | #include <inttypes.h> |
10 | #include <libgen.h> | |
11 | #include <pthread.h> | |
12 | #include <sched.h> | |
13 | #include <stdbool.h> | |
14 | #include <stdint.h> | |
15 | #include <stdio.h> | |
16 | #include <stdlib.h> | |
17 | #include <string.h> | |
18 | #include <time.h> | |
19 | #include <unistd.h> | |
20 | #include <wait.h> | |
21 | #include <linux/magic.h> | |
22 | #include <linux/sched.h> | |
23 | #include <sys/epoll.h> | |
24 | #include <sys/mman.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> | |
30 | #include <sys/vfs.h> | |
31 | ||
e01afbb7 CB |
32 | #include "sysfs_fuse.h" |
33 | ||
71f17cd2 | 34 | #include "bindings.h" |
700dd417 | 35 | #include "memory_utils.h" |
77f4399a | 36 | #include "cgroups/cgroup.h" |
ec2043ed | 37 | #include "lxcfs_fuse_compat.h" |
1d81c6a6 | 38 | #include "utils.h" |
71f17cd2 | 39 | |
26b717d0 ZL |
40 | /* Create cpumask from cpulist aka turn: |
41 | * | |
42 | * 0,2-3 | |
43 | * | |
44 | * into bit array | |
45 | * | |
46 | * 1 0 1 1 | |
47 | */ | |
a3c8d33c | 48 | static int lxc_cpumask(char *buf, __u32 **bitarr, __u32 *last_set_bit) |
26b717d0 | 49 | { |
a3c8d33c CB |
50 | __do_free __u32 *arr_u32 = NULL; |
51 | __u32 cur_last_set_bit = 0, nbits = 256; | |
52 | __u32 nr_u32; | |
26b717d0 | 53 | char *token; |
26b717d0 | 54 | |
a3c8d33c CB |
55 | nr_u32 = BITS_TO_LONGS(nbits); |
56 | arr_u32 = zalloc(nr_u32 * sizeof(__u32)); | |
57 | if (!arr_u32) | |
58 | return ret_errno(ENOMEM); | |
26b717d0 ZL |
59 | |
60 | lxc_iterate_parts(token, buf, ",") { | |
a3c8d33c CB |
61 | __u32 last_bit, first_bit; |
62 | char *range; | |
63 | ||
26b717d0 | 64 | errno = 0; |
a3c8d33c CB |
65 | first_bit = strtoul(token, NULL, 0); |
66 | last_bit = first_bit; | |
67 | range = strchr(token, '-'); | |
68 | if (range) | |
69 | last_bit = strtoul(range + 1, NULL, 0); | |
70 | ||
71 | if (!(first_bit <= last_bit)) | |
72 | return ret_errno(EINVAL); | |
73 | ||
74 | if (last_bit >= nbits) { | |
75 | __u32 add_bits = last_bit - nbits + 32; | |
76 | __u32 new_nr_u32; | |
77 | __u32 *p; | |
78 | ||
79 | new_nr_u32 = BITS_TO_LONGS(nbits + add_bits); | |
80 | p = realloc(arr_u32, new_nr_u32 * sizeof(uint32_t)); | |
81 | if (!p) | |
82 | return ret_errno(ENOMEM); | |
83 | arr_u32 = move_ptr(p); | |
84 | ||
85 | memset(arr_u32 + nr_u32, 0, | |
86 | (new_nr_u32 - nr_u32) * sizeof(uint32_t)); | |
87 | nbits += add_bits; | |
88 | } | |
89 | ||
90 | while (first_bit <= last_bit) | |
91 | set_bit(first_bit++, arr_u32); | |
92 | ||
93 | if (last_bit > cur_last_set_bit) | |
94 | cur_last_set_bit = last_bit; | |
95 | } | |
96 | ||
97 | *last_set_bit = cur_last_set_bit; | |
98 | *bitarr = move_ptr(arr_u32); | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static int lxc_cpumask_update(char *buf, __u32 *bitarr, __u32 last_set_bit, | |
103 | bool clear) | |
104 | { | |
105 | bool flipped = false; | |
106 | char *token; | |
107 | ||
108 | lxc_iterate_parts(token, buf, ",") { | |
109 | __u32 last_bit, first_bit; | |
26b717d0 ZL |
110 | char *range; |
111 | ||
a3c8d33c CB |
112 | errno = 0; |
113 | first_bit = strtoul(token, NULL, 0); | |
114 | last_bit = first_bit; | |
26b717d0 ZL |
115 | range = strchr(token, '-'); |
116 | if (range) | |
a3c8d33c CB |
117 | last_bit = strtoul(range + 1, NULL, 0); |
118 | ||
119 | if (!(first_bit <= last_bit)) { | |
120 | lxcfs_debug("The cup range seems to be inverted: %u-%u", first_bit, last_bit); | |
121 | continue; | |
122 | } | |
26b717d0 | 123 | |
a3c8d33c CB |
124 | if (last_bit > last_set_bit) |
125 | continue; | |
126 | ||
127 | while (first_bit <= last_bit) { | |
128 | if (clear && is_set(first_bit, bitarr)) { | |
129 | flipped = true; | |
130 | clear_bit(first_bit, bitarr); | |
131 | } else if (!clear && !is_set(first_bit, bitarr)) { | |
132 | flipped = true; | |
133 | set_bit(first_bit, bitarr); | |
134 | } | |
135 | ||
136 | first_bit++; | |
137 | } | |
138 | } | |
139 | ||
140 | if (flipped) | |
141 | return 1; | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | #define __ISOL_CPUS "/sys/devices/system/cpu/isolated" | |
147 | #define __OFFLINE_CPUS "/sys/devices/system/cpu/offline" | |
148 | static int cpumask(char *posscpus, __u32 **bitarr, __u32 *last_set_bit) | |
149 | { | |
150 | __do_free char *isolcpus = NULL, *offlinecpus = NULL; | |
151 | __do_free __u32 *possmask = NULL; | |
152 | int ret; | |
153 | __u32 poss_last_set_bit = 0; | |
154 | ||
155 | if (file_exists(__ISOL_CPUS)) { | |
156 | isolcpus = read_file_at(-EBADF, __ISOL_CPUS, PROTECT_OPEN); | |
157 | if (!isolcpus) | |
158 | return -1; | |
159 | ||
160 | if (!isdigit(isolcpus[0])) | |
161 | free_disarm(isolcpus); | |
162 | } else { | |
163 | lxcfs_debug("The path \""__ISOL_CPUS"\" to read isolated cpus from does not exist"); | |
164 | } | |
26b717d0 | 165 | |
a3c8d33c CB |
166 | if (file_exists(__OFFLINE_CPUS)) { |
167 | offlinecpus = read_file_at(-EBADF, __OFFLINE_CPUS, PROTECT_OPEN); | |
168 | if (!offlinecpus) | |
169 | return -1; | |
26b717d0 | 170 | |
a3c8d33c CB |
171 | if (!isdigit(offlinecpus[0])) |
172 | free_disarm(offlinecpus); | |
173 | } else { | |
174 | lxcfs_debug("The path \""__OFFLINE_CPUS"\" to read offline cpus from does not exist"); | |
26b717d0 ZL |
175 | } |
176 | ||
a3c8d33c CB |
177 | ret = lxc_cpumask(posscpus, &possmask, &poss_last_set_bit); |
178 | if (ret) | |
179 | return ret; | |
180 | ||
181 | if (isolcpus) | |
182 | ret = lxc_cpumask_update(isolcpus, possmask, poss_last_set_bit, true); | |
183 | ||
184 | if (offlinecpus) | |
185 | ret |= lxc_cpumask_update(offlinecpus, possmask, poss_last_set_bit, true); | |
186 | if (ret) | |
187 | return ret; | |
188 | ||
189 | *bitarr = move_ptr(possmask); | |
190 | *last_set_bit = poss_last_set_bit; | |
191 | return 0; | |
26b717d0 ZL |
192 | } |
193 | ||
71f17cd2 YB |
194 | static int sys_devices_system_cpu_online_read(char *buf, size_t size, |
195 | off_t offset, | |
196 | struct fuse_file_info *fi) | |
197 | { | |
700dd417 | 198 | __do_free char *cg = NULL, *cpuset = NULL; |
71f17cd2 | 199 | struct fuse_context *fc = fuse_get_context(); |
8044f626 | 200 | struct lxcfs_opts *opts = (struct lxcfs_opts *)fc->private_data; |
99b183fb | 201 | struct file_info *d = INTTYPE_TO_PTR(fi->fh); |
71f17cd2 | 202 | char *cache = d->buf; |
71f17cd2 | 203 | pid_t initpid; |
a3c8d33c | 204 | int max_cpus = 0; |
71f17cd2 | 205 | ssize_t total_len = 0; |
a3c8d33c | 206 | bool use_view; |
71f17cd2 YB |
207 | |
208 | if (offset) { | |
3cf1e562 | 209 | size_t left; |
700dd417 | 210 | |
71f17cd2 YB |
211 | if (!d->cached) |
212 | return 0; | |
700dd417 | 213 | |
71f17cd2 YB |
214 | if (offset > d->size) |
215 | return -EINVAL; | |
700dd417 CB |
216 | |
217 | left = d->size - offset; | |
71f17cd2 YB |
218 | total_len = left > size ? size : left; |
219 | memcpy(buf, cache + offset, total_len); | |
700dd417 | 220 | |
71f17cd2 YB |
221 | return total_len; |
222 | } | |
223 | ||
224 | initpid = lookup_initpid_in_store(fc->pid); | |
a9f0d623 | 225 | if (initpid <= 1 || is_shared_pidns(initpid)) |
71f17cd2 | 226 | initpid = fc->pid; |
a9f0d623 | 227 | |
71f17cd2 YB |
228 | cg = get_pid_cgroup(initpid, "cpuset"); |
229 | if (!cg) | |
5fbea8a6 | 230 | return read_file_fuse("/sys/devices/system/cpu/online", buf, size, d); |
71f17cd2 YB |
231 | prune_init_slice(cg); |
232 | ||
233 | cpuset = get_cpuset(cg); | |
234 | if (!cpuset) | |
700dd417 | 235 | return 0; |
71f17cd2 | 236 | |
8044f626 CB |
237 | if (cgroup_ops->can_use_cpuview(cgroup_ops) && opts && opts->use_cfs) |
238 | use_view = true; | |
239 | else | |
240 | use_view = false; | |
26b717d0 | 241 | |
71f17cd2 YB |
242 | if (use_view) |
243 | max_cpus = max_cpu_count(cg); | |
244 | ||
b3c85f5d CB |
245 | if (use_view) { |
246 | if (max_cpus > 1) | |
247 | total_len = snprintf(d->buf, d->buflen, "0-%d\n", max_cpus - 1); | |
248 | else | |
249 | total_len = snprintf(d->buf, d->buflen, "0\n"); | |
b3c85f5d | 250 | } else { |
133cba47 | 251 | total_len = snprintf(d->buf, d->buflen, "%s\n", cpuset); |
b3c85f5d | 252 | } |
cf07826e CB |
253 | if (total_len < 0 || total_len >= d->buflen) |
254 | return log_error(0, "Failed to write to cache"); | |
71f17cd2 YB |
255 | |
256 | d->size = (int)total_len; | |
257 | d->cached = 1; | |
258 | ||
3cf1e562 | 259 | if ((size_t)total_len > size) |
71f17cd2 YB |
260 | total_len = size; |
261 | ||
262 | memcpy(buf, d->buf, total_len); | |
700dd417 | 263 | |
71f17cd2 YB |
264 | return total_len; |
265 | } | |
266 | ||
26b717d0 ZL |
267 | static int filler_sys_devices_system_cpu(const char *path, void *buf, |
268 | fuse_fill_dir_t filler) | |
269 | { | |
a3c8d33c | 270 | __do_free __u32 *bitarr = NULL; |
26b717d0 ZL |
271 | __do_free char *cg = NULL, *cpuset = NULL; |
272 | __do_closedir DIR *dir = NULL; | |
26b717d0 | 273 | struct fuse_context *fc = fuse_get_context(); |
a3c8d33c CB |
274 | __u32 last_set_bit = 0; |
275 | int ret; | |
276 | struct dirent *dirent; | |
26b717d0 | 277 | pid_t initpid; |
26b717d0 ZL |
278 | |
279 | initpid = lookup_initpid_in_store(fc->pid); | |
280 | if (initpid <= 1 || is_shared_pidns(initpid)) | |
281 | initpid = fc->pid; | |
282 | ||
283 | cg = get_pid_cgroup(initpid, "cpuset"); | |
284 | if (!cg) | |
285 | return 0; | |
286 | prune_init_slice(cg); | |
287 | ||
288 | cpuset = get_cpuset(cg); | |
289 | if (!cpuset) | |
290 | return 0; | |
291 | ||
a3c8d33c CB |
292 | ret = cpumask(cpuset, &bitarr, &last_set_bit); |
293 | if (ret) | |
294 | return ret; | |
26b717d0 | 295 | |
a3c8d33c | 296 | for (__u32 bit = 0; bit <= last_set_bit; bit++) { |
d9c820ee | 297 | char cpu[100]; |
26b717d0 | 298 | |
a3c8d33c | 299 | if (!is_set(bit, bitarr)) |
26b717d0 ZL |
300 | continue; |
301 | ||
a3c8d33c | 302 | ret = snprintf(cpu, sizeof(cpu), "cpu%u", bit); |
3cf1e562 | 303 | if (ret < 0 || (size_t)ret >= sizeof(cpu)) |
26b717d0 ZL |
304 | continue; |
305 | ||
5aff2eb2 | 306 | if (dir_filler(filler, buf, cpu, 0) != 0) |
26b717d0 ZL |
307 | return -ENOENT; |
308 | } | |
309 | ||
310 | dir = opendir(path); | |
311 | if (!dir) | |
312 | return -ENOENT; | |
313 | ||
314 | while ((dirent = readdir(dir))) { | |
d9c820ee CB |
315 | char *entry = dirent->d_name; |
316 | ||
ece0d2bb SG |
317 | if (strlen(entry) > 3) { |
318 | entry += 3; | |
d9c820ee | 319 | |
ece0d2bb SG |
320 | /* Don't emit entries we already filtered above. */ |
321 | if (isdigit(*entry)) | |
322 | continue; | |
323 | } | |
26b717d0 | 324 | |
02fa563b | 325 | if (dirent_fillerat(filler, dir, dirent, buf, 0) != 0) |
26b717d0 ZL |
326 | return -ENOENT; |
327 | } | |
328 | ||
329 | return 0; | |
330 | } | |
331 | ||
d9c820ee | 332 | static int get_st_mode(const char *path, mode_t *mode) |
26b717d0 ZL |
333 | { |
334 | struct stat sb; | |
335 | int ret; | |
336 | ||
337 | ret = lstat(path, &sb); | |
338 | if (ret < 0) | |
339 | return -ENOENT; | |
340 | ||
d9c820ee CB |
341 | *mode = sb.st_mode; |
342 | return 0; | |
26b717d0 ZL |
343 | } |
344 | ||
71f17cd2 YB |
345 | static off_t get_sysfile_size(const char *which) |
346 | { | |
700dd417 CB |
347 | __do_fclose FILE *f = NULL; |
348 | __do_free char *line = NULL; | |
71f17cd2 YB |
349 | size_t len = 0; |
350 | ssize_t sz, answer = 0; | |
351 | ||
700dd417 | 352 | f = fopen(which, "re"); |
71f17cd2 YB |
353 | if (!f) |
354 | return 0; | |
355 | ||
356 | while ((sz = getline(&line, &len, f)) != -1) | |
357 | answer += sz; | |
71f17cd2 YB |
358 | |
359 | return answer; | |
360 | } | |
361 | ||
0d5383b7 CB |
362 | static int sys_getattr_legacy(const char *path, struct stat *sb) |
363 | { | |
364 | struct timespec now; | |
365 | ||
366 | memset(sb, 0, sizeof(struct stat)); | |
367 | if (clock_gettime(CLOCK_REALTIME, &now) < 0) | |
368 | return -EINVAL; | |
369 | ||
370 | sb->st_uid = sb->st_gid = 0; | |
371 | sb->st_atim = sb->st_mtim = sb->st_ctim = now; | |
372 | if (strcmp(path, "/sys") == 0) { | |
373 | sb->st_mode = S_IFDIR | 00555; | |
374 | sb->st_nlink = 2; | |
375 | return 0; | |
376 | } | |
377 | ||
378 | if (strcmp(path, "/sys/devices") == 0) { | |
379 | sb->st_mode = S_IFDIR | 00555; | |
380 | sb->st_nlink = 2; | |
381 | return 0; | |
382 | } | |
383 | ||
384 | if (strcmp(path, "/sys/devices/system") == 0) { | |
385 | sb->st_mode = S_IFDIR | 00555; | |
386 | sb->st_nlink = 2; | |
387 | return 0; | |
388 | } | |
389 | ||
390 | if (strcmp(path, "/sys/devices/system/cpu") == 0) { | |
391 | sb->st_mode = S_IFDIR | 00555; | |
392 | sb->st_nlink = 2; | |
393 | return 0; | |
394 | } | |
395 | ||
396 | if (strcmp(path, "/sys/devices/system/cpu/online") == 0) { | |
397 | sb->st_size = get_sysfile_size (path); | |
398 | sb->st_mode = S_IFREG | 00444; | |
399 | sb->st_nlink = 1; | |
400 | return 0; | |
401 | } | |
402 | ||
403 | return -ENOENT; | |
404 | } | |
405 | ||
2d7bcab7 | 406 | __lxcfs_fuse_ops int sys_getattr(const char *path, struct stat *sb) |
71f17cd2 | 407 | { |
d9c820ee | 408 | int ret; |
71f17cd2 | 409 | struct timespec now; |
26b717d0 ZL |
410 | mode_t st_mode; |
411 | ||
412 | if (!liblxcfs_functional()) | |
413 | return -EIO; | |
71f17cd2 | 414 | |
0d5383b7 CB |
415 | if (!liblxcfs_can_use_sys_cpu()) |
416 | return sys_getattr_legacy(path, sb); | |
417 | ||
71f17cd2 YB |
418 | memset(sb, 0, sizeof(struct stat)); |
419 | if (clock_gettime(CLOCK_REALTIME, &now) < 0) | |
420 | return -EINVAL; | |
cf07826e | 421 | |
71f17cd2 YB |
422 | sb->st_uid = sb->st_gid = 0; |
423 | sb->st_atim = sb->st_mtim = sb->st_ctim = now; | |
cf07826e | 424 | |
d9c820ee CB |
425 | ret = get_st_mode(path, &st_mode); |
426 | if (ret) | |
26b717d0 | 427 | return -ENOENT; |
cf07826e | 428 | |
26b717d0 ZL |
429 | if (S_ISDIR(st_mode)) { |
430 | sb->st_mode = st_mode; | |
71f17cd2 YB |
431 | sb->st_nlink = 2; |
432 | return 0; | |
433 | } | |
cf07826e | 434 | |
26b717d0 ZL |
435 | if (S_ISREG(st_mode) || S_ISLNK(st_mode)) { |
436 | sb->st_size = get_sysfile_size(path); | |
437 | sb->st_mode = st_mode; | |
71f17cd2 YB |
438 | sb->st_nlink = 1; |
439 | return 0; | |
440 | } | |
441 | ||
442 | return -ENOENT; | |
443 | } | |
444 | ||
0d5383b7 CB |
445 | __lxcfs_fuse_ops int sys_release(const char *path, struct fuse_file_info *fi) |
446 | { | |
447 | do_release_file_info(fi); | |
448 | return 0; | |
449 | } | |
450 | ||
451 | __lxcfs_fuse_ops int sys_releasedir(const char *path, struct fuse_file_info *fi) | |
452 | { | |
453 | do_release_file_info(fi); | |
454 | return 0; | |
455 | } | |
456 | ||
457 | __lxcfs_fuse_ops int sys_write(const char *path, const char *buf, size_t size, | |
458 | off_t offset, struct fuse_file_info *fi) | |
26b717d0 ZL |
459 | { |
460 | __do_close int fd = -EBADF; | |
461 | struct file_info *f = INTTYPE_TO_PTR(fi->fh); | |
462 | ||
463 | if (!liblxcfs_functional()) | |
464 | return -EIO; | |
465 | ||
466 | if (f->type != LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE) | |
467 | return -EINVAL; | |
468 | ||
469 | fd = open(path, O_WRONLY | O_CLOEXEC); | |
470 | if (fd == -1) | |
471 | return -errno; | |
472 | ||
473 | return pwrite(fd, buf, size, offset); | |
474 | } | |
475 | ||
0d5383b7 CB |
476 | static int sys_readdir_legacy(const char *path, void *buf, fuse_fill_dir_t filler, |
477 | off_t offset, struct fuse_file_info *fi) | |
478 | { | |
479 | if (strcmp(path, "/sys") == 0) { | |
5aff2eb2 CBM |
480 | if (dir_filler(filler, buf, ".", 0) != 0 || |
481 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 482 | dirent_filler(filler, path, "devices", buf, 0) != 0) |
0d5383b7 CB |
483 | return -ENOENT; |
484 | ||
485 | return 0; | |
486 | } | |
487 | if (strcmp(path, "/sys/devices") == 0) { | |
5aff2eb2 CBM |
488 | if (dir_filler(filler, buf, ".", 0) != 0 || |
489 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 490 | dirent_filler(filler, path, "system", buf, 0) != 0) |
0d5383b7 CB |
491 | return -ENOENT; |
492 | ||
493 | return 0; | |
494 | } | |
495 | if (strcmp(path, "/sys/devices/system") == 0) { | |
5aff2eb2 CBM |
496 | if (dir_filler(filler, buf, ".", 0) != 0 || |
497 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 498 | dirent_filler(filler, path, "cpu", buf, 0) != 0) |
0d5383b7 CB |
499 | return -ENOENT; |
500 | ||
501 | return 0; | |
502 | } | |
503 | if (strcmp(path, "/sys/devices/system/cpu") == 0) { | |
5aff2eb2 CBM |
504 | if (dir_filler(filler, buf, ".", 0) != 0 || |
505 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 506 | dirent_filler(filler, path, "online", buf, 0) != 0) |
0d5383b7 CB |
507 | return -ENOENT; |
508 | ||
509 | return 0; | |
510 | } | |
511 | ||
512 | return 0; | |
513 | } | |
514 | ||
2d7bcab7 | 515 | __lxcfs_fuse_ops int sys_readdir(const char *path, void *buf, |
0d5383b7 CB |
516 | fuse_fill_dir_t filler, off_t offset, |
517 | struct fuse_file_info *fi) | |
71f17cd2 | 518 | { |
a6fd03eb | 519 | __do_closedir DIR *dirp = NULL; |
26b717d0 ZL |
520 | struct dirent *dirent; |
521 | struct file_info *f = INTTYPE_TO_PTR(fi->fh); | |
cf07826e | 522 | |
26b717d0 ZL |
523 | if (!liblxcfs_functional()) |
524 | return -EIO; | |
cf07826e | 525 | |
0d5383b7 CB |
526 | if (!liblxcfs_can_use_sys_cpu()) |
527 | return sys_readdir_legacy(path, buf, filler, offset, fi); | |
528 | ||
529 | /* | |
530 | * When we reload LXCFS and we don't load the lxcfs binary itself | |
531 | * changes to such functions as lxcfs_opendir() aren't reflected so | |
532 | * sys_opendir() doesn't run but sys_readdir() does. We need to account | |
533 | * for that here. | |
534 | */ | |
535 | if (!f) | |
536 | return -EIO; | |
537 | ||
26b717d0 ZL |
538 | switch (f->type) { |
539 | case LXC_TYPE_SYS: { | |
5aff2eb2 CBM |
540 | if (dir_filler(filler, buf, ".", 0) != 0 || |
541 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 542 | dirent_filler(filler, path, "devices", buf, 0) != 0) |
26b717d0 | 543 | return -ENOENT; |
cf07826e | 544 | |
26b717d0 ZL |
545 | return 0; |
546 | } | |
547 | case LXC_TYPE_SYS_DEVICES: { | |
5aff2eb2 CBM |
548 | if (dir_filler(filler, buf, ".", 0) != 0 || |
549 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 550 | dirent_filler(filler, path, "system", buf, 0) != 0) |
26b717d0 | 551 | return -ENOENT; |
cf07826e | 552 | |
26b717d0 ZL |
553 | return 0; |
554 | } | |
555 | case LXC_TYPE_SYS_DEVICES_SYSTEM: { | |
5aff2eb2 CBM |
556 | if (dir_filler(filler, buf, ".", 0) != 0 || |
557 | dir_filler(filler, buf, "..", 0) != 0 || | |
02fa563b | 558 | dirent_filler(filler, path, "cpu", buf, 0) != 0) |
26b717d0 ZL |
559 | return -ENOENT; |
560 | ||
561 | return 0; | |
562 | } | |
563 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU: | |
5aff2eb2 CBM |
564 | if (dir_filler(filler, buf, ".", 0) != 0 || |
565 | dir_filler(filler, buf, "..", 0) != 0) | |
76a883b0 CB |
566 | return -ENOENT; |
567 | ||
568 | return filler_sys_devices_system_cpu(path, buf, filler); | |
26b717d0 | 569 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR: { |
a6fd03eb CBM |
570 | dirp = opathdir(path); |
571 | if (!dirp) | |
572 | return -errno; | |
26b717d0 | 573 | |
a6fd03eb CBM |
574 | while ((dirent = readdir(dirp))) { |
575 | if (dirent_fillerat(filler, dirp, dirent, buf, 0) != 0) | |
26b717d0 ZL |
576 | return -ENOENT; |
577 | } | |
578 | ||
579 | return 0; | |
580 | } | |
71f17cd2 YB |
581 | } |
582 | ||
26b717d0 | 583 | return -EINVAL; |
71f17cd2 YB |
584 | } |
585 | ||
26b717d0 ZL |
586 | __lxcfs_fuse_ops int sys_readlink(const char *path, char *buf, size_t size) |
587 | { | |
3cf1e562 | 588 | ssize_t ret; |
26b717d0 ZL |
589 | |
590 | if (!liblxcfs_functional()) | |
591 | return -EIO; | |
592 | ||
3cf1e562 | 593 | ret = readlink(path, buf, size); |
26b717d0 ZL |
594 | if (ret < 0) |
595 | return -errno; | |
3cf1e562 CB |
596 | |
597 | if ((size_t)ret > size) | |
26b717d0 ZL |
598 | return -1; |
599 | ||
600 | buf[ret] = '\0'; | |
601 | ||
602 | return 0; | |
603 | } | |
0d5383b7 CB |
604 | |
605 | static int sys_open_legacy(const char *path, struct fuse_file_info *fi) | |
606 | { | |
607 | __do_free struct file_info *info = NULL; | |
608 | int type = -1; | |
609 | ||
610 | if (strcmp(path, "/sys/devices") == 0) | |
611 | type = LXC_TYPE_SYS_DEVICES; | |
612 | if (strcmp(path, "/sys/devices/system") == 0) | |
613 | type = LXC_TYPE_SYS_DEVICES_SYSTEM; | |
614 | if (strcmp(path, "/sys/devices/system/cpu") == 0) | |
615 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU; | |
616 | if (strcmp(path, "/sys/devices/system/cpu/online") == 0) | |
617 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE; | |
618 | if (type == -1) | |
619 | return -ENOENT; | |
620 | ||
621 | info = malloc(sizeof(*info)); | |
622 | if (!info) | |
623 | return -ENOMEM; | |
624 | ||
625 | memset(info, 0, sizeof(*info)); | |
626 | info->type = type; | |
627 | ||
628 | info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE; | |
629 | ||
630 | info->buf = malloc(info->buflen); | |
631 | if (!info->buf) | |
632 | return -ENOMEM; | |
633 | ||
634 | memset(info->buf, 0, info->buflen); | |
635 | /* set actual size to buffer size */ | |
636 | info->size = info->buflen; | |
637 | ||
638 | fi->fh = PTR_TO_UINT64(move_ptr(info)); | |
639 | return 0; | |
640 | } | |
641 | ||
2d7bcab7 | 642 | __lxcfs_fuse_ops int sys_open(const char *path, struct fuse_file_info *fi) |
71f17cd2 | 643 | { |
700dd417 | 644 | __do_free struct file_info *info = NULL; |
71f17cd2 | 645 | int type = -1; |
71f17cd2 | 646 | |
26b717d0 ZL |
647 | if (!liblxcfs_functional()) |
648 | return -EIO; | |
649 | ||
0d5383b7 CB |
650 | if (!liblxcfs_can_use_sys_cpu()) |
651 | return sys_open_legacy(path, fi); | |
652 | ||
d9c820ee | 653 | if (strcmp(path, "/sys/devices/system/cpu/online") == 0) { |
71f17cd2 | 654 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE; |
d9c820ee CB |
655 | } else if (strncmp(path, "/sys/devices/system/cpu/", |
656 | STRLITERALLEN("/sys/devices/system/cpu/")) == 0) { | |
657 | int ret; | |
658 | mode_t st_mode; | |
659 | ||
660 | ret = get_st_mode(path, &st_mode); | |
661 | if (ret) | |
662 | return ret; | |
663 | ||
664 | if (S_ISREG(st_mode)) | |
665 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE; | |
666 | } | |
71f17cd2 YB |
667 | if (type == -1) |
668 | return -ENOENT; | |
669 | ||
670 | info = malloc(sizeof(*info)); | |
671 | if (!info) | |
672 | return -ENOMEM; | |
673 | ||
674 | memset(info, 0, sizeof(*info)); | |
675 | info->type = type; | |
676 | ||
677 | info->buflen = get_sysfile_size(path) + BUF_RESERVE_SIZE; | |
700dd417 CB |
678 | |
679 | info->buf = malloc(info->buflen); | |
680 | if (!info->buf) | |
681 | return -ENOMEM; | |
682 | ||
71f17cd2 YB |
683 | memset(info->buf, 0, info->buflen); |
684 | /* set actual size to buffer size */ | |
685 | info->size = info->buflen; | |
686 | ||
700dd417 | 687 | fi->fh = PTR_TO_UINT64(move_ptr(info)); |
71f17cd2 YB |
688 | return 0; |
689 | } | |
690 | ||
26b717d0 | 691 | __lxcfs_fuse_ops int sys_opendir(const char *path, struct fuse_file_info *fi) |
71f17cd2 | 692 | { |
26b717d0 ZL |
693 | __do_free struct file_info *dir_info = NULL; |
694 | int type = -1; | |
cf07826e | 695 | |
26b717d0 ZL |
696 | if (!liblxcfs_functional()) |
697 | return -EIO; | |
cf07826e | 698 | |
d9c820ee | 699 | if (strcmp(path, "/sys") == 0) { |
26b717d0 | 700 | type = LXC_TYPE_SYS; |
d9c820ee | 701 | } else if (strcmp(path, "/sys/devices") == 0) { |
26b717d0 | 702 | type = LXC_TYPE_SYS_DEVICES; |
d9c820ee | 703 | } else if (strcmp(path, "/sys/devices/system") == 0) { |
26b717d0 | 704 | type = LXC_TYPE_SYS_DEVICES_SYSTEM; |
d9c820ee | 705 | } else if (strcmp(path, "/sys/devices/system/cpu") == 0) { |
26b717d0 | 706 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU; |
d9c820ee CB |
707 | } else if (strncmp(path, "/sys/devices/system/cpu/", |
708 | STRLITERALLEN("/sys/devices/system/cpu/")) == 0) { | |
709 | int ret; | |
710 | mode_t st_mode; | |
711 | ||
712 | ret = get_st_mode(path, &st_mode); | |
713 | if (ret) | |
714 | return ret; | |
715 | ||
716 | if (S_ISDIR(st_mode)) | |
717 | type = LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBDIR; | |
718 | } | |
26b717d0 ZL |
719 | if (type == -1) |
720 | return -ENOENT; | |
cf07826e | 721 | |
26b717d0 ZL |
722 | dir_info = malloc(sizeof(*dir_info)); |
723 | if (!dir_info) | |
724 | return -ENOMEM; | |
cf07826e | 725 | |
26b717d0 ZL |
726 | memset(dir_info, 0, sizeof(*dir_info)); |
727 | dir_info->type = type; | |
728 | dir_info->buf = NULL; | |
729 | dir_info->file = NULL; | |
730 | dir_info->buflen = 0; | |
cf07826e | 731 | |
26b717d0 | 732 | fi->fh = PTR_TO_UINT64(move_ptr(dir_info)); |
71f17cd2 YB |
733 | return 0; |
734 | } | |
735 | ||
0d5383b7 CB |
736 | static int sys_access_legacy(const char *path, int mask) |
737 | { | |
738 | if (strcmp(path, "/sys") == 0 && access(path, R_OK) == 0) | |
739 | return 0; | |
740 | ||
741 | if (strcmp(path, "/sys/devices") == 0 && access(path, R_OK) == 0) | |
742 | return 0; | |
743 | ||
744 | if (strcmp(path, "/sys/devices/system") == 0 && access(path, R_OK) == 0) | |
745 | return 0; | |
746 | ||
747 | if (strcmp(path, "/sys/devices/system/cpu") == 0 && | |
748 | access(path, R_OK) == 0) | |
749 | return 0; | |
750 | ||
751 | /* these are all read-only */ | |
752 | if ((mask & ~R_OK) != 0) | |
753 | return -EACCES; | |
754 | ||
755 | return 0; | |
756 | } | |
757 | ||
26b717d0 ZL |
758 | __lxcfs_fuse_ops int sys_access(const char *path, int mask) |
759 | { | |
760 | if (!liblxcfs_functional()) | |
761 | return -EIO; | |
762 | ||
0d5383b7 CB |
763 | if (!liblxcfs_can_use_sys_cpu()) |
764 | return sys_access_legacy(path, mask); | |
765 | ||
26b717d0 ZL |
766 | return access(path, mask); |
767 | } | |
768 | ||
0d5383b7 CB |
769 | static int sys_read_legacy(const char *path, char *buf, size_t size, |
770 | off_t offset, struct fuse_file_info *fi) | |
71f17cd2 | 771 | { |
0d5383b7 | 772 | struct file_info *f = INTTYPE_TO_PTR(fi->fh); |
71f17cd2 | 773 | |
0d5383b7 CB |
774 | switch (f->type) { |
775 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE: | |
776 | if (liblxcfs_functional()) | |
777 | return sys_devices_system_cpu_online_read(buf, size, offset, fi); | |
778 | ||
779 | return read_file_fuse_with_offset(LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE_PATH, | |
780 | buf, size, offset, f); | |
781 | case LXC_TYPE_SYS_DEVICES: | |
782 | break; | |
783 | case LXC_TYPE_SYS_DEVICES_SYSTEM: | |
784 | break; | |
785 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU: | |
786 | break; | |
787 | } | |
788 | ||
789 | return -EINVAL; | |
71f17cd2 YB |
790 | } |
791 | ||
2d7bcab7 | 792 | __lxcfs_fuse_ops int sys_read(const char *path, char *buf, size_t size, |
0d5383b7 | 793 | off_t offset, struct fuse_file_info *fi) |
71f17cd2 | 794 | { |
99b183fb | 795 | struct file_info *f = INTTYPE_TO_PTR(fi->fh); |
71f17cd2 | 796 | |
26b717d0 ZL |
797 | if (!liblxcfs_functional()) |
798 | return -EIO; | |
799 | ||
0d5383b7 CB |
800 | if (!liblxcfs_can_use_sys_cpu()) |
801 | return sys_read_legacy(path, buf, size, offset, fi); | |
802 | ||
71f17cd2 | 803 | switch (f->type) { |
36817cdd | 804 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_ONLINE: |
26b717d0 ZL |
805 | return sys_devices_system_cpu_online_read(buf, size, offset, fi); |
806 | case LXC_TYPE_SYS_DEVICES_SYSTEM_CPU_SUBFILE: | |
807 | return read_file_fuse_with_offset(path, buf, size, offset, f); | |
71f17cd2 | 808 | } |
700dd417 CB |
809 | |
810 | return -EINVAL; | |
71f17cd2 | 811 | } |