]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/mount-setup.c
udev: docs - updated index page to match common gtk style
[thirdparty/systemd.git] / src / core / mount-setup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8e274523
LP
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
5430f7f2
LP
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
8e274523
LP
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
5430f7f2 16 Lesser General Public License for more details.
8e274523 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e274523
LP
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>
5c0532d1 29#include <unistd.h>
1829dc9d 30#include <ftw.h>
8e274523
LP
31
32#include "mount-setup.h"
33#include "log.h"
c9af1080
LP
34#include "macro.h"
35#include "util.h"
5275d3c1 36#include "label.h"
0c85a4f3
LP
37#include "set.h"
38#include "strv.h"
49e942b2 39#include "mkdir.h"
8e274523 40
bef2733f
LP
41#ifndef TTY_GID
42#define TTY_GID 5
43#endif
44
ca714c0e
LP
45typedef struct MountPoint {
46 const char *what;
47 const char *where;
48 const char *type;
49 const char *options;
50 unsigned long flags;
2076ca54 51 bool fatal;
ca714c0e
LP
52} MountPoint;
53
4ef31082 54/* The first three entries we might need before SELinux is up. The
160481f6
RS
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
4ef31082 58
ca714c0e 59static const MountPoint mount_table[] = {
77d5f105
LP
60 { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
61 { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
635f7d8c 62 { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
160481f6 63 { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
635f7d8c 64 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
bef2733f 65 { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, false },
635f7d8c
KS
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 },
e5a53dc7 68 { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, false },
8e274523
LP
69};
70
949c6510 71/* These are API file systems that might be mounted by other software,
46ff0ed7 72 * we just list them here so that we know that we should ignore them */
949c6510
LP
73
74static const char * const ignore_paths[] = {
ef9d7dca 75 "/sys/fs/selinux",
949c6510 76 "/selinux",
af49ec2c 77 "/proc/bus/usb"
949c6510
LP
78};
79
dad08730
LP
80bool 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
ca714c0e 86 for (i = 0; i < ELEMENTSOF(mount_table); i ++)
449ddb2d 87 if (path_equal(path, mount_table[i].where))
dad08730
LP
88 return true;
89
57f2a956
KS
90 return path_startswith(path, "/sys/fs/cgroup/");
91}
92
93bool mount_point_ignore(const char *path) {
46ff0ed7 94 unsigned i;
57f2a956 95
949c6510 96 for (i = 0; i < ELEMENTSOF(ignore_paths); i++)
449ddb2d 97 if (path_equal(path, ignore_paths[i]))
949c6510
LP
98 return true;
99
57f2a956 100 return false;
dad08730
LP
101}
102
4ef31082 103static int mount_one(const MountPoint *p, bool relabel) {
8e274523
LP
104 int r;
105
ca714c0e 106 assert(p);
8e274523 107
51b4af2c 108 /* Relabel first, just in case */
4ef31082
LP
109 if (relabel)
110 label_fix(p->where, true);
51b4af2c 111
0c85a4f3 112 if ((r = path_is_mount_point(p->where, true)) < 0)
8e274523
LP
113 return r;
114
115 if (r > 0)
51b4af2c 116 return 0;
8e274523 117
a04f58d6
LP
118 /* The access mode here doesn't really matter too much, since
119 * the mounted file system will take precedence anyway. */
ca714c0e 120 mkdir_p(p->where, 0755);
a04f58d6 121
8e274523 122 log_debug("Mounting %s to %s of type %s with options %s.",
ca714c0e
LP
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));
2076ca54 134 return p->fatal ? -errno : 0;
8e274523
LP
135 }
136
51b4af2c 137 /* Relabel again, since we now mounted something fresh here */
4ef31082
LP
138 if (relabel)
139 label_fix(p->where, false);
5275d3c1 140
0c85a4f3 141 return 1;
8e274523
LP
142}
143
4ef31082
LP
144int 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
0c85a4f3 163int mount_cgroup_controllers(char ***join_controllers) {
2076ca54
LP
164 int r;
165 FILE *f;
20c03b7b 166 char buf[LINE_MAX];
0c85a4f3 167 Set *controllers;
2076ca54 168
670802d4 169 /* Mount all available cgroup controllers that are built into the kernel. */
2076ca54 170
0c85a4f3
LP
171 f = fopen("/proc/cgroups", "re");
172 if (!f) {
016e9849
LP
173 log_error("Failed to enumerate cgroup controllers: %m");
174 return 0;
175 }
2076ca54 176
0c85a4f3
LP
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
2076ca54 184 /* Ignore the header line */
bab45044 185 (void) fgets(buf, sizeof(buf), f);
2076ca54
LP
186
187 for (;;) {
0c85a4f3
LP
188 char *controller;
189 int enabled = 0;
2076ca54 190
16f6682d 191 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2076ca54
LP
192
193 if (feof(f))
194 break;
195
196 log_error("Failed to parse /proc/cgroups.");
197 r = -EIO;
198 goto finish;
199 }
200
600a328f
LP
201 if (!enabled) {
202 free(controller);
203 continue;
204 }
205
0c85a4f3
LP
206 r = set_put(controllers, controller);
207 if (r < 0) {
208 log_error("Failed to add controller to set.");
2076ca54 209 free(controller);
0c85a4f3
LP
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);
2076ca54
LP
266 r = -ENOMEM;
267 goto finish;
268 }
269
270 zero(p);
271 p.what = "cgroup";
272 p.where = where;
273 p.type = "cgroup";
0c85a4f3 274 p.options = options;
2076ca54
LP
275 p.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV;
276 p.fatal = false;
277
4ef31082 278 r = mount_one(&p, true);
2076ca54
LP
279 free(controller);
280 free(where);
281
0c85a4f3
LP
282 if (r < 0) {
283 free(options);
2076ca54 284 goto finish;
0c85a4f3
LP
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);
2076ca54
LP
314 }
315
316 r = 0;
317
318finish:
0c85a4f3
LP
319 set_free_free(controllers);
320
2076ca54
LP
321 fclose(f);
322
323 return r;
324}
325
5c0532d1
LP
326static 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
e9a5ef7c
KS
332 r = label_context_set(new_path, S_IFLNK);
333 if (r < 0)
5c0532d1
LP
334 return r;
335
336 if (symlink(old_path, new_path) < 0)
337 r = -errno;
338
e9a5ef7c 339 label_context_clear();
5c0532d1
LP
340
341 return r;
342}
343
1829dc9d
LP
344static int nftw_cb(
345 const char *fpath,
346 const struct stat *sb,
347 int tflag,
348 struct FTW *ftwbuf) {
349
9fe117ea 350 /* No need to label /dev twice in a row... */
edb49778
LP
351 if (_unlikely_(ftwbuf->level == 0))
352 return FTW_CONTINUE;
353
af65c248
LP
354 label_fix(fpath, true);
355
edb49778 356 /* /run/initramfs is static data and big, no need to
af65c248 357 * dynamically relabel its contents at boot... */
edb49778
LP
358 if (_unlikely_(ftwbuf->level == 1 &&
359 tflag == FTW_D &&
360 streq(fpath, "/run/initramfs")))
361 return FTW_SKIP_SUBTREE;
9fe117ea 362
edb49778 363 return FTW_CONTINUE;
1829dc9d
LP
364};
365
0b3325e7 366int mount_setup(bool loaded_policy) {
5c0532d1 367
af65c248 368 static const char symlinks[] =
5c0532d1
LP
369 "/proc/kcore\0" "/dev/core\0"
370 "/proc/self/fd\0" "/dev/fd\0"
371 "/proc/self/fd/0\0" "/dev/stdin\0"
372 "/proc/self/fd/1\0" "/dev/stdout\0"
34df5a34 373 "/proc/self/fd/2\0" "/dev/stderr\0";
5c0532d1 374
af65c248
LP
375 static const char relabel[] =
376 "/run/initramfs/root-fsck\0"
377 "/run/initramfs/shutdown\0";
378
8e274523 379 int r;
dad08730 380 unsigned i;
5c0532d1 381 const char *j, *k;
8e274523 382
4ef31082
LP
383 for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
384 r = mount_one(mount_table + i, true);
385
386 if (r < 0)
8e274523 387 return r;
4ef31082 388 }
8e274523 389
f1d19aa4
LP
390 /* Nodes in devtmpfs and /run need to be manually updated for
391 * the appropriate labels, after mounting. The other virtual
392 * API file systems like /sys and /proc do not need that, they
393 * use the same label for all their files. */
0b3325e7
LP
394 if (loaded_policy) {
395 usec_t before_relabel, after_relabel;
396 char timespan[FORMAT_TIMESPAN_MAX];
397
398 before_relabel = now(CLOCK_MONOTONIC);
399
edb49778
LP
400 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
401 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
0b3325e7 402
af65c248
LP
403 /* Explicitly relabel these */
404 NULSTR_FOREACH(j, relabel)
405 label_fix(j, true);
406
0b3325e7
LP
407 after_relabel = now(CLOCK_MONOTONIC);
408
409 log_info("Relabelled /dev and /run in %s.",
410 format_timespan(timespan, sizeof(timespan), after_relabel - before_relabel));
3bbecb2f 411 }
1829dc9d 412
5c0532d1 413 /* Create a few default symlinks, which are normally created
f1d19aa4 414 * by udevd, but some scripts might need them before we start
5c0532d1 415 * udevd. */
5c0532d1
LP
416 NULSTR_FOREACH_PAIR(j, k, symlinks)
417 symlink_and_label(j, k);
418
b925e726 419 /* Create a few directories we always want around */
af65c248
LP
420 label_mkdir("/run/systemd", 0755);
421 label_mkdir("/run/systemd/system", 0755);
b925e726 422
0c85a4f3 423 return 0;
8e274523 424}