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 "mount-util.h"
30 #include "nspawn-mount.h"
31 #include "parse-util.h"
32 #include "path-util.h"
35 #include "string-util.h"
39 CustomMount
* custom_mount_add(CustomMount
**l
, unsigned *n
, CustomMountType t
) {
45 assert(t
< _CUSTOM_MOUNT_TYPE_MAX
);
47 c
= realloc(*l
, (*n
+ 1) * sizeof(CustomMount
));
55 *ret
= (CustomMount
) { .type
= t
};
60 void custom_mount_free_all(CustomMount
*l
, unsigned n
) {
63 for (i
= 0; i
< n
; i
++) {
64 CustomMount
*m
= l
+ i
;
71 (void) rm_rf(m
->work_dir
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
81 int custom_mount_compare(const void *a
, const void *b
) {
82 const CustomMount
*x
= a
, *y
= b
;
85 r
= path_compare(x
->destination
, y
->destination
);
89 if (x
->type
< y
->type
)
91 if (x
->type
> y
->type
)
97 int bind_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
, bool read_only
) {
98 _cleanup_free_
char *source
= NULL
, *destination
= NULL
, *opts
= NULL
;
106 r
= extract_many_words(&p
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
, &source
, &destination
, NULL
);
113 destination
= strdup(source
);
118 if (r
== 2 && !isempty(p
)) {
124 if (!path_is_absolute(source
))
127 if (!path_is_absolute(destination
))
130 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_BIND
);
135 m
->destination
= destination
;
136 m
->read_only
= read_only
;
139 source
= destination
= opts
= NULL
;
143 int tmpfs_mount_parse(CustomMount
**l
, unsigned *n
, const char *s
) {
144 _cleanup_free_
char *path
= NULL
, *opts
= NULL
;
153 r
= extract_first_word(&p
, &path
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
160 opts
= strdup("mode=0755");
166 if (!path_is_absolute(path
))
169 m
= custom_mount_add(l
, n
, CUSTOM_MOUNT_TMPFS
);
173 m
->destination
= path
;
180 static int tmpfs_patch_options(
182 bool userns
, uid_t uid_shift
, uid_t uid_range
,
183 const char *selinux_apifs_context
,
188 if (userns
&& uid_shift
!= 0) {
189 assert(uid_shift
!= UID_INVALID
);
192 (void) asprintf(&buf
, "%s,uid=" UID_FMT
",gid=" UID_FMT
, options
, uid_shift
, uid_shift
);
194 (void) asprintf(&buf
, "uid=" UID_FMT
",gid=" UID_FMT
, uid_shift
, uid_shift
);
202 if (selinux_apifs_context
) {
206 t
= strjoin(options
, ",context=\"", selinux_apifs_context
, "\"", NULL
);
208 t
= strjoin("context=\"", selinux_apifs_context
, "\"", NULL
);
223 int mount_sysfs(const char *dest
) {
224 const char *full
, *top
, *x
;
227 top
= prefix_roota(dest
, "/sys");
228 r
= path_check_fstype(top
, SYSFS_MAGIC
);
230 return log_error_errno(r
, "Failed to determine filesystem type of %s: %m", top
);
231 /* /sys might already be mounted as sysfs by the outer child in the
232 * !netns case. In this case, it's all good. Don't touch it because we
233 * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555.
238 full
= prefix_roota(top
, "/full");
240 (void) mkdir(full
, 0755);
242 if (mount("sysfs", full
, "sysfs", MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, NULL
) < 0)
243 return log_error_errno(errno
, "Failed to mount sysfs to %s: %m", full
);
245 FOREACH_STRING(x
, "block", "bus", "class", "dev", "devices", "kernel") {
246 _cleanup_free_
char *from
= NULL
, *to
= NULL
;
248 from
= prefix_root(full
, x
);
252 to
= prefix_root(top
, x
);
256 (void) mkdir(to
, 0755);
258 if (mount(from
, to
, NULL
, MS_BIND
, NULL
) < 0)
259 return log_error_errno(errno
, "Failed to mount /sys/%s into place: %m", x
);
261 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
262 return log_error_errno(errno
, "Failed to mount /sys/%s read-only: %m", x
);
265 if (umount(full
) < 0)
266 return log_error_errno(errno
, "Failed to unmount %s: %m", full
);
269 return log_error_errno(errno
, "Failed to remove %s: %m", full
);
271 x
= prefix_roota(top
, "/fs/kdbus");
272 (void) mkdir(x
, 0755);
274 if (mount(NULL
, top
, NULL
, MS_BIND
|MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_REMOUNT
, NULL
) < 0)
275 return log_error_errno(errno
, "Failed to make %s read-only: %m", top
);
280 int mount_all(const char *dest
,
281 bool use_userns
, bool in_userns
,
283 uid_t uid_shift
, uid_t uid_range
,
284 const char *selinux_apifs_context
) {
286 typedef struct MountPoint
{
297 static const MountPoint mount_table
[] = {
298 { "proc", "/proc", "proc", NULL
, MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, true, false },
299 { "/proc/sys", "/proc/sys", NULL
, NULL
, MS_BIND
, true, true, false }, /* Bind mount first */
300 { 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 */
301 { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, true },
302 { "sysfs", "/sys", "sysfs", NULL
, MS_RDONLY
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, true, false, false },
303 { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID
|MS_STRICTATIME
, true, false, false },
304 { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
305 { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID
|MS_NODEV
|MS_STRICTATIME
, true, false, false },
306 { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME
, true, false, false },
308 { "/sys/fs/selinux", "/sys/fs/selinux", NULL
, NULL
, MS_BIND
, false, false, false }, /* Bind mount first */
309 { 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 */
316 for (k
= 0; k
< ELEMENTSOF(mount_table
); k
++) {
317 _cleanup_free_
char *where
= NULL
, *options
= NULL
;
320 if (in_userns
!= mount_table
[k
].in_userns
)
323 if (!use_netns
&& mount_table
[k
].use_netns
)
326 where
= prefix_root(dest
, mount_table
[k
].where
);
330 r
= path_is_mount_point(where
, AT_SYMLINK_FOLLOW
);
331 if (r
< 0 && r
!= -ENOENT
)
332 return log_error_errno(r
, "Failed to detect whether %s is a mount point: %m", where
);
334 /* Skip this entry if it is not a remount. */
335 if (mount_table
[k
].what
&& r
> 0)
338 r
= mkdir_p(where
, 0755);
340 if (mount_table
[k
].fatal
)
341 return log_error_errno(r
, "Failed to create directory %s: %m", where
);
343 log_warning_errno(r
, "Failed to create directory %s: %m", where
);
347 o
= mount_table
[k
].options
;
348 if (streq_ptr(mount_table
[k
].type
, "tmpfs")) {
349 r
= tmpfs_patch_options(o
, use_userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
356 if (mount(mount_table
[k
].what
,
359 mount_table
[k
].flags
,
362 if (mount_table
[k
].fatal
)
363 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
365 log_warning_errno(errno
, "mount(%s) failed, ignoring: %m", where
);
372 static int parse_mount_bind_options(const char *options
, unsigned long *mount_flags
, char **mount_opts
) {
373 const char *p
= options
;
374 unsigned long flags
= *mount_flags
;
380 _cleanup_free_
char *word
= NULL
;
381 int r
= extract_first_word(&p
, &word
, ",", 0);
383 return log_error_errno(r
, "Failed to extract mount option: %m");
387 if (streq(word
, "rbind"))
389 else if (streq(word
, "norbind"))
392 log_error("Invalid bind mount option: %s", word
);
397 *mount_flags
= flags
;
398 /* in the future mount_opts will hold string options for mount(2) */
404 static int mount_bind(const char *dest
, CustomMount
*m
) {
405 struct stat source_st
, dest_st
;
407 unsigned long mount_flags
= MS_BIND
| MS_REC
;
408 _cleanup_free_
char *mount_opts
= NULL
;
414 r
= parse_mount_bind_options(m
->options
, &mount_flags
, &mount_opts
);
419 if (stat(m
->source
, &source_st
) < 0)
420 return log_error_errno(errno
, "Failed to stat %s: %m", m
->source
);
422 where
= prefix_roota(dest
, m
->destination
);
424 if (stat(where
, &dest_st
) >= 0) {
425 if (S_ISDIR(source_st
.st_mode
) && !S_ISDIR(dest_st
.st_mode
)) {
426 log_error("Cannot bind mount directory %s on file %s.", m
->source
, where
);
430 if (!S_ISDIR(source_st
.st_mode
) && S_ISDIR(dest_st
.st_mode
)) {
431 log_error("Cannot bind mount file %s on directory %s.", m
->source
, where
);
435 } else if (errno
== ENOENT
) {
436 r
= mkdir_parents_label(where
, 0755);
438 return log_error_errno(r
, "Failed to make parents of %s: %m", where
);
440 log_error_errno(errno
, "Failed to stat %s: %m", where
);
444 /* Create the mount point. Any non-directory file can be
445 * mounted on any non-directory file (regular, fifo, socket,
448 if (S_ISDIR(source_st
.st_mode
))
449 r
= mkdir_label(where
, 0755);
452 if (r
< 0 && r
!= -EEXIST
)
453 return log_error_errno(r
, "Failed to create mount point %s: %m", where
);
455 if (mount(m
->source
, where
, NULL
, mount_flags
, mount_opts
) < 0)
456 return log_error_errno(errno
, "mount(%s) failed: %m", where
);
459 r
= bind_remount_recursive(where
, true);
461 return log_error_errno(r
, "Read-only bind mount failed: %m");
467 static int mount_tmpfs(
470 bool userns
, uid_t uid_shift
, uid_t uid_range
,
471 const char *selinux_apifs_context
) {
473 const char *where
, *options
;
474 _cleanup_free_
char *buf
= NULL
;
480 where
= prefix_roota(dest
, m
->destination
);
482 r
= mkdir_p_label(where
, 0755);
483 if (r
< 0 && r
!= -EEXIST
)
484 return log_error_errno(r
, "Creating mount point for tmpfs %s failed: %m", where
);
486 r
= tmpfs_patch_options(m
->options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
489 options
= r
> 0 ? buf
: m
->options
;
491 if (mount("tmpfs", where
, "tmpfs", MS_NODEV
|MS_STRICTATIME
, options
) < 0)
492 return log_error_errno(errno
, "tmpfs mount to %s failed: %m", where
);
497 static char *joined_and_escaped_lower_dirs(char * const *lower
) {
498 _cleanup_strv_free_
char **sv
= NULL
;
500 sv
= strv_copy(lower
);
506 if (!strv_shell_escape(sv
, ",:"))
509 return strv_join(sv
, ":");
512 static int mount_overlay(const char *dest
, CustomMount
*m
) {
513 _cleanup_free_
char *lower
= NULL
;
514 const char *where
, *options
;
520 where
= prefix_roota(dest
, m
->destination
);
522 r
= mkdir_label(where
, 0755);
523 if (r
< 0 && r
!= -EEXIST
)
524 return log_error_errno(r
, "Creating mount point for overlay %s failed: %m", where
);
526 (void) mkdir_p_label(m
->source
, 0755);
528 lower
= joined_and_escaped_lower_dirs(m
->lower
);
533 _cleanup_free_
char *escaped_source
= NULL
;
535 escaped_source
= shell_escape(m
->source
, ",:");
539 options
= strjoina("lowerdir=", escaped_source
, ":", lower
);
541 _cleanup_free_
char *escaped_source
= NULL
, *escaped_work_dir
= NULL
;
544 (void) mkdir_label(m
->work_dir
, 0700);
546 escaped_source
= shell_escape(m
->source
, ",:");
549 escaped_work_dir
= shell_escape(m
->work_dir
, ",:");
550 if (!escaped_work_dir
)
553 options
= strjoina("lowerdir=", lower
, ",upperdir=", escaped_source
, ",workdir=", escaped_work_dir
);
556 if (mount("overlay", where
, "overlay", m
->read_only
? MS_RDONLY
: 0, options
) < 0)
557 return log_error_errno(errno
, "overlay mount to %s failed: %m", where
);
564 CustomMount
*mounts
, unsigned n
,
565 bool userns
, uid_t uid_shift
, uid_t uid_range
,
566 const char *selinux_apifs_context
) {
573 for (i
= 0; i
< n
; i
++) {
574 CustomMount
*m
= mounts
+ i
;
578 case CUSTOM_MOUNT_BIND
:
579 r
= mount_bind(dest
, m
);
582 case CUSTOM_MOUNT_TMPFS
:
583 r
= mount_tmpfs(dest
, m
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
586 case CUSTOM_MOUNT_OVERLAY
:
587 r
= mount_overlay(dest
, m
);
591 assert_not_reached("Unknown custom mount type");
601 static int mount_legacy_cgroup_hierarchy(const char *dest
, const char *controller
, const char *hierarchy
, bool read_only
) {
605 to
= strjoina(strempty(dest
), "/sys/fs/cgroup/", hierarchy
);
607 r
= path_is_mount_point(to
, 0);
608 if (r
< 0 && r
!= -ENOENT
)
609 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", to
);
615 /* The superblock mount options of the mount point need to be
616 * identical to the hosts', and hence writable... */
617 if (mount("cgroup", to
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, controller
) < 0)
618 return log_error_errno(errno
, "Failed to mount to %s: %m", to
);
620 /* ... hence let's only make the bind mount read-only, not the
623 if (mount(NULL
, to
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
624 return log_error_errno(errno
, "Failed to remount %s read-only: %m", to
);
629 static int mount_legacy_cgroups(
631 bool userns
, uid_t uid_shift
, uid_t uid_range
,
632 const char *selinux_apifs_context
) {
634 _cleanup_set_free_free_ Set
*controllers
= NULL
;
635 const char *cgroup_root
;
638 cgroup_root
= prefix_roota(dest
, "/sys/fs/cgroup");
640 (void) mkdir_p(cgroup_root
, 0755);
642 /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
643 r
= path_is_mount_point(cgroup_root
, AT_SYMLINK_FOLLOW
);
645 return log_error_errno(r
, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
647 _cleanup_free_
char *options
= NULL
;
649 r
= tmpfs_patch_options("mode=755", userns
, uid_shift
, uid_range
, selinux_apifs_context
, &options
);
653 if (mount("tmpfs", cgroup_root
, "tmpfs", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
, options
) < 0)
654 return log_error_errno(errno
, "Failed to mount /sys/fs/cgroup: %m");
657 if (cg_unified() > 0)
658 goto skip_controllers
;
660 controllers
= set_new(&string_hash_ops
);
664 r
= cg_kernel_controllers(controllers
);
666 return log_error_errno(r
, "Failed to determine cgroup controllers: %m");
669 _cleanup_free_
char *controller
= NULL
, *origin
= NULL
, *combined
= NULL
;
671 controller
= set_steal_first(controllers
);
675 origin
= prefix_root("/sys/fs/cgroup/", controller
);
679 r
= readlink_malloc(origin
, &combined
);
681 /* Not a symbolic link, but directly a single cgroup hierarchy */
683 r
= mount_legacy_cgroup_hierarchy(dest
, controller
, controller
, true);
688 return log_error_errno(r
, "Failed to read link %s: %m", origin
);
690 _cleanup_free_
char *target
= NULL
;
692 target
= prefix_root(dest
, origin
);
696 /* A symbolic link, a combination of controllers in one hierarchy */
698 if (!filename_is_valid(combined
)) {
699 log_warning("Ignoring invalid combined hierarchy %s.", combined
);
703 r
= mount_legacy_cgroup_hierarchy(dest
, combined
, combined
, true);
707 r
= symlink_idempotent(combined
, target
);
709 log_error("Invalid existing symlink for combined hierarchy");
713 return log_error_errno(r
, "Failed to create symlink for combined hierarchy: %m");
718 r
= mount_legacy_cgroup_hierarchy(dest
, "none,name=systemd,xattr", "systemd", false);
722 if (mount(NULL
, cgroup_root
, NULL
, MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_STRICTATIME
|MS_RDONLY
, "mode=755") < 0)
723 return log_error_errno(errno
, "Failed to remount %s read-only: %m", cgroup_root
);
728 static int mount_unified_cgroups(const char *dest
) {
734 p
= prefix_roota(dest
, "/sys/fs/cgroup");
736 (void) mkdir_p(p
, 0755);
738 r
= path_is_mount_point(p
, AT_SYMLINK_FOLLOW
);
740 return log_error_errno(r
, "Failed to determine if %s is mounted already: %m", p
);
742 p
= prefix_roota(dest
, "/sys/fs/cgroup/cgroup.procs");
743 if (access(p
, F_OK
) >= 0)
746 return log_error_errno(errno
, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p
);
748 log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p
);
752 if (mount("cgroup", p
, "cgroup", MS_NOSUID
|MS_NOEXEC
|MS_NODEV
, "__DEVEL__sane_behavior") < 0)
753 return log_error_errno(errno
, "Failed to mount unified cgroup hierarchy to %s: %m", p
);
760 bool unified_requested
,
761 bool userns
, uid_t uid_shift
, uid_t uid_range
,
762 const char *selinux_apifs_context
) {
764 if (unified_requested
)
765 return mount_unified_cgroups(dest
);
767 return mount_legacy_cgroups(dest
, userns
, uid_shift
, uid_range
, selinux_apifs_context
);
770 int mount_systemd_cgroup_writable(
772 bool unified_requested
) {
774 _cleanup_free_
char *own_cgroup_path
= NULL
;
775 const char *systemd_root
, *systemd_own
;
780 r
= cg_pid_get_path(NULL
, 0, &own_cgroup_path
);
782 return log_error_errno(r
, "Failed to determine our own cgroup path: %m");
784 /* If we are living in the top-level, then there's nothing to do... */
785 if (path_equal(own_cgroup_path
, "/"))
788 if (unified_requested
) {
789 systemd_own
= strjoina(dest
, "/sys/fs/cgroup", own_cgroup_path
);
790 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup");
792 systemd_own
= strjoina(dest
, "/sys/fs/cgroup/systemd", own_cgroup_path
);
793 systemd_root
= prefix_roota(dest
, "/sys/fs/cgroup/systemd");
796 /* Make our own cgroup a (writable) bind mount */
797 if (mount(systemd_own
, systemd_own
, NULL
, MS_BIND
, NULL
) < 0)
798 return log_error_errno(errno
, "Failed to turn %s into a bind mount: %m", own_cgroup_path
);
800 /* And then remount the systemd cgroup root read-only */
801 if (mount(NULL
, systemd_root
, NULL
, MS_BIND
|MS_REMOUNT
|MS_NOSUID
|MS_NOEXEC
|MS_NODEV
|MS_RDONLY
, NULL
) < 0)
802 return log_error_errno(errno
, "Failed to mount cgroup root read-only: %m");
807 int setup_volatile_state(
808 const char *directory
,
810 bool userns
, uid_t uid_shift
, uid_t uid_range
,
811 const char *selinux_apifs_context
) {
813 _cleanup_free_
char *buf
= NULL
;
814 const char *p
, *options
;
819 if (mode
!= VOLATILE_STATE
)
822 /* --volatile=state means we simply overmount /var
823 with a tmpfs, and the rest read-only. */
825 r
= bind_remount_recursive(directory
, true);
827 return log_error_errno(r
, "Failed to remount %s read-only: %m", directory
);
829 p
= prefix_roota(directory
, "/var");
831 if (r
< 0 && errno
!= EEXIST
)
832 return log_error_errno(errno
, "Failed to create %s: %m", directory
);
834 options
= "mode=755";
835 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
841 if (mount("tmpfs", p
, "tmpfs", MS_STRICTATIME
, options
) < 0)
842 return log_error_errno(errno
, "Failed to mount tmpfs to /var: %m");
848 const char *directory
,
850 bool userns
, uid_t uid_shift
, uid_t uid_range
,
851 const char *selinux_apifs_context
) {
853 bool tmpfs_mounted
= false, bind_mounted
= false;
854 char template[] = "/tmp/nspawn-volatile-XXXXXX";
855 _cleanup_free_
char *buf
= NULL
;
856 const char *f
, *t
, *options
;
861 if (mode
!= VOLATILE_YES
)
864 /* --volatile=yes means we mount a tmpfs to the root dir, and
865 the original /usr to use inside it, and that read-only. */
867 if (!mkdtemp(template))
868 return log_error_errno(errno
, "Failed to create temporary directory: %m");
870 options
= "mode=755";
871 r
= tmpfs_patch_options(options
, userns
, uid_shift
, uid_range
, selinux_apifs_context
, &buf
);
877 if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME
, options
) < 0) {
878 r
= log_error_errno(errno
, "Failed to mount tmpfs for root directory: %m");
882 tmpfs_mounted
= true;
884 f
= prefix_roota(directory
, "/usr");
885 t
= prefix_roota(template, "/usr");
888 if (r
< 0 && errno
!= EEXIST
) {
889 r
= log_error_errno(errno
, "Failed to create %s: %m", t
);
893 if (mount(f
, t
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
894 r
= log_error_errno(errno
, "Failed to create /usr bind mount: %m");
900 r
= bind_remount_recursive(t
, true);
902 log_error_errno(r
, "Failed to remount %s read-only: %m", t
);
906 if (mount(template, directory
, NULL
, MS_MOVE
, NULL
) < 0) {
907 r
= log_error_errno(errno
, "Failed to move root mount: %m");
911 (void) rmdir(template);
920 (void) umount(template);
921 (void) rmdir(template);
925 VolatileMode
volatile_mode_from_string(const char *s
) {
929 return _VOLATILE_MODE_INVALID
;
931 b
= parse_boolean(s
);
937 if (streq(s
, "state"))
938 return VOLATILE_STATE
;
940 return _VOLATILE_MODE_INVALID
;