]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/namespace.c
"-" prefix for InaccessibleDirectories and ReadOnlyDirectories
[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 bool ignore;
55 } BindMount;
56
57 static int append_mounts(BindMount **p, char **strv, MountMode mode) {
58 char **i;
59
60 STRV_FOREACH(i, strv) {
61
62 (*p)->ignore = false;
63
64 if ((mode == INACCESSIBLE || mode == READONLY) && (*i)[0] == '-') {
65 (*p)->ignore = true;
66 (*i)++;
67 }
68
69 if (!path_is_absolute(*i))
70 return -EINVAL;
71
72 (*p)->path = *i;
73 (*p)->mode = mode;
74 (*p)++;
75 }
76
77 return 0;
78 }
79
80 static int mount_path_compare(const void *a, const void *b) {
81 const BindMount *p = a, *q = b;
82
83 if (path_equal(p->path, q->path)) {
84
85 /* If the paths are equal, check the mode */
86 if (p->mode < q->mode)
87 return -1;
88
89 if (p->mode > q->mode)
90 return 1;
91
92 return 0;
93 }
94
95 /* If the paths are not equal, then order prefixes first */
96 if (path_startswith(p->path, q->path))
97 return 1;
98
99 if (path_startswith(q->path, p->path))
100 return -1;
101
102 return 0;
103 }
104
105 static void drop_duplicates(BindMount *m, unsigned *n) {
106 BindMount *f, *t, *previous;
107
108 assert(m);
109 assert(n);
110
111 for (f = m, t = m, previous = NULL; f < m+*n; f++) {
112
113 /* The first one wins */
114 if (previous && path_equal(f->path, previous->path))
115 continue;
116
117 t->path = f->path;
118 t->mode = f->mode;
119
120 previous = t;
121
122 t++;
123 }
124
125 *n = t - m;
126 }
127
128 static int apply_mount(
129 BindMount *m,
130 const char *tmp_dir,
131 const char *var_tmp_dir) {
132
133 const char *what;
134 int r;
135
136 assert(m);
137
138 switch (m->mode) {
139
140 case INACCESSIBLE:
141 what = "/run/systemd/inaccessible";
142 break;
143
144 case READONLY:
145 case READWRITE:
146 what = m->path;
147 break;
148
149 case PRIVATE_TMP:
150 what = tmp_dir;
151 break;
152
153 case PRIVATE_VAR_TMP:
154 what = var_tmp_dir;
155 break;
156
157 default:
158 assert_not_reached("Unknown mode");
159 }
160
161 assert(what);
162
163 r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
164 if (r >= 0)
165 log_debug("Successfully mounted %s to %s", what, m->path);
166 else if (m->ignore && errno == ENOENT)
167 r = 0;
168
169 return r;
170 }
171
172 static int make_read_only(BindMount *m) {
173 int r;
174
175 assert(m);
176
177 if (m->mode != INACCESSIBLE && m->mode != READONLY)
178 return 0;
179
180 r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
181 if (r < 0 && !(m->ignore && errno == ENOENT))
182 return -errno;
183
184 return 0;
185 }
186
187 int setup_tmpdirs(char **tmp_dir,
188 char **var_tmp_dir) {
189 int r = 0;
190 char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
191 var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
192
193 assert(tmp_dir);
194 assert(var_tmp_dir);
195
196 r = create_tmp_dir(tmp_dir_template, tmp_dir);
197 if (r < 0)
198 return r;
199
200 r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir);
201 if (r == 0)
202 return 0;
203
204 /* failure */
205 rmdir(*tmp_dir);
206 rmdir(tmp_dir_template);
207 free(*tmp_dir);
208 *tmp_dir = NULL;
209
210 return r;
211 }
212
213 int setup_namespace(char** read_write_dirs,
214 char** read_only_dirs,
215 char** inaccessible_dirs,
216 char* tmp_dir,
217 char* var_tmp_dir,
218 bool private_tmp,
219 unsigned mount_flags) {
220
221 unsigned n = strv_length(read_write_dirs) +
222 strv_length(read_only_dirs) +
223 strv_length(inaccessible_dirs) +
224 (private_tmp ? 2 : 0);
225 BindMount *m, *mounts;
226 int r = 0;
227
228 if (!mount_flags)
229 mount_flags = MS_SHARED;
230
231 if (unshare(CLONE_NEWNS) < 0)
232 return -errno;
233
234 m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
235 if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
236 (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
237 (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
238 return r;
239
240 if (private_tmp) {
241 m->path = "/tmp";
242 m->mode = PRIVATE_TMP;
243 m++;
244
245 m->path = "/var/tmp";
246 m->mode = PRIVATE_VAR_TMP;
247 m++;
248 }
249
250 assert(mounts + n == m);
251
252 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
253 drop_duplicates(mounts, &n);
254
255 /* Remount / as SLAVE so that nothing now mounted in the namespace
256 shows up in the parent */
257 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
258 return -errno;
259
260 for (m = mounts; m < mounts + n; ++m) {
261 r = apply_mount(m, tmp_dir, var_tmp_dir);
262 if (r < 0)
263 goto undo_mounts;
264 }
265
266 for (m = mounts; m < mounts + n; ++m) {
267 r = make_read_only(m);
268 if (r < 0)
269 goto undo_mounts;
270 }
271
272 /* Remount / as the desired mode */
273 if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
274 r = -errno;
275 goto undo_mounts;
276 }
277
278 return 0;
279
280 undo_mounts:
281 for (m = mounts; m < mounts + n; ++m) {
282 if (m->done)
283 umount2(m->path, MNT_DETACH);
284 }
285
286 return r;
287 }