chiark / gitweb /
0fd112f0d199810276c1f2f694d21d0cdfc98611
[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 <sys/stat.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <libgen.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <ftw.h>
31
32 #include "mount-setup.h"
33 #include "dev-setup.h"
34 #include "log.h"
35 #include "macro.h"
36 #include "util.h"
37 #include "label.h"
38 #include "set.h"
39 #include "strv.h"
40 #include "mkdir.h"
41 #include "path-util.h"
42 #include "missing.h"
43 #include "virt.h"
44
45 #ifndef TTY_GID
46 #define TTY_GID 5
47 #endif
48
49 typedef struct MountPoint {
50         const char *what;
51         const char *where;
52         const char *type;
53         const char *options;
54         unsigned long flags;
55         bool fatal;
56         bool in_container;
57 } MountPoint;
58
59 /* The first three entries we might need before SELinux is up. The
60  * fourth (securityfs) is needed by IMA to load a custom policy. The
61  * other ones we can delay until SELinux and IMA are loaded. */
62 #define N_EARLY_MOUNT 4
63
64 static const MountPoint mount_table[] = {
65         { "proc",     "/proc",                  "proc",     NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV,                true,  true  },
66         { "sysfs",    "/sys",                   "sysfs",    NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV,                true,  true  },
67         { "devtmpfs", "/dev",                   "devtmpfs", "mode=755",          MS_NOSUID|MS_STRICTATIME,                    true,  true  },
68         { "securityfs", "/sys/kernel/security", "securityfs", NULL,              MS_NOSUID|MS_NOEXEC|MS_NODEV,                false, false },
69         { "tmpfs",    "/dev/shm",               "tmpfs",    "mode=1777",         MS_NOSUID|MS_NODEV|MS_STRICTATIME,           true,  true  },
70         { "devpts",   "/dev/pts",               "devpts",   "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,          false, true  },
71         { "tmpfs",    "/run",                   "tmpfs",    "mode=755",          MS_NOSUID|MS_NODEV|MS_STRICTATIME,           true,  true  },
72         { "tmpfs",    "/sys/fs/cgroup",         "tmpfs",    "mode=755",          MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false, true  },
73         { "cgroup",   "/sys/fs/cgroup/systemd", "cgroup",   "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,                false, true  },
74 };
75
76 /* These are API file systems that might be mounted by other software,
77  * we just list them here so that we know that we should ignore them */
78
79 static const char ignore_paths[] =
80         /* SELinux file systems */
81         "/sys/fs/selinux\0"
82         "/selinux\0"
83         /* Legacy cgroup mount points */
84         "/dev/cgroup\0"
85         "/cgroup\0"
86         /* Legacy kernel file system */
87         "/proc/bus/usb\0"
88         /* Container bind mounts */
89         "/proc/sys\0"
90         "/dev/console\0"
91         "/proc/kmsg\0";
92
93 bool mount_point_is_api(const char *path) {
94         unsigned i;
95
96         /* Checks if this mount point is considered "API", and hence
97          * should be ignored */
98
99         for (i = 0; i < ELEMENTSOF(mount_table); i ++)
100                 if (path_equal(path, mount_table[i].where))
101                         return true;
102
103         return path_startswith(path, "/sys/fs/cgroup/");
104 }
105
106 bool mount_point_ignore(const char *path) {
107         const char *i;
108
109         NULSTR_FOREACH(i, ignore_paths)
110                 if (path_equal(path, i))
111                         return true;
112
113         return false;
114 }
115
116 static int mount_one(const MountPoint *p, bool relabel) {
117         int r;
118
119         assert(p);
120
121         /* Relabel first, just in case */
122         if (relabel)
123                 label_fix(p->where, true, true);
124
125         r = path_is_mount_point(p->where, true);
126         if (r < 0)
127                 return r;
128
129         if (r > 0)
130                 return 0;
131
132         /* Skip securityfs in a container */
133         if (!p->in_container && detect_container(NULL) > 0)
134                 return 0;
135
136         /* The access mode here doesn't really matter too much, since
137          * the mounted file system will take precedence anyway. */
138         mkdir_p_label(p->where, 0755);
139
140         log_debug("Mounting %s to %s of type %s with options %s.",
141                   p->what,
142                   p->where,
143                   p->type,
144                   strna(p->options));
145
146         if (mount(p->what,
147                   p->where,
148                   p->type,
149                   p->flags,
150                   p->options) < 0) {
151                 log_full(p->fatal ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno));
152                 return p->fatal ? -errno : 0;
153         }
154
155         /* Relabel again, since we now mounted something fresh here */
156         if (relabel)
157                 label_fix(p->where, false, false);
158
159         return 1;
160 }
161
162 int mount_setup_early(void) {
163         unsigned i;
164         int r = 0;
165
166         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
167
168         /* Do a minimal mount of /proc and friends to enable the most
169          * basic stuff, such as SELinux */
170         for (i = 0; i < N_EARLY_MOUNT; i ++)  {
171                 int j;
172
173                 j = mount_one(mount_table + i, false);
174                 if (r == 0)
175                         r = j;
176         }
177
178         return r;
179 }
180
181 int mount_cgroup_controllers(char ***join_controllers) {
182         int r;
183         FILE *f;
184         char buf[LINE_MAX];
185         Set *controllers;
186
187         /* Mount all available cgroup controllers that are built into the kernel. */
188
189         f = fopen("/proc/cgroups", "re");
190         if (!f) {
191                 log_error("Failed to enumerate cgroup controllers: %m");
192                 return 0;
193         }
194
195         controllers = set_new(string_hash_func, string_compare_func);
196         if (!controllers) {
197                 r = log_oom();
198                 goto finish;
199         }
200
201         /* Ignore the header line */
202         (void) fgets(buf, sizeof(buf), f);
203
204         for (;;) {
205                 char *controller;
206                 int enabled = 0;
207
208                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
209
210                         if (feof(f))
211                                 break;
212
213                         log_error("Failed to parse /proc/cgroups.");
214                         r = -EIO;
215                         goto finish;
216                 }
217
218                 if (!enabled) {
219                         free(controller);
220                         continue;
221                 }
222
223                 r = set_put(controllers, controller);
224                 if (r < 0) {
225                         log_error("Failed to add controller to set.");
226                         free(controller);
227                         goto finish;
228                 }
229         }
230
231         for (;;) {
232                 MountPoint p;
233                 char *controller, *where, *options;
234                 char ***k = NULL;
235
236                 controller = set_steal_first(controllers);
237                 if (!controller)
238                         break;
239
240                 if (join_controllers)
241                         for (k = join_controllers; *k; k++)
242                                 if (strv_find(*k, controller))
243                                         break;
244
245                 if (k && *k) {
246                         char **i, **j;
247
248                         for (i = *k, j = *k; *i; i++) {
249
250                                 if (!streq(*i, controller)) {
251                                         char *t;
252
253                                         t = set_remove(controllers, *i);
254                                         if (!t) {
255                                                 free(*i);
256                                                 continue;
257                                         }
258                                         free(t);
259                                 }
260
261                                 *(j++) = *i;
262                         }
263
264                         *j = NULL;
265
266                         options = strv_join(*k, ",");
267                         if (!options) {
268                                 free(controller);
269                                 r = log_oom();
270                                 goto finish;
271                         }
272
273                 } else {
274                         options = controller;
275                         controller = NULL;
276                 }
277
278                 where = strappend("/sys/fs/cgroup/", options);
279                 if (!where) {
280                         free(options);
281                         r = log_oom();
282                         goto finish;
283                 }
284
285                 zero(p);
286                 p.what = "cgroup";
287                 p.where = where;
288                 p.type = "cgroup";
289                 p.options = options;
290                 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
291                 p.fatal = false;
292
293                 r = mount_one(&p, true);
294                 free(controller);
295                 free(where);
296
297                 if (r < 0) {
298                         free(options);
299                         goto finish;
300                 }
301
302                 if (r > 0 && k && *k) {
303                         char **i;
304
305                         for (i = *k; *i; i++) {
306                                 char *t;
307
308                                 t = strappend("/sys/fs/cgroup/", *i);
309                                 if (!t) {
310                                         r = log_oom();
311                                         free(options);
312                                         goto finish;
313                                 }
314
315                                 r = symlink(options, t);
316                                 free(t);
317
318                                 if (r < 0 && errno != EEXIST) {
319                                         log_error("Failed to create symlink: %m");
320                                         r = -errno;
321                                         free(options);
322                                         goto finish;
323                                 }
324                         }
325                 }
326
327                 free(options);
328         }
329
330         r = 0;
331
332 finish:
333         set_free_free(controllers);
334
335         fclose(f);
336
337         return r;
338 }
339
340 static int nftw_cb(
341                 const char *fpath,
342                 const struct stat *sb,
343                 int tflag,
344                 struct FTW *ftwbuf) {
345
346         /* No need to label /dev twice in a row... */
347         if (_unlikely_(ftwbuf->level == 0))
348                 return FTW_CONTINUE;
349
350         label_fix(fpath, false, false);
351
352         /* /run/initramfs is static data and big, no need to
353          * dynamically relabel its contents at boot... */
354         if (_unlikely_(ftwbuf->level == 1 &&
355                       tflag == FTW_D &&
356                       streq(fpath, "/run/initramfs")))
357                 return FTW_SKIP_SUBTREE;
358
359         return FTW_CONTINUE;
360 };
361
362 int mount_setup(bool loaded_policy) {
363
364         static const char relabel[] =
365                 "/run/initramfs/root-fsck\0"
366                 "/run/initramfs/shutdown\0";
367
368         int r;
369         unsigned i;
370         const char *j;
371
372         for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
373                 r = mount_one(mount_table + i, true);
374
375                 if (r < 0)
376                         return r;
377         }
378
379         /* Nodes in devtmpfs and /run need to be manually updated for
380          * the appropriate labels, after mounting. The other virtual
381          * API file systems like /sys and /proc do not need that, they
382          * use the same label for all their files. */
383         if (loaded_policy) {
384                 usec_t before_relabel, after_relabel;
385                 char timespan[FORMAT_TIMESPAN_MAX];
386
387                 before_relabel = now(CLOCK_MONOTONIC);
388
389                 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
390                 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
391
392                 /* Explicitly relabel these */
393                 NULSTR_FOREACH(j, relabel)
394                         label_fix(j, true, false);
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));
400         }
401
402         /* Create a few default symlinks, which are normally created
403          * by udevd, but some scripts might need them before we start
404          * udevd. */
405         dev_setup(NULL);
406
407         /* Mark the root directory as shared in regards to mount
408          * propagation. The kernel defaults to "private", but we think
409          * it makes more sense to have a default of "shared" so that
410          * nspawn and the container tools work out of the box. If
411          * specific setups need other settings they can reset the
412          * propagation mode to private if needed. */
413         if (detect_container(NULL) <= 0)
414                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
415                         log_warning("Failed to set up the root directory for shared mount propagation: %m");
416
417         /* Create a few directories we always want around */
418         mkdir_label("/run/systemd", 0755);
419         mkdir_label("/run/systemd/system", 0755);
420
421         return 0;
422 }