1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-10 01:17:44 +03:00

Merge pull request #25280 from keszybz/initrd-with-overlayfs

Initrd with overlayfs
This commit is contained in:
Frantisek Sumsal 2022-11-10 08:22:15 +00:00 committed by GitHub
commit a92401d558
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 83 additions and 114 deletions

View File

@ -73,13 +73,9 @@ All tools:
(relevant in particular for the system manager and `systemd-hostnamed`).
Must be a valid hostname (either a single label or a FQDN).
* `$SYSTEMD_IN_INITRD=[auto|lenient|0|1]` — if set, specifies initrd detection
method. Defaults to `auto`. Behavior is defined as follows:
`auto`: Checks if `/etc/initrd-release` exists, and a temporary fs is mounted
on `/`. If both conditions meet, then it's in initrd.
`lenient`: Similar to `auto`, but the rootfs check is skipped.
`0|1`: Simply overrides initrd detection. This is useful for debugging and
testing initrd-only programs in the main system.
* `$SYSTEMD_IN_INITRD` — takes a boolean. If set, overrides initrd detection.
This is useful for debugging and testing initrd-only programs in the main
system.
* `$SYSTEMD_BUS_TIMEOUT=SECS` — specifies the maximum time to wait for method call
completion. If no time unit is specified, assumes seconds. The usual other units

View File

@ -3,6 +3,7 @@
#include <unistd.h>
#include "env-util.h"
#include "errno-util.h"
#include "initrd-util.h"
#include "parse-util.h"
#include "stat-util.h"
@ -12,59 +13,25 @@ static int saved_in_initrd = -1;
bool in_initrd(void) {
int r;
const char *e;
bool lenient = false;
if (saved_in_initrd >= 0)
return saved_in_initrd;
/* We have two checks here:
*
* 1. the flag file /etc/initrd-release must exist
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
* initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*
* If env var $SYSTEMD_IN_INITRD is not set or set to "auto",
* both checks are used. If it's set to "lenient", only check
* 1 is used. If set to a boolean value, then the boolean
* value is returned.
/* If /etc/initrd-release exists, we're in an initrd.
* This can be overridden by setting SYSTEMD_IN_INITRD=0|1.
*/
e = secure_getenv("SYSTEMD_IN_INITRD");
if (e) {
if (streq(e, "lenient"))
lenient = true;
else if (!streq(e, "auto")) {
r = parse_boolean(e);
if (r >= 0) {
saved_in_initrd = r > 0;
return saved_in_initrd;
}
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
}
}
if (!lenient) {
r = path_is_temporary_fs("/");
if (r < 0)
log_debug_errno(r, "Couldn't determine if / is a temporary file system: %m");
r = getenv_bool_secure("SYSTEMD_IN_INITRD");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m");
if (r >= 0)
saved_in_initrd = r > 0;
}
r = access("/etc/initrd-release", F_OK);
if (r >= 0) {
if (saved_in_initrd == 0)
log_debug("/etc/initrd-release exists, but it's not an initrd.");
else
saved_in_initrd = 1;
} else {
if (errno != ENOENT)
log_debug_errno(errno, "Failed to test if /etc/initrd-release exists: %m");
saved_in_initrd = 0;
else {
r = RET_NERRNO(access("/etc/initrd-release", F_OK));
if (r < 0 && r != -ENOENT)
log_debug_errno(r, "Failed to check if /etc/initrd-release exists, assuming it does not: %m");
saved_in_initrd = r >= 0;
}
return saved_in_initrd;

View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <stdio.h>
#include "libmount-util.h"
int libmount_parse(
const char *path,
FILE *source,
struct libmnt_table **ret_table,
struct libmnt_iter **ret_iter) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
int r;
/* Older libmount seems to require this. */
assert(!source || path);
table = mnt_new_table();
iter = mnt_new_iter(MNT_ITER_FORWARD);
if (!table || !iter)
return -ENOMEM;
/* If source or path are specified, we use on the functions which ignore utab.
* Only if both are empty, we use mnt_table_parse_mtab(). */
if (source)
r = mnt_table_parse_stream(table, source, path);
else if (path)
r = mnt_table_parse_file(table, path);
else
r = mnt_table_parse_mtab(table, NULL);
if (r < 0)
return r;
*ret_table = TAKE_PTR(table);
*ret_iter = TAKE_PTR(iter);
return 0;
}

View File

@ -1,8 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdio.h>
/* This needs to be after sys/mount.h */
#include <libmount.h>
@ -11,37 +9,8 @@
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct libmnt_table*, mnt_free_table, NULL);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct libmnt_iter*, mnt_free_iter, NULL);
static inline int libmount_parse(
int libmount_parse(
const char *path,
FILE *source,
struct libmnt_table **ret_table,
struct libmnt_iter **ret_iter) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
int r;
/* Older libmount seems to require this. */
assert(!source || path);
table = mnt_new_table();
iter = mnt_new_iter(MNT_ITER_FORWARD);
if (!table || !iter)
return -ENOMEM;
/* If source or path are specified, we use on the functions which ignore utab.
* Only if both are empty, we use mnt_table_parse_mtab(). */
if (source)
r = mnt_table_parse_stream(table, source, path);
else if (path)
r = mnt_table_parse_file(table, path);
else
r = mnt_table_parse_mtab(table, NULL);
if (r < 0)
return r;
*ret_table = TAKE_PTR(table);
*ret_iter = TAKE_PTR(iter);
return 0;
}
struct libmnt_iter **ret_iter);

View File

@ -193,6 +193,7 @@ shared_sources = files(
'libcrypt-util.h',
'libfido2-util.c',
'libfido2-util.h',
'libmount-util.c',
'libmount-util.h',
'linux/auto_dev-ioctl.h',
'linux/bpf.h',

View File

@ -718,20 +718,18 @@ int mount_option_mangle(
_cleanup_free_ char *ret = NULL;
int r;
/* This extracts mount flags from the mount options, and store
/* This extracts mount flags from the mount options, and stores
* non-mount-flag options to '*ret_remaining_options'.
* E.g.,
* "rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000"
* is split to MS_NOSUID|MS_NODEV|MS_RELATIME and
* "size=1630748k,mode=700,uid=1000,gid=1000".
* See more examples in test-mount-utils.c.
* See more examples in test-mount-util.c.
*
* Note that if 'options' does not contain any non-mount-flag options,
* If 'options' does not contain any non-mount-flag options,
* then '*ret_remaining_options' is set to NULL instead of empty string.
* Note that this does not check validity of options stored in
* '*ret_remaining_options'.
* Note that if 'options' is NULL, then this just copies 'mount_flags'
* to '*ret_mount_flags'. */
* The validity of options stored in '*ret_remaining_options' is not checked.
* If 'options' is NULL, this just copies 'mount_flags' to *ret_mount_flags. */
assert(ret_mount_flags);
assert(ret_remaining_options);

View File

@ -32,7 +32,6 @@ int switch_root(const char *new_root,
_cleanup_free_ char *resolved_old_root_after = NULL;
_cleanup_close_ int old_root_fd = -1;
bool old_root_remove;
int r;
assert(new_root);
@ -42,12 +41,16 @@ int switch_root(const char *new_root,
return 0;
/* 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");
}
old_root_fd = open("/", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (old_root_fd < 0)
return log_error_errno(errno, "Failed to open root directory: %m");
r = fd_is_temporary_fs(old_root_fd);
if (r < 0)
return log_error_errno(r, "Failed to stat root directory: %m");
if (r > 0)
log_debug("Root directory is on tmpfs, will do cleanup later.");
else
old_root_fd = safe_close(old_root_fd);
/* 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, NULL);
@ -117,9 +120,8 @@ int switch_root(const char *new_root,
struct stat rb;
if (fstat(old_root_fd, &rb) < 0)
log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
else
(void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
return log_error_errno(errno, "Failed to stat old root directory: %m");
(void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
}
return 0;

View File

@ -322,8 +322,8 @@ TEST(close_all_fds) {
int r;
/* Runs the test four times. Once as is. Once with close_range() syscall blocked via seccomp, once
* with /proc overmounted, and once with the combination of both. This should trigger all fallbacks in
* the close_range_all() function. */
* with /proc/ overmounted, and once with the combination of both. This should trigger all fallbacks
* in the close_range_all() function. */
r = safe_fork("(caf-plain)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
if (r == 0) {
@ -332,26 +332,22 @@ TEST(close_all_fds) {
}
assert_se(r >= 0);
if (geteuid() != 0) {
log_notice("Lacking privileges, skipping running tests with blocked close_range() and with /proc/ overnmounted.");
return;
}
if (geteuid() != 0)
return (void) log_tests_skipped("Lacking privileges for test with close_range() blocked and /proc/ overmounted");
r = safe_fork("(caf-noproc)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL);
if (r == 0) {
r = mount_nofollow_verbose(LOG_WARNING, "tmpfs", "/proc", "tmpfs", 0, NULL);
if (r < 0)
log_notice("Overmounting /proc didn#t work, skipping close_all_fds() with masked /proc/.");
log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
else
test_close_all_fds_inner();
_exit(EXIT_SUCCESS);
}
assert_se(r >= 0);
if (!is_seccomp_available()) {
log_notice("Seccomp not available, skipping seccomp tests in %s", __func__);
return;
}
if (!is_seccomp_available())
return (void) log_tests_skipped("Seccomp not available");
r = safe_fork("(caf-seccomp)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
if (r == 0) {
@ -373,7 +369,7 @@ TEST(close_all_fds) {
else {
r = mount_nofollow_verbose(LOG_WARNING, "tmpfs", "/proc", "tmpfs", 0, NULL);
if (r < 0)
log_notice("Overmounting /proc didn#t work, skipping close_all_fds() with masked /proc/.");
log_notice("Overmounting /proc/ didn't work, skipping close_all_fds() with masked /proc/.");
else
test_close_all_fds_inner();
}