]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/namespace.c
7e33d84156348497d574308a7557f7e330aa9b7f
[thirdparty/systemd.git] / src / core / namespace.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <sys/mount.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sched.h>
30 #include <sys/syscall.h>
31 #include <limits.h>
32 #include <linux/fs.h>
33
34 #include "strv.h"
35 #include "util.h"
36 #include "path-util.h"
37 #include "namespace.h"
38 #include "missing.h"
39 #include "execute.h"
40
41 typedef enum MountMode {
42 /* This is ordered by priority! */
43 INACCESSIBLE,
44 READONLY,
45 PRIVATE_TMP,
46 PRIVATE_VAR_TMP,
47 READWRITE
48 } MountMode;
49
50 typedef struct BindMount {
51 const char *path;
52 MountMode mode;
53 bool done;
54 } BindMount;
55
56 static int append_mounts(BindMount **p, char **strv, MountMode mode) {
57 char **i;
58
59 STRV_FOREACH(i, strv) {
60
61 if (!path_is_absolute(*i))
62 return -EINVAL;
63
64 (*p)->path = *i;
65 (*p)->mode = mode;
66 (*p)++;
67 }
68
69 return 0;
70 }
71
72 static int mount_path_compare(const void *a, const void *b) {
73 const BindMount *p = a, *q = b;
74
75 if (path_equal(p->path, q->path)) {
76
77 /* If the paths are equal, check the mode */
78 if (p->mode < q->mode)
79 return -1;
80
81 if (p->mode > q->mode)
82 return 1;
83
84 return 0;
85 }
86
87 /* If the paths are not equal, then order prefixes first */
88 if (path_startswith(p->path, q->path))
89 return 1;
90
91 if (path_startswith(q->path, p->path))
92 return -1;
93
94 return 0;
95 }
96
97 static void drop_duplicates(BindMount *m, unsigned *n) {
98 BindMount *f, *t, *previous;
99
100 assert(m);
101 assert(n);
102
103 for (f = m, t = m, previous = NULL; f < m+*n; f++) {
104
105 /* The first one wins */
106 if (previous && path_equal(f->path, previous->path))
107 continue;
108
109 t->path = f->path;
110 t->mode = f->mode;
111
112 previous = t;
113
114 t++;
115 }
116
117 *n = t - m;
118 }
119
120 static int apply_mount(
121 BindMount *m,
122 const char *tmp_dir,
123 const char *var_tmp_dir) {
124
125 const char *what;
126 int r;
127
128 assert(m);
129
130 switch (m->mode) {
131
132 case INACCESSIBLE:
133 what = "/run/systemd/inaccessible";
134 break;
135
136 case READONLY:
137 case READWRITE:
138 what = m->path;
139 break;
140
141 case PRIVATE_TMP:
142 what = tmp_dir;
143 break;
144
145 case PRIVATE_VAR_TMP:
146 what = var_tmp_dir;
147 break;
148
149 default:
150 assert_not_reached("Unknown mode");
151 }
152
153 assert(what);
154
155 r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
156 if (r >= 0)
157 log_debug("Successfully mounted %s to %s", what, m->path);
158
159 return r;
160 }
161
162 static int make_read_only(BindMount *m) {
163 int r;
164
165 assert(m);
166
167 if (m->mode != INACCESSIBLE && m->mode != READONLY)
168 return 0;
169
170 r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
171 if (r < 0)
172 return -errno;
173
174 return 0;
175 }
176
177 int setup_tmpdirs(char **tmp_dir,
178 char **var_tmp_dir) {
179 int r = 0;
180 char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
181 var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
182
183 assert(tmp_dir);
184 assert(var_tmp_dir);
185
186 r = create_tmp_dir(tmp_dir_template, tmp_dir);
187 if (r < 0)
188 return r;
189
190 r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
191 if (r == 0)
192 return 0;
193
194 /* failure */
195 rmdir(*tmp_dir);
196 rmdir(tmp_dir_template);
197 free(*tmp_dir);
198 *tmp_dir = NULL;
199
200 return r;
201 }
202
203 int setup_namespace(char** read_write_dirs,
204 char** read_only_dirs,
205 char** inaccessible_dirs,
206 char* tmp_dir,
207 char* var_tmp_dir,
208 bool private_tmp,
209 unsigned mount_flags) {
210
211 unsigned n = strv_length(read_write_dirs) +
212 strv_length(read_only_dirs) +
213 strv_length(inaccessible_dirs) +
214 (private_tmp ? 2 : 0);
215 BindMount *m, *mounts;
216 int r = 0;
217
218 if (!mount_flags)
219 mount_flags = MS_SHARED;
220
221 if (unshare(CLONE_NEWNS) < 0)
222 return -errno;
223
224 m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
225 if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
226 (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
227 (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
228 return r;
229
230 if (private_tmp) {
231 m->path = "/tmp";
232 m->mode = PRIVATE_TMP;
233 m++;
234
235 m->path = "/var/tmp";
236 m->mode = PRIVATE_VAR_TMP;
237 m++;
238 }
239
240 assert(mounts + n == m);
241
242 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
243 drop_duplicates(mounts, &n);
244
245 /* Remount / as SLAVE so that nothing now mounted in the namespace
246 shows up in the parent */
247 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
248 return -errno;
249
250 for (m = mounts; m < mounts + n; ++m) {
251 r = apply_mount(m, tmp_dir, var_tmp_dir);
252 if (r < 0)
253 goto undo_mounts;
254 }
255
256 for (m = mounts; m < mounts + n; ++m) {
257 r = make_read_only(m);
258 if (r < 0)
259 goto undo_mounts;
260 }
261
262 /* Remount / as the desired mode */
263 if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
264 r = -errno;
265 goto undo_mounts;
266 }
267
268 return 0;
269
270 undo_mounts:
271 for (m = mounts; m < mounts + n; ++m) {
272 if (m->done)
273 umount2(m->path, MNT_DETACH);
274 }
275
276 return r;
277 }