]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/mount-setup.c
udev: unify /dev static symlink setup
[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
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_error("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 }