]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
15ae422b LP |
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 | |
5430f7f2 LP |
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 | |
15ae422b LP |
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 | |
5430f7f2 | 16 | Lesser General Public License for more details. |
15ae422b | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
15ae422b LP |
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> | |
25e870b5 | 32 | #include <linux/fs.h> |
15ae422b LP |
33 | |
34 | #include "strv.h" | |
35 | #include "util.h" | |
9eb977db | 36 | #include "path-util.h" |
15ae422b LP |
37 | #include "namespace.h" |
38 | #include "missing.h" | |
c17ec25e | 39 | #include "execute.h" |
15ae422b | 40 | |
c17ec25e | 41 | typedef enum MountMode { |
15ae422b LP |
42 | /* This is ordered by priority! */ |
43 | INACCESSIBLE, | |
44 | READONLY, | |
ac0930c8 LP |
45 | PRIVATE_TMP, |
46 | PRIVATE_VAR_TMP, | |
15ae422b | 47 | READWRITE |
c17ec25e | 48 | } MountMode; |
15ae422b | 49 | |
c17ec25e | 50 | typedef struct BindMount { |
15ae422b | 51 | const char *path; |
c17ec25e | 52 | MountMode mode; |
ac0930c8 | 53 | bool done; |
c17ec25e | 54 | } BindMount; |
15ae422b | 55 | |
c17ec25e | 56 | static int append_mounts(BindMount **p, char **strv, MountMode mode) { |
15ae422b LP |
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 | ||
c17ec25e MS |
72 | static int mount_path_compare(const void *a, const void *b) { |
73 | const BindMount *p = a, *q = b; | |
15ae422b LP |
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 | ||
c17ec25e MS |
97 | static void drop_duplicates(BindMount *m, unsigned *n) { |
98 | BindMount *f, *t, *previous; | |
15ae422b | 99 | |
c17ec25e | 100 | assert(m); |
15ae422b | 101 | assert(n); |
15ae422b | 102 | |
c17ec25e | 103 | for (f = m, t = m, previous = NULL; f < m+*n; f++) { |
15ae422b | 104 | |
ac0930c8 | 105 | /* The first one wins */ |
15ae422b LP |
106 | if (previous && path_equal(f->path, previous->path)) |
107 | continue; | |
108 | ||
109 | t->path = f->path; | |
110 | t->mode = f->mode; | |
111 | ||
15ae422b LP |
112 | previous = t; |
113 | ||
114 | t++; | |
115 | } | |
116 | ||
c17ec25e | 117 | *n = t - m; |
15ae422b LP |
118 | } |
119 | ||
ac0930c8 | 120 | static int apply_mount( |
c17ec25e | 121 | BindMount *m, |
ac0930c8 | 122 | const char *tmp_dir, |
c17ec25e | 123 | const char *var_tmp_dir) { |
ac0930c8 | 124 | |
15ae422b | 125 | const char *what; |
15ae422b | 126 | int r; |
15ae422b | 127 | |
c17ec25e | 128 | assert(m); |
15ae422b | 129 | |
c17ec25e | 130 | switch (m->mode) { |
15ae422b LP |
131 | |
132 | case INACCESSIBLE: | |
c17ec25e | 133 | what = "/run/systemd/inaccessible"; |
15ae422b LP |
134 | break; |
135 | ||
136 | case READONLY: | |
15ae422b | 137 | case READWRITE: |
c17ec25e | 138 | what = m->path; |
15ae422b LP |
139 | break; |
140 | ||
ac0930c8 LP |
141 | case PRIVATE_TMP: |
142 | what = tmp_dir; | |
143 | break; | |
144 | ||
145 | case PRIVATE_VAR_TMP: | |
146 | what = var_tmp_dir; | |
15ae422b | 147 | break; |
e364ad06 LP |
148 | |
149 | default: | |
150 | assert_not_reached("Unknown mode"); | |
15ae422b LP |
151 | } |
152 | ||
ac0930c8 | 153 | assert(what); |
15ae422b | 154 | |
c17ec25e | 155 | r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); |
ac0930c8 | 156 | if (r >= 0) |
c17ec25e | 157 | log_debug("Successfully mounted %s to %s", what, m->path); |
15ae422b | 158 | |
ac0930c8 LP |
159 | return r; |
160 | } | |
15ae422b | 161 | |
c17ec25e | 162 | static int make_read_only(BindMount *m) { |
ac0930c8 | 163 | int r; |
15ae422b | 164 | |
c17ec25e | 165 | assert(m); |
ac0930c8 | 166 | |
c17ec25e | 167 | if (m->mode != INACCESSIBLE && m->mode != READONLY) |
ac0930c8 LP |
168 | return 0; |
169 | ||
c17ec25e | 170 | r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); |
ac0930c8 LP |
171 | if (r < 0) |
172 | return -errno; | |
173 | ||
174 | return 0; | |
15ae422b LP |
175 | } |
176 | ||
c17ec25e MS |
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"; | |
15ae422b | 182 | |
c17ec25e MS |
183 | assert(tmp_dir); |
184 | assert(var_tmp_dir); | |
15ae422b | 185 | |
d34cd374 | 186 | r = create_tmp_dir(tmp_dir_template, tmp_dir); |
c17ec25e | 187 | if (r < 0) |
d34cd374 | 188 | return r; |
15ae422b | 189 | |
d34cd374 ZJS |
190 | r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir); |
191 | if (r == 0) | |
192 | return 0; | |
15ae422b | 193 | |
d34cd374 | 194 | /* failure */ |
c17ec25e | 195 | rmdir(*tmp_dir); |
d34cd374 | 196 | rmdir(tmp_dir_template); |
c17ec25e MS |
197 | free(*tmp_dir); |
198 | *tmp_dir = NULL; | |
15ae422b | 199 | |
c17ec25e MS |
200 | return r; |
201 | } | |
15ae422b | 202 | |
c17ec25e MS |
203 | int setup_namespace(char** read_write_dirs, |
204 | char** read_only_dirs, | |
205 | char** inaccessible_dirs, | |
206 | char* tmp_dir, | |
207 | char* var_tmp_dir, | |
208 | bool private_tmp, | |
209 | unsigned mount_flags) { | |
15ae422b | 210 | |
c17ec25e MS |
211 | unsigned n = strv_length(read_write_dirs) + |
212 | strv_length(read_only_dirs) + | |
213 | strv_length(inaccessible_dirs) + | |
214 | (private_tmp ? 2 : 0); | |
215 | BindMount *m, *mounts; | |
216 | int r = 0; | |
15ae422b | 217 | |
c17ec25e MS |
218 | if (!mount_flags) |
219 | mount_flags = MS_SHARED; | |
ac0930c8 | 220 | |
d5a3f0ea ZJS |
221 | if (unshare(CLONE_NEWNS) < 0) |
222 | return -errno; | |
15ae422b | 223 | |
c17ec25e MS |
224 | m = mounts = (BindMount *) alloca(n * sizeof(BindMount)); |
225 | if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 || | |
226 | (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 || | |
227 | (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0) | |
d5a3f0ea | 228 | return r; |
ac0930c8 | 229 | |
c17ec25e MS |
230 | if (private_tmp) { |
231 | m->path = "/tmp"; | |
232 | m->mode = PRIVATE_TMP; | |
233 | m++; | |
ac0930c8 | 234 | |
c17ec25e MS |
235 | m->path = "/var/tmp"; |
236 | m->mode = PRIVATE_VAR_TMP; | |
237 | m++; | |
15ae422b LP |
238 | } |
239 | ||
c17ec25e MS |
240 | assert(mounts + n == m); |
241 | ||
242 | qsort(mounts, n, sizeof(BindMount), mount_path_compare); | |
243 | drop_duplicates(mounts, &n); | |
15ae422b | 244 | |
ac0930c8 | 245 | /* Remount / as SLAVE so that nothing now mounted in the namespace |
dc4b0200 | 246 | shows up in the parent */ |
d5a3f0ea ZJS |
247 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) |
248 | return -errno; | |
15ae422b | 249 | |
c17ec25e MS |
250 | for (m = mounts; m < mounts + n; ++m) { |
251 | r = apply_mount(m, tmp_dir, var_tmp_dir); | |
c1d70f7c | 252 | if (r < 0) |
15ae422b | 253 | goto undo_mounts; |
c1d70f7c | 254 | } |
15ae422b | 255 | |
c17ec25e MS |
256 | for (m = mounts; m < mounts + n; ++m) { |
257 | r = make_read_only(m); | |
ac0930c8 LP |
258 | if (r < 0) |
259 | goto undo_mounts; | |
15ae422b LP |
260 | } |
261 | ||
ac0930c8 | 262 | /* Remount / as the desired mode */ |
c17ec25e | 263 | if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) { |
15ae422b LP |
264 | r = -errno; |
265 | goto undo_mounts; | |
266 | } | |
267 | ||
15ae422b LP |
268 | return 0; |
269 | ||
270 | undo_mounts: | |
c17ec25e MS |
271 | for (m = mounts; m < mounts + n; ++m) { |
272 | if (m->done) | |
273 | umount2(m->path, MNT_DETACH); | |
274 | } | |
15ae422b | 275 | |
15ae422b LP |
276 | return r; |
277 | } |