Line data Source code
1 : /* SPDX-License-Identifier: GPL-2.0-only */
2 : /*
3 : * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4 : */
5 :
6 : #define _GNU_SOURCE
7 :
8 : #include "core/ns.h"
9 :
10 : #include <errno.h>
11 : #include <fcntl.h>
12 : #include <sched.h>
13 : #include <stdio.h>
14 : #include <sys/stat.h>
15 : #include <unistd.h>
16 :
17 : #include "core/helper.h"
18 : #include "core/logger.h"
19 :
20 : #define NS_DIR_PATH_LEN 32
21 :
22 : /**
23 : * Initialize a `bf_ns_info` structure for a given namespace.
24 : *
25 : * @param info `bf_ns_info` object to initialise. On failure, this parameter is
26 : * unchanged. Can't be NULL.
27 : * @param name Name of the namespace to open. Can't be NULL.
28 : * @param dir_fd File descriptor of the directory to open the namespace from.
29 : * @return 0 on success, or a negative errno value on failure.
30 : */
31 6 : static int _bf_ns_info_init(struct bf_ns_info *info, const char *name,
32 : int dir_fd)
33 : {
34 : _cleanup_close_ int fd = -1;
35 : struct stat stats;
36 : int r;
37 :
38 6 : bf_assert(info && name);
39 :
40 6 : fd = openat(dir_fd, name, O_RDONLY, 0);
41 6 : if (fd < 0)
42 0 : return -errno;
43 :
44 6 : r = fstat(fd, &stats);
45 6 : if (r)
46 0 : return -errno;
47 :
48 6 : info->fd = TAKE_FD(fd);
49 6 : info->inode = stats.st_ino;
50 :
51 : return 0;
52 : }
53 :
54 3 : int bf_ns_init(struct bf_ns *ns, pid_t pid)
55 : {
56 3 : _clean_bf_ns_ struct bf_ns _ns = bf_ns_default();
57 : _cleanup_close_ int dirfd = -1;
58 : char ns_dir_path[NS_DIR_PATH_LEN];
59 : int r;
60 :
61 3 : bf_assert(ns);
62 :
63 3 : (void)snprintf(ns_dir_path, NS_DIR_PATH_LEN, "/proc/%d/ns", pid);
64 3 : dirfd = open(ns_dir_path, O_DIRECTORY, O_RDONLY);
65 3 : if (dirfd < 0)
66 0 : return bf_err_r(errno, "failed to open ns directory '%s'", ns_dir_path);
67 :
68 3 : r = _bf_ns_info_init(&_ns.net, "net", dirfd);
69 3 : if (r) {
70 0 : return bf_err_r(r, "failed to read 'net' namespace in '%s'",
71 : ns_dir_path);
72 : }
73 :
74 3 : r = _bf_ns_info_init(&_ns.mnt, "mnt", dirfd);
75 3 : if (r) {
76 0 : return bf_err_r(r, "failed to read 'mnt' namespace in '%s'",
77 : ns_dir_path);
78 : }
79 :
80 3 : *ns = bf_ns_move(_ns);
81 :
82 3 : return 0;
83 : }
84 :
85 6 : void bf_ns_clean(struct bf_ns *ns)
86 : {
87 6 : bf_assert(ns);
88 :
89 : closep(&ns->net.fd);
90 : closep(&ns->mnt.fd);
91 6 : }
92 :
93 0 : int bf_ns_set(const struct bf_ns *ns, const struct bf_ns *oldns)
94 : {
95 : int r;
96 :
97 0 : if (!oldns || ns->net.inode != oldns->net.inode) {
98 0 : r = setns(ns->net.fd, CLONE_NEWNET);
99 0 : if (r)
100 0 : return bf_err_r(r, "failed to switch to a network namespace");
101 : }
102 :
103 0 : if (!oldns || ns->mnt.inode != oldns->mnt.inode) {
104 0 : r = setns(ns->mnt.fd, CLONE_NEWNS);
105 0 : if (r)
106 0 : return bf_err_r(r, "failed to switch to a mount namespace");
107 : }
108 :
109 : return 0;
110 : }
|