chiark / gitweb /
core: output unit status output strings to console, only if we actually are changing...
[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 <assert.h>
28 #include <unistd.h>
29 #include <ftw.h>
30
31 #include "mount-setup.h"
32 #include "dev-setup.h"
33 #include "log.h"
34 #include "macro.h"
35 #include "util.h"
36 #include "label.h"
37 #include "set.h"
38 #include "strv.h"
39 #include "mkdir.h"
40 #include "path-util.h"
41 #include "missing.h"
42 #include "virt.h"
43 #include "efivars.h"
44 #include "smack-util.h"
45 #include "def.h"
46 #include "cgroup-util.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,
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         /* Container bind mounts */
124         "/proc/sys\0"
125         "/dev/console\0"
126         "/proc/kmsg\0";
127
128 bool mount_point_is_api(const char *path) {
129         unsigned i;
130
131         /* Checks if this mount point is considered "API", and hence
132          * should be ignored */
133
134         for (i = 0; i < ELEMENTSOF(mount_table); i ++)
135                 if (path_equal(path, mount_table[i].where))
136                         return true;
137
138         return path_startswith(path, "/sys/fs/cgroup/");
139 }
140
141 bool mount_point_ignore(const char *path) {
142         const char *i;
143
144         NULSTR_FOREACH(i, ignore_paths)
145                 if (path_equal(path, i))
146                         return true;
147
148         return false;
149 }
150
151 static int mount_one(const MountPoint *p, bool relabel) {
152         int r;
153
154         assert(p);
155
156         if (p->condition_fn && !p->condition_fn())
157                 return 0;
158
159         /* Relabel first, just in case */
160         if (relabel)
161                 label_fix(p->where, true, true);
162
163         r = path_is_mount_point(p->where, true);
164         if (r < 0)
165                 return r;
166
167         if (r > 0)
168                 return 0;
169
170         /* Skip securityfs in a container */
171         if (!(p->mode & MNT_IN_CONTAINER) && detect_container(NULL) > 0)
172                 return 0;
173
174         /* The access mode here doesn't really matter too much, since
175          * the mounted file system will take precedence anyway. */
176         if (relabel)
177                 mkdir_p_label(p->where, 0755);
178         else
179                 mkdir_p(p->where, 0755);
180
181         log_debug("Mounting %s to %s of type %s with options %s.",
182                   p->what,
183                   p->where,
184                   p->type,
185                   strna(p->options));
186
187         if (mount(p->what,
188                   p->where,
189                   p->type,
190                   p->flags,
191                   p->options) < 0) {
192                 log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s at %s: %m", p->type, p->where);
193                 return (p->mode & MNT_FATAL) ? -errno : 0;
194         }
195
196         /* Relabel again, since we now mounted something fresh here */
197         if (relabel)
198                 label_fix(p->where, false, false);
199
200         return 1;
201 }
202
203 int mount_setup_early(void) {
204         unsigned i;
205         int r = 0;
206
207         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
208
209         /* Do a minimal mount of /proc and friends to enable the most
210          * basic stuff, such as SELinux */
211         for (i = 0; i < N_EARLY_MOUNT; i ++)  {
212                 int j;
213
214                 j = mount_one(mount_table + i, false);
215                 if (r == 0)
216                         r = j;
217         }
218
219         return r;
220 }
221
222 int mount_cgroup_controllers(char ***join_controllers) {
223         _cleanup_set_free_free_ Set *controllers = NULL;
224         int r;
225
226         /* Mount all available cgroup controllers that are built into the kernel. */
227
228         controllers = set_new(&string_hash_ops);
229         if (!controllers)
230                 return log_oom();
231
232         r = cg_kernel_controllers(controllers);
233         if (r < 0)
234                 return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
235
236         for (;;) {
237                 _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
238                 MountPoint p = {
239                         .what = "cgroup",
240                         .type = "cgroup",
241                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
242                         .mode = MNT_IN_CONTAINER,
243                 };
244                 char ***k = NULL;
245
246                 controller = set_steal_first(controllers);
247                 if (!controller)
248                         break;
249
250                 if (join_controllers)
251                         for (k = join_controllers; *k; k++)
252                                 if (strv_find(*k, controller))
253                                         break;
254
255                 if (k && *k) {
256                         char **i, **j;
257
258                         for (i = *k, j = *k; *i; i++) {
259
260                                 if (!streq(*i, controller)) {
261                                         _cleanup_free_ char *t;
262
263                                         t = set_remove(controllers, *i);
264                                         if (!t) {
265                                                 free(*i);
266                                                 continue;
267                                         }
268                                 }
269
270                                 *(j++) = *i;
271                         }
272
273                         *j = NULL;
274
275                         options = strv_join(*k, ",");
276                         if (!options)
277                                 return log_oom();
278                 } else {
279                         options = controller;
280                         controller = NULL;
281                 }
282
283                 where = strappend("/sys/fs/cgroup/", options);
284                 if (!where)
285                         return log_oom();
286
287                 p.where = where;
288                 p.options = options;
289
290                 r = mount_one(&p, true);
291                 if (r < 0)
292                         return r;
293
294                 if (r > 0 && k && *k) {
295                         char **i;
296
297                         for (i = *k; *i; i++) {
298                                 _cleanup_free_ char *t = NULL;
299
300                                 t = strappend("/sys/fs/cgroup/", *i);
301                                 if (!t)
302                                         return log_oom();
303
304                                 r = symlink(options, t);
305                                 if (r < 0 && errno != EEXIST)
306                                         return log_error_errno(errno, "Failed to create symlink %s: %m", t);
307                         }
308                 }
309         }
310
311         /* Now that we mounted everything, let's make the tmpfs the
312          * cgroup file systems are mounted into read-only. */
313         (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
314
315         return 0;
316 }
317
318 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
319 static int nftw_cb(
320                 const char *fpath,
321                 const struct stat *sb,
322                 int tflag,
323                 struct FTW *ftwbuf) {
324
325         /* No need to label /dev twice in a row... */
326         if (_unlikely_(ftwbuf->level == 0))
327                 return FTW_CONTINUE;
328
329         label_fix(fpath, false, false);
330
331         /* /run/initramfs is static data and big, no need to
332          * dynamically relabel its contents at boot... */
333         if (_unlikely_(ftwbuf->level == 1 &&
334                       tflag == FTW_D &&
335                       streq(fpath, "/run/initramfs")))
336                 return FTW_SKIP_SUBTREE;
337
338         return FTW_CONTINUE;
339 };
340 #endif
341
342 int mount_setup(bool loaded_policy) {
343         unsigned i;
344         int r = 0;
345
346         for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
347                 int j;
348
349                 j = mount_one(mount_table + i, loaded_policy);
350                 if (r == 0)
351                         r = j;
352         }
353
354         if (r < 0)
355                 return r;
356
357 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
358         /* Nodes in devtmpfs and /run need to be manually updated for
359          * the appropriate labels, after mounting. The other virtual
360          * API file systems like /sys and /proc do not need that, they
361          * use the same label for all their files. */
362         if (loaded_policy) {
363                 usec_t before_relabel, after_relabel;
364                 char timespan[FORMAT_TIMESPAN_MAX];
365
366                 before_relabel = now(CLOCK_MONOTONIC);
367
368                 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
369                 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
370
371                 after_relabel = now(CLOCK_MONOTONIC);
372
373                 log_info("Relabelled /dev and /run in %s.",
374                          format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
375         }
376 #endif
377
378         /* Create a few default symlinks, which are normally created
379          * by udevd, but some scripts might need them before we start
380          * udevd. */
381         dev_setup(NULL);
382
383         /* Mark the root directory as shared in regards to mount
384          * propagation. The kernel defaults to "private", but we think
385          * it makes more sense to have a default of "shared" so that
386          * nspawn and the container tools work out of the box. If
387          * specific setups need other settings they can reset the
388          * propagation mode to private if needed. */
389         if (detect_container(NULL) <= 0)
390                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
391                         log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
392
393         /* Create a few directories we always want around, Note that
394          * sd_booted() checks for /run/systemd/system, so this mkdir
395          * really needs to stay for good, otherwise software that
396          * copied sd-daemon.c into their sources will misdetect
397          * systemd. */
398         mkdir_label("/run/systemd", 0755);
399         mkdir_label("/run/systemd/system", 0755);
400         mkdir_label("/run/systemd/inaccessible", 0000);
401
402         return 0;
403 }