chiark / gitweb /
unit: avoid re-definition of enum for older gcc versions
[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
44 #ifndef TTY_GID
45 #define TTY_GID 5
46 #endif
47
48 typedef struct MountPoint {
49         const char *what;
50         const char *where;
51         const char *type;
52         const char *options;
53         unsigned long flags;
54         bool fatal;
55 } MountPoint;
56
57 /* The first three entries we might need before SELinux is up. The
58  * fourth (securityfs) is needed by IMA to load a custom policy. The
59  * other ones we can delay until SELinux and IMA are loaded. */
60 #define N_EARLY_MOUNT 4
61
62 static const MountPoint mount_table[] = {
63         { "proc",     "/proc",                  "proc",     NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
64         { "sysfs",    "/sys",                   "sysfs",    NULL,                MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
65         { "devtmpfs", "/dev",                   "devtmpfs", "mode=755",          MS_NOSUID|MS_STRICTATIME,     true },
66         { "securityfs", "/sys/kernel/security", "securityfs", NULL,              MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
67         { "tmpfs",    "/dev/shm",               "tmpfs",    "mode=1777",         MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
68         { "devpts",   "/dev/pts",               "devpts",   "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
69         { "tmpfs",    "/run",                   "tmpfs",    "mode=755",          MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
70         { "tmpfs",    "/sys/fs/cgroup",         "tmpfs",    "mode=755",          MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, false },
71         { "cgroup",   "/sys/fs/cgroup/systemd", "cgroup",   "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
72 };
73
74 /* These are API file systems that might be mounted by other software,
75  * we just list them here so that we know that we should ignore them */
76
77 static const char ignore_paths[] =
78         /* SELinux file systems */
79         "/sys/fs/selinux\0"
80         "/selinux\0"
81         /* Legacy cgroup mount points */
82         "/dev/cgroup\0"
83         "/cgroup\0"
84         /* Legacy kernel file system */
85         "/proc/bus/usb\0"
86         /* Container bind mounts */
87         "/proc/sys\0"
88         "/dev/console\0"
89         "/proc/kmsg\0"
90         "/etc/localtime\0"
91         "/etc/timezone\0"
92         "/etc/machine-id\0";
93
94 bool mount_point_is_api(const char *path) {
95         unsigned i;
96
97         /* Checks if this mount point is considered "API", and hence
98          * should be ignored */
99
100         for (i = 0; i < ELEMENTSOF(mount_table); i ++)
101                 if (path_equal(path, mount_table[i].where))
102                         return true;
103
104         return path_startswith(path, "/sys/fs/cgroup/");
105 }
106
107 bool mount_point_ignore(const char *path) {
108         const char *i;
109
110         NULSTR_FOREACH(i, ignore_paths)
111                 if (path_equal(path, i))
112                         return true;
113
114         return false;
115 }
116
117 static int mount_one(const MountPoint *p, bool relabel) {
118         int r;
119
120         assert(p);
121
122         /* Relabel first, just in case */
123         if (relabel)
124                 label_fix(p->where, true, true);
125
126         if ((r = path_is_mount_point(p->where, true)) < 0)
127                 return r;
128
129         if (r > 0)
130                 return 0;
131
132         /* The access mode here doesn't really matter too much, since
133          * the mounted file system will take precedence anyway. */
134         mkdir_p_label(p->where, 0755);
135
136         log_debug("Mounting %s to %s of type %s with options %s.",
137                   p->what,
138                   p->where,
139                   p->type,
140                   strna(p->options));
141
142         if (mount(p->what,
143                   p->where,
144                   p->type,
145                   p->flags,
146                   p->options) < 0) {
147                 log_full(p->fatal ? LOG_ERR : LOG_DEBUG, "Failed to mount %s: %s", p->where, strerror(errno));
148                 return p->fatal ? -errno : 0;
149         }
150
151         /* Relabel again, since we now mounted something fresh here */
152         if (relabel)
153                 label_fix(p->where, false, false);
154
155         return 1;
156 }
157
158 int mount_setup_early(void) {
159         unsigned i;
160         int r = 0;
161
162         assert_cc(N_EARLY_MOUNT <= ELEMENTSOF(mount_table));
163
164         /* Do a minimal mount of /proc and friends to enable the most
165          * basic stuff, such as SELinux */
166         for (i = 0; i < N_EARLY_MOUNT; i ++)  {
167                 int j;
168
169                 j = mount_one(mount_table + i, false);
170                 if (r == 0)
171                         r = j;
172         }
173
174         return r;
175 }
176
177 int mount_cgroup_controllers(char ***join_controllers) {
178         int r;
179         FILE *f;
180         char buf[LINE_MAX];
181         Set *controllers;
182
183         /* Mount all available cgroup controllers that are built into the kernel. */
184
185         f = fopen("/proc/cgroups", "re");
186         if (!f) {
187                 log_error("Failed to enumerate cgroup controllers: %m");
188                 return 0;
189         }
190
191         controllers = set_new(string_hash_func, string_compare_func);
192         if (!controllers) {
193                 r = -ENOMEM;
194                 log_error("Failed to allocate controller set.");
195                 goto finish;
196         }
197
198         /* Ignore the header line */
199         (void) fgets(buf, sizeof(buf), f);
200
201         for (;;) {
202                 char *controller;
203                 int enabled = 0;
204
205                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
206
207                         if (feof(f))
208                                 break;
209
210                         log_error("Failed to parse /proc/cgroups.");
211                         r = -EIO;
212                         goto finish;
213                 }
214
215                 if (!enabled) {
216                         free(controller);
217                         continue;
218                 }
219
220                 r = set_put(controllers, controller);
221                 if (r < 0) {
222                         log_error("Failed to add controller to set.");
223                         free(controller);
224                         goto finish;
225                 }
226         }
227
228         for (;;) {
229                 MountPoint p;
230                 char *controller, *where, *options;
231                 char ***k = NULL;
232
233                 controller = set_steal_first(controllers);
234                 if (!controller)
235                         break;
236
237                 if (join_controllers)
238                         for (k = join_controllers; *k; k++)
239                                 if (strv_find(*k, controller))
240                                         break;
241
242                 if (k && *k) {
243                         char **i, **j;
244
245                         for (i = *k, j = *k; *i; i++) {
246
247                                 if (!streq(*i, controller)) {
248                                         char *t;
249
250                                         t = set_remove(controllers, *i);
251                                         if (!t) {
252                                                 free(*i);
253                                                 continue;
254                                         }
255                                         free(t);
256                                 }
257
258                                 *(j++) = *i;
259                         }
260
261                         *j = NULL;
262
263                         options = strv_join(*k, ",");
264                         if (!options) {
265                                 log_error("Failed to join options");
266                                 free(controller);
267                                 r = -ENOMEM;
268                                 goto finish;
269                         }
270
271                 } else {
272                         options = controller;
273                         controller = NULL;
274                 }
275
276                 where = strappend("/sys/fs/cgroup/", options);
277                 if (!where) {
278                         log_error("Failed to build path");
279                         free(options);
280                         r = -ENOMEM;
281                         goto finish;
282                 }
283
284                 zero(p);
285                 p.what = "cgroup";
286                 p.where = where;
287                 p.type = "cgroup";
288                 p.options = options;
289                 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
290                 p.fatal = false;
291
292                 r = mount_one(&p, true);
293                 free(controller);
294                 free(where);
295
296                 if (r < 0) {
297                         free(options);
298                         goto finish;
299                 }
300
301                 if (r > 0 && k && *k) {
302                         char **i;
303
304                         for (i = *k; *i; i++) {
305                                 char *t;
306
307                                 t = strappend("/sys/fs/cgroup/", *i);
308                                 if (!t) {
309                                         log_error("Failed to build path");
310                                         r = -ENOMEM;
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();
406
407         /* Create a few directories we always want around */
408         mkdir_label("/run/systemd", 0755);
409         mkdir_label("/run/systemd/system", 0755);
410
411         return 0;
412 }