summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/base-filesystem.c2
-rw-r--r--src/shared/dissect-image.c116
-rw-r--r--src/shared/dissect-image.h5
-rw-r--r--src/shared/fstab-util.c2
-rw-r--r--src/shared/machine-image.c49
-rw-r--r--src/shared/switch-root.c139
-rw-r--r--src/shared/volatile-util.c68
-rw-r--r--src/shared/volatile-util.h32
8 files changed, 275 insertions, 138 deletions
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index f1fbce9dca..127cbe44e3 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -101,7 +101,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
- if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (uid_is_valid(uid) || gid_is_valid(gid)) {
if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
}
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index d3ba9b9dde..878cb008aa 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -84,7 +84,7 @@ not_found:
#endif
}
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) {
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
#ifdef HAVE_BLKID
sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
@@ -95,7 +95,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
_cleanup_udev_unref_ struct udev *udev = NULL;
_cleanup_free_ char *generic_node = NULL;
- const char *pttype = NULL, *usage = NULL;
+ sd_id128_t generic_uuid = SD_ID128_NULL;
+ const char *pttype = NULL;
struct udev_list_entry *first, *item;
blkid_partlist pl;
int r, generic_nr;
@@ -147,8 +148,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
return -errno;
}
- blkid_probe_enable_superblocks(b, 1);
- blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
+ /* Look for file system superblocks, unless we only shall look for GPT partition tables */
+ blkid_probe_enable_superblocks(b, 1);
+ blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
+ }
+
blkid_probe_enable_partitions(b, 1);
blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
@@ -169,40 +174,45 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!m)
return -ENOMEM;
- (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
- if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
- _cleanup_free_ char *t = NULL, *n = NULL;
- const char *fstype = NULL;
+ if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
+ (flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
+ const char *usage = NULL;
- /* OK, we have found a file system, that's our root partition then. */
- (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
+ (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
+ if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
+ _cleanup_free_ char *t = NULL, *n = NULL;
+ const char *fstype = NULL;
- if (fstype) {
- t = strdup(fstype);
- if (!t)
- return -ENOMEM;
- }
+ /* OK, we have found a file system, that's our root partition then. */
+ (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
- if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
- return -ENOMEM;
+ if (fstype) {
+ t = strdup(fstype);
+ if (!t)
+ return -ENOMEM;
+ }
+
+ if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+ return -ENOMEM;
- m->partitions[PARTITION_ROOT] = (DissectedPartition) {
- .found = true,
- .rw = true,
- .partno = -1,
- .architecture = _ARCHITECTURE_INVALID,
- .fstype = t,
- .node = n,
- };
+ m->partitions[PARTITION_ROOT] = (DissectedPartition) {
+ .found = true,
+ .rw = true,
+ .partno = -1,
+ .architecture = _ARCHITECTURE_INVALID,
+ .fstype = t,
+ .node = n,
+ };
- t = n = NULL;
+ t = n = NULL;
- m->encrypted = streq(fstype, "crypto_LUKS");
+ m->encrypted = streq(fstype, "crypto_LUKS");
- *ret = m;
- m = NULL;
+ *ret = m;
+ m = NULL;
- return 0;
+ return 0;
+ }
}
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
@@ -212,7 +222,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
is_gpt = streq_ptr(pttype, "gpt");
is_mbr = streq_ptr(pttype, "dos");
- if (!is_gpt && !is_mbr)
+ if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
return -ENOPKG;
errno = 0;
@@ -300,7 +310,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
first = udev_enumerate_get_list_entry(e);
udev_list_entry_foreach(item, first) {
_cleanup_udev_device_unref_ struct udev_device *q;
- unsigned long long flags;
+ unsigned long long pflags;
blkid_partition pp;
const char *node;
dev_t qn;
@@ -325,7 +335,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (!pp)
continue;
- flags = blkid_partition_get_flags(pp);
+ pflags = blkid_partition_get_flags(pp);
nr = blkid_partition_get_partno(pp);
if (nr < 0)
@@ -337,7 +347,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
sd_id128_t type_id, id;
bool rw = true;
- if (flags & GPT_FLAG_NO_AUTO)
+ if (pflags & GPT_FLAG_NO_AUTO)
continue;
sid = blkid_partition_get_uuid(pp);
@@ -354,10 +364,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
if (sd_id128_equal(type_id, GPT_HOME)) {
designator = PARTITION_HOME;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_SRV)) {
designator = PARTITION_SRV;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ESP)) {
designator = PARTITION_ESP;
fstype = "vfat";
@@ -371,7 +381,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT;
architecture = native_architecture();
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
m->can_verity = true;
@@ -395,9 +405,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
designator = PARTITION_ROOT_SECONDARY;
architecture = SECONDARY_ARCHITECTURE;
- rw = !(flags & GPT_FLAG_READ_ONLY);
+ rw = !(pflags & GPT_FLAG_READ_ONLY);
} else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
-
m->can_verity = true;
/* Ignore verity unless root has is specified */
@@ -419,7 +428,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
multiple_generic = true;
else {
generic_nr = nr;
- generic_rw = !(flags & GPT_FLAG_READ_ONLY);
+ generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
+ generic_uuid = id;
generic_node = strdup(node);
if (!generic_node)
return -ENOMEM;
@@ -450,6 +460,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.architecture = architecture,
.node = n,
.fstype = t,
+ .uuid = id,
};
n = t = NULL;
@@ -457,7 +468,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
} else if (is_mbr) {
- if (flags != 0x80) /* Bootable flag */
+ if (pflags != 0x80) /* Bootable flag */
continue;
if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
@@ -480,7 +491,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
* either, then check if there's a single generic one, and use that. */
if (m->partitions[PARTITION_ROOT_VERITY].found)
- return -ENXIO;
+ return -EADDRNOTAVAIL;
if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
@@ -489,8 +500,19 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
- } else if (generic_node && !root_hash) {
+ } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
+
+ /* If the root has was set, then we won't fallback to a generic node, because the root hash
+ * decides */
+ if (root_hash)
+ return -EADDRNOTAVAIL;
+
+ /* If we didn't find a generic node, then we can't fix this up either */
+ if (!generic_node)
+ return -ENXIO;
+ /* If we didn't find a properly marked root partition, but we did find a single suitable
+ * generic Linux partition, then use this as root partition, if the caller asked for it. */
if (multiple_generic)
return -ENOTUNIQ;
@@ -500,17 +522,15 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte
.partno = generic_nr,
.architecture = _ARCHITECTURE_INVALID,
.node = generic_node,
+ .uuid = generic_uuid,
};
generic_node = NULL;
- } else
- return -ENXIO;
+ }
}
- assert(m->partitions[PARTITION_ROOT].found);
-
if (root_hash) {
- if (!m->partitions[PARTITION_ROOT_VERITY].found)
+ if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
return -EADDRNOTAVAIL;
/* If we found the primary root with the hash, then we definitely want to suppress any secondary root
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 175ddd8ea0..26319bd8e7 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -32,6 +32,7 @@ struct DissectedPartition {
bool rw:1;
int partno; /* -1 if there was no partition and the images contains a file system directly */
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
+ sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */
char *fstype;
char *node;
char *decrypted_node;
@@ -67,6 +68,8 @@ typedef enum DissectImageFlags {
DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP |
DISSECT_IMAGE_DISCARD |
DISSECT_IMAGE_DISCARD_ON_CRYPTO,
+ DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */
+ DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */
} DissectImageFlags;
struct DissectedImage {
@@ -76,7 +79,7 @@ struct DissectedImage {
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
};
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret);
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
DissectedImage* dissected_image_unref(DissectedImage *m);
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
index f0bfb30bb5..87b520b540 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -38,7 +38,7 @@ bool fstab_is_mount_point(const char *mount) {
_cleanup_endmntent_ FILE *f = NULL;
struct mntent *m;
- f = setmntent("/etc/fstab", "r");
+ f = setmntent("/etc/fstab", "re");
if (!f)
return false;
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 712aff65b9..7bc5c0a128 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -99,6 +99,16 @@ static char **image_settings_path(Image *image) {
return ret;
}
+static char *image_roothash_path(Image *image) {
+ const char *fn;
+
+ assert(image);
+
+ fn = strjoina(image->name, ".roothash");
+
+ return file_in_same_dir(image->path, fn);
+}
+
static int image_new(
ImageType t,
const char *pretty,
@@ -397,6 +407,7 @@ void image_hashmap_free(Hashmap *map) {
int image_remove(Image *i) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
+ _cleanup_free_ char *roothash = NULL;
char **j;
int r;
@@ -409,6 +420,10 @@ int image_remove(Image *i) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -445,14 +460,17 @@ int image_remove(Image *i) {
log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j);
}
+ if (unlink(roothash) < 0 && errno != ENOENT)
+ log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash);
+
return 0;
}
-static int rename_settings_file(const char *path, const char *new_name) {
+static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
- fn = strjoina(new_name, ".nspawn");
+ fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@@ -463,7 +481,7 @@ static int rename_settings_file(const char *path, const char *new_name) {
int image_rename(Image *i, const char *new_name) {
_cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT;
- _cleanup_free_ char *new_path = NULL, *nn = NULL;
+ _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL;
_cleanup_strv_free_ char **settings = NULL;
unsigned file_attr = 0;
char **j;
@@ -481,6 +499,10 @@ int image_rename(Image *i, const char *new_name) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure we don't interfere with a running nspawn */
r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock);
if (r < 0)
@@ -550,19 +572,23 @@ int image_rename(Image *i, const char *new_name) {
nn = NULL;
STRV_FOREACH(j, settings) {
- r = rename_settings_file(*j, new_name);
+ r = rename_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j);
}
+ r = rename_auxiliary_file(roothash, new_name, ".roothash");
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash);
+
return 0;
}
-static int clone_settings_file(const char *path, const char *new_name) {
+static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) {
_cleanup_free_ char *rs = NULL;
const char *fn;
- fn = strjoina(new_name, ".nspawn");
+ fn = strjoina(new_name, suffix);
rs = file_in_same_dir(path, fn);
if (!rs)
@@ -574,6 +600,7 @@ static int clone_settings_file(const char *path, const char *new_name) {
int image_clone(Image *i, const char *new_name, bool read_only) {
_cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT;
_cleanup_strv_free_ char **settings = NULL;
+ _cleanup_free_ char *roothash = NULL;
const char *new_path;
char **j;
int r;
@@ -587,6 +614,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
if (!settings)
return -ENOMEM;
+ roothash = image_roothash_path(i);
+ if (!roothash)
+ return -ENOMEM;
+
/* Make sure nobody takes the new name, between the time we
* checked it is currently unused in all search paths, and the
* time we take possession of it */
@@ -636,11 +667,15 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
return r;
STRV_FOREACH(j, settings) {
- r = clone_settings_file(*j, new_name);
+ r = clone_auxiliary_file(*j, new_name, ".nspawn");
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j);
}
+ r = clone_auxiliary_file(roothash, new_name, ".roothash");
+ if (r < 0 && r != -ENOENT)
+ log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash);
+
return 0;
}
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 4eff4f692e..afdf1ab5ad 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -28,123 +28,102 @@
#include "base-filesystem.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
#include "missing.h"
#include "mkdir.h"
+#include "mount-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
#include "switch-root.h"
#include "user-util.h"
#include "util.h"
-int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) {
-
- /* Don't try to unmount/move the old "/", there's no way to do it. */
- static const char move_mounts[] =
- "/dev\0"
- "/proc\0"
- "/sys\0"
- "/run\0";
+int switch_root(const char *new_root,
+ const char *old_root_after, /* path below the new root, where to place the old root after the transition */
+ bool unmount_old_root,
+ unsigned long mount_flags) { /* MS_MOVE or MS_BIND */
+ _cleanup_free_ char *resolved_old_root_after = NULL;
_cleanup_close_ int old_root_fd = -1;
- struct stat new_root_stat;
bool old_root_remove;
- const char *i, *temporary_old_root;
+ const char *i;
+ int r;
+
+ assert(new_root);
+ assert(old_root_after);
if (path_equal(new_root, "/"))
return 0;
- temporary_old_root = strjoina(new_root, oldroot);
- mkdir_p_label(temporary_old_root, 0755);
-
+ /* Check if we shall remove the contents of the old root */
old_root_remove = in_initrd();
+ if (old_root_remove) {
+ old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
+ if (old_root_fd < 0)
+ return log_error_errno(errno, "Failed to open root directory: %m");
+ }
- if (stat(new_root, &new_root_stat) < 0)
- return log_error_errno(errno, "Failed to stat directory %s: %m", new_root);
+ /* Determine where we shall place the old root after the transition */
+ r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, old_root_after);
+ if (r == 0) /* Doesn't exist yet. Let's create it */
+ (void) mkdir_p_label(resolved_old_root_after, 0755);
- /* Work-around for kernel design: the kernel refuses switching
- * root if any file systems are mounted MS_SHARED. Hence
+ /* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted MS_SHARED. Hence
* remount them MS_PRIVATE here as a work-around.
*
* https://bugzilla.redhat.com/show_bug.cgi?id=847418 */
if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0)
- log_warning_errno(errno, "Failed to make \"/\" private mount: %m");
-
- NULSTR_FOREACH(i, move_mounts) {
- char new_mount[PATH_MAX];
- struct stat sb;
- size_t n;
-
- n = snprintf(new_mount, sizeof new_mount, "%s%s", new_root, i);
- if (n >= sizeof new_mount) {
- bool move = mountflags & MS_MOVE;
-
- log_warning("New path is too long, %s: %s%s",
- move ? "forcing unmount instead" : "ignoring",
- new_root, i);
-
- if (move)
- if (umount2(i, MNT_FORCE) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
- continue;
- }
-
- mkdir_p_label(new_mount, 0755);
-
- if (stat(new_mount, &sb) < 0 ||
- sb.st_dev != new_root_stat.st_dev) {
-
- /* Mount point seems to be mounted already or
- * stat failed. Unmount the old mount point. */
- if (umount2(i, MNT_DETACH) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
- continue;
- }
-
- if (mount(i, new_mount, NULL, mountflags, NULL) < 0) {
- if (mountflags & MS_MOVE) {
- log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount);
-
- if (umount2(i, MNT_FORCE) < 0)
- log_warning_errno(errno, "Failed to unmount %s: %m", i);
-
- } else if (mountflags & MS_BIND)
- log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount);
- }
+ return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
+
+ FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") {
+ _cleanup_free_ char *chased = NULL;
+
+ r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i);
+ if (r > 0) {
+ /* Already exists. Let's see if it is a mount point already. */
+ r = path_is_mount_point(chased, NULL, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased);
+ if (r > 0) /* If it is already mounted, then do nothing */
+ continue;
+ } else
+ /* Doesn't exist yet? */
+ (void) mkdir_p_label(chased, 0755);
+
+ if (mount(i, chased, NULL, mount_flags, NULL) < 0)
+ return log_error_errno(r, "Failed to mount %s to %s: %m", i, chased);
}
- /* Do not fail, if base_filesystem_create() fails. Not all
- * switch roots are like base_filesystem_create() wants them
- * to look like. They might even boot, if they are RO and
- * don't have the FS layout. Just ignore the error and
- * switch_root() nevertheless. */
+ /* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants
+ * them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error
+ * and switch_root() nevertheless. */
(void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
- if (old_root_remove) {
- old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
- if (old_root_fd < 0)
- log_warning_errno(errno, "Failed to open root directory: %m");
- }
-
- /* We first try a pivot_root() so that we can umount the old
- * root dir. In many cases (i.e. where rootfs is /), that's
- * not possible however, and hence we simply overmount root */
- if (pivot_root(new_root, temporary_old_root) >= 0) {
+ /* We first try a pivot_root() so that we can umount the old root dir. In many cases (i.e. where rootfs is /),
+ * that's not possible however, and hence we simply overmount root */
+ if (pivot_root(new_root, resolved_old_root_after) >= 0) {
/* Immediately get rid of the old root, if detach_oldroot is set.
* Since we are running off it we need to do this lazily. */
- if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0)
- log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m",
- oldroot,
- errno == ENOENT ? "ignoring" : "leaving it around");
+ if (unmount_old_root) {
+ r = umount_recursive(old_root_after, MNT_DETACH);
+ if (r < 0)
+ log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m");
+ }
} else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0)
- return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root);
+ return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
if (chroot(".") < 0)
return log_error_errno(errno, "Failed to change root: %m");
diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c
new file mode 100644
index 0000000000..e7e9721411
--- /dev/null
+++ b/src/shared/volatile-util.c
@@ -0,0 +1,68 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2015 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "proc-cmdline.h"
+#include "string-util.h"
+#include "volatile-util.h"
+
+VolatileMode volatile_mode_from_string(const char *s) {
+ int b;
+
+ if (isempty(s))
+ return _VOLATILE_MODE_INVALID;
+
+ b = parse_boolean(s);
+ if (b > 0)
+ return VOLATILE_YES;
+ if (b == 0)
+ return VOLATILE_NO;
+
+ if (streq(s, "state"))
+ return VOLATILE_STATE;
+
+ return _VOLATILE_MODE_INVALID;
+}
+
+int query_volatile_mode(VolatileMode *ret) {
+ _cleanup_free_ char *mode = NULL;
+ VolatileMode m = VOLATILE_NO;
+ int r;
+
+ r = proc_cmdline_get_key("systemd.volatile", PROC_CMDLINE_VALUE_OPTIONAL, &mode);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ goto finish;
+
+ if (mode) {
+ m = volatile_mode_from_string(mode);
+ if (m < 0)
+ return -EINVAL;
+ } else
+ m = VOLATILE_YES;
+
+ r = 1;
+
+finish:
+ *ret = m;
+ return r;
+}
diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h
new file mode 100644
index 0000000000..17930ba6ae
--- /dev/null
+++ b/src/shared/volatile-util.h
@@ -0,0 +1,32 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef enum VolatileMode {
+ VOLATILE_NO,
+ VOLATILE_YES,
+ VOLATILE_STATE,
+ _VOLATILE_MODE_MAX,
+ _VOLATILE_MODE_INVALID = -1
+} VolatileMode;
+
+VolatileMode volatile_mode_from_string(const char *s);
+
+int query_volatile_mode(VolatileMode *ret);