]> git.proxmox.com Git - mirror_lxcfs.git/blame - src/utils.c
proc_fuse: cap swap to global values
[mirror_lxcfs.git] / src / utils.c
CommitLineData
db0463bf 1/* SPDX-License-Identifier: LGPL-2.1+ */
1f5596dd
CB
2
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE
5#endif
6
7#ifndef FUSE_USE_VERSION
8#define FUSE_USE_VERSION 26
9#endif
10
11#define _FILE_OFFSET_BITS 64
12
b0f33646 13#include <ctype.h>
1d81c6a6
CB
14#include <errno.h>
15#include <fcntl.h>
1f5596dd 16#include <fuse.h>
1d81c6a6
CB
17#include <inttypes.h>
18#include <sched.h>
19#include <stdarg.h>
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
1f5596dd 25#include <sys/epoll.h>
1d81c6a6
CB
26#include <sys/stat.h>
27#include <sys/types.h>
4ec5c9da 28#include <sys/wait.h>
1d81c6a6
CB
29#include <unistd.h>
30
1f5596dd 31#include "bindings.h"
1d81c6a6
CB
32#include "config.h"
33#include "macro.h"
34#include "memory_utils.h"
35#include "utils.h"
36
37/*
38 * append the given formatted string to *src.
39 * src: a pointer to a char* in which to append the formatted string.
40 * sz: the number of characters printed so far, minus trailing \0.
41 * asz: the allocated size so far
42 * format: string format. See printf for details.
43 * ...: varargs. See printf for details.
44 */
87f7558b
CB
45/*
46 * append the given formatted string to *src.
47 * src: a pointer to a char* in which to append the formatted string.
48 * sz: the number of characters printed so far, minus trailing \0.
49 * asz: the allocated size so far
50 * format: string format. See printf for details.
51 * ...: varargs. See printf for details.
52 */
53char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
1d81c6a6
CB
54{
55 char tmp[BUF_RESERVE_SIZE];
87f7558b
CB
56 va_list args;
57 int tmplen;
1d81c6a6
CB
58
59 va_start (args, format);
87f7558b 60 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
1d81c6a6
CB
61 va_end(args);
62
63 if (!*src || tmplen + *sz + 1 >= *asz) {
87f7558b 64 char *str;
1d81c6a6 65 do {
87f7558b
CB
66 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
67 } while (!str);
68 *src = str;
1d81c6a6
CB
69 *asz += BUF_RESERVE_SIZE;
70 }
71 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
72 *sz += tmplen;
87f7558b
CB
73
74 return *src;
1d81c6a6
CB
75}
76
77/**
78 * in_same_namespace - Check whether two processes are in the same namespace.
79 * @pid1 - PID of the first process.
80 * @pid2 - PID of the second process.
81 * @ns - Name of the namespace to check. Must correspond to one of the names
82 * for the namespaces as shown in /proc/<pid/ns/
83 *
84 * If the two processes are not in the same namespace returns an fd to the
85 * namespace of the second process identified by @pid2. If the two processes are
86 * in the same namespace returns -EINVAL, -1 if an error occurred.
87 */
88static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
89{
05b7a16d 90 __do_close int ns_fd1 = -1, ns_fd2 = -1;
1d81c6a6
CB
91 int ret = -1;
92 struct stat ns_st1, ns_st2;
93
94 ns_fd1 = preserve_ns(pid1, ns);
95 if (ns_fd1 < 0) {
96 /* The kernel does not support this namespace. This is not an
97 * error.
98 */
99 if (errno == ENOENT)
100 return -EINVAL;
101
102 return -1;
103 }
104
105 ns_fd2 = preserve_ns(pid2, ns);
106 if (ns_fd2 < 0)
107 return -1;
108
109 ret = fstat(ns_fd1, &ns_st1);
110 if (ret < 0)
111 return -1;
112
113 ret = fstat(ns_fd2, &ns_st2);
114 if (ret < 0)
115 return -1;
116
117 /* processes are in the same namespace */
118 if ((ns_st1.st_dev == ns_st2.st_dev) && (ns_st1.st_ino == ns_st2.st_ino))
119 return -EINVAL;
120
121 /* processes are in different namespaces */
122 return move_fd(ns_fd2);
123}
124
125bool is_shared_pidns(pid_t pid)
126{
802b0d20
CB
127 __do_close int fd = -EBADF;
128
1d81c6a6
CB
129 if (pid != 1)
130 return false;
131
802b0d20
CB
132 fd = in_same_namespace(pid, getpid(), "pid");
133 if (fd == EINVAL)
1d81c6a6
CB
134 return true;
135
136 return false;
137}
138
139int preserve_ns(const int pid, const char *ns)
140{
141 int ret;
142/* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
143#define __NS_PATH_LEN 50
144 char path[__NS_PATH_LEN];
145
146 /* This way we can use this function to also check whether namespaces
147 * are supported by the kernel by passing in the NULL or the empty
148 * string.
149 */
150 ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
151 !ns || strcmp(ns, "") == 0 ? "" : "/",
152 !ns || strcmp(ns, "") == 0 ? "" : ns);
153 if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
154 errno = EFBIG;
155 return -1;
156 }
157
158 return open(path, O_RDONLY | O_CLOEXEC);
159}
580fe4df
CB
160
161void do_release_file_info(struct fuse_file_info *fi)
162{
99b183fb 163 struct file_info *f;
580fe4df 164
99b183fb 165 f = INTTYPE_TO_PTR(fi->fh);
580fe4df
CB
166 if (!f)
167 return;
168
169 fi->fh = 0;
170
171 free_disarm(f->controller);
172 free_disarm(f->cgroup);
173 free_disarm(f->file);
174 free_disarm(f->buf);
175 free_disarm(f);
176}
1f5596dd
CB
177
178#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
179
180bool wait_for_sock(int sock, int timeout)
181{
05b7a16d 182 __do_close int epfd = -EBADF;
1f5596dd 183 struct epoll_event ev;
915c14d0 184 int ret, now, starttime, deltatime;
1f5596dd
CB
185
186 if ((starttime = time(NULL)) < 0)
187 return false;
188
41eb015d
CB
189 epfd = epoll_create(1);
190 if (epfd < 0)
191 return log_error(false, "%s\n", "Failed to create epoll socket: %m");
1f5596dd
CB
192
193 ev.events = POLLIN_SET;
194 ev.data.fd = sock;
41eb015d
CB
195 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
196 return log_error(false, "Failed adding socket to epoll: %m");
1f5596dd
CB
197
198again:
41eb015d 199 if ((now = time(NULL)) < 0)
1f5596dd 200 return false;
1f5596dd
CB
201
202 deltatime = (starttime + timeout) - now;
41eb015d 203 if (deltatime < 0)
1f5596dd 204 return false;
41eb015d 205
1f5596dd
CB
206 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
207 if (ret < 0 && errno == EINTR)
208 goto again;
1f5596dd 209
41eb015d 210 if (ret <= 0)
1f5596dd 211 return false;
41eb015d 212
1f5596dd
CB
213 return true;
214}
215
216bool recv_creds(int sock, struct ucred *cred, char *v)
217{
dac3dc93 218 struct msghdr msg = {};
1f5596dd
CB
219 struct iovec iov;
220 struct cmsghdr *cmsg;
dac3dc93
CB
221 ssize_t ret;
222 char cmsgbuf[CMSG_SPACE(sizeof(*cred))] = {};
223 char buf = '1';
1f5596dd
CB
224 int optval = 1;
225
1f5596dd
CB
226 msg.msg_name = NULL;
227 msg.msg_namelen = 0;
228 msg.msg_control = cmsgbuf;
229 msg.msg_controllen = sizeof(cmsgbuf);
230
dac3dc93 231 iov.iov_base = &buf;
1f5596dd
CB
232 iov.iov_len = sizeof(buf);
233 msg.msg_iov = &iov;
234 msg.msg_iovlen = 1;
235
dac3dc93
CB
236 *v = buf;
237
238 ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
239 if (ret < 0)
240 return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
241
242 ret = write_nointr(sock, &buf, sizeof(buf));
243 if (ret != sizeof(buf))
244 return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
245
41eb015d
CB
246 if (!wait_for_sock(sock, 2))
247 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
248
1f5596dd 249 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
41eb015d
CB
250 if (ret < 0)
251 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno));
1f5596dd
CB
252
253 cmsg = CMSG_FIRSTHDR(&msg);
254
dac3dc93
CB
255 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(*cred)) &&
256 cmsg->cmsg_level == SOL_SOCKET &&
257 cmsg->cmsg_type == SCM_CREDENTIALS) {
1f5596dd
CB
258 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
259 }
dac3dc93 260 *v = buf;
1f5596dd
CB
261
262 return true;
263}
264
265static int msgrecv(int sockfd, void *buf, size_t len)
266{
267 if (!wait_for_sock(sockfd, 2))
268 return -1;
41eb015d 269
1f5596dd
CB
270 return recv(sockfd, buf, len, MSG_DONTWAIT);
271}
272
273int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
274{
275 struct msghdr msg = { 0 };
276 struct iovec iov;
277 struct cmsghdr *cmsg;
278 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
279 char buf[1];
280 buf[0] = 'p';
281
41eb015d
CB
282 if (pingfirst && msgrecv(sock, buf, 1) != 1)
283 return log_error(SEND_CREDS_FAIL, "%s - Failed getting reply from server over socketpair: %d",
284 strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
285
286 msg.msg_control = cmsgbuf;
287 msg.msg_controllen = sizeof(cmsgbuf);
288
289 cmsg = CMSG_FIRSTHDR(&msg);
290 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
291 cmsg->cmsg_level = SOL_SOCKET;
292 cmsg->cmsg_type = SCM_CREDENTIALS;
293 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
294
295 msg.msg_name = NULL;
296 msg.msg_namelen = 0;
297
298 buf[0] = v;
299 iov.iov_base = buf;
300 iov.iov_len = sizeof(buf);
301 msg.msg_iov = &iov;
302 msg.msg_iovlen = 1;
303
304 if (sendmsg(sock, &msg, 0) < 0) {
1f5596dd 305 if (errno == 3)
41eb015d
CB
306 return log_error(SEND_CREDS_NOTSK, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_NOTSK);
307
308 return log_error(SEND_CREDS_FAIL, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
309 }
310
311 return SEND_CREDS_OK;
312}
313
314int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
315{
316 __do_free char *line = NULL;
317 __do_fclose FILE *f = NULL;
318 size_t linelen = 0, total_len = 0;
319 char *cache = d->buf;
320 size_t cache_size = d->buflen;
321
dbb1f822 322 f = fopen(path, "re");
1f5596dd
CB
323 if (!f)
324 return 0;
325
326 while (getline(&line, &linelen, f) != -1) {
327 ssize_t l = snprintf(cache, cache_size, "%s", line);
41eb015d
CB
328 if (l < 0)
329 return log_error(0, "Failed to write cache");
330 if (l >= cache_size)
331 return log_error(0, "Write to cache was truncated");
332
1f5596dd
CB
333 cache += l;
334 cache_size -= l;
335 total_len += l;
336 }
337
338 d->size = total_len;
339 if (total_len > size)
340 total_len = size;
341
342 /* read from off 0 */
343 memcpy(buf, d->buf, total_len);
344
345 if (d->size > total_len)
346 d->cached = d->size - total_len;
cbfc55fd 347
1f5596dd
CB
348 return total_len;
349}
4ec5c9da 350
cbfc55fd
CB
351int read_file_fuse_with_offset(const char *path, char *buf, size_t size,
352 off_t offset, struct file_info *d)
353{
354 if (offset) {
355 ssize_t total_len = 0;
356 char *cache = d->buf;
357 int left;
358
359 if (offset > d->size)
360 return -EINVAL;
361
362 if (!d->cached)
363 return 0;
364
365 left = d->size - offset;
366 total_len = left > size ? size : left;
367 memcpy(buf, cache + offset, total_len);
368
369 return total_len;
370 }
371
372 return read_file_fuse(path, buf, size, d);
373}
374
4ec5c9da
CB
375#define INITSCOPE "/init.scope"
376void prune_init_slice(char *cg)
377{
378 char *point;
379 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
380
381 if (cg_len < initscope_len)
382 return;
383
384 point = cg + cg_len - initscope_len;
385 if (strcmp(point, INITSCOPE) == 0) {
386 if (point == cg)
700dd417 387 *(point + 1) = '\0';
4ec5c9da
CB
388 else
389 *point = '\0';
390 }
391}
392
393int wait_for_pid(pid_t pid)
394{
395 int status, ret;
396
397 if (pid <= 0)
398 return -1;
399
400again:
401 ret = waitpid(pid, &status, 0);
402 if (ret == -1) {
403 if (errno == EINTR)
404 goto again;
405 return -1;
406 }
407 if (ret != pid)
408 goto again;
409 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
410 return -1;
411 return 0;
412}
757a63e7
CB
413
414static ssize_t read_nointr(int fd, void *buf, size_t count)
415{
416 ssize_t ret;
417again:
418 ret = read(fd, buf, count);
419 if (ret < 0 && errno == EINTR)
420 goto again;
421
422 return ret;
423}
424
425static void *must_realloc(void *orig, size_t sz)
426{
427 void *ret;
428
429 do {
430 ret = realloc(orig, sz);
431 } while (!ret);
432
433 return ret;
434}
435
28519477 436static char *fd_to_buf(int fd, size_t *length)
757a63e7 437{
757a63e7 438 __do_free char *copy = NULL;
757a63e7
CB
439
440 if (!length)
441 return NULL;
442
757a63e7
CB
443 *length = 0;
444 for (;;) {
28519477
CB
445 ssize_t bytes_read;
446 char buf[4096];
757a63e7
CB
447 char *old = copy;
448
28519477
CB
449 bytes_read = read_nointr(fd, buf, sizeof(buf));
450 if (bytes_read < 0)
757a63e7 451 return NULL;
28519477
CB
452
453 if (!bytes_read)
757a63e7
CB
454 break;
455
28519477
CB
456 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
457 memcpy(copy + *length, buf, bytes_read);
458 *length += bytes_read;
757a63e7
CB
459 }
460
461 return move_ptr(copy);
462}
463
28519477
CB
464static char *file_to_buf(const char *path, size_t *length)
465{
05b7a16d 466 __do_close int fd = -EBADF;
28519477
CB
467
468 if (!length)
469 return NULL;
470
471 fd = open(path, O_RDONLY | O_CLOEXEC);
472 if (fd < 0)
473 return NULL;
474
475 return fd_to_buf(fd, length);
476}
477
757a63e7
CB
478FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
479{
480 __do_free char *buf = NULL;
481 size_t len = 0;
482 FILE *f;
483
484 buf = file_to_buf(path, &len);
485 if (!buf)
486 return NULL;
487
488 f = fmemopen(buf, len, mode);
489 if (!f)
490 return NULL;
491 *caller_freed_buffer = move_ptr(buf);
492 return f;
493}
28519477 494
28519477
CB
495FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
496{
497 __do_free char *buf = NULL;
498 size_t len = 0;
499 FILE *f;
500
501 buf = fd_to_buf(fd, &len);
502 if (!buf)
503 return NULL;
504
505 f = fmemopen(buf, len, mode);
506 if (!f)
507 return NULL;
508
509 *caller_freed_buffer = move_ptr(buf);
510 return f;
511}
2f543378
CB
512
513ssize_t write_nointr(int fd, const void *buf, size_t count)
514{
515 ssize_t ret;
516
517 do {
518 ret = write(fd, buf, count);
519 } while (ret < 0 && errno == EINTR);
520
521 return ret;
522}
b0f33646
CB
523
524int safe_uint64(const char *numstr, uint64_t *converted, int base)
525{
526 char *err = NULL;
527 uint64_t u;
528
529 while (isspace(*numstr))
530 numstr++;
531
532 if (*numstr == '-')
533 return -EINVAL;
534
535 errno = 0;
536 u = strtoull(numstr, &err, base);
537 if (errno == ERANGE && u == UINT64_MAX)
538 return -ERANGE;
539
540 if (err == numstr || *err != '\0')
541 return -EINVAL;
542
543 *converted = u;
544 return 0;
545}