]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/mount-setup.c
continue work with error messages, log_oom()
[thirdparty/systemd.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 = log_oom();
194 goto finish;
195 }
196
197 /* Ignore the header line */
198 (void) fgets(buf, sizeof(buf), f);
199
200 for (;;) {
201 char *controller;
202 int enabled = 0;
203
204 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
205
206 if (feof(f))
207 break;
208
209 log_error("Failed to parse /proc/cgroups.");
210 r = -EIO;
211 goto finish;
212 }
213
214 if (!enabled) {
215 free(controller);
216 continue;
217 }
218
219 r = set_put(controllers, controller);
220 if (r < 0) {
221 log_error("Failed to add controller to set.");
222 free(controller);
223 goto finish;
224 }
225 }
226
227 for (;;) {
228 MountPoint p;
229 char *controller, *where, *options;
230 char ***k = NULL;
231
232 controller = set_steal_first(controllers);
233 if (!controller)
234 break;
235
236 if (join_controllers)
237 for (k = join_controllers; *k; k++)
238 if (strv_find(*k, controller))
239 break;
240
241 if (k && *k) {
242 char **i, **j;
243
244 for (i = *k, j = *k; *i; i++) {
245
246 if (!streq(*i, controller)) {
247 char *t;
248
249 t = set_remove(controllers, *i);
250 if (!t) {
251 free(*i);
252 continue;
253 }
254 free(t);
255 }
256
257 *(j++) = *i;
258 }
259
260 *j = NULL;
261
262 options = strv_join(*k, ",");
263 if (!options) {
264 free(controller);
265 r = log_oom();
266 goto finish;
267 }
268
269 } else {
270 options = controller;
271 controller = NULL;
272 }
273
274 where = strappend("/sys/fs/cgroup/", options);
275 if (!where) {
276 free(options);
277 r = log_oom();
278 goto finish;
279 }
280
281 zero(p);
282 p.what = "cgroup";
283 p.where = where;
284 p.type = "cgroup";
285 p.options = options;
286 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
287 p.fatal = false;
288
289 r = mount_one(&p, true);
290 free(controller);
291 free(where);
292
293 if (r < 0) {
294 free(options);
295 goto finish;
296 }
297
298 if (r > 0 && k && *k) {
299 char **i;
300
301 for (i = *k; *i; i++) {
302 char *t;
303
304 t = strappend("/sys/fs/cgroup/", *i);
305 if (!t) {
306 r = log_oom();
307 free(options);
308 goto finish;
309 }
310
311 r = symlink(options, t);
312 free(t);
313
314 if (r < 0 && errno != EEXIST) {
315 log_error("Failed to create symlink: %m");
316 r = -errno;
317 free(options);
318 goto finish;
319 }
320 }
321 }
322
323 free(options);
324 }
325
326 r = 0;
327
328 finish:
329 set_free_free(controllers);
330
331 fclose(f);
332
333 return r;
334 }
335
336 static int nftw_cb(
337 const char *fpath,
338 const struct stat *sb,
339 int tflag,
340 struct FTW *ftwbuf) {
341
342 /* No need to label /dev twice in a row... */
343 if (_unlikely_(ftwbuf->level == 0))
344 return FTW_CONTINUE;
345
346 label_fix(fpath, false, false);
347
348 /* /run/initramfs is static data and big, no need to
349 * dynamically relabel its contents at boot... */
350 if (_unlikely_(ftwbuf->level == 1 &&
351 tflag == FTW_D &&
352 streq(fpath, "/run/initramfs")))
353 return FTW_SKIP_SUBTREE;
354
355 return FTW_CONTINUE;
356 };
357
358 int mount_setup(bool loaded_policy) {
359
360 static const char relabel[] =
361 "/run/initramfs/root-fsck\0"
362 "/run/initramfs/shutdown\0";
363
364 int r;
365 unsigned i;
366 const char *j;
367
368 for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
369 r = mount_one(mount_table + i, true);
370
371 if (r < 0)
372 return r;
373 }
374
375 /* Nodes in devtmpfs and /run need to be manually updated for
376 * the appropriate labels, after mounting. The other virtual
377 * API file systems like /sys and /proc do not need that, they
378 * use the same label for all their files. */
379 if (loaded_policy) {
380 usec_t before_relabel, after_relabel;
381 char timespan[FORMAT_TIMESPAN_MAX];
382
383 before_relabel = now(CLOCK_MONOTONIC);
384
385 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
386 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
387
388 /* Explicitly relabel these */
389 NULSTR_FOREACH(j, relabel)
390 label_fix(j, true, false);
391
392 after_relabel = now(CLOCK_MONOTONIC);
393
394 log_info("Relabelled /dev and /run in %s.",
395 format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
396 }
397
398 /* Create a few default symlinks, which are normally created
399 * by udevd, but some scripts might need them before we start
400 * udevd. */
401 dev_setup();
402
403 /* Create a few directories we always want around */
404 mkdir_label("/run/systemd", 0755);
405 mkdir_label("/run/systemd/system", 0755);
406
407 return 0;
408 }