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