chiark / gitweb /
Add set_consume which always takes ownership
[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         FILE *f;
215         char buf[LINE_MAX];
216         Set *controllers;
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                 r = log_oom();
229                 goto finish;
230         }
231
232         /* Ignore the header line */
233         (void) fgets(buf, sizeof(buf), f);
234
235         for (;;) {
236                 char *controller;
237                 int enabled = 0;
238
239                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
240
241                         if (feof(f))
242                                 break;
243
244                         log_error("Failed to parse /proc/cgroups.");
245                         r = -EIO;
246                         goto finish;
247                 }
248
249                 if (!enabled) {
250                         free(controller);
251                         continue;
252                 }
253
254                 r = set_consume(controllers, controller);
255                 if (r < 0) {
256                         log_error("Failed to add controller to set.");
257                         goto finish;
258                 }
259         }
260
261         for (;;) {
262                 MountPoint p;
263                 char *controller, *where, *options;
264                 char ***k = NULL;
265
266                 controller = set_steal_first(controllers);
267                 if (!controller)
268                         break;
269
270                 if (join_controllers)
271                         for (k = join_controllers; *k; k++)
272                                 if (strv_find(*k, controller))
273                                         break;
274
275                 if (k && *k) {
276                         char **i, **j;
277
278                         for (i = *k, j = *k; *i; i++) {
279
280                                 if (!streq(*i, controller)) {
281                                         char *t;
282
283                                         t = set_remove(controllers, *i);
284                                         if (!t) {
285                                                 free(*i);
286                                                 continue;
287                                         }
288                                         free(t);
289                                 }
290
291                                 *(j++) = *i;
292                         }
293
294                         *j = NULL;
295
296                         options = strv_join(*k, ",");
297                         if (!options) {
298                                 free(controller);
299                                 r = log_oom();
300                                 goto finish;
301                         }
302
303                 } else {
304                         options = controller;
305                         controller = NULL;
306                 }
307
308                 where = strappend("/sys/fs/cgroup/", options);
309                 if (!where) {
310                         free(options);
311                         r = log_oom();
312                         goto finish;
313                 }
314
315                 zero(p);
316                 p.what = "cgroup";
317                 p.where = where;
318                 p.type = "cgroup";
319                 p.options = options;
320                 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
321                 p.mode = MNT_IN_CONTAINER;
322
323                 r = mount_one(&p, true);
324                 free(controller);
325                 free(where);
326
327                 if (r < 0) {
328                         free(options);
329                         goto finish;
330                 }
331
332                 if (r > 0 && k && *k) {
333                         char **i;
334
335                         for (i = *k; *i; i++) {
336                                 _cleanup_free_ char *t;
337
338                                 t = strappend("/sys/fs/cgroup/", *i);
339                                 if (!t) {
340                                         r = log_oom();
341                                         free(options);
342                                         goto finish;
343                                 }
344
345                                 r = symlink(options, t);
346                                 if (r < 0 && errno != EEXIST) {
347                                         log_error("Failed to create symlink %s: %m", t);
348                                         r = -errno;
349                                         free(options);
350                                         goto finish;
351                                 }
352                         }
353                 }
354
355                 free(options);
356         }
357
358         r = 0;
359
360 finish:
361         set_free_free(controllers);
362
363         fclose(f);
364
365         return r;
366 }
367
368 static int nftw_cb(
369                 const char *fpath,
370                 const struct stat *sb,
371                 int tflag,
372                 struct FTW *ftwbuf) {
373
374         /* No need to label /dev twice in a row... */
375         if (_unlikely_(ftwbuf->level == 0))
376                 return FTW_CONTINUE;
377
378         label_fix(fpath, false, false);
379
380         /* /run/initramfs is static data and big, no need to
381          * dynamically relabel its contents at boot... */
382         if (_unlikely_(ftwbuf->level == 1 &&
383                       tflag == FTW_D &&
384                       streq(fpath, "/run/initramfs")))
385                 return FTW_SKIP_SUBTREE;
386
387         return FTW_CONTINUE;
388 };
389
390 int mount_setup(bool loaded_policy) {
391
392         static const char relabel[] =
393                 "/run/initramfs/root-fsck\0"
394                 "/run/initramfs/shutdown\0";
395
396         int r;
397         unsigned i;
398         const char *j;
399
400         for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
401                 r = mount_one(mount_table + i, true);
402
403                 if (r < 0)
404                         return r;
405         }
406
407         /* Nodes in devtmpfs and /run need to be manually updated for
408          * the appropriate labels, after mounting. The other virtual
409          * API file systems like /sys and /proc do not need that, they
410          * use the same label for all their files. */
411         if (loaded_policy) {
412                 usec_t before_relabel, after_relabel;
413                 char timespan[FORMAT_TIMESPAN_MAX];
414
415                 before_relabel = now(CLOCK_MONOTONIC);
416
417                 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
418                 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
419
420                 /* Explicitly relabel these */
421                 NULSTR_FOREACH(j, relabel)
422                         label_fix(j, true, false);
423
424                 after_relabel = now(CLOCK_MONOTONIC);
425
426                 log_info("Relabelled /dev and /run in %s.",
427                          format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
428         }
429
430         /* Create a few default symlinks, which are normally created
431          * by udevd, but some scripts might need them before we start
432          * udevd. */
433         dev_setup(NULL);
434
435         /* Mark the root directory as shared in regards to mount
436          * propagation. The kernel defaults to "private", but we think
437          * it makes more sense to have a default of "shared" so that
438          * nspawn and the container tools work out of the box. If
439          * specific setups need other settings they can reset the
440          * propagation mode to private if needed. */
441         if (detect_container(NULL) <= 0)
442                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
443                         log_warning("Failed to set up the root directory for shared mount propagation: %m");
444
445         /* Create a few directories we always want around, Note that
446          * sd_booted() checks for /run/systemd/system, so this mkdir
447          * really needs to stay for good, otherwise software that
448          * copied sd-daemon.c into their sources will misdetect
449          * systemd. */
450         mkdir_label("/run/systemd", 0755);
451         mkdir_label("/run/systemd/system", 0755);
452         mkdir_label("/run/systemd/inaccessible", 0000);
453
454         return 0;
455 }