chiark / gitweb /
cgroup: add missing equals for BlockIOWeight
[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;
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 }