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