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