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