]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/mount-setup.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / 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
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
1829dc9d
LP
343static int nftw_cb(
344 const char *fpath,
345 const struct stat *sb,
346 int tflag,
347 struct FTW *ftwbuf) {
348
9fe117ea 349 /* No need to label /dev twice in a row... */
edb49778
LP
350 if (_unlikely_(ftwbuf->level == 0))
351 return FTW_CONTINUE;
352
af65c248
LP
353 label_fix(fpath, true);
354
edb49778 355 /* /run/initramfs is static data and big, no need to
af65c248 356 * dynamically relabel its contents at boot... */
edb49778
LP
357 if (_unlikely_(ftwbuf->level == 1 &&
358 tflag == FTW_D &&
359 streq(fpath, "/run/initramfs")))
360 return FTW_SKIP_SUBTREE;
9fe117ea 361
edb49778 362 return FTW_CONTINUE;
1829dc9d
LP
363};
364
0b3325e7 365int mount_setup(bool loaded_policy) {
5c0532d1 366
af65c248 367 static const char symlinks[] =
5c0532d1
LP
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"
34df5a34 372 "/proc/self/fd/2\0" "/dev/stderr\0";
5c0532d1 373
af65c248
LP
374 static const char relabel[] =
375 "/run/initramfs/root-fsck\0"
376 "/run/initramfs/shutdown\0";
377
8e274523 378 int r;
dad08730 379 unsigned i;
5c0532d1 380 const char *j, *k;
8e274523 381
4ef31082
LP
382 for (i = 0; i < ELEMENTSOF(mount_table); i ++) {
383 r = mount_one(mount_table + i, true);
384
385 if (r < 0)
8e274523 386 return r;
4ef31082 387 }
8e274523 388
f1d19aa4
LP
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. */
0b3325e7
LP
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
edb49778
LP
399 nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
400 nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL);
0b3325e7 401
af65c248
LP
402 /* Explicitly relabel these */
403 NULSTR_FOREACH(j, relabel)
404 label_fix(j, true);
405
0b3325e7
LP
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));
3bbecb2f 410 }
1829dc9d 411
5c0532d1 412 /* Create a few default symlinks, which are normally created
f1d19aa4 413 * by udevd, but some scripts might need them before we start
5c0532d1 414 * udevd. */
5c0532d1
LP
415 NULSTR_FOREACH_PAIR(j, k, symlinks)
416 symlink_and_label(j, k);
417
b925e726 418 /* Create a few directories we always want around */
af65c248
LP
419 label_mkdir("/run/systemd", 0755);
420 label_mkdir("/run/systemd/system", 0755);
b925e726 421
0c85a4f3 422 return 0;
8e274523 423}