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