1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
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.
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.
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/>.
22 #include <sys/mount.h>
23 #include <linux/magic.h>
28 #include "path-util.h"
32 #include "cgroup-util.h"
34 #include "nspawn-mount.h"
36 CustomMount
* custom_mount_add(CustomMount
**l
, unsigned *n
, CustomMountType t
) {
42 assert(t
< _CUSTOM_MOUNT_TYPE_MAX
);
44 c
= realloc(*l
, (*n
+ 1) * sizeof(CustomMount
));
52 *ret
= (CustomMount
) { .type
= t
};
57 void custom_mount_free_all(CustomMount
*l
, unsigned n
) {
60 for (i
= 0; i
< n
; i
++) {
61 CustomMount
*m
= l
+ i
;
68 (void) rm_rf(m
->work_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
78 int custom_mount_compare(const void *a
, const void *b
) {
79 const CustomMount
*x
= a
, *y
= b
;
82 r
= path_compare(x
->destination
, y
->destination
);
86 if (x
->type
< y
->type
)
88 if (x
->type
> y
->type
)
94 int bind_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
, bool read_only
) {
95 _cleanup_free_
char *source
= NULL
, *destination
= NULL
, *opts
= NULL
;
103 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &source
, &destination
, NULL
);
110 destination
= strdup(source
);
115 if (r
== 2 && !isempty(p
)) {
121 if (!path_is_absolute(source
))
124 if (!path_is_absolute(destination
))
127 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_BIND
);
132 m
->destination
= destination
;
133 m
->read_only
= read_only
;
136 source
= destination
= opts
= NULL
;
140 int tmpfs_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
) {
141 _cleanup_free_
char *path
= NULL
, *opts
= NULL
;
150 r
= extract_first_word(&p
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
157 opts
= strdup("mode=0755");
163 if (!path_is_absolute(path
))
166 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_TMPFS
);
170 m
->destination
= path
;
177 static int tmpfs_patch_options(
179 bool userns
, uid_t uid_shift
, uid_t uid_range
,
180 const char *selinux_apifs_context
,
185 if (userns
&& uid_shift
!= 0) {
186 assert(uid_shift
!= UID_INVALID
);
189 (void) asprintf(&buf
, "%s,uid=" UID_FMT
",gid=" UID_FMT
, options
, uid_shift
, uid_shift
);
191 (void) asprintf(&buf
, "uid=" UID_FMT
",gid=" UID_FMT
, uid_shift
, uid_shift
);
199 if (selinux_apifs_context
) {
203 t
= strjoin(options
, ",context=\"", selinux_apifs_context
, "\"", NULL
);
205 t
= strjoin("context=\"", selinux_apifs_context
, "\"", NULL
);
220 int mount_sysfs(const char *dest
) {
221 const char *full
, *top
, *x
;
224 top
= prefix_roota(dest
, "/sys");
225 r
= path_check_fstype(top
, SYSFS_MAGIC
);
227 return log_error_errno(r
, "Failed to determine filesystem type of %s: %m", top
);
228 /* /sys might already be mounted as sysfs by the outer child in the
229 * !netns case. In this case, it's all good. Don't touch it because we
230 * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
235 full
= prefix_roota(top
, "/full");
237 (void) mkdir(full
, 0755);
239 if (mount("sysfs", full
, "sysfs", MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
240 return log_error_errno(errno
, "Failed to mount sysfs to %s: %m", full
);
242 FOREACH_STRING(x
, "block", "bus", "class", "dev", "devices", "kernel") {
243 _cleanup_free_
char *from
= NULL
, *to
= NULL
;
245 from
= prefix_root(full
, x
);
249 to
= prefix_root(top
, x
);
253 (void) mkdir(to
, 0755);
255 if (mount(from
, to
, NULL
, MS_BIND
, NULL
) < 0)
256 return log_error_errno(errno
, "Failed to mount /sys/%s into place: %m", x
);
258 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
259 return log_error_errno(errno
, "Failed to mount /sys/%s read-only: %m", x
);
262 if (umount(full
) < 0)
263 return log_error_errno(errno
, "Failed to unmount %s: %m", full
);
266 return log_error_errno(errno
, "Failed to remove %s: %m", full
);
268 x
= prefix_roota(top
, "/fs/kdbus");
269 (void) mkdir(x
, 0755);
271 if (mount(NULL
, top
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
272 return log_error_errno(errno
, "Failed to make %s read-only: %m", top
);
277 int mount_all(const char *dest
,
278 bool use_userns
, bool in_userns
,
280 uid_t uid_shift
, uid_t uid_range
,
281 const char *selinux_apifs_context
) {
283 typedef struct MountPoint
{
294 static const MountPoint mount_table
[] = {
295 { "proc", "/proc", "proc", NULL
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, true, false },
296 { "/proc/sys", "/proc/sys", NULL
, NULL
, MS_BIND
, true, true, false }, /* Bind mount first */
297 { NULL
, "/proc/sys", NULL
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, true, true, false }, /* Then, make it r/o */
298 { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, true },
299 { "sysfs", "/sys", "sysfs", NULL
, MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, false },
300 { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID
|MS_STRICTATIME
, true, false, false },
301 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
302 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
303 { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME
, true, false, false },
305 { "/sys/fs/selinux", "/sys/fs/selinux", NULL
, NULL
, MS_BIND
, false, false, false }, /* Bind mount first */
306 { NULL
, "/sys/fs/selinux", NULL
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, false, false, false }, /* Then, make it r/o */
313 for (k
= 0; k
< ELEMENTSOF(mount_table
); k
++) {
314 _cleanup_free_
char *where
= NULL
, *options
= NULL
;
317 if (in_userns
!= mount_table
[k
].in_userns
)
320 if (!use_netns
&& mount_table
[k
].use_netns
)
323 where
= prefix_root(dest
, mount_table
[k
].where
);
327 r
= path_is_mount_point(where
, AT_SYMLINK_FOLLOW
);
328 if (r
< 0 && r
!= -ENOENT
)
329 return log_error_errno(r
, "Failed to detect whether %s is a mount point: %m", where
);
331 /* Skip this entry if it is not a remount. */
332 if (mount_table
[k
].what
&& r
> 0)
335 r
= mkdir_p(where
, 0755);
337 if (mount_table
[k
].fatal
)
338 return log_error_errno(r
, "Failed to create directory %s: %m", where
);
340 log_warning_errno(r
, "Failed to create directory %s: %m", where
);
344 o
= mount_table
[k
].options
;
345 if (streq_ptr(mount_table
[k
].type
, "tmpfs")) {
346 r
= tmpfs_patch_options(o
, use_userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
353 if (mount(mount_table
[k
].what
,
356 mount_table
[k
].flags
,
359 if (mount_table
[k
].fatal
)
360 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
362 log_warning_errno(errno
, "mount(%s) failed, ignoring: %m", where
);
369 static int parse_mount_bind_options(const char *options
, unsigned long *mount_flags
, char **mount_opts
) {
370 const char *p
= options
;
371 unsigned long flags
= *mount_flags
;
377 _cleanup_free_
char *word
= NULL
;
378 int r
= extract_first_word(&p
, &word
, ",", 0);
380 return log_error_errno(r
, "Failed to extract mount option: %m");
384 if (streq(word
, "rbind"))
386 else if (streq(word
, "norbind"))
389 log_error("Invalid bind mount option: %s", word
);
394 *mount_flags
= flags
;
395 /* in the future mount_opts will hold string options for mount(2) */
401 static int mount_bind(const char *dest
, CustomMount
*m
) {
402 struct stat source_st
, dest_st
;
404 unsigned long mount_flags
= MS_BIND
| MS_REC
;
405 _cleanup_free_
char *mount_opts
= NULL
;
411 r
= parse_mount_bind_options(m
->options
, &mount_flags
, &mount_opts
);
416 if (stat(m
->source
, &source_st
) < 0)
417 return log_error_errno(errno
, "Failed to stat %s: %m", m
->source
);
419 where
= prefix_roota(dest
, m
->destination
);
421 if (stat(where
, &dest_st
) >= 0) {
422 if (S_ISDIR(source_st
.st_mode
) && !S_ISDIR(dest_st
.st_mode
)) {
423 log_error("Cannot bind mount directory %s on file %s.", m
->source
, where
);
427 if (!S_ISDIR(source_st
.st_mode
) && S_ISDIR(dest_st
.st_mode
)) {
428 log_error("Cannot bind mount file %s on directory %s.", m
->source
, where
);
432 } else if (errno
== ENOENT
) {
433 r
= mkdir_parents_label(where
, 0755);
435 return log_error_errno(r
, "Failed to make parents of %s: %m", where
);
437 log_error_errno(errno
, "Failed to stat %s: %m", where
);
441 /* Create the mount point. Any non-directory file can be
442 * mounted on any non-directory file (regular, fifo, socket,
445 if (S_ISDIR(source_st
.st_mode
))
446 r
= mkdir_label(where
, 0755);
449 if (r
< 0 && r
!= -EEXIST
)
450 return log_error_errno(r
, "Failed to create mount point %s: %m", where
);
452 if (mount(m
->source
, where
, NULL
, mount_flags
, mount_opts
) < 0)
453 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
456 r
= bind_remount_recursive(where
, true);
458 return log_error_errno(r
, "Read-only bind mount failed: %m");
464 static int mount_tmpfs(
467 bool userns
, uid_t uid_shift
, uid_t uid_range
,
468 const char *selinux_apifs_context
) {
470 const char *where
, *options
;
471 _cleanup_free_
char *buf
= NULL
;
477 where
= prefix_roota(dest
, m
->destination
);
479 r
= mkdir_p_label(where
, 0755);
480 if (r
< 0 && r
!= -EEXIST
)
481 return log_error_errno(r
, "Creating mount point for tmpfs %s failed: %m", where
);
483 r
= tmpfs_patch_options(m
->options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
486 options
= r
> 0 ? buf
: m
->options
;
488 if (mount("tmpfs", where
, "tmpfs", MS_NODEV
|MS_STRICTATIME
, options
) < 0)
489 return log_error_errno(errno
, "tmpfs mount to %s failed: %m", where
);
494 static char *joined_and_escaped_lower_dirs(char * const *lower
) {
495 _cleanup_strv_free_
char **sv
= NULL
;
497 sv
= strv_copy(lower
);
503 if (!strv_shell_escape(sv
, ",:"))
506 return strv_join(sv
, ":");
509 static int mount_overlay(const char *dest
, CustomMount
*m
) {
510 _cleanup_free_
char *lower
= NULL
;
511 const char *where
, *options
;
517 where
= prefix_roota(dest
, m
->destination
);
519 r
= mkdir_label(where
, 0755);
520 if (r
< 0 && r
!= -EEXIST
)
521 return log_error_errno(r
, "Creating mount point for overlay %s failed: %m", where
);
523 (void) mkdir_p_label(m
->source
, 0755);
525 lower
= joined_and_escaped_lower_dirs(m
->lower
);
530 _cleanup_free_
char *escaped_source
= NULL
;
532 escaped_source
= shell_escape(m
->source
, ",:");
536 options
= strjoina("lowerdir=", escaped_source
, ":", lower
);
538 _cleanup_free_
char *escaped_source
= NULL
, *escaped_work_dir
= NULL
;
541 (void) mkdir_label(m
->work_dir
, 0700);
543 escaped_source
= shell_escape(m
->source
, ",:");
546 escaped_work_dir
= shell_escape(m
->work_dir
, ",:");
547 if (!escaped_work_dir
)
550 options
= strjoina("lowerdir=", lower
, ",upperdir=", escaped_source
, ",workdir=", escaped_work_dir
);
553 if (mount("overlay", where
, "overlay", m
->read_only
? MS_RDONLY
: 0, options
) < 0)
554 return log_error_errno(errno
, "overlay mount to %s failed: %m", where
);
561 CustomMount
*mounts
, unsigned n
,
562 bool userns
, uid_t uid_shift
, uid_t uid_range
,
563 const char *selinux_apifs_context
) {
570 for (i
= 0; i
< n
; i
++) {
571 CustomMount
*m
= mounts
+ i
;
575 case CUSTOM_MOUNT_BIND
:
576 r
= mount_bind(dest
, m
);
579 case CUSTOM_MOUNT_TMPFS
:
580 r
= mount_tmpfs(dest
, m
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
583 case CUSTOM_MOUNT_OVERLAY
:
584 r
= mount_overlay(dest
, m
);
588 assert_not_reached("Unknown custom mount type");
598 static int mount_legacy_cgroup_hierarchy(const char *dest
, const char *controller
, const char *hierarchy
, bool read_only
) {
602 to
= strjoina(strempty(dest
), "/sys/fs/cgroup/", hierarchy
);
604 r
= path_is_mount_point(to
, 0);
605 if (r
< 0 && r
!= -ENOENT
)
606 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", to
);
612 /* The superblock mount options of the mount point need to be
613 * identical to the hosts', and hence writable... */
614 if (mount("cgroup", to
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, controller
) < 0)
615 return log_error_errno(errno
, "Failed to mount to %s: %m", to
);
617 /* ... hence let's only make the bind mount read-only, not the
620 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
621 return log_error_errno(errno
, "Failed to remount %s read-only: %m", to
);
626 static int mount_legacy_cgroups(
628 bool userns
, uid_t uid_shift
, uid_t uid_range
,
629 const char *selinux_apifs_context
) {
631 _cleanup_set_free_free_ Set
*controllers
= NULL
;
632 const char *cgroup_root
;
635 cgroup_root
= prefix_roota(dest
, "/sys/fs/cgroup");
637 (void) mkdir_p(cgroup_root
, 0755);
639 /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
640 r
= path_is_mount_point(cgroup_root
, AT_SYMLINK_FOLLOW
);
642 return log_error_errno(r
, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
644 _cleanup_free_
char *options
= NULL
;
646 r
= tmpfs_patch_options("mode=755", userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
650 if (mount("tmpfs", cgroup_root
, "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
, options
) < 0)
651 return log_error_errno(errno
, "Failed to mount /sys/fs/cgroup: %m");
654 if (cg_unified() > 0)
655 goto skip_controllers
;
657 controllers
= set_new(&string_hash_ops
);
661 r
= cg_kernel_controllers(controllers
);
663 return log_error_errno(r
, "Failed to determine cgroup controllers: %m");
666 _cleanup_free_
char *controller
= NULL
, *origin
= NULL
, *combined
= NULL
;
668 controller
= set_steal_first(controllers
);
672 origin
= prefix_root("/sys/fs/cgroup/", controller
);
676 r
= readlink_malloc(origin
, &combined
);
678 /* Not a symbolic link, but directly a single cgroup hierarchy */
680 r
= mount_legacy_cgroup_hierarchy(dest
, controller
, controller
, true);
685 return log_error_errno(r
, "Failed to read link %s: %m", origin
);
687 _cleanup_free_
char *target
= NULL
;
689 target
= prefix_root(dest
, origin
);
693 /* A symbolic link, a combination of controllers in one hierarchy */
695 if (!filename_is_valid(combined
)) {
696 log_warning("Ignoring invalid combined hierarchy %s.", combined
);
700 r
= mount_legacy_cgroup_hierarchy(dest
, combined
, combined
, true);
704 r
= symlink_idempotent(combined
, target
);
706 log_error("Invalid existing symlink for combined hierarchy");
710 return log_error_errno(r
, "Failed to create symlink for combined hierarchy: %m");
715 r
= mount_legacy_cgroup_hierarchy(dest
, "none,name=systemd,xattr", "systemd", false);
719 if (mount(NULL
, cgroup_root
, NULL
, MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
|MS_RDONLY
, "mode=755") < 0)
720 return log_error_errno(errno
, "Failed to remount %s read-only: %m", cgroup_root
);
725 static int mount_unified_cgroups(const char *dest
) {
731 p
= prefix_roota(dest
, "/sys/fs/cgroup");
733 (void) mkdir_p(p
, 0755);
735 r
= path_is_mount_point(p
, AT_SYMLINK_FOLLOW
);
737 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", p
);
739 p
= prefix_roota(dest
, "/sys/fs/cgroup/cgroup.procs");
740 if (access(p
, F_OK
) >= 0)
743 return log_error_errno(errno
, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p
);
745 log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p
);
749 if (mount("cgroup", p
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, "__DEVEL__sane_behavior") < 0)
750 return log_error_errno(errno
, "Failed to mount unified cgroup hierarchy to %s: %m", p
);
757 bool unified_requested
,
758 bool userns
, uid_t uid_shift
, uid_t uid_range
,
759 const char *selinux_apifs_context
) {
761 if (unified_requested
)
762 return mount_unified_cgroups(dest
);
764 return mount_legacy_cgroups(dest
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
767 int mount_systemd_cgroup_writable(
769 bool unified_requested
) {
771 _cleanup_free_
char *own_cgroup_path
= NULL
;
772 const char *systemd_root
, *systemd_own
;
777 r
= cg_pid_get_path(NULL
, 0, &own_cgroup_path
);
779 return log_error_errno(r
, "Failed to determine our own cgroup path: %m");
781 /* If we are living in the top-level, then there's nothing to do... */
782 if (path_equal(own_cgroup_path
, "/"))
785 if (unified_requested
) {
786 systemd_own
= strjoina(dest
, "/sys/fs/cgroup", own_cgroup_path
);
787 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup");
789 systemd_own
= strjoina(dest
, "/sys/fs/cgroup/systemd", own_cgroup_path
);
790 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup/systemd");
793 /* Make our own cgroup a (writable) bind mount */
794 if (mount(systemd_own
, systemd_own
, NULL
, MS_BIND
, NULL
) < 0)
795 return log_error_errno(errno
, "Failed to turn %s into a bind mount: %m", own_cgroup_path
);
797 /* And then remount the systemd cgroup root read-only */
798 if (mount(NULL
, systemd_root
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
799 return log_error_errno(errno
, "Failed to mount cgroup root read-only: %m");
804 int setup_volatile_state(
805 const char *directory
,
807 bool userns
, uid_t uid_shift
, uid_t uid_range
,
808 const char *selinux_apifs_context
) {
810 _cleanup_free_
char *buf
= NULL
;
811 const char *p
, *options
;
816 if (mode
!= VOLATILE_STATE
)
819 /* --volatile=state means we simply overmount /var
820 with a tmpfs, and the rest read-only. */
822 r
= bind_remount_recursive(directory
, true);
824 return log_error_errno(r
, "Failed to remount %s read-only: %m", directory
);
826 p
= prefix_roota(directory
, "/var");
828 if (r
< 0 && errno
!= EEXIST
)
829 return log_error_errno(errno
, "Failed to create %s: %m", directory
);
831 options
= "mode=755";
832 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
838 if (mount("tmpfs", p
, "tmpfs", MS_STRICTATIME
, options
) < 0)
839 return log_error_errno(errno
, "Failed to mount tmpfs to /var: %m");
845 const char *directory
,
847 bool userns
, uid_t uid_shift
, uid_t uid_range
,
848 const char *selinux_apifs_context
) {
850 bool tmpfs_mounted
= false, bind_mounted
= false;
851 char template[] = "/tmp/nspawn-volatile-XXXXXX";
852 _cleanup_free_
char *buf
= NULL
;
853 const char *f
, *t
, *options
;
858 if (mode
!= VOLATILE_YES
)
861 /* --volatile=yes means we mount a tmpfs to the root dir, and
862 the original /usr to use inside it, and that read-only. */
864 if (!mkdtemp(template))
865 return log_error_errno(errno
, "Failed to create temporary directory: %m");
867 options
= "mode=755";
868 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
874 if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME
, options
) < 0) {
875 r
= log_error_errno(errno
, "Failed to mount tmpfs for root directory: %m");
879 tmpfs_mounted
= true;
881 f
= prefix_roota(directory
, "/usr");
882 t
= prefix_roota(template, "/usr");
885 if (r
< 0 && errno
!= EEXIST
) {
886 r
= log_error_errno(errno
, "Failed to create %s: %m", t
);
890 if (mount(f
, t
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
891 r
= log_error_errno(errno
, "Failed to create /usr bind mount: %m");
897 r
= bind_remount_recursive(t
, true);
899 log_error_errno(r
, "Failed to remount %s read-only: %m", t
);
903 if (mount(template, directory
, NULL
, MS_MOVE
, NULL
) < 0) {
904 r
= log_error_errno(errno
, "Failed to move root mount: %m");
908 (void) rmdir(template);
917 (void) umount(template);
918 (void) rmdir(template);
922 VolatileMode
volatile_mode_from_string(const char *s
) {
926 return _VOLATILE_MODE_INVALID
;
928 b
= parse_boolean(s
);
934 if (streq(s
, "state"))
935 return VOLATILE_STATE
;
937 return _VOLATILE_MODE_INVALID
;