chiark / gitweb /
basic: parse_timestamp UTC and fractional seconds support
[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 <stdlib.h>
25 #include <unistd.h>
26 #include <ftw.h>
27
28 #include "mount-setup.h"
29 //#include "dev-setup.h"
30 //#include "bus-util.h"
31 //#include "log.h"
32 #include "macro.h"
33 //#include "util.h"
34 #include "label.h"
35 //#include "set.h"
36 //#include "strv.h"
37 #include "mkdir.h"
38 #include "path-util.h"
39 //#include "missing.h"
40 #include "virt.h"
41 //#include "efivars.h"
42 //#include "smack-util.h"
43 #include "cgroup-util.h"
44
45 typedef enum MountMode {
46         MNT_NONE  =        0,
47         MNT_FATAL =        1 <<  0,
48         MNT_IN_CONTAINER = 1 <<  1,
49 } MountMode;
50
51 typedef struct MountPoint {
52         const char *what;
53         const char *where;
54         const char *type;
55         const char *options;
56         unsigned long flags;
57         bool (*condition_fn)(void);
58         MountMode mode;
59 } MountPoint;
60
61 /* The first three entries we might need before SELinux is up. The
62  * fourth (securityfs) is needed by IMA to load a custom policy. The
63  * other ones we can delay until SELinux and IMA are loaded. When
64  * SMACK is enabled we need smackfs, too, so it's a fifth one. */
65 #ifdef HAVE_SMACK
66 #define N_EARLY_MOUNT 5
67 #else
68 #define N_EARLY_MOUNT 4
69 #endif
70
71 static const MountPoint mount_table[] = {
72 /// UNNEEDED by elogind
73 #if 0
74         { "sysfs",       "/sys",                      "sysfs",      NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
75           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
76         { "proc",        "/proc",                     "proc",       NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
77           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
78         { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755",                MS_NOSUID|MS_STRICTATIME,
79           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
80         { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
81           NULL,          MNT_NONE                   },
82 #ifdef HAVE_SMACK
83         { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",            MS_NOSUID|MS_NOEXEC|MS_NODEV,
84           mac_smack_use, MNT_FATAL                  },
85         { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
86           mac_smack_use, MNT_FATAL                  },
87 #endif
88         { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
89           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
90         { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
91           NULL,          MNT_IN_CONTAINER           },
92 #ifdef HAVE_SMACK
93         { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*",  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
94           mac_smack_use, MNT_FATAL                  },
95 #endif
96         { "tmpfs",       "/run",                      "tmpfs",      "mode=755",                MS_NOSUID|MS_NODEV|MS_STRICTATIME,
97           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
98         { "cgroup",      "/sys/fs/cgroup",            "cgroup",     "__DEVEL__sane_behavior",  MS_NOSUID|MS_NOEXEC|MS_NODEV,
99           cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER },
100 #endif // 0
101         { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755",                MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
102           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
103 /// UNNEEDED by elogind
104 #if 0
105         { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
106           cg_is_legacy_wanted, MNT_IN_CONTAINER           },
107         { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",       MS_NOSUID|MS_NOEXEC|MS_NODEV,
108           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
109 #else
110         { "cgroup",      "/sys/fs/cgroup/elogind",    "cgroup",     "none,name=elogind,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
111           cg_is_legacy_wanted, MNT_IN_CONTAINER           },
112         { "cgroup",      "/sys/fs/cgroup/elogind",    "cgroup",     "none,name=elogind",       MS_NOSUID|MS_NOEXEC|MS_NODEV,
113           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
114 #endif // 0
115 /// UNNEEDED by elogind
116 #if 0
117         { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
118           NULL,          MNT_NONE                   },
119 #ifdef ENABLE_EFI
120         { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
121           is_efi_boot,   MNT_NONE                   },
122 #endif
123         { "kdbusfs",    "/sys/fs/kdbus",             "kdbusfs",    NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
124           is_kdbus_wanted,       MNT_IN_CONTAINER },
125 #endif // 0
126 };
127
128 /// UNNEEDED by elogind
129 #if 0
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 /// UNNEEDED by elogind
217 #if 0
218 int mount_setup_early(void) {
219         unsigned i;
220         int r = 0;
221
222         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
223
224         /* Do a minimal mount of /proc and friends to enable the most
225          * basic stuff, such as SELinux */
226         for (i = 0; i < N_EARLY_MOUNT; i ++)  {
227                 int j;
228
229                 j = mount_one(mount_table + i, false);
230                 if (j != 0 && r >= 0)
231                         r = j;
232         }
233
234         return r;
235 }
236
237 int mount_cgroup_controllers(char ***join_controllers) {
238         _cleanup_set_free_free_ Set *controllers = NULL;
239         int r;
240
241         if (!cg_is_legacy_wanted())
242                 return 0;
243
244         /* Mount all available cgroup controllers that are built into the kernel. */
245
246         controllers = set_new(&string_hash_ops);
247         if (!controllers)
248                 return log_oom();
249
250         r = cg_kernel_controllers(controllers);
251         if (r < 0)
252                 return log_error_errno(r, "Failed to enumerate cgroup controllers: %m");
253
254         for (;;) {
255                 _cleanup_free_ char *options = NULL, *controller = NULL, *where = NULL;
256                 MountPoint p = {
257                         .what = "cgroup",
258                         .type = "cgroup",
259                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
260                         .mode = MNT_IN_CONTAINER,
261                 };
262                 char ***k = NULL;
263
264                 controller = set_steal_first(controllers);
265                 if (!controller)
266                         break;
267
268                 if (join_controllers)
269                         for (k = join_controllers; *k; k++)
270                                 if (strv_find(*k, controller))
271                                         break;
272
273                 if (k && *k) {
274                         char **i, **j;
275
276                         for (i = *k, j = *k; *i; i++) {
277
278                                 if (!streq(*i, controller)) {
279                                         _cleanup_free_ char *t;
280
281                                         t = set_remove(controllers, *i);
282                                         if (!t) {
283                                                 free(*i);
284                                                 continue;
285                                         }
286                                 }
287
288                                 *(j++) = *i;
289                         }
290
291                         *j = NULL;
292
293                         options = strv_join(*k, ",");
294                         if (!options)
295                                 return log_oom();
296                 } else {
297                         options = controller;
298                         controller = NULL;
299                 }
300
301                 where = strappend("/sys/fs/cgroup/", options);
302                 if (!where)
303                         return log_oom();
304
305                 p.where = where;
306                 p.options = options;
307
308                 r = mount_one(&p, true);
309                 if (r < 0)
310                         return r;
311
312                 if (r > 0 && k && *k) {
313                         char **i;
314
315                         for (i = *k; *i; i++) {
316                                 _cleanup_free_ char *t = NULL;
317
318                                 t = strappend("/sys/fs/cgroup/", *i);
319                                 if (!t)
320                                         return log_oom();
321
322                                 r = symlink(options, t);
323                                 if (r < 0 && errno != EEXIST)
324                                         return log_error_errno(errno, "Failed to create symlink %s: %m", t);
325 #ifdef SMACK_RUN_LABEL
326                                 r = mac_smack_copy(t, options);
327                                 if (r < 0 && r != -EOPNOTSUPP)
328                                         return log_error_errno(r, "Failed to copy smack label from %s to %s: %m", options, t);
329 #endif
330                         }
331                 }
332         }
333
334         /* Now that we mounted everything, let's make the tmpfs the
335          * cgroup file systems are mounted into read-only. */
336         (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
337
338         return 0;
339 }
340
341 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
342 static int nftw_cb(
343                 const char *fpath,
344                 const struct stat *sb,
345                 int tflag,
346                 struct FTW *ftwbuf) {
347
348         /* No need to label /dev twice in a row... */
349         if (_unlikely_(ftwbuf->level == 0))
350                 return FTW_CONTINUE;
351
352         label_fix(fpath, false, false);
353
354         /* /run/initramfs is static data and big, no need to
355          * dynamically relabel its contents at boot... */
356         if (_unlikely_(ftwbuf->level == 1 &&
357                       tflag == FTW_D &&
358                       streq(fpath, "/run/initramfs")))
359                 return FTW_SKIP_SUBTREE;
360
361         return FTW_CONTINUE;
362 };
363 #endif
364 #endif // 0
365
366 int mount_setup(bool loaded_policy) {
367         unsigned i;
368         int r = 0;
369
370         for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
371                 int j;
372
373                 j = mount_one(mount_table + i, loaded_policy);
374                 if (j != 0 && r >= 0)
375                         r = j;
376         }
377
378         if (r < 0)
379                 return r;
380
381 /// elogind does not control /, /dev, /run and /run/systemd/* are setup elsewhere.
382 #if 0
383 #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
384         /* Nodes in devtmpfs and /run need to be manually updated for
385          * the appropriate labels, after mounting. The other virtual
386          * API file systems like /sys and /proc do not need that, they
387          * use the same label for all their files. */
388         if (loaded_policy) {
389                 usec_t before_relabel, after_relabel;
390                 char timespan[FORMAT_TIMESPAN_MAX];
391
392                 before_relabel = now(CLOCK_MONOTONIC);
393
394                 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
395                 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
396
397                 after_relabel = now(CLOCK_MONOTONIC);
398
399                 log_info("Relabelled /dev and /run in %s.",
400                          format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel, 0));
401         }
402 #endif
403
404         /* Create a few default symlinks, which are normally created
405          * by udevd, but some scripts might need them before we start
406          * udevd. */
407         dev_setup(NULL, UID_INVALID, GID_INVALID);
408
409         /* Mark the root directory as shared in regards to mount
410          * propagation. The kernel defaults to "private", but we think
411          * it makes more sense to have a default of "shared" so that
412          * nspawn and the container tools work out of the box. If
413          * specific setups need other settings they can reset the
414          * propagation mode to private if needed. */
415         if (detect_container() <= 0)
416                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
417                         log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
418
419         /* Create a few directories we always want around, Note that
420          * sd_booted() checks for /run/systemd/system, so this mkdir
421          * really needs to stay for good, otherwise software that
422          * copied sd-daemon.c into their sources will misdetect
423          * systemd. */
424         mkdir_label("/run/systemd", 0755);
425         mkdir_label("/run/systemd/system", 0755);
426         mkdir_label("/run/systemd/inaccessible", 0000);
427 #endif // 0
428
429         return 0;
430 }