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