chiark / gitweb /
936f36839b7140cbc4027c8aa3cf045e0f179a0e
[elogind.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 = NULL;
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         if (n) {
235                 m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
236                 if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
237                     (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
238                     (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
239                         return r;
240
241                 if (private_tmp) {
242                         m->path = "/tmp";
243                         m->mode = PRIVATE_TMP;
244                         m++;
245
246                         m->path = "/var/tmp";
247                         m->mode = PRIVATE_VAR_TMP;
248                         m++;
249                 }
250
251                 assert(mounts + n == m);
252
253                 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
254                 drop_duplicates(mounts, &n);
255         }
256
257         /* Remount / as SLAVE so that nothing now mounted in the namespace
258            shows up in the parent */
259         if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
260                 return -errno;
261
262         for (m = mounts; m < mounts + n; ++m) {
263                 r = apply_mount(m, tmp_dir, var_tmp_dir);
264                 if (r < 0)
265                         goto undo_mounts;
266         }
267
268         for (m = mounts; m < mounts + n; ++m) {
269                 r = make_read_only(m);
270                 if (r < 0)
271                         goto undo_mounts;
272         }
273
274         /* Remount / as the desired mode */
275         if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
276                 r = -errno;
277                 goto undo_mounts;
278         }
279
280         return 0;
281
282 undo_mounts:
283         for (m = mounts; m < mounts + n; ++m) {
284                 if (m->done)
285                         umount2(m->path, MNT_DETACH);
286         }
287
288         return r;
289 }