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>
25 #include "cgroup-util.h"
29 #include "nspawn-mount.h"
30 #include "parse-util.h"
31 #include "path-util.h"
34 #include "string-util.h"
38 CustomMount
* custom_mount_add(CustomMount
**l
, unsigned *n
, CustomMountType t
) {
44 assert(t
< _CUSTOM_MOUNT_TYPE_MAX
);
46 c
= realloc(*l
, (*n
+ 1) * sizeof(CustomMount
));
54 *ret
= (CustomMount
) { .type
= t
};
59 void custom_mount_free_all(CustomMount
*l
, unsigned n
) {
62 for (i
= 0; i
< n
; i
++) {
63 CustomMount
*m
= l
+ i
;
70 (void) rm_rf(m
->work_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
80 int custom_mount_compare(const void *a
, const void *b
) {
81 const CustomMount
*x
= a
, *y
= b
;
84 r
= path_compare(x
->destination
, y
->destination
);
88 if (x
->type
< y
->type
)
90 if (x
->type
> y
->type
)
96 int bind_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
, bool read_only
) {
97 _cleanup_free_
char *source
= NULL
, *destination
= NULL
, *opts
= NULL
;
105 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &source
, &destination
, NULL
);
112 destination
= strdup(source
);
117 if (r
== 2 && !isempty(p
)) {
123 if (!path_is_absolute(source
))
126 if (!path_is_absolute(destination
))
129 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_BIND
);
134 m
->destination
= destination
;
135 m
->read_only
= read_only
;
138 source
= destination
= opts
= NULL
;
142 int tmpfs_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
) {
143 _cleanup_free_
char *path
= NULL
, *opts
= NULL
;
152 r
= extract_first_word(&p
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
159 opts
= strdup("mode=0755");
165 if (!path_is_absolute(path
))
168 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_TMPFS
);
172 m
->destination
= path
;
179 static int tmpfs_patch_options(
181 bool userns
, uid_t uid_shift
, uid_t uid_range
,
182 const char *selinux_apifs_context
,
187 if (userns
&& uid_shift
!= 0) {
188 assert(uid_shift
!= UID_INVALID
);
191 (void) asprintf(&buf
, "%s,uid=" UID_FMT
",gid=" UID_FMT
, options
, uid_shift
, uid_shift
);
193 (void) asprintf(&buf
, "uid=" UID_FMT
",gid=" UID_FMT
, uid_shift
, uid_shift
);
201 if (selinux_apifs_context
) {
205 t
= strjoin(options
, ",context=\"", selinux_apifs_context
, "\"", NULL
);
207 t
= strjoin("context=\"", selinux_apifs_context
, "\"", NULL
);
222 int mount_sysfs(const char *dest
) {
223 const char *full
, *top
, *x
;
226 top
= prefix_roota(dest
, "/sys");
227 r
= path_check_fstype(top
, SYSFS_MAGIC
);
229 return log_error_errno(r
, "Failed to determine filesystem type of %s: %m", top
);
230 /* /sys might already be mounted as sysfs by the outer child in the
231 * !netns case. In this case, it's all good. Don't touch it because we
232 * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
237 full
= prefix_roota(top
, "/full");
239 (void) mkdir(full
, 0755);
241 if (mount("sysfs", full
, "sysfs", MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
242 return log_error_errno(errno
, "Failed to mount sysfs to %s: %m", full
);
244 FOREACH_STRING(x
, "block", "bus", "class", "dev", "devices", "kernel") {
245 _cleanup_free_
char *from
= NULL
, *to
= NULL
;
247 from
= prefix_root(full
, x
);
251 to
= prefix_root(top
, x
);
255 (void) mkdir(to
, 0755);
257 if (mount(from
, to
, NULL
, MS_BIND
, NULL
) < 0)
258 return log_error_errno(errno
, "Failed to mount /sys/%s into place: %m", x
);
260 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
261 return log_error_errno(errno
, "Failed to mount /sys/%s read-only: %m", x
);
264 if (umount(full
) < 0)
265 return log_error_errno(errno
, "Failed to unmount %s: %m", full
);
268 return log_error_errno(errno
, "Failed to remove %s: %m", full
);
270 x
= prefix_roota(top
, "/fs/kdbus");
271 (void) mkdir(x
, 0755);
273 if (mount(NULL
, top
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
274 return log_error_errno(errno
, "Failed to make %s read-only: %m", top
);
279 int mount_all(const char *dest
,
280 bool use_userns
, bool in_userns
,
282 uid_t uid_shift
, uid_t uid_range
,
283 const char *selinux_apifs_context
) {
285 typedef struct MountPoint
{
296 static const MountPoint mount_table
[] = {
297 { "proc", "/proc", "proc", NULL
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, true, false },
298 { "/proc/sys", "/proc/sys", NULL
, NULL
, MS_BIND
, true, true, false }, /* Bind mount first */
299 { 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 */
300 { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, true },
301 { "sysfs", "/sys", "sysfs", NULL
, MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, false },
302 { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID
|MS_STRICTATIME
, true, false, false },
303 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
304 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
305 { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME
, true, false, false },
307 { "/sys/fs/selinux", "/sys/fs/selinux", NULL
, NULL
, MS_BIND
, false, false, false }, /* Bind mount first */
308 { 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 */
315 for (k
= 0; k
< ELEMENTSOF(mount_table
); k
++) {
316 _cleanup_free_
char *where
= NULL
, *options
= NULL
;
319 if (in_userns
!= mount_table
[k
].in_userns
)
322 if (!use_netns
&& mount_table
[k
].use_netns
)
325 where
= prefix_root(dest
, mount_table
[k
].where
);
329 r
= path_is_mount_point(where
, AT_SYMLINK_FOLLOW
);
330 if (r
< 0 && r
!= -ENOENT
)
331 return log_error_errno(r
, "Failed to detect whether %s is a mount point: %m", where
);
333 /* Skip this entry if it is not a remount. */
334 if (mount_table
[k
].what
&& r
> 0)
337 r
= mkdir_p(where
, 0755);
339 if (mount_table
[k
].fatal
)
340 return log_error_errno(r
, "Failed to create directory %s: %m", where
);
342 log_warning_errno(r
, "Failed to create directory %s: %m", where
);
346 o
= mount_table
[k
].options
;
347 if (streq_ptr(mount_table
[k
].type
, "tmpfs")) {
348 r
= tmpfs_patch_options(o
, use_userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
355 if (mount(mount_table
[k
].what
,
358 mount_table
[k
].flags
,
361 if (mount_table
[k
].fatal
)
362 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
364 log_warning_errno(errno
, "mount(%s) failed, ignoring: %m", where
);
371 static int parse_mount_bind_options(const char *options
, unsigned long *mount_flags
, char **mount_opts
) {
372 const char *p
= options
;
373 unsigned long flags
= *mount_flags
;
379 _cleanup_free_
char *word
= NULL
;
380 int r
= extract_first_word(&p
, &word
, ",", 0);
382 return log_error_errno(r
, "Failed to extract mount option: %m");
386 if (streq(word
, "rbind"))
388 else if (streq(word
, "norbind"))
391 log_error("Invalid bind mount option: %s", word
);
396 *mount_flags
= flags
;
397 /* in the future mount_opts will hold string options for mount(2) */
403 static int mount_bind(const char *dest
, CustomMount
*m
) {
404 struct stat source_st
, dest_st
;
406 unsigned long mount_flags
= MS_BIND
| MS_REC
;
407 _cleanup_free_
char *mount_opts
= NULL
;
413 r
= parse_mount_bind_options(m
->options
, &mount_flags
, &mount_opts
);
418 if (stat(m
->source
, &source_st
) < 0)
419 return log_error_errno(errno
, "Failed to stat %s: %m", m
->source
);
421 where
= prefix_roota(dest
, m
->destination
);
423 if (stat(where
, &dest_st
) >= 0) {
424 if (S_ISDIR(source_st
.st_mode
) && !S_ISDIR(dest_st
.st_mode
)) {
425 log_error("Cannot bind mount directory %s on file %s.", m
->source
, where
);
429 if (!S_ISDIR(source_st
.st_mode
) && S_ISDIR(dest_st
.st_mode
)) {
430 log_error("Cannot bind mount file %s on directory %s.", m
->source
, where
);
434 } else if (errno
== ENOENT
) {
435 r
= mkdir_parents_label(where
, 0755);
437 return log_error_errno(r
, "Failed to make parents of %s: %m", where
);
439 log_error_errno(errno
, "Failed to stat %s: %m", where
);
443 /* Create the mount point. Any non-directory file can be
444 * mounted on any non-directory file (regular, fifo, socket,
447 if (S_ISDIR(source_st
.st_mode
))
448 r
= mkdir_label(where
, 0755);
451 if (r
< 0 && r
!= -EEXIST
)
452 return log_error_errno(r
, "Failed to create mount point %s: %m", where
);
454 if (mount(m
->source
, where
, NULL
, mount_flags
, mount_opts
) < 0)
455 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
458 r
= bind_remount_recursive(where
, true);
460 return log_error_errno(r
, "Read-only bind mount failed: %m");
466 static int mount_tmpfs(
469 bool userns
, uid_t uid_shift
, uid_t uid_range
,
470 const char *selinux_apifs_context
) {
472 const char *where
, *options
;
473 _cleanup_free_
char *buf
= NULL
;
479 where
= prefix_roota(dest
, m
->destination
);
481 r
= mkdir_p_label(where
, 0755);
482 if (r
< 0 && r
!= -EEXIST
)
483 return log_error_errno(r
, "Creating mount point for tmpfs %s failed: %m", where
);
485 r
= tmpfs_patch_options(m
->options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
488 options
= r
> 0 ? buf
: m
->options
;
490 if (mount("tmpfs", where
, "tmpfs", MS_NODEV
|MS_STRICTATIME
, options
) < 0)
491 return log_error_errno(errno
, "tmpfs mount to %s failed: %m", where
);
496 static char *joined_and_escaped_lower_dirs(char * const *lower
) {
497 _cleanup_strv_free_
char **sv
= NULL
;
499 sv
= strv_copy(lower
);
505 if (!strv_shell_escape(sv
, ",:"))
508 return strv_join(sv
, ":");
511 static int mount_overlay(const char *dest
, CustomMount
*m
) {
512 _cleanup_free_
char *lower
= NULL
;
513 const char *where
, *options
;
519 where
= prefix_roota(dest
, m
->destination
);
521 r
= mkdir_label(where
, 0755);
522 if (r
< 0 && r
!= -EEXIST
)
523 return log_error_errno(r
, "Creating mount point for overlay %s failed: %m", where
);
525 (void) mkdir_p_label(m
->source
, 0755);
527 lower
= joined_and_escaped_lower_dirs(m
->lower
);
532 _cleanup_free_
char *escaped_source
= NULL
;
534 escaped_source
= shell_escape(m
->source
, ",:");
538 options
= strjoina("lowerdir=", escaped_source
, ":", lower
);
540 _cleanup_free_
char *escaped_source
= NULL
, *escaped_work_dir
= NULL
;
543 (void) mkdir_label(m
->work_dir
, 0700);
545 escaped_source
= shell_escape(m
->source
, ",:");
548 escaped_work_dir
= shell_escape(m
->work_dir
, ",:");
549 if (!escaped_work_dir
)
552 options
= strjoina("lowerdir=", lower
, ",upperdir=", escaped_source
, ",workdir=", escaped_work_dir
);
555 if (mount("overlay", where
, "overlay", m
->read_only
? MS_RDONLY
: 0, options
) < 0)
556 return log_error_errno(errno
, "overlay mount to %s failed: %m", where
);
563 CustomMount
*mounts
, unsigned n
,
564 bool userns
, uid_t uid_shift
, uid_t uid_range
,
565 const char *selinux_apifs_context
) {
572 for (i
= 0; i
< n
; i
++) {
573 CustomMount
*m
= mounts
+ i
;
577 case CUSTOM_MOUNT_BIND
:
578 r
= mount_bind(dest
, m
);
581 case CUSTOM_MOUNT_TMPFS
:
582 r
= mount_tmpfs(dest
, m
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
585 case CUSTOM_MOUNT_OVERLAY
:
586 r
= mount_overlay(dest
, m
);
590 assert_not_reached("Unknown custom mount type");
600 static int mount_legacy_cgroup_hierarchy(const char *dest
, const char *controller
, const char *hierarchy
, bool read_only
) {
604 to
= strjoina(strempty(dest
), "/sys/fs/cgroup/", hierarchy
);
606 r
= path_is_mount_point(to
, 0);
607 if (r
< 0 && r
!= -ENOENT
)
608 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", to
);
614 /* The superblock mount options of the mount point need to be
615 * identical to the hosts', and hence writable... */
616 if (mount("cgroup", to
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, controller
) < 0)
617 return log_error_errno(errno
, "Failed to mount to %s: %m", to
);
619 /* ... hence let's only make the bind mount read-only, not the
622 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
623 return log_error_errno(errno
, "Failed to remount %s read-only: %m", to
);
628 static int mount_legacy_cgroups(
630 bool userns
, uid_t uid_shift
, uid_t uid_range
,
631 const char *selinux_apifs_context
) {
633 _cleanup_set_free_free_ Set
*controllers
= NULL
;
634 const char *cgroup_root
;
637 cgroup_root
= prefix_roota(dest
, "/sys/fs/cgroup");
639 (void) mkdir_p(cgroup_root
, 0755);
641 /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
642 r
= path_is_mount_point(cgroup_root
, AT_SYMLINK_FOLLOW
);
644 return log_error_errno(r
, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
646 _cleanup_free_
char *options
= NULL
;
648 r
= tmpfs_patch_options("mode=755", userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
652 if (mount("tmpfs", cgroup_root
, "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
, options
) < 0)
653 return log_error_errno(errno
, "Failed to mount /sys/fs/cgroup: %m");
656 if (cg_unified() > 0)
657 goto skip_controllers
;
659 controllers
= set_new(&string_hash_ops
);
663 r
= cg_kernel_controllers(controllers
);
665 return log_error_errno(r
, "Failed to determine cgroup controllers: %m");
668 _cleanup_free_
char *controller
= NULL
, *origin
= NULL
, *combined
= NULL
;
670 controller
= set_steal_first(controllers
);
674 origin
= prefix_root("/sys/fs/cgroup/", controller
);
678 r
= readlink_malloc(origin
, &combined
);
680 /* Not a symbolic link, but directly a single cgroup hierarchy */
682 r
= mount_legacy_cgroup_hierarchy(dest
, controller
, controller
, true);
687 return log_error_errno(r
, "Failed to read link %s: %m", origin
);
689 _cleanup_free_
char *target
= NULL
;
691 target
= prefix_root(dest
, origin
);
695 /* A symbolic link, a combination of controllers in one hierarchy */
697 if (!filename_is_valid(combined
)) {
698 log_warning("Ignoring invalid combined hierarchy %s.", combined
);
702 r
= mount_legacy_cgroup_hierarchy(dest
, combined
, combined
, true);
706 r
= symlink_idempotent(combined
, target
);
708 log_error("Invalid existing symlink for combined hierarchy");
712 return log_error_errno(r
, "Failed to create symlink for combined hierarchy: %m");
717 r
= mount_legacy_cgroup_hierarchy(dest
, "none,name=systemd,xattr", "systemd", false);
721 if (mount(NULL
, cgroup_root
, NULL
, MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
|MS_RDONLY
, "mode=755") < 0)
722 return log_error_errno(errno
, "Failed to remount %s read-only: %m", cgroup_root
);
727 static int mount_unified_cgroups(const char *dest
) {
733 p
= prefix_roota(dest
, "/sys/fs/cgroup");
735 (void) mkdir_p(p
, 0755);
737 r
= path_is_mount_point(p
, AT_SYMLINK_FOLLOW
);
739 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", p
);
741 p
= prefix_roota(dest
, "/sys/fs/cgroup/cgroup.procs");
742 if (access(p
, F_OK
) >= 0)
745 return log_error_errno(errno
, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p
);
747 log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p
);
751 if (mount("cgroup", p
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, "__DEVEL__sane_behavior") < 0)
752 return log_error_errno(errno
, "Failed to mount unified cgroup hierarchy to %s: %m", p
);
759 bool unified_requested
,
760 bool userns
, uid_t uid_shift
, uid_t uid_range
,
761 const char *selinux_apifs_context
) {
763 if (unified_requested
)
764 return mount_unified_cgroups(dest
);
766 return mount_legacy_cgroups(dest
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
769 int mount_systemd_cgroup_writable(
771 bool unified_requested
) {
773 _cleanup_free_
char *own_cgroup_path
= NULL
;
774 const char *systemd_root
, *systemd_own
;
779 r
= cg_pid_get_path(NULL
, 0, &own_cgroup_path
);
781 return log_error_errno(r
, "Failed to determine our own cgroup path: %m");
783 /* If we are living in the top-level, then there's nothing to do... */
784 if (path_equal(own_cgroup_path
, "/"))
787 if (unified_requested
) {
788 systemd_own
= strjoina(dest
, "/sys/fs/cgroup", own_cgroup_path
);
789 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup");
791 systemd_own
= strjoina(dest
, "/sys/fs/cgroup/systemd", own_cgroup_path
);
792 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup/systemd");
795 /* Make our own cgroup a (writable) bind mount */
796 if (mount(systemd_own
, systemd_own
, NULL
, MS_BIND
, NULL
) < 0)
797 return log_error_errno(errno
, "Failed to turn %s into a bind mount: %m", own_cgroup_path
);
799 /* And then remount the systemd cgroup root read-only */
800 if (mount(NULL
, systemd_root
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
801 return log_error_errno(errno
, "Failed to mount cgroup root read-only: %m");
806 int setup_volatile_state(
807 const char *directory
,
809 bool userns
, uid_t uid_shift
, uid_t uid_range
,
810 const char *selinux_apifs_context
) {
812 _cleanup_free_
char *buf
= NULL
;
813 const char *p
, *options
;
818 if (mode
!= VOLATILE_STATE
)
821 /* --volatile=state means we simply overmount /var
822 with a tmpfs, and the rest read-only. */
824 r
= bind_remount_recursive(directory
, true);
826 return log_error_errno(r
, "Failed to remount %s read-only: %m", directory
);
828 p
= prefix_roota(directory
, "/var");
830 if (r
< 0 && errno
!= EEXIST
)
831 return log_error_errno(errno
, "Failed to create %s: %m", directory
);
833 options
= "mode=755";
834 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
840 if (mount("tmpfs", p
, "tmpfs", MS_STRICTATIME
, options
) < 0)
841 return log_error_errno(errno
, "Failed to mount tmpfs to /var: %m");
847 const char *directory
,
849 bool userns
, uid_t uid_shift
, uid_t uid_range
,
850 const char *selinux_apifs_context
) {
852 bool tmpfs_mounted
= false, bind_mounted
= false;
853 char template[] = "/tmp/nspawn-volatile-XXXXXX";
854 _cleanup_free_
char *buf
= NULL
;
855 const char *f
, *t
, *options
;
860 if (mode
!= VOLATILE_YES
)
863 /* --volatile=yes means we mount a tmpfs to the root dir, and
864 the original /usr to use inside it, and that read-only. */
866 if (!mkdtemp(template))
867 return log_error_errno(errno
, "Failed to create temporary directory: %m");
869 options
= "mode=755";
870 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
876 if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME
, options
) < 0) {
877 r
= log_error_errno(errno
, "Failed to mount tmpfs for root directory: %m");
881 tmpfs_mounted
= true;
883 f
= prefix_roota(directory
, "/usr");
884 t
= prefix_roota(template, "/usr");
887 if (r
< 0 && errno
!= EEXIST
) {
888 r
= log_error_errno(errno
, "Failed to create %s: %m", t
);
892 if (mount(f
, t
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
893 r
= log_error_errno(errno
, "Failed to create /usr bind mount: %m");
899 r
= bind_remount_recursive(t
, true);
901 log_error_errno(r
, "Failed to remount %s read-only: %m", t
);
905 if (mount(template, directory
, NULL
, MS_MOVE
, NULL
) < 0) {
906 r
= log_error_errno(errno
, "Failed to move root mount: %m");
910 (void) rmdir(template);
919 (void) umount(template);
920 (void) rmdir(template);
924 VolatileMode
volatile_mode_from_string(const char *s
) {
928 return _VOLATILE_MODE_INVALID
;
930 b
= parse_boolean(s
);
936 if (streq(s
, "state"))
937 return VOLATILE_STATE
;
939 return _VOLATILE_MODE_INVALID
;