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