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