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