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