chiark / gitweb /
tree-wide: remove Emacs lines from all files
[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                 (void) 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                 log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
180                 return (p->mode & MNT_FATAL) ? r : 0;
181         }
182         if (r > 0)
183                 return 0;
184
185         /* Skip securityfs in a container */
186         if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
187                 return 0;
188
189         /* The access mode here doesn't really matter too much, since
190          * the mounted file system will take precedence anyway. */
191         if (relabel)
192                 (void) mkdir_p_label(p->where, 0755);
193         else
194                 (void) mkdir_p(p->where, 0755);
195
196         log_debug("Mounting %s to %s of type %s with options %s.",
197                   p->what,
198                   p->where,
199                   p->type,
200                   strna(p->options));
201
202         if (mount(p->what,
203                   p->where,
204                   p->type,
205                   p->flags,
206                   p->options) < 0) {
207                 log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where);
208                 return (p->mode & MNT_FATAL) ? -errno : 0;
209         }
210
211         /* Relabel again, since we now mounted something fresh here */
212         if (relabel)
213                 (void) label_fix(p->where, false, false);
214
215         return 1;
216 }
217
218 #if 0 /// UNNEEDED by elogind
219 int mount_setup_early(void) {
220         unsigned i;
221         int r = 0;
222
223         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
224
225         /* Do a minimal mount of /proc and friends to enable the most
226          * basic stuff, such as SELinux */
227         for (i = 0; i < N_EARLY_MOUNT; i ++)  {
228                 int j;
229
230                 j = mount_one(mount_table + i, false);
231                 if (j != 0 && r >= 0)
232                         r = j;
233         }
234
235         return r;
236 }
237
238 int mount_cgroup_controllers(char ***join_controllers) {
239         _cleanup_set_free_free_ Set *controllers = NULL;
240         int r;
241
242         if (!cg_is_legacy_wanted())
243                 return 0;
244
245         /* Mount all available cgroup controllers that are built into the kernel. */
246
247         controllers = set_new(&string_hash_ops);
248         if (!controllers)
249                 return log_oom();
250
251         r = cg_kernel_controllers(controllers);
252         if (r < 0)
253                 return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
254
255         for (;;) {
256                 _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
257                 MountPoint p = {
258                         .what = "cgroup",
259                         .type = "cgroup",
260                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
261                         .mode = MNT_IN_CONTAINER,
262                 };
263                 char ***k = NULL;
264
265                 controller = set_steal_first(controllers);
266                 if (!controller)
267                         break;
268
269                 if (join_controllers)
270                         for (k = join_controllers; *k; k++)
271                                 if (strv_find(*k, controller))
272                                         break;
273
274                 if (k && *k) {
275                         char **i, **j;
276
277                         for (i = *k, j = *k; *i; i++) {
278
279                                 if (!streq(*i, controller)) {
280                                         _cleanup_free_ char *t;
281
282                                         t = set_remove(controllers, *i);
283                                         if (!t) {
284                                                 free(*i);
285                                                 continue;
286                                         }
287                                 }
288
289                                 *(j++) = *i;
290                         }
291
292                         *j = NULL;
293
294                         options = strv_join(*k, ",");
295                         if (!options)
296                                 return log_oom();
297                 } else {
298                         options = controller;
299                         controller = NULL;
300                 }
301
302                 where = strappend("/sys/fs/cgroup/", options);
303                 if (!where)
304                         return log_oom();
305
306                 p.where = where;
307                 p.options = options;
308
309                 r = mount_one(&p, true);
310                 if (r < 0)
311                         return r;
312
313                 if (r > 0 && k && *k) {
314                         char **i;
315
316                         for (i = *k; *i; i++) {
317                                 _cleanup_free_ char *t = NULL;
318
319                                 t = strappend("/sys/fs/cgroup/", *i);
320                                 if (!t)
321                                         return log_oom();
322
323                                 r = symlink(options, t);
324                                 if (r >= 0) {
325 #ifdef SMACK_RUN_LABEL
326                                         _cleanup_free_ char *src;
327                                         src = strappend("/sys/fs/cgroup/", options);
328                                         if (!src)
329                                                 return log_oom();
330                                         r = mac_smack_copy(t, src);
331                                         if (r < 0 && r != -EOPNOTSUPP)
332                                                 return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", src, t);
333 #endif
334                                 } else if (errno != EEXIST)
335                                         return log_error_errno(errno, "Failed to create symlink %s: %m", t);
336                         }
337                 }
338         }
339
340         /* Now that we mounted everything, let's make the tmpfs the
341          * cgroup file systems are mounted into read-only. */
342         (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
343
344         return 0;
345 }
346
347 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
348 static int nftw_cb(
349                 const char *fpath,
350                 const struct stat *sb,
351                 int tflag,
352                 struct FTW *ftwbuf) {
353
354         /* No need to label /dev twice in a row... */
355         if (_unlikely_(ftwbuf->level == 0))
356                 return FTW_CONTINUE;
357
358         label_fix(fpath, false, false);
359
360         /* /run/initramfs is static data and big, no need to
361          * dynamically relabel its contents at boot... */
362         if (_unlikely_(ftwbuf->level == 1 &&
363                       tflag == FTW_D &&
364                       streq(fpath, "/run/initramfs")))
365                 return FTW_SKIP_SUBTREE;
366
367         return FTW_CONTINUE;
368 };
369 #endif
370 #endif // 0
371
372 int mount_setup(bool loaded_policy) {
373         unsigned i;
374         int r = 0;
375
376         for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
377                 int j;
378
379                 j = mount_one(mount_table + i, loaded_policy);
380                 if (j != 0 && r >= 0)
381                         r = j;
382         }
383
384         if (r < 0)
385                 return r;
386
387
388 #if 0 /// elogind does not control /, /dev, /run and /run/systemd/* are setup elsewhere.
389 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
390         /* Nodes in devtmpfs and /run need to be manually updated for
391          * the appropriate labels, after mounting. The other virtual
392          * API file systems like /sys and /proc do not need that, they
393          * use the same label for all their files. */
394         if (loaded_policy) {
395                 usec_t before_relabel, after_relabel;
396                 char timespan[FORMAT_TIMESPAN_MAX];
397
398                 before_relabel = now(CLOCK_MONOTONIC);
399
400                 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
401                 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
402
403                 after_relabel = now(CLOCK_MONOTONIC);
404
405                 log_info("Relabelled /dev and /run in %s.",
406                          format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
407         }
408 #endif
409
410         /* Create a few default symlinks, which are normally created
411          * by udevd, but some scripts might need them before we start
412          * udevd. */
413         dev_setup(NULL, UID_INVALID, GID_INVALID);
414
415         /* Mark the root directory as shared in regards to mount
416          * propagation. The kernel defaults to "private", but we think
417          * it makes more sense to have a default of "shared" so that
418          * nspawn and the container tools work out of the box. If
419          * specific setups need other settings they can reset the
420          * propagation mode to private if needed. */
421         if (detect_container() <= 0)
422                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
423                         log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
424
425         /* Create a few directories we always want around, Note that
426          * sd_booted() checks for /run/systemd/system, so this mkdir
427          * really needs to stay for good, otherwise software that
428          * copied sd-daemon.c into their sources will misdetect
429          * systemd. */
430         mkdir_label("/run/systemd", 0755);
431         mkdir_label("/run/systemd/system", 0755);
432         mkdir_label("/run/systemd/inaccessible", 0000);
433 #endif // 0
434
435         return 0;
436 }