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 <linux/magic.h>
23 #include <sys/mount.h>
25 #include "cgroup-util.h"
29 #include "path-util.h"
35 #include "nspawn-mount.h"
37 CustomMount
* custom_mount_add(CustomMount
**l
, unsigned *n
, CustomMountType t
) {
43 assert(t
< _CUSTOM_MOUNT_TYPE_MAX
);
45 c
= realloc(*l
, (*n
+ 1) * sizeof(CustomMount
));
53 *ret
= (CustomMount
) { .type
= t
};
58 void custom_mount_free_all(CustomMount
*l
, unsigned n
) {
61 for (i
= 0; i
< n
; i
++) {
62 CustomMount
*m
= l
+ i
;
69 (void) rm_rf(m
->work_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
79 int custom_mount_compare(const void *a
, const void *b
) {
80 const CustomMount
*x
= a
, *y
= b
;
83 r
= path_compare(x
->destination
, y
->destination
);
87 if (x
->type
< y
->type
)
89 if (x
->type
> y
->type
)
95 int bind_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
, bool read_only
) {
96 _cleanup_free_
char *source
= NULL
, *destination
= NULL
, *opts
= NULL
;
104 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &source
, &destination
, NULL
);
111 destination
= strdup(source
);
116 if (r
== 2 && !isempty(p
)) {
122 if (!path_is_absolute(source
))
125 if (!path_is_absolute(destination
))
128 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_BIND
);
133 m
->destination
= destination
;
134 m
->read_only
= read_only
;
137 source
= destination
= opts
= NULL
;
141 int tmpfs_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
) {
142 _cleanup_free_
char *path
= NULL
, *opts
= NULL
;
151 r
= extract_first_word(&p
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
158 opts
= strdup("mode=0755");
164 if (!path_is_absolute(path
))
167 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_TMPFS
);
171 m
->destination
= path
;
178 static int tmpfs_patch_options(
180 bool userns
, uid_t uid_shift
, uid_t uid_range
,
181 const char *selinux_apifs_context
,
186 if (userns
&& uid_shift
!= 0) {
187 assert(uid_shift
!= UID_INVALID
);
190 (void) asprintf(&buf
, "%s,uid=" UID_FMT
",gid=" UID_FMT
, options
, uid_shift
, uid_shift
);
192 (void) asprintf(&buf
, "uid=" UID_FMT
",gid=" UID_FMT
, uid_shift
, uid_shift
);
200 if (selinux_apifs_context
) {
204 t
= strjoin(options
, ",context=\"", selinux_apifs_context
, "\"", NULL
);
206 t
= strjoin("context=\"", selinux_apifs_context
, "\"", NULL
);
221 int mount_sysfs(const char *dest
) {
222 const char *full
, *top
, *x
;
225 top
= prefix_roota(dest
, "/sys");
226 r
= path_check_fstype(top
, SYSFS_MAGIC
);
228 return log_error_errno(r
, "Failed to determine filesystem type of %s: %m", top
);
229 /* /sys might already be mounted as sysfs by the outer child in the
230 * !netns case. In this case, it's all good. Don't touch it because we
231 * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
236 full
= prefix_roota(top
, "/full");
238 (void) mkdir(full
, 0755);
240 if (mount("sysfs", full
, "sysfs", MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
241 return log_error_errno(errno
, "Failed to mount sysfs to %s: %m", full
);
243 FOREACH_STRING(x
, "block", "bus", "class", "dev", "devices", "kernel") {
244 _cleanup_free_
char *from
= NULL
, *to
= NULL
;
246 from
= prefix_root(full
, x
);
250 to
= prefix_root(top
, x
);
254 (void) mkdir(to
, 0755);
256 if (mount(from
, to
, NULL
, MS_BIND
, NULL
) < 0)
257 return log_error_errno(errno
, "Failed to mount /sys/%s into place: %m", x
);
259 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
260 return log_error_errno(errno
, "Failed to mount /sys/%s read-only: %m", x
);
263 if (umount(full
) < 0)
264 return log_error_errno(errno
, "Failed to unmount %s: %m", full
);
267 return log_error_errno(errno
, "Failed to remove %s: %m", full
);
269 x
= prefix_roota(top
, "/fs/kdbus");
270 (void) mkdir(x
, 0755);
272 if (mount(NULL
, top
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
273 return log_error_errno(errno
, "Failed to make %s read-only: %m", top
);
278 int mount_all(const char *dest
,
279 bool use_userns
, bool in_userns
,
281 uid_t uid_shift
, uid_t uid_range
,
282 const char *selinux_apifs_context
) {
284 typedef struct MountPoint
{
295 static const MountPoint mount_table
[] = {
296 { "proc", "/proc", "proc", NULL
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, true, false },
297 { "/proc/sys", "/proc/sys", NULL
, NULL
, MS_BIND
, true, true, false }, /* Bind mount first */
298 { 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 */
299 { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, true },
300 { "sysfs", "/sys", "sysfs", NULL
, MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, false },
301 { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID
|MS_STRICTATIME
, true, false, false },
302 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
303 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
304 { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME
, true, false, false },
306 { "/sys/fs/selinux", "/sys/fs/selinux", NULL
, NULL
, MS_BIND
, false, false, false }, /* Bind mount first */
307 { 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 */
314 for (k
= 0; k
< ELEMENTSOF(mount_table
); k
++) {
315 _cleanup_free_
char *where
= NULL
, *options
= NULL
;
318 if (in_userns
!= mount_table
[k
].in_userns
)
321 if (!use_netns
&& mount_table
[k
].use_netns
)
324 where
= prefix_root(dest
, mount_table
[k
].where
);
328 r
= path_is_mount_point(where
, AT_SYMLINK_FOLLOW
);
329 if (r
< 0 && r
!= -ENOENT
)
330 return log_error_errno(r
, "Failed to detect whether %s is a mount point: %m", where
);
332 /* Skip this entry if it is not a remount. */
333 if (mount_table
[k
].what
&& r
> 0)
336 r
= mkdir_p(where
, 0755);
338 if (mount_table
[k
].fatal
)
339 return log_error_errno(r
, "Failed to create directory %s: %m", where
);
341 log_warning_errno(r
, "Failed to create directory %s: %m", where
);
345 o
= mount_table
[k
].options
;
346 if (streq_ptr(mount_table
[k
].type
, "tmpfs")) {
347 r
= tmpfs_patch_options(o
, use_userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
354 if (mount(mount_table
[k
].what
,
357 mount_table
[k
].flags
,
360 if (mount_table
[k
].fatal
)
361 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
363 log_warning_errno(errno
, "mount(%s) failed, ignoring: %m", where
);
370 static int parse_mount_bind_options(const char *options
, unsigned long *mount_flags
, char **mount_opts
) {
371 const char *p
= options
;
372 unsigned long flags
= *mount_flags
;
378 _cleanup_free_
char *word
= NULL
;
379 int r
= extract_first_word(&p
, &word
, ",", 0);
381 return log_error_errno(r
, "Failed to extract mount option: %m");
385 if (streq(word
, "rbind"))
387 else if (streq(word
, "norbind"))
390 log_error("Invalid bind mount option: %s", word
);
395 *mount_flags
= flags
;
396 /* in the future mount_opts will hold string options for mount(2) */
402 static int mount_bind(const char *dest
, CustomMount
*m
) {
403 struct stat source_st
, dest_st
;
405 unsigned long mount_flags
= MS_BIND
| MS_REC
;
406 _cleanup_free_
char *mount_opts
= NULL
;
412 r
= parse_mount_bind_options(m
->options
, &mount_flags
, &mount_opts
);
417 if (stat(m
->source
, &source_st
) < 0)
418 return log_error_errno(errno
, "Failed to stat %s: %m", m
->source
);
420 where
= prefix_roota(dest
, m
->destination
);
422 if (stat(where
, &dest_st
) >= 0) {
423 if (S_ISDIR(source_st
.st_mode
) && !S_ISDIR(dest_st
.st_mode
)) {
424 log_error("Cannot bind mount directory %s on file %s.", m
->source
, where
);
428 if (!S_ISDIR(source_st
.st_mode
) && S_ISDIR(dest_st
.st_mode
)) {
429 log_error("Cannot bind mount file %s on directory %s.", m
->source
, where
);
433 } else if (errno
== ENOENT
) {
434 r
= mkdir_parents_label(where
, 0755);
436 return log_error_errno(r
, "Failed to make parents of %s: %m", where
);
438 log_error_errno(errno
, "Failed to stat %s: %m", where
);
442 /* Create the mount point. Any non-directory file can be
443 * mounted on any non-directory file (regular, fifo, socket,
446 if (S_ISDIR(source_st
.st_mode
))
447 r
= mkdir_label(where
, 0755);
450 if (r
< 0 && r
!= -EEXIST
)
451 return log_error_errno(r
, "Failed to create mount point %s: %m", where
);
453 if (mount(m
->source
, where
, NULL
, mount_flags
, mount_opts
) < 0)
454 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
457 r
= bind_remount_recursive(where
, true);
459 return log_error_errno(r
, "Read-only bind mount failed: %m");
465 static int mount_tmpfs(
468 bool userns
, uid_t uid_shift
, uid_t uid_range
,
469 const char *selinux_apifs_context
) {
471 const char *where
, *options
;
472 _cleanup_free_
char *buf
= NULL
;
478 where
= prefix_roota(dest
, m
->destination
);
480 r
= mkdir_p_label(where
, 0755);
481 if (r
< 0 && r
!= -EEXIST
)
482 return log_error_errno(r
, "Creating mount point for tmpfs %s failed: %m", where
);
484 r
= tmpfs_patch_options(m
->options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
487 options
= r
> 0 ? buf
: m
->options
;
489 if (mount("tmpfs", where
, "tmpfs", MS_NODEV
|MS_STRICTATIME
, options
) < 0)
490 return log_error_errno(errno
, "tmpfs mount to %s failed: %m", where
);
495 static char *joined_and_escaped_lower_dirs(char * const *lower
) {
496 _cleanup_strv_free_
char **sv
= NULL
;
498 sv
= strv_copy(lower
);
504 if (!strv_shell_escape(sv
, ",:"))
507 return strv_join(sv
, ":");
510 static int mount_overlay(const char *dest
, CustomMount
*m
) {
511 _cleanup_free_
char *lower
= NULL
;
512 const char *where
, *options
;
518 where
= prefix_roota(dest
, m
->destination
);
520 r
= mkdir_label(where
, 0755);
521 if (r
< 0 && r
!= -EEXIST
)
522 return log_error_errno(r
, "Creating mount point for overlay %s failed: %m", where
);
524 (void) mkdir_p_label(m
->source
, 0755);
526 lower
= joined_and_escaped_lower_dirs(m
->lower
);
531 _cleanup_free_
char *escaped_source
= NULL
;
533 escaped_source
= shell_escape(m
->source
, ",:");
537 options
= strjoina("lowerdir=", escaped_source
, ":", lower
);
539 _cleanup_free_
char *escaped_source
= NULL
, *escaped_work_dir
= NULL
;
542 (void) mkdir_label(m
->work_dir
, 0700);
544 escaped_source
= shell_escape(m
->source
, ",:");
547 escaped_work_dir
= shell_escape(m
->work_dir
, ",:");
548 if (!escaped_work_dir
)
551 options
= strjoina("lowerdir=", lower
, ",upperdir=", escaped_source
, ",workdir=", escaped_work_dir
);
554 if (mount("overlay", where
, "overlay", m
->read_only
? MS_RDONLY
: 0, options
) < 0)
555 return log_error_errno(errno
, "overlay mount to %s failed: %m", where
);
562 CustomMount
*mounts
, unsigned n
,
563 bool userns
, uid_t uid_shift
, uid_t uid_range
,
564 const char *selinux_apifs_context
) {
571 for (i
= 0; i
< n
; i
++) {
572 CustomMount
*m
= mounts
+ i
;
576 case CUSTOM_MOUNT_BIND
:
577 r
= mount_bind(dest
, m
);
580 case CUSTOM_MOUNT_TMPFS
:
581 r
= mount_tmpfs(dest
, m
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
584 case CUSTOM_MOUNT_OVERLAY
:
585 r
= mount_overlay(dest
, m
);
589 assert_not_reached("Unknown custom mount type");
599 static int mount_legacy_cgroup_hierarchy(const char *dest
, const char *controller
, const char *hierarchy
, bool read_only
) {
603 to
= strjoina(strempty(dest
), "/sys/fs/cgroup/", hierarchy
);
605 r
= path_is_mount_point(to
, 0);
606 if (r
< 0 && r
!= -ENOENT
)
607 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", to
);
613 /* The superblock mount options of the mount point need to be
614 * identical to the hosts', and hence writable... */
615 if (mount("cgroup", to
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, controller
) < 0)
616 return log_error_errno(errno
, "Failed to mount to %s: %m", to
);
618 /* ... hence let's only make the bind mount read-only, not the
621 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
622 return log_error_errno(errno
, "Failed to remount %s read-only: %m", to
);
627 static int mount_legacy_cgroups(
629 bool userns
, uid_t uid_shift
, uid_t uid_range
,
630 const char *selinux_apifs_context
) {
632 _cleanup_set_free_free_ Set
*controllers
= NULL
;
633 const char *cgroup_root
;
636 cgroup_root
= prefix_roota(dest
, "/sys/fs/cgroup");
638 (void) mkdir_p(cgroup_root
, 0755);
640 /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
641 r
= path_is_mount_point(cgroup_root
, AT_SYMLINK_FOLLOW
);
643 return log_error_errno(r
, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
645 _cleanup_free_
char *options
= NULL
;
647 r
= tmpfs_patch_options("mode=755", userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
651 if (mount("tmpfs", cgroup_root
, "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
, options
) < 0)
652 return log_error_errno(errno
, "Failed to mount /sys/fs/cgroup: %m");
655 if (cg_unified() > 0)
656 goto skip_controllers
;
658 controllers
= set_new(&string_hash_ops
);
662 r
= cg_kernel_controllers(controllers
);
664 return log_error_errno(r
, "Failed to determine cgroup controllers: %m");
667 _cleanup_free_
char *controller
= NULL
, *origin
= NULL
, *combined
= NULL
;
669 controller
= set_steal_first(controllers
);
673 origin
= prefix_root("/sys/fs/cgroup/", controller
);
677 r
= readlink_malloc(origin
, &combined
);
679 /* Not a symbolic link, but directly a single cgroup hierarchy */
681 r
= mount_legacy_cgroup_hierarchy(dest
, controller
, controller
, true);
686 return log_error_errno(r
, "Failed to read link %s: %m", origin
);
688 _cleanup_free_
char *target
= NULL
;
690 target
= prefix_root(dest
, origin
);
694 /* A symbolic link, a combination of controllers in one hierarchy */
696 if (!filename_is_valid(combined
)) {
697 log_warning("Ignoring invalid combined hierarchy %s.", combined
);
701 r
= mount_legacy_cgroup_hierarchy(dest
, combined
, combined
, true);
705 r
= symlink_idempotent(combined
, target
);
707 log_error("Invalid existing symlink for combined hierarchy");
711 return log_error_errno(r
, "Failed to create symlink for combined hierarchy: %m");
716 r
= mount_legacy_cgroup_hierarchy(dest
, "none,name=systemd,xattr", "systemd", false);
720 if (mount(NULL
, cgroup_root
, NULL
, MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
|MS_RDONLY
, "mode=755") < 0)
721 return log_error_errno(errno
, "Failed to remount %s read-only: %m", cgroup_root
);
726 static int mount_unified_cgroups(const char *dest
) {
732 p
= prefix_roota(dest
, "/sys/fs/cgroup");
734 (void) mkdir_p(p
, 0755);
736 r
= path_is_mount_point(p
, AT_SYMLINK_FOLLOW
);
738 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", p
);
740 p
= prefix_roota(dest
, "/sys/fs/cgroup/cgroup.procs");
741 if (access(p
, F_OK
) >= 0)
744 return log_error_errno(errno
, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p
);
746 log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p
);
750 if (mount("cgroup", p
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, "__DEVEL__sane_behavior") < 0)
751 return log_error_errno(errno
, "Failed to mount unified cgroup hierarchy to %s: %m", p
);
758 bool unified_requested
,
759 bool userns
, uid_t uid_shift
, uid_t uid_range
,
760 const char *selinux_apifs_context
) {
762 if (unified_requested
)
763 return mount_unified_cgroups(dest
);
765 return mount_legacy_cgroups(dest
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
768 int mount_systemd_cgroup_writable(
770 bool unified_requested
) {
772 _cleanup_free_
char *own_cgroup_path
= NULL
;
773 const char *systemd_root
, *systemd_own
;
778 r
= cg_pid_get_path(NULL
, 0, &own_cgroup_path
);
780 return log_error_errno(r
, "Failed to determine our own cgroup path: %m");
782 /* If we are living in the top-level, then there's nothing to do... */
783 if (path_equal(own_cgroup_path
, "/"))
786 if (unified_requested
) {
787 systemd_own
= strjoina(dest
, "/sys/fs/cgroup", own_cgroup_path
);
788 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup");
790 systemd_own
= strjoina(dest
, "/sys/fs/cgroup/systemd", own_cgroup_path
);
791 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup/systemd");
794 /* Make our own cgroup a (writable) bind mount */
795 if (mount(systemd_own
, systemd_own
, NULL
, MS_BIND
, NULL
) < 0)
796 return log_error_errno(errno
, "Failed to turn %s into a bind mount: %m", own_cgroup_path
);
798 /* And then remount the systemd cgroup root read-only */
799 if (mount(NULL
, systemd_root
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
800 return log_error_errno(errno
, "Failed to mount cgroup root read-only: %m");
805 int setup_volatile_state(
806 const char *directory
,
808 bool userns
, uid_t uid_shift
, uid_t uid_range
,
809 const char *selinux_apifs_context
) {
811 _cleanup_free_
char *buf
= NULL
;
812 const char *p
, *options
;
817 if (mode
!= VOLATILE_STATE
)
820 /* --volatile=state means we simply overmount /var
821 with a tmpfs, and the rest read-only. */
823 r
= bind_remount_recursive(directory
, true);
825 return log_error_errno(r
, "Failed to remount %s read-only: %m", directory
);
827 p
= prefix_roota(directory
, "/var");
829 if (r
< 0 && errno
!= EEXIST
)
830 return log_error_errno(errno
, "Failed to create %s: %m", directory
);
832 options
= "mode=755";
833 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
839 if (mount("tmpfs", p
, "tmpfs", MS_STRICTATIME
, options
) < 0)
840 return log_error_errno(errno
, "Failed to mount tmpfs to /var: %m");
846 const char *directory
,
848 bool userns
, uid_t uid_shift
, uid_t uid_range
,
849 const char *selinux_apifs_context
) {
851 bool tmpfs_mounted
= false, bind_mounted
= false;
852 char template[] = "/tmp/nspawn-volatile-XXXXXX";
853 _cleanup_free_
char *buf
= NULL
;
854 const char *f
, *t
, *options
;
859 if (mode
!= VOLATILE_YES
)
862 /* --volatile=yes means we mount a tmpfs to the root dir, and
863 the original /usr to use inside it, and that read-only. */
865 if (!mkdtemp(template))
866 return log_error_errno(errno
, "Failed to create temporary directory: %m");
868 options
= "mode=755";
869 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
875 if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME
, options
) < 0) {
876 r
= log_error_errno(errno
, "Failed to mount tmpfs for root directory: %m");
880 tmpfs_mounted
= true;
882 f
= prefix_roota(directory
, "/usr");
883 t
= prefix_roota(template, "/usr");
886 if (r
< 0 && errno
!= EEXIST
) {
887 r
= log_error_errno(errno
, "Failed to create %s: %m", t
);
891 if (mount(f
, t
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
892 r
= log_error_errno(errno
, "Failed to create /usr bind mount: %m");
898 r
= bind_remount_recursive(t
, true);
900 log_error_errno(r
, "Failed to remount %s read-only: %m", t
);
904 if (mount(template, directory
, NULL
, MS_MOVE
, NULL
) < 0) {
905 r
= log_error_errno(errno
, "Failed to move root mount: %m");
909 (void) rmdir(template);
918 (void) umount(template);
919 (void) rmdir(template);
923 VolatileMode
volatile_mode_from_string(const char *s
) {
927 return _VOLATILE_MODE_INVALID
;
929 b
= parse_boolean(s
);
935 if (streq(s
, "state"))
936 return VOLATILE_STATE
;
938 return _VOLATILE_MODE_INVALID
;