chiark / gitweb /
ceeed2e1aede3ed09f143620eeb2fecf1cb5422b
[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 } 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, 0000, true, tmp_dir);
187         if (r < 0)
188                 goto fail2;
189
190         r = create_tmp_dir(var_tmp_dir_template, 0000, true, var_tmp_dir);
191         if (r < 0)
192                 goto fail1;
193
194         return 0;
195
196 fail1:
197         rmdir(*tmp_dir);
198         free(*tmp_dir);
199         *tmp_dir = NULL;
200
201 fail2:
202         return r;
203 }
204
205 int setup_namespace(char** read_write_dirs,
206                     char** read_only_dirs,
207                     char** inaccessible_dirs,
208                     char* tmp_dir,
209                     char* var_tmp_dir,
210                     bool private_tmp,
211                     unsigned mount_flags) {
212
213         unsigned n = strv_length(read_write_dirs) +
214                      strv_length(read_only_dirs) +
215                      strv_length(inaccessible_dirs) +
216                      (private_tmp ? 2 : 0);
217         BindMount *m, *mounts;
218         int r = 0;
219
220         if (!mount_flags)
221                 mount_flags = MS_SHARED;
222
223         if (unshare(CLONE_NEWNS) < 0) {
224                 r = -errno;
225                 goto fail;
226         }
227
228         m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
229         if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
230                 (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
231                 (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
232                 goto fail;
233
234         if (private_tmp) {
235                 m->path = "/tmp";
236                 m->mode = PRIVATE_TMP;
237                 m++;
238
239                 m->path = "/var/tmp";
240                 m->mode = PRIVATE_VAR_TMP;
241                 m++;
242         }
243
244         assert(mounts + n == m);
245
246         qsort(mounts, n, sizeof(BindMount), mount_path_compare);
247         drop_duplicates(mounts, &n);
248
249         /* Remount / as SLAVE so that nothing now mounted in the namespace
250            shows up in the parent */
251         if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
252                 r = -errno;
253                 goto fail;
254         }
255
256         for (m = mounts; m < mounts + n; ++m) {
257                 r = apply_mount(m, tmp_dir, var_tmp_dir);
258                 if (r < 0)
259                         goto undo_mounts;
260         }
261
262         for (m = mounts; m < mounts + n; ++m) {
263                 r = make_read_only(m);
264                 if (r < 0)
265                         goto undo_mounts;
266         }
267
268         /* Remount / as the desired mode */
269         if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
270                 r = -errno;
271                 goto undo_mounts;
272         }
273
274         return 0;
275
276 undo_mounts:
277         for (m = mounts; m < mounts + n; ++m) {
278                 if (m->done)
279                         umount2(m->path, MNT_DETACH);
280         }
281
282 fail:
283         return r;
284 }