chiark / gitweb /
machine-image,mount-setup: minor coding style fixes
[elogind.git] / src / core / mount-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <ftw.h>
23 #include <stdlib.h>
24 #include <sys/mount.h>
25 //#include <sys/statvfs.h>
26 #include <unistd.h>
27
28 #include "alloc-util.h"
29 //#include "bus-util.h"
30 #include "cgroup-util.h"
31 //#include "dev-setup.h"
32 //#include "efivars.h"
33 //#include "fileio.h"
34 #include "fs-util.h"
35 #include "label.h"
36 //#include "log.h"
37 #include "macro.h"
38 //#include "missing.h"
39 #include "mkdir.h"
40 #include "mount-setup.h"
41 #include "mount-util.h"
42 #include "path-util.h"
43 //#include "set.h"
44 //#include "smack-util.h"
45 //#include "strv.h"
46 #include "user-util.h"
47 //#include "util.h"
48 #include "virt.h"
49
50 /// Additional includes needed by elogind
51 #include "string-util.h"
52
53 typedef enum MountMode {
54         MNT_NONE  =           0,
55         MNT_FATAL =           1 <<  0,
56         MNT_IN_CONTAINER =    1 <<  1,
57         MNT_CHECK_WRITABLE  = 1 <<  2,
58 } MountMode;
59
60 typedef struct MountPoint {
61         const char *what;
62         const char *where;
63         const char *type;
64         const char *options;
65         unsigned long flags;
66         bool (*condition_fn)(void);
67         MountMode mode;
68 } MountPoint;
69
70 /* The first three entries we might need before SELinux is up. The
71  * fourth (securityfs) is needed by IMA to load a custom policy. The
72  * other ones we can delay until SELinux and IMA are loaded. When
73  * SMACK is enabled we need smackfs, too, so it's a fifth one. */
74 #if ENABLE_SMACK
75 #define N_EARLY_MOUNT 5
76 #else
77 #define N_EARLY_MOUNT 4
78 #endif
79
80 static const MountPoint mount_table[] = {
81 #if 0 /// UNNEEDED by elogind
82         { "sysfs",       "/sys",                      "sysfs",      NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
83           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
84         { "proc",        "/proc",                     "proc",       NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
85           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
86         { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755",                MS_NOSUID|MS_STRICTATIME,
87           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
88         { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
89           NULL,          MNT_NONE                   },
90 #if ENABLE_SMACK
91         { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",            MS_NOSUID|MS_NOEXEC|MS_NODEV,
92           mac_smack_use, MNT_FATAL                  },
93         { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
94           mac_smack_use, MNT_FATAL                  },
95 #endif
96         { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
97           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
98         { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
99           NULL,          MNT_IN_CONTAINER           },
100 #if ENABLE_SMACK
101         { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*",  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
102           mac_smack_use, MNT_FATAL                  },
103 #endif
104         { "tmpfs",       "/run",                      "tmpfs",      "mode=755",                MS_NOSUID|MS_NODEV|MS_STRICTATIME,
105           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
106 #endif // 0
107         { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",              MS_NOSUID|MS_NOEXEC|MS_NODEV,
108           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
109         { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
110           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
111         { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755",                MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
112           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
113         { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",              MS_NOSUID|MS_NOEXEC|MS_NODEV,
114           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
115         { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
116           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
117 #if 0 /// UNNEEDED by elogind
118         { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
119           cg_is_legacy_wanted, MNT_IN_CONTAINER     },
120         { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",       MS_NOSUID|MS_NOEXEC|MS_NODEV,
121           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
122         { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
123           NULL,          MNT_NONE                   },
124 #if ENABLE_EFI
125         { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
126           is_efi_boot,   MNT_NONE                   },
127 #endif
128         { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                MS_NOSUID|MS_NOEXEC|MS_NODEV,
129           NULL,          MNT_NONE,                  },
130 #else
131         { "cgroup",      "/sys/fs/cgroup/elogind",    "cgroup",     "none,name=elogind,release_agent="SYSTEMD_CGROUP_AGENT_PATH",xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
132           cg_is_legacy_wanted, MNT_IN_CONTAINER  },
133         { "cgroup",      "/sys/fs/cgroup/elogind",    "cgroup",     "none,name=elogind,release_agent="SYSTEMD_CGROUP_AGENT_PATH,         MS_NOSUID|MS_NOEXEC|MS_NODEV,
134           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
135 #endif // 0
136 };
137
138 #if 0 /// UNNEEDED by elogind
139 /* These are API file systems that might be mounted by other software,
140  * we just list them here so that we know that we should ignore them */
141
142 static const char ignore_paths[] =
143         /* SELinux file systems */
144         "/sys/fs/selinux\0"
145         /* Container bind mounts */
146         "/proc/sys\0"
147         "/dev/console\0"
148         "/proc/kmsg\0";
149
150 bool mount_point_is_api(const char *path) {
151         unsigned i;
152
153         /* Checks if this mount point is considered "API", and hence
154          * should be ignored */
155
156         for (i = 0; i < ELEMENTSOF(mount_table); i ++)
157                 if (path_equal(path, mount_table[i].where))
158                         return true;
159
160         return path_startswith(path, "/sys/fs/cgroup/");
161 }
162
163 bool mount_point_ignore(const char *path) {
164         const char *i;
165
166         NULSTR_FOREACH(i, ignore_paths)
167                 if (path_equal(path, i))
168                         return true;
169
170         return false;
171 }
172 #endif // 0
173
174 static int mount_one(const MountPoint *p, bool relabel) {
175         int r, priority;
176
177         assert(p);
178
179         priority = (p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG;
180
181         if (p->condition_fn && !p->condition_fn())
182                 return 0;
183
184         /* Relabel first, just in case */
185         if (relabel)
186                 (void) label_fix(p->where, LABEL_IGNORE_ENOENT|LABEL_IGNORE_EROFS);
187
188         r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
189         if (r < 0 && r != -ENOENT) {
190                 log_full_errno(priority, r, "Failed to determine whether %s is a mount point: %m", p->where);
191                 return (p->mode & MNT_FATAL) ? r : 0;
192         }
193         if (r > 0)
194                 return 0;
195
196         /* Skip securityfs in a container */
197         if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
198                 return 0;
199
200         /* The access mode here doesn't really matter too much, since
201          * the mounted file system will take precedence anyway. */
202         if (relabel)
203                 (void) mkdir_p_label(p->where, 0755);
204         else
205                 (void) mkdir_p(p->where, 0755);
206
207         log_debug("Mounting %s to %s of type %s with options %s.",
208                   p->what,
209                   p->where,
210                   p->type,
211                   strna(p->options));
212
213         if (mount(p->what,
214                   p->where,
215                   p->type,
216                   p->flags,
217                   p->options) < 0) {
218                 log_full_errno(priority, errno, "Failed to mount %s at %s: %m", p->type, p->where);
219                 return (p->mode & MNT_FATAL) ? -errno : 0;
220         }
221
222         /* Relabel again, since we now mounted something fresh here */
223         if (relabel)
224                 (void) label_fix(p->where, 0);
225
226         if (p->mode & MNT_CHECK_WRITABLE) {
227                 if (access(p->where, W_OK) < 0) {
228                         r = -errno;
229
230                         (void) umount(p->where);
231                         (void) rmdir(p->where);
232
233                         log_full_errno(priority, r, "Mount point %s not writable after mounting: %m", p->where);
234                         return (p->mode & MNT_FATAL) ? r : 0;
235                 }
236         }
237
238         return 1;
239 }
240
241 static int mount_points_setup(unsigned n, bool loaded_policy) {
242         unsigned i;
243         int r = 0;
244
245         for (i = 0; i < n; i ++) {
246                 int j;
247
248                 j = mount_one(mount_table + i, loaded_policy);
249                 if (j != 0 && r >= 0)
250                         r = j;
251         }
252
253         return r;
254 }
255
256 #if 0 /// UNNEEDED by elogind
257 int mount_setup_early(void) {
258         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
259
260         /* Do a minimal mount of /proc and friends to enable the most
261          * basic stuff, such as SELinux */
262         return mount_points_setup(N_EARLY_MOUNT, false);
263 }
264
265 int mount_cgroup_controllers(char ***join_controllers) {
266         _cleanup_set_free_free_ Set *controllers = NULL;
267         bool has_argument = !!join_controllers;
268         int r;
269
270         if (!cg_is_legacy_wanted())
271                 return 0;
272
273         /* Mount all available cgroup controllers that are built into the kernel. */
274
275         if (!has_argument)
276                 /* The defaults:
277                  * mount "cpu" + "cpuacct" together, and "net_cls" + "net_prio".
278                  *
279                  * We'd like to add "cpuset" to the mix, but "cpuset" doesn't really
280                  * work for groups with no initialized attributes.
281                  */
282                 join_controllers = (char**[]) {
283                         STRV_MAKE("cpu", "cpuacct"),
284                         STRV_MAKE("net_cls", "net_prio"),
285                         NULL,
286                 };
287
288         r = cg_kernel_controllers(&controllers);
289         if (r < 0)
290                 return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
291
292         for (;;) {
293                 _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
294                 MountPoint p = {
295                         .what = "cgroup",
296                         .type = "cgroup",
297                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
298                         .mode = MNT_IN_CONTAINER,
299                 };
300                 char ***k = NULL;
301
302                 controller = set_steal_first(controllers);
303                 if (!controller)
304                         break;
305
306                 for (k = join_controllers; *k; k++)
307                         if (strv_find(*k, controller))
308                                 break;
309
310                 if (k && *k) {
311                         char **i, **j;
312
313                         for (i = *k, j = *k; *i; i++) {
314
315                                 if (!streq(*i, controller)) {
316                                         _cleanup_free_ char *t;
317
318                                         t = set_remove(controllers, *i);
319                                         if (!t) {
320                                                 if (has_argument)
321                                                         free(*i);
322                                                 continue;
323                                         }
324                                 }
325
326                                 *(j++) = *i;
327                         }
328
329                         *j = NULL;
330
331                         options = strv_join(*k, ",");
332                         if (!options)
333                                 return log_oom();
334                 } else
335                         options = TAKE_PTR(controller);
336
337                 where = strappend("/sys/fs/cgroup/", options);
338                 if (!where)
339                         return log_oom();
340
341                 p.where = where;
342                 p.options = options;
343
344                 r = mount_one(&p, true);
345                 if (r < 0)
346                         return r;
347
348                 if (r > 0 && k && *k) {
349                         char **i;
350
351                         for (i = *k; *i; i++) {
352                                 _cleanup_free_ char *t = NULL;
353
354                                 t = strappend("/sys/fs/cgroup/", *i);
355                                 if (!t)
356                                         return log_oom();
357
358                                 r = symlink(options, t);
359                                 if (r >= 0) {
360 #ifdef SMACK_RUN_LABEL
361                                         _cleanup_free_ char *src;
362                                         src = strappend("/sys/fs/cgroup/", options);
363                                         if (!src)
364                                                 return log_oom();
365                                         r = mac_smack_copy(t, src);
366                                         if (r < 0 && r != -EOPNOTSUPP)
367                                                 return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
368 #endif
369                                 } else if (errno != EEXIST)
370                                         return log_error_errno(errno, "Failed to create symlink %s: %m", t);
371                         }
372                 }
373         }
374
375         /* Now that we mounted everything, let's make the tmpfs the
376          * cgroup file systems are mounted into read-only. */
377         (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
378
379         return 0;
380 }
381
382 #if HAVE_SELINUX || ENABLE_SMACK
383 static int nftw_cb(
384                 const char *fpath,
385                 const struct stat *sb,
386                 int tflag,
387                 struct FTW *ftwbuf) {
388
389         /* No need to label /dev twice in a row... */
390         if (_unlikely_(ftwbuf->level == 0))
391                 return FTW_CONTINUE;
392
393         (void) label_fix(fpath, 0);
394
395         /* /run/initramfs is static data and big, no need to
396          * dynamically relabel its contents at boot... */
397         if (_unlikely_(ftwbuf->level == 1 &&
398                       tflag == FTW_D &&
399                       streq(fpath, "/run/initramfs")))
400                 return FTW_SKIP_SUBTREE;
401
402         return FTW_CONTINUE;
403 };
404
405 static int relabel_cgroup_filesystems(void) {
406         int r;
407         struct statfs st;
408
409         r = cg_all_unified();
410         if (r == 0) {
411                 /* Temporarily remount the root cgroup filesystem to give it a proper label. Do this
412                    only when the filesystem has been already populated by a previous instance of systemd
413                    running from initrd. Otherwise don't remount anything and leave the filesystem read-write
414                    for the cgroup filesystems to be mounted inside. */
415                 if (statfs("/sys/fs/cgroup", &st) < 0)
416                         return log_error_errno(errno, "Failed to determine mount flags for /sys/fs/cgroup: %m");
417
418                 if (st.f_flags & ST_RDONLY)
419                         (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT, NULL);
420
421                 (void) label_fix("/sys/fs/cgroup", 0);
422                 (void) nftw("/sys/fs/cgroup", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
423
424                 if (st.f_flags & ST_RDONLY)
425                         (void) mount(NULL, "/sys/fs/cgroup", NULL, MS_REMOUNT|MS_RDONLY, NULL);
426
427         } else if (r < 0)
428                 return log_error_errno(r, "Failed to determine whether we are in all unified mode: %m");
429
430         return 0;
431 }
432 #endif
433 #endif // 0
434
435 int mount_setup(bool loaded_policy) {
436         int r = 0;
437
438         r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
439         if (r < 0)
440                 return r;
441
442 #if 0 /// elogind does not control /, /dev, /run and /run/systemd/* are setup elsewhere.
443 #if HAVE_SELINUX || ENABLE_SMACK
444         /* Nodes in devtmpfs and /run need to be manually updated for
445          * the appropriate labels, after mounting. The other virtual
446          * API file systems like /sys and /proc do not need that, they
447          * use the same label for all their files. */
448         if (loaded_policy) {
449                 usec_t before_relabel, after_relabel;
450                 char timespan[FORMAT_TIMESPAN_MAX];
451
452                 before_relabel = now(CLOCK_MONOTONIC);
453
454                 (void) nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
455                 (void) nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
456                 (void) nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
457
458                 r = relabel_cgroup_filesystems();
459                 if (r < 0)
460                         return r;
461
462                 after_relabel = now(CLOCK_MONOTONIC);
463
464                 log_info("Relabelled /dev, /run and /sys/fs/cgroup in %s.",
465                          format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
466         }
467 #endif
468
469         /* Create a few default symlinks, which are normally created
470          * by udevd, but some scripts might need them before we start
471          * udevd. */
472         dev_setup(NULL, UID_INVALID, GID_INVALID);
473
474         /* Mark the root directory as shared in regards to mount propagation. The kernel defaults to "private", but we
475          * think it makes more sense to have a default of "shared" so that nspawn and the container tools work out of
476          * the box. If specific setups need other settings they can reset the propagation mode to private if
477          * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a
478          * container manager we assume the container manager knows what it is doing (for example, because it set up
479          * some directories with different propagation modes). */
480         if (detect_container() <= 0)
481                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
482                         log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
483
484 #endif // 0
485         /* Create a few directories we always want around, Note that sd_booted() checks for /run/systemd/system, so
486          * this mkdir really needs to stay for good, otherwise software that copied sd-daemon.c into their sources will
487          * misdetect systemd. */
488         (void) mkdir_label("/run/systemd", 0755);
489 #if 0 /// Yeah, but elogind is not used with systemd, so this directory would be toxic.
490         (void) mkdir_label("/run/systemd/system", 0755);
491 #endif // 0
492
493         /* Set up inaccessible items */
494         (void) mkdir_label("/run/systemd/inaccessible", 0000);
495         (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0);
496         (void) mkdir_label("/run/systemd/inaccessible/dir", 0000);
497         (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0));
498         (void) mknod("/run/systemd/inaccessible/blk", S_IFBLK | 0000, makedev(0, 0));
499         (void) mkfifo("/run/systemd/inaccessible/fifo", 0000);
500         (void) mknod("/run/systemd/inaccessible/sock", S_IFSOCK | 0000, 0);
501
502         return 0;
503 }