]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/mount-setup.c
core: move mount_setup_early() to main.c
[thirdparty/systemd.git] / src / core / mount-setup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8e274523
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
8e274523
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.
8e274523 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e274523
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/mount.h>
23#include <errno.h>
24#include <sys/stat.h>
25#include <stdlib.h>
26#include <string.h>
27#include <libgen.h>
28#include <assert.h>
5c0532d1 29#include <unistd.h>
1829dc9d 30#include <ftw.h>
8e274523
LP
31
32#include "mount-setup.h"
5ba2dc25 33#include "dev-setup.h"
8e274523 34#include "log.h"
c9af1080
LP
35#include "macro.h"
36#include "util.h"
5275d3c1 37#include "label.h"
0c85a4f3
LP
38#include "set.h"
39#include "strv.h"
49e942b2 40#include "mkdir.h"
9eb977db 41#include "path-util.h"
48ac500b 42#include "missing.h"
c481f78b 43#include "virt.h"
34e5a31e 44#include "efivars.h"
8e274523 45
bef2733f
LP
46#ifndef TTY_GID
47#define TTY_GID 5
48#endif
49
6aa220e0
KS
50typedef enum MountMode {
51 MNT_NONE = 0,
52 MNT_FATAL = 1 << 0,
53 MNT_IN_CONTAINER = 1 << 1,
54} MountMode;
55
ca714c0e
LP
56typedef struct MountPoint {
57 const char *what;
58 const char *where;
59 const char *type;
60 const char *options;
61 unsigned long flags;
6aa220e0
KS
62 bool (*condition_fn)(void);
63 MountMode mode;
ca714c0e
LP
64} MountPoint;
65
4ef31082 66/* The first three entries we might need before SELinux is up. The
160481f6
RS
67 * fourth (securityfs) is needed by IMA to load a custom policy. The
68 * other ones we can delay until SELinux and IMA are loaded. */
69#define N_EARLY_MOUNT 4
4ef31082 70
ca714c0e 71static const MountPoint mount_table[] = {
6aa220e0
KS
72 { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
73 NULL, MNT_FATAL|MNT_IN_CONTAINER },
74 { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
75 NULL, MNT_FATAL|MNT_IN_CONTAINER },
76 { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME,
77 NULL, MNT_FATAL|MNT_IN_CONTAINER },
78 { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
79 NULL, MNT_NONE },
6aa220e0
KS
80 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
81 NULL, MNT_FATAL|MNT_IN_CONTAINER },
82 { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
83 NULL, MNT_IN_CONTAINER },
84 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
85 NULL, MNT_FATAL|MNT_IN_CONTAINER },
86 { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
87 NULL, MNT_IN_CONTAINER },
88 { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
89 NULL, MNT_IN_CONTAINER },
c06bf414
KS
90 { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
91 NULL, MNT_NONE },
92#ifdef ENABLE_EFI
93 { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
94 is_efi_boot, MNT_NONE },
95#endif
8e274523
LP
96};
97
949c6510 98/* These are API file systems that might be mounted by other software,
46ff0ed7 99 * we just list them here so that we know that we should ignore them */
949c6510 100
eaeb18db
LP
101static const char ignore_paths[] =
102 /* SELinux file systems */
103 "/sys/fs/selinux\0"
104 "/selinux\0"
105 /* Legacy cgroup mount points */
106 "/dev/cgroup\0"
107 "/cgroup\0"
108 /* Legacy kernel file system */
109 "/proc/bus/usb\0"
110 /* Container bind mounts */
111 "/proc/sys\0"
112 "/dev/console\0"
c481f78b 113 "/proc/kmsg\0";
949c6510 114
dad08730
LP
115bool mount_point_is_api(const char *path) {
116 unsigned i;
117
118 /* Checks if this mount point is considered "API", and hence
119 * should be ignored */
120
ca714c0e 121 for (i = 0; i < ELEMENTSOF(mount_table); i ++)
449ddb2d 122 if (path_equal(path, mount_table[i].where))
dad08730
LP
123 return true;
124
57f2a956
KS
125 return path_startswith(path, "/sys/fs/cgroup/");
126}
127
128bool mount_point_ignore(const char *path) {
eaeb18db 129 const char *i;
57f2a956 130
eaeb18db
LP
131 NULSTR_FOREACH(i, ignore_paths)
132 if (path_equal(path, i))
949c6510
LP
133 return true;
134
57f2a956 135 return false;
dad08730
LP
136}
137
4ef31082 138static int mount_one(const MountPoint *p, bool relabel) {
8e274523
LP
139 int r;
140
ca714c0e 141 assert(p);
8e274523 142
6aa220e0
KS
143 if (p->condition_fn && !p->condition_fn())
144 return 0;
145
51b4af2c 146 /* Relabel first, just in case */
4ef31082 147 if (relabel)
c9bc0764 148 label_fix(p->where, true, true);
51b4af2c 149
c481f78b
LP
150 r = path_is_mount_point(p->where, true);
151 if (r < 0)
8e274523
LP
152 return r;
153
154 if (r > 0)
51b4af2c 155 return 0;
8e274523 156
c481f78b 157 /* Skip securityfs in a container */
6aa220e0 158 if (!(p->mode & MNT_IN_CONTAINER) && detect_container(NULL) > 0)
c481f78b
LP
159 return 0;
160
a04f58d6
LP
161 /* The access mode here doesn't really matter too much, since
162 * the mounted file system will take precedence anyway. */
d2e54fae 163 mkdir_p_label(p->where, 0755);
a04f58d6 164
8e274523 165 log_debug("Mounting %s to %s of type %s with options %s.",
ca714c0e
LP
166 p->what,
167 p->where,
168 p->type,
169 strna(p->options));
170
171 if (mount(p->what,
172 p->where,
173 p->type,
174 p->flags,
175 p->options) < 0) {
6aa220e0
KS
176 log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno));
177 return (p->mode & MNT_FATAL) ? -errno : 0;
8e274523
LP
178 }
179
51b4af2c 180 /* Relabel again, since we now mounted something fresh here */
4ef31082 181 if (relabel)
c9bc0764 182 label_fix(p->where, false, false);
5275d3c1 183
0c85a4f3 184 return 1;
8e274523
LP
185}
186
4ef31082
LP
187int mount_setup_early(void) {
188 unsigned i;
189 int r = 0;
190
191 assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
192
193 /* Do a minimal mount of /proc and friends to enable the most
194 * basic stuff, such as SELinux */
195 for (i = 0; i < N_EARLY_MOUNT; i ++) {
196 int j;
197
198 j = mount_one(mount_table + i, false);
199 if (r == 0)
200 r = j;
201 }
202
203 return r;
204}
205
0c85a4f3 206int mount_cgroup_controllers(char ***join_controllers) {
2076ca54
LP
207 int r;
208 FILE *f;
20c03b7b 209 char buf[LINE_MAX];
0c85a4f3 210 Set *controllers;
2076ca54 211
670802d4 212 /* Mount all available cgroup controllers that are built into the kernel. */
2076ca54 213
0c85a4f3
LP
214 f = fopen("/proc/cgroups", "re");
215 if (!f) {
016e9849
LP
216 log_error("Failed to enumerate cgroup controllers: %m");
217 return 0;
218 }
2076ca54 219
0c85a4f3
LP
220 controllers = set_new(string_hash_func, string_compare_func);
221 if (!controllers) {
14212119 222 r = log_oom();
0c85a4f3
LP
223 goto finish;
224 }
225
2076ca54 226 /* Ignore the header line */
bab45044 227 (void) fgets(buf, sizeof(buf), f);
2076ca54
LP
228
229 for (;;) {
0c85a4f3
LP
230 char *controller;
231 int enabled = 0;
2076ca54 232
16f6682d 233 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2076ca54
LP
234
235 if (feof(f))
236 break;
237
238 log_error("Failed to parse /proc/cgroups.");
239 r = -EIO;
240 goto finish;
241 }
242
600a328f
LP
243 if (!enabled) {
244 free(controller);
245 continue;
246 }
247
0c85a4f3
LP
248 r = set_put(controllers, controller);
249 if (r < 0) {
250 log_error("Failed to add controller to set.");
2076ca54 251 free(controller);
0c85a4f3
LP
252 goto finish;
253 }
254 }
255
256 for (;;) {
257 MountPoint p;
258 char *controller, *where, *options;
259 char ***k = NULL;
260
261 controller = set_steal_first(controllers);
262 if (!controller)
263 break;
264
265 if (join_controllers)
266 for (k = join_controllers; *k; k++)
267 if (strv_find(*k, controller))
268 break;
269
270 if (k && *k) {
271 char **i, **j;
272
273 for (i = *k, j = *k; *i; i++) {
274
275 if (!streq(*i, controller)) {
276 char *t;
277
278 t = set_remove(controllers, *i);
279 if (!t) {
280 free(*i);
281 continue;
282 }
283 free(t);
284 }
285
286 *(j++) = *i;
287 }
288
289 *j = NULL;
290
291 options = strv_join(*k, ",");
292 if (!options) {
0c85a4f3 293 free(controller);
14212119 294 r = log_oom();
0c85a4f3
LP
295 goto finish;
296 }
297
298 } else {
299 options = controller;
300 controller = NULL;
301 }
302
303 where = strappend("/sys/fs/cgroup/", options);
304 if (!where) {
0c85a4f3 305 free(options);
14212119 306 r = log_oom();
2076ca54
LP
307 goto finish;
308 }
309
310 zero(p);
311 p.what = "cgroup";
312 p.where = where;
313 p.type = "cgroup";
0c85a4f3 314 p.options = options;
2076ca54 315 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
2076ca54 316
4ef31082 317 r = mount_one(&p, true);
2076ca54
LP
318 free(controller);
319 free(where);
320
0c85a4f3
LP
321 if (r < 0) {
322 free(options);
2076ca54 323 goto finish;
0c85a4f3
LP
324 }
325
326 if (r > 0 && k && *k) {
327 char **i;
328
329 for (i = *k; *i; i++) {
330 char *t;
331
332 t = strappend("/sys/fs/cgroup/", *i);
333 if (!t) {
14212119 334 r = log_oom();
0c85a4f3
LP
335 free(options);
336 goto finish;
337 }
338
339 r = symlink(options, t);
340 free(t);
341
342 if (r < 0 && errno != EEXIST) {
343 log_error("Failed to create symlink: %m");
344 r = -errno;
345 free(options);
346 goto finish;
347 }
348 }
349 }
350
351 free(options);
2076ca54
LP
352 }
353
354 r = 0;
355
356finish:
0c85a4f3
LP
357 set_free_free(controllers);
358
2076ca54
LP
359 fclose(f);
360
361 return r;
362}
363
1829dc9d
LP
364static int nftw_cb(
365 const char *fpath,
366 const struct stat *sb,
367 int tflag,
368 struct FTW *ftwbuf) {
369
9fe117ea 370 /* No need to label /dev twice in a row... */
edb49778
LP
371 if (_unlikely_(ftwbuf->level == 0))
372 return FTW_CONTINUE;
373
c9bc0764 374 label_fix(fpath, false, false);
af65c248 375
edb49778 376 /* /run/initramfs is static data and big, no need to
af65c248 377 * dynamically relabel its contents at boot... */
edb49778
LP
378 if (_unlikely_(ftwbuf->level == 1 &&
379 tflag == FTW_D &&
380 streq(fpath, "/run/initramfs")))
381 return FTW_SKIP_SUBTREE;
9fe117ea 382
edb49778 383 return FTW_CONTINUE;
1829dc9d
LP
384};
385
0b3325e7 386int mount_setup(bool loaded_policy) {
5c0532d1 387
af65c248
LP
388 static const char relabel[] =
389 "/run/initramfs/root-fsck\0"
390 "/run/initramfs/shutdown\0";
391
8e274523 392 int r;
dad08730 393 unsigned i;
5ba2dc25 394 const char *j;
8e274523 395
4ef31082
LP
396 for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
397 r = mount_one(mount_table + i, true);
398
399 if (r < 0)
8e274523 400 return r;
4ef31082 401 }
8e274523 402
f1d19aa4
LP
403 /* Nodes in devtmpfs and /run need to be manually updated for
404 * the appropriate labels, after mounting. The other virtual
405 * API file systems like /sys and /proc do not need that, they
406 * use the same label for all their files. */
0b3325e7
LP
407 if (loaded_policy) {
408 usec_t before_relabel, after_relabel;
409 char timespan[FORMAT_TIMESPAN_MAX];
410
411 before_relabel = now(CLOCK_MONOTONIC);
412
edb49778
LP
413 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
414 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
0b3325e7 415
af65c248
LP
416 /* Explicitly relabel these */
417 NULSTR_FOREACH(j, relabel)
c9bc0764 418 label_fix(j, true, false);
af65c248 419
0b3325e7
LP
420 after_relabel = now(CLOCK_MONOTONIC);
421
422 log_info("Relabelled /dev and /run in %s.",
423 format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
3bbecb2f 424 }
1829dc9d 425
5c0532d1 426 /* Create a few default symlinks, which are normally created
f1d19aa4 427 * by udevd, but some scripts might need them before we start
5c0532d1 428 * udevd. */
01ed0e23 429 dev_setup(NULL);
5c0532d1 430
b3ac5f8c
LP
431 /* Mark the root directory as shared in regards to mount
432 * propagation. The kernel defaults to "private", but we think
433 * it makes more sense to have a default of "shared" so that
434 * nspawn and the container tools work out of the box. If
435 * specific setups need other settings they can reset the
436 * propagation mode to private if needed. */
c481f78b
LP
437 if (detect_container(NULL) <= 0)
438 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
439 log_warning("Failed to set up the root directory for shared mount propagation: %m");
b3ac5f8c 440
b925e726 441 /* Create a few directories we always want around */
d2e54fae
KS
442 mkdir_label("/run/systemd", 0755);
443 mkdir_label("/run/systemd/system", 0755);
b925e726 444
0c85a4f3 445 return 0;
8e274523 446}