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