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