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