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