2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include <sys/mount.h>
21 #include <linux/magic.h>
23 #include "alloc-util.h"
24 #include "cgroup-util.h"
29 #include "mount-util.h"
30 #include "nspawn-mount.h"
31 #include "parse-util.h"
32 #include "path-util.h"
35 #include "stat-util.h"
36 #include "string-util.h"
38 #include "user-util.h"
41 CustomMount
* custom_mount_add(CustomMount
**l
, unsigned *n
, CustomMountType t
) {
47 assert(t
< _CUSTOM_MOUNT_TYPE_MAX
);
49 c
= realloc(*l
, (*n
+ 1) * sizeof(CustomMount
));
57 *ret
= (CustomMount
) { .type
= t
};
62 void custom_mount_free_all(CustomMount
*l
, unsigned n
) {
65 for (i
= 0; i
< n
; i
++) {
66 CustomMount
*m
= l
+ i
;
73 (void) rm_rf(m
->work_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
83 int custom_mount_compare(const void *a
, const void *b
) {
84 const CustomMount
*x
= a
, *y
= b
;
87 r
= path_compare(x
->destination
, y
->destination
);
91 if (x
->type
< y
->type
)
93 if (x
->type
> y
->type
)
99 int bind_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
, bool read_only
) {
100 _cleanup_free_
char *source
= NULL
, *destination
= NULL
, *opts
= NULL
;
108 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &source
, &destination
, NULL
);
115 destination
= strdup(source
);
120 if (r
== 2 && !isempty(p
)) {
126 if (!path_is_absolute(source
))
129 if (!path_is_absolute(destination
))
132 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_BIND
);
137 m
->destination
= destination
;
138 m
->read_only
= read_only
;
141 source
= destination
= opts
= NULL
;
145 int tmpfs_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
) {
146 _cleanup_free_
char *path
= NULL
, *opts
= NULL
;
155 r
= extract_first_word(&p
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
162 opts
= strdup("mode=0755");
168 if (!path_is_absolute(path
))
171 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_TMPFS
);
175 m
->destination
= path
;
182 static int tmpfs_patch_options(
184 bool userns
, uid_t uid_shift
, uid_t uid_range
,
185 const char *selinux_apifs_context
,
190 if (userns
&& uid_shift
!= 0) {
191 assert(uid_shift
!= UID_INVALID
);
194 (void) asprintf(&buf
, "%s,uid=" UID_FMT
",gid=" UID_FMT
, options
, uid_shift
, uid_shift
);
196 (void) asprintf(&buf
, "uid=" UID_FMT
",gid=" UID_FMT
, uid_shift
, uid_shift
);
204 if (selinux_apifs_context
) {
208 t
= strjoin(options
, ",context=\"", selinux_apifs_context
, "\"", NULL
);
210 t
= strjoin("context=\"", selinux_apifs_context
, "\"", NULL
);
225 int mount_sysfs(const char *dest
) {
226 const char *full
, *top
, *x
;
229 top
= prefix_roota(dest
, "/sys");
230 r
= path_check_fstype(top
, SYSFS_MAGIC
);
232 return log_error_errno(r
, "Failed to determine filesystem type of %s: %m", top
);
233 /* /sys might already be mounted as sysfs by the outer child in the
234 * !netns case. In this case, it's all good. Don't touch it because we
235 * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
240 full
= prefix_roota(top
, "/full");
242 (void) mkdir(full
, 0755);
244 if (mount("sysfs", full
, "sysfs", MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
245 return log_error_errno(errno
, "Failed to mount sysfs to %s: %m", full
);
247 FOREACH_STRING(x
, "block", "bus", "class", "dev", "devices", "kernel") {
248 _cleanup_free_
char *from
= NULL
, *to
= NULL
;
250 from
= prefix_root(full
, x
);
254 to
= prefix_root(top
, x
);
258 (void) mkdir(to
, 0755);
260 if (mount(from
, to
, NULL
, MS_BIND
, NULL
) < 0)
261 return log_error_errno(errno
, "Failed to mount /sys/%s into place: %m", x
);
263 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
264 return log_error_errno(errno
, "Failed to mount /sys/%s read-only: %m", x
);
267 if (umount(full
) < 0)
268 return log_error_errno(errno
, "Failed to unmount %s: %m", full
);
271 return log_error_errno(errno
, "Failed to remove %s: %m", full
);
273 x
= prefix_roota(top
, "/fs/kdbus");
274 (void) mkdir(x
, 0755);
276 if (mount(NULL
, top
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
277 return log_error_errno(errno
, "Failed to make %s read-only: %m", top
);
282 int mount_all(const char *dest
,
283 bool use_userns
, bool in_userns
,
285 uid_t uid_shift
, uid_t uid_range
,
286 const char *selinux_apifs_context
) {
288 typedef struct MountPoint
{
299 static const MountPoint mount_table
[] = {
300 { "proc", "/proc", "proc", NULL
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, true, false },
301 { "/proc/sys", "/proc/sys", NULL
, NULL
, MS_BIND
, true, true, false }, /* Bind mount first */
302 { 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 */
303 { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, true },
304 { "sysfs", "/sys", "sysfs", NULL
, MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, false },
305 { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID
|MS_STRICTATIME
, true, false, false },
306 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
307 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
308 { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME
, true, false, false },
310 { "/sys/fs/selinux", "/sys/fs/selinux", NULL
, NULL
, MS_BIND
, false, false, false }, /* Bind mount first */
311 { 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 */
318 for (k
= 0; k
< ELEMENTSOF(mount_table
); k
++) {
319 _cleanup_free_
char *where
= NULL
, *options
= NULL
;
322 if (in_userns
!= mount_table
[k
].in_userns
)
325 if (!use_netns
&& mount_table
[k
].use_netns
)
328 where
= prefix_root(dest
, mount_table
[k
].where
);
332 r
= path_is_mount_point(where
, AT_SYMLINK_FOLLOW
);
333 if (r
< 0 && r
!= -ENOENT
)
334 return log_error_errno(r
, "Failed to detect whether %s is a mount point: %m", where
);
336 /* Skip this entry if it is not a remount. */
337 if (mount_table
[k
].what
&& r
> 0)
340 r
= mkdir_p(where
, 0755);
342 if (mount_table
[k
].fatal
)
343 return log_error_errno(r
, "Failed to create directory %s: %m", where
);
345 log_warning_errno(r
, "Failed to create directory %s: %m", where
);
349 o
= mount_table
[k
].options
;
350 if (streq_ptr(mount_table
[k
].type
, "tmpfs")) {
351 r
= tmpfs_patch_options(o
, use_userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
358 if (mount(mount_table
[k
].what
,
361 mount_table
[k
].flags
,
364 if (mount_table
[k
].fatal
)
365 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
367 log_warning_errno(errno
, "mount(%s) failed, ignoring: %m", where
);
374 static int parse_mount_bind_options(const char *options
, unsigned long *mount_flags
, char **mount_opts
) {
375 const char *p
= options
;
376 unsigned long flags
= *mount_flags
;
382 _cleanup_free_
char *word
= NULL
;
383 int r
= extract_first_word(&p
, &word
, ",", 0);
385 return log_error_errno(r
, "Failed to extract mount option: %m");
389 if (streq(word
, "rbind"))
391 else if (streq(word
, "norbind"))
394 log_error("Invalid bind mount option: %s", word
);
399 *mount_flags
= flags
;
400 /* in the future mount_opts will hold string options for mount(2) */
406 static int mount_bind(const char *dest
, CustomMount
*m
) {
407 struct stat source_st
, dest_st
;
409 unsigned long mount_flags
= MS_BIND
| MS_REC
;
410 _cleanup_free_
char *mount_opts
= NULL
;
416 r
= parse_mount_bind_options(m
->options
, &mount_flags
, &mount_opts
);
421 if (stat(m
->source
, &source_st
) < 0)
422 return log_error_errno(errno
, "Failed to stat %s: %m", m
->source
);
424 where
= prefix_roota(dest
, m
->destination
);
426 if (stat(where
, &dest_st
) >= 0) {
427 if (S_ISDIR(source_st
.st_mode
) && !S_ISDIR(dest_st
.st_mode
)) {
428 log_error("Cannot bind mount directory %s on file %s.", m
->source
, where
);
432 if (!S_ISDIR(source_st
.st_mode
) && S_ISDIR(dest_st
.st_mode
)) {
433 log_error("Cannot bind mount file %s on directory %s.", m
->source
, where
);
437 } else if (errno
== ENOENT
) {
438 r
= mkdir_parents_label(where
, 0755);
440 return log_error_errno(r
, "Failed to make parents of %s: %m", where
);
442 return log_error_errno(errno
, "Failed to stat %s: %m", where
);
445 /* Create the mount point. Any non-directory file can be
446 * mounted on any non-directory file (regular, fifo, socket,
449 if (S_ISDIR(source_st
.st_mode
))
450 r
= mkdir_label(where
, 0755);
453 if (r
< 0 && r
!= -EEXIST
)
454 return log_error_errno(r
, "Failed to create mount point %s: %m", where
);
456 if (mount(m
->source
, where
, NULL
, mount_flags
, mount_opts
) < 0)
457 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
460 r
= bind_remount_recursive(where
, true);
462 return log_error_errno(r
, "Read-only bind mount failed: %m");
468 static int mount_tmpfs(
471 bool userns
, uid_t uid_shift
, uid_t uid_range
,
472 const char *selinux_apifs_context
) {
474 const char *where
, *options
;
475 _cleanup_free_
char *buf
= NULL
;
481 where
= prefix_roota(dest
, m
->destination
);
483 r
= mkdir_p_label(where
, 0755);
484 if (r
< 0 && r
!= -EEXIST
)
485 return log_error_errno(r
, "Creating mount point for tmpfs %s failed: %m", where
);
487 r
= tmpfs_patch_options(m
->options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
490 options
= r
> 0 ? buf
: m
->options
;
492 if (mount("tmpfs", where
, "tmpfs", MS_NODEV
|MS_STRICTATIME
, options
) < 0)
493 return log_error_errno(errno
, "tmpfs mount to %s failed: %m", where
);
498 static char *joined_and_escaped_lower_dirs(char * const *lower
) {
499 _cleanup_strv_free_
char **sv
= NULL
;
501 sv
= strv_copy(lower
);
507 if (!strv_shell_escape(sv
, ",:"))
510 return strv_join(sv
, ":");
513 static int mount_overlay(const char *dest
, CustomMount
*m
) {
514 _cleanup_free_
char *lower
= NULL
;
515 const char *where
, *options
;
521 where
= prefix_roota(dest
, m
->destination
);
523 r
= mkdir_label(where
, 0755);
524 if (r
< 0 && r
!= -EEXIST
)
525 return log_error_errno(r
, "Creating mount point for overlay %s failed: %m", where
);
527 (void) mkdir_p_label(m
->source
, 0755);
529 lower
= joined_and_escaped_lower_dirs(m
->lower
);
534 _cleanup_free_
char *escaped_source
= NULL
;
536 escaped_source
= shell_escape(m
->source
, ",:");
540 options
= strjoina("lowerdir=", escaped_source
, ":", lower
);
542 _cleanup_free_
char *escaped_source
= NULL
, *escaped_work_dir
= NULL
;
545 (void) mkdir_label(m
->work_dir
, 0700);
547 escaped_source
= shell_escape(m
->source
, ",:");
550 escaped_work_dir
= shell_escape(m
->work_dir
, ",:");
551 if (!escaped_work_dir
)
554 options
= strjoina("lowerdir=", lower
, ",upperdir=", escaped_source
, ",workdir=", escaped_work_dir
);
557 if (mount("overlay", where
, "overlay", m
->read_only
? MS_RDONLY
: 0, options
) < 0)
558 return log_error_errno(errno
, "overlay mount to %s failed: %m", where
);
565 CustomMount
*mounts
, unsigned n
,
566 bool userns
, uid_t uid_shift
, uid_t uid_range
,
567 const char *selinux_apifs_context
) {
574 for (i
= 0; i
< n
; i
++) {
575 CustomMount
*m
= mounts
+ i
;
579 case CUSTOM_MOUNT_BIND
:
580 r
= mount_bind(dest
, m
);
583 case CUSTOM_MOUNT_TMPFS
:
584 r
= mount_tmpfs(dest
, m
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
587 case CUSTOM_MOUNT_OVERLAY
:
588 r
= mount_overlay(dest
, m
);
592 assert_not_reached("Unknown custom mount type");
602 static int mount_legacy_cgroup_hierarchy(const char *dest
, const char *controller
, const char *hierarchy
, bool read_only
) {
606 to
= strjoina(strempty(dest
), "/sys/fs/cgroup/", hierarchy
);
608 r
= path_is_mount_point(to
, 0);
609 if (r
< 0 && r
!= -ENOENT
)
610 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", to
);
616 /* The superblock mount options of the mount point need to be
617 * identical to the hosts', and hence writable... */
618 if (mount("cgroup", to
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, controller
) < 0)
619 return log_error_errno(errno
, "Failed to mount to %s: %m", to
);
621 /* ... hence let's only make the bind mount read-only, not the
624 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
625 return log_error_errno(errno
, "Failed to remount %s read-only: %m", to
);
630 static int mount_legacy_cgroups(
632 bool userns
, uid_t uid_shift
, uid_t uid_range
,
633 const char *selinux_apifs_context
) {
635 _cleanup_set_free_free_ Set
*controllers
= NULL
;
636 const char *cgroup_root
;
639 cgroup_root
= prefix_roota(dest
, "/sys/fs/cgroup");
641 (void) mkdir_p(cgroup_root
, 0755);
643 /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
644 r
= path_is_mount_point(cgroup_root
, AT_SYMLINK_FOLLOW
);
646 return log_error_errno(r
, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
648 _cleanup_free_
char *options
= NULL
;
650 r
= tmpfs_patch_options("mode=755", userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
654 if (mount("tmpfs", cgroup_root
, "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
, options
) < 0)
655 return log_error_errno(errno
, "Failed to mount /sys/fs/cgroup: %m");
658 if (cg_unified() > 0)
659 goto skip_controllers
;
661 controllers
= set_new(&string_hash_ops
);
665 r
= cg_kernel_controllers(controllers
);
667 return log_error_errno(r
, "Failed to determine cgroup controllers: %m");
670 _cleanup_free_
char *controller
= NULL
, *origin
= NULL
, *combined
= NULL
;
672 controller
= set_steal_first(controllers
);
676 origin
= prefix_root("/sys/fs/cgroup/", controller
);
680 r
= readlink_malloc(origin
, &combined
);
682 /* Not a symbolic link, but directly a single cgroup hierarchy */
684 r
= mount_legacy_cgroup_hierarchy(dest
, controller
, controller
, true);
689 return log_error_errno(r
, "Failed to read link %s: %m", origin
);
691 _cleanup_free_
char *target
= NULL
;
693 target
= prefix_root(dest
, origin
);
697 /* A symbolic link, a combination of controllers in one hierarchy */
699 if (!filename_is_valid(combined
)) {
700 log_warning("Ignoring invalid combined hierarchy %s.", combined
);
704 r
= mount_legacy_cgroup_hierarchy(dest
, combined
, combined
, true);
708 r
= symlink_idempotent(combined
, target
);
710 log_error("Invalid existing symlink for combined hierarchy");
714 return log_error_errno(r
, "Failed to create symlink for combined hierarchy: %m");
719 r
= mount_legacy_cgroup_hierarchy(dest
, "none,name=systemd,xattr", "systemd", false);
723 if (mount(NULL
, cgroup_root
, NULL
, MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
|MS_RDONLY
, "mode=755") < 0)
724 return log_error_errno(errno
, "Failed to remount %s read-only: %m", cgroup_root
);
729 static int mount_unified_cgroups(const char *dest
) {
735 p
= prefix_roota(dest
, "/sys/fs/cgroup");
737 (void) mkdir_p(p
, 0755);
739 r
= path_is_mount_point(p
, AT_SYMLINK_FOLLOW
);
741 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", p
);
743 p
= prefix_roota(dest
, "/sys/fs/cgroup/cgroup.procs");
744 if (access(p
, F_OK
) >= 0)
747 return log_error_errno(errno
, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p
);
749 log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p
);
753 if (mount("cgroup", p
, "cgroup2", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
754 return log_error_errno(errno
, "Failed to mount unified cgroup hierarchy to %s: %m", p
);
761 bool unified_requested
,
762 bool userns
, uid_t uid_shift
, uid_t uid_range
,
763 const char *selinux_apifs_context
) {
765 if (unified_requested
)
766 return mount_unified_cgroups(dest
);
768 return mount_legacy_cgroups(dest
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
771 int mount_systemd_cgroup_writable(
773 bool unified_requested
) {
775 _cleanup_free_
char *own_cgroup_path
= NULL
;
776 const char *systemd_root
, *systemd_own
;
781 r
= cg_pid_get_path(NULL
, 0, &own_cgroup_path
);
783 return log_error_errno(r
, "Failed to determine our own cgroup path: %m");
785 /* If we are living in the top-level, then there's nothing to do... */
786 if (path_equal(own_cgroup_path
, "/"))
789 if (unified_requested
) {
790 systemd_own
= strjoina(dest
, "/sys/fs/cgroup", own_cgroup_path
);
791 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup");
793 systemd_own
= strjoina(dest
, "/sys/fs/cgroup/systemd", own_cgroup_path
);
794 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup/systemd");
797 /* Make our own cgroup a (writable) bind mount */
798 if (mount(systemd_own
, systemd_own
, NULL
, MS_BIND
, NULL
) < 0)
799 return log_error_errno(errno
, "Failed to turn %s into a bind mount: %m", own_cgroup_path
);
801 /* And then remount the systemd cgroup root read-only */
802 if (mount(NULL
, systemd_root
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
803 return log_error_errno(errno
, "Failed to mount cgroup root read-only: %m");
808 int setup_volatile_state(
809 const char *directory
,
811 bool userns
, uid_t uid_shift
, uid_t uid_range
,
812 const char *selinux_apifs_context
) {
814 _cleanup_free_
char *buf
= NULL
;
815 const char *p
, *options
;
820 if (mode
!= VOLATILE_STATE
)
823 /* --volatile=state means we simply overmount /var
824 with a tmpfs, and the rest read-only. */
826 r
= bind_remount_recursive(directory
, true);
828 return log_error_errno(r
, "Failed to remount %s read-only: %m", directory
);
830 p
= prefix_roota(directory
, "/var");
832 if (r
< 0 && errno
!= EEXIST
)
833 return log_error_errno(errno
, "Failed to create %s: %m", directory
);
835 options
= "mode=755";
836 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
842 if (mount("tmpfs", p
, "tmpfs", MS_STRICTATIME
, options
) < 0)
843 return log_error_errno(errno
, "Failed to mount tmpfs to /var: %m");
849 const char *directory
,
851 bool userns
, uid_t uid_shift
, uid_t uid_range
,
852 const char *selinux_apifs_context
) {
854 bool tmpfs_mounted
= false, bind_mounted
= false;
855 char template[] = "/tmp/nspawn-volatile-XXXXXX";
856 _cleanup_free_
char *buf
= NULL
;
857 const char *f
, *t
, *options
;
862 if (mode
!= VOLATILE_YES
)
865 /* --volatile=yes means we mount a tmpfs to the root dir, and
866 the original /usr to use inside it, and that read-only. */
868 if (!mkdtemp(template))
869 return log_error_errno(errno
, "Failed to create temporary directory: %m");
871 options
= "mode=755";
872 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
878 if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME
, options
) < 0) {
879 r
= log_error_errno(errno
, "Failed to mount tmpfs for root directory: %m");
883 tmpfs_mounted
= true;
885 f
= prefix_roota(directory
, "/usr");
886 t
= prefix_roota(template, "/usr");
889 if (r
< 0 && errno
!= EEXIST
) {
890 r
= log_error_errno(errno
, "Failed to create %s: %m", t
);
894 if (mount(f
, t
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
895 r
= log_error_errno(errno
, "Failed to create /usr bind mount: %m");
901 r
= bind_remount_recursive(t
, true);
903 log_error_errno(r
, "Failed to remount %s read-only: %m", t
);
907 if (mount(template, directory
, NULL
, MS_MOVE
, NULL
) < 0) {
908 r
= log_error_errno(errno
, "Failed to move root mount: %m");
912 (void) rmdir(template);
921 (void) umount(template);
922 (void) rmdir(template);
926 VolatileMode
volatile_mode_from_string(const char *s
) {
930 return _VOLATILE_MODE_INVALID
;
932 b
= parse_boolean(s
);
938 if (streq(s
, "state"))
939 return VOLATILE_STATE
;
941 return _VOLATILE_MODE_INVALID
;