Fix gpt-auto-generator on usr-only root in presence of sysexts (#43591)
This adds a fallback to `blockdev_get_root`, to handle the case where `/usr` is a sysext overlay. Additinal changes are: - move `extension_overlay_block` to `extension-util.h`, and make it generic to work in both confext and sysext cases (though only sysext is actually used currently) - add a usr-only integration test to exercise the failure Closes #43565.
This commit is contained in:
@@ -17,9 +17,12 @@
|
||||
#include "devnum-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "extension-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "macro.h"
|
||||
#include "os-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "string-util.h"
|
||||
@@ -875,6 +878,13 @@ int blockdev_get_root(int level, dev_t *ret) {
|
||||
return btrfs_log_dev_root(level, r, "/usr");
|
||||
if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to determine block device of /usr/ file system: %m");
|
||||
if (r == 0) { /* Not backed by a single block device, we might be looking at a sysext overlay */
|
||||
r = extension_overlay_block("/usr", IMAGE_SYSEXT, &devno);
|
||||
if (IN_SET(r, -ENOENT, -ENOTTY))
|
||||
r = 0; /* no sysext overlay metadata */
|
||||
else if (r < 0)
|
||||
return log_full_errno(level, r, "Failed to determine backing device of /usr/ extension overlay: %m");
|
||||
}
|
||||
if (r == 0) { /* /usr/ not backed by single block device, either. */
|
||||
log_debug("Neither root nor /usr/ file system are on a (single) block device.");
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <linux/magic.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "architecture.h"
|
||||
#include "blockdev-util.h"
|
||||
#include "chase.h"
|
||||
#include "devnum-util.h"
|
||||
#include "env-util.h"
|
||||
#include "extension-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "os-util.h"
|
||||
#include "path-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
@@ -178,3 +187,46 @@ int extension_has_forbidden_content(const char *root) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extension_overlay_block(const char *p, ImageClass image_class, dev_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
assert(IN_SET(image_class, IMAGE_SYSEXT, IMAGE_CONFEXT));
|
||||
|
||||
/* Tries to read the backing device information systemd-sysext puts in
|
||||
* the virtual file .systemd-sysext/.systemd-confext */
|
||||
|
||||
_cleanup_free_ char *j = path_join(p, image_class == IMAGE_CONFEXT ? ".systemd-confext" : ".systemd-sysext");
|
||||
if (!j)
|
||||
return log_oom_debug();
|
||||
|
||||
_cleanup_close_ int fd = open(j, O_RDONLY|O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open '%s': %m", j);
|
||||
|
||||
r = fd_is_fs_type(fd, OVERLAYFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine backing file system of '%s': %m", j);
|
||||
if (r == 0)
|
||||
return log_debug_errno(
|
||||
SYNTHETIC_ERRNO(ENOTTY), "Backing file system of '%s' is not an overlayfs.", j);
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
r = read_one_line_file_at(fd, "backing", &buf);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read contents of '%s/backing': %m", j);
|
||||
|
||||
r = parse_devnum(buf, ret);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse contents of '%s/backing': %m", j);
|
||||
|
||||
if (major(*ret) == 0) { /* not a block device? */
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) block_get_originating(*ret, ret, /* recursive= */ false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -22,3 +22,6 @@ int parse_env_extension_hierarchies(char ***ret_hierarchies, const char *hierarc
|
||||
/* Insist that extension images do not overwrite the underlying OS release file (it's fine if they place one
|
||||
* in /etc/os-release, i.e. where things don't matter, as they aren't merged.) */
|
||||
int extension_has_forbidden_content(const char *root);
|
||||
|
||||
/* Find the backing device. Returns >0 on success, 0 if the volume is not an extension, and <0 on error. */
|
||||
int extension_overlay_block(const char *p, ImageClass image_class, dev_t *ret);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -10,10 +9,10 @@
|
||||
#include "build-path.h"
|
||||
#include "chase.h"
|
||||
#include "device-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "dirent-util.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "extension-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdisk-util.h"
|
||||
#include "fileio.h"
|
||||
@@ -23,6 +22,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "import-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "os-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
@@ -766,47 +766,6 @@ Instance* resource_find_instance(Resource *rr, const char *version) {
|
||||
return *found;
|
||||
}
|
||||
|
||||
static int get_sysext_overlay_block(const char *p, dev_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
/* Tries to read the backing device information systemd-sysext puts in the virtual file
|
||||
* /usr/.systemd-sysext/backing */
|
||||
|
||||
_cleanup_free_ char *j = path_join(p, ".systemd-sysext");
|
||||
if (!j)
|
||||
return log_oom_debug();
|
||||
|
||||
_cleanup_close_ int fd = open(j, O_RDONLY|O_DIRECTORY);
|
||||
if (fd < 0)
|
||||
return log_debug_errno(errno, "Failed to open '%s': %m", j);
|
||||
|
||||
r = fd_is_fs_type(fd, OVERLAYFS_SUPER_MAGIC);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine backing file system of '%s': %m", j);
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTTY), "Backing file system of '%s' is not an overlayfs.", j);
|
||||
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
r = read_one_line_file_at(fd, "backing", &buf);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read contents of '%s/backing': %m", j);
|
||||
|
||||
r = parse_devnum(buf, ret);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse contents of '%s/backing': %m", j);
|
||||
|
||||
if (major(*ret) == 0) { /* not a block device? */
|
||||
*ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) block_get_originating(*ret, ret, /* recursive= */ false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int resource_resolve_path(
|
||||
Resource *rr,
|
||||
const char *root,
|
||||
@@ -858,7 +817,7 @@ int resource_resolve_path(
|
||||
/* volatile-root not found */
|
||||
r = get_block_device_harder("/usr/", &d);
|
||||
if (r == 0) /* Not backed by a block device? Let's see if this is a sysext overlayfs instance */
|
||||
r = get_sysext_overlay_block("/usr/", &d);
|
||||
r = extension_overlay_block("/usr/", IMAGE_SYSEXT, &d);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to determine block device of file system: %m");
|
||||
} else if (!S_ISBLK(orig_root_stats.st_mode)) /* symlink was present but not block device */
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
GENERATOR_BIN="/usr/lib/systemd/system-generators/systemd-gpt-auto-generator"
|
||||
WORK_DIR="$(mktemp --directory /var/tmp/test-gpt-auto-generator.XXXXXX)"
|
||||
DEFINITIONS="$WORK_DIR/definitions"
|
||||
IMAGE="$WORK_DIR/image.raw"
|
||||
OUTPUT="$WORK_DIR/output"
|
||||
ROOT="$WORK_DIR/root"
|
||||
LOOP=""
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
|
||||
if [[ -n "$LOOP" ]]; then
|
||||
losetup --detach "$LOOP"
|
||||
fi
|
||||
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
|
||||
trap at_exit EXIT
|
||||
|
||||
test -x "$GENERATOR_BIN"
|
||||
mkdir -p "$DEFINITIONS" "$OUTPUT"/{normal,early,late} "$ROOT"
|
||||
|
||||
cat >"$DEFINITIONS/10-usr.conf" <<EOF
|
||||
[Partition]
|
||||
Type=usr
|
||||
SizeMinBytes=10M
|
||||
SizeMaxBytes=10M
|
||||
EOF
|
||||
|
||||
cat >"$DEFINITIONS/20-home.conf" <<EOF
|
||||
[Partition]
|
||||
Type=home
|
||||
SizeMinBytes=10M
|
||||
SizeMaxBytes=10M
|
||||
EOF
|
||||
|
||||
systemd-repart \
|
||||
--definitions="$DEFINITIONS" \
|
||||
--empty=create \
|
||||
--size=32M \
|
||||
--dry-run=no \
|
||||
--offline=yes \
|
||||
"$IMAGE"
|
||||
|
||||
LOOP="$(losetup --show --find --partscan "$IMAGE")"
|
||||
udevadm wait --timeout=60 --settle --initialized=no "$LOOP"p1 "$LOOP"p2
|
||||
|
||||
# Recreate a root=tmpfs system whose /usr/ is backed by a GPT partition and overlaid by systemd-sysext.
|
||||
# Run the generator as PID 1 because generators deliberately do nothing in containers.
|
||||
unshare --mount --pid --fork --mount-proc \
|
||||
bash -euxo pipefail -s -- "$ROOT" "$OUTPUT" "$WORK_DIR" "$LOOP" "$GENERATOR_BIN" <<'EOF'
|
||||
mount --make-rprivate /
|
||||
|
||||
mount --types tmpfs tmpfs "$1"
|
||||
mkdir -p "$1"/{dev,proc,run/generator-output,sys,usr}
|
||||
mount --bind /dev "$1/dev"
|
||||
mount --bind /proc "$1/proc"
|
||||
mount --bind /sys "$1/sys"
|
||||
mount --bind "$2" "$1/run/generator-output"
|
||||
|
||||
mkdir -p "$3/usr-upper" "$3/usr-work"
|
||||
mount --types overlay overlay \
|
||||
--options lowerdir=/usr,upperdir="$3/usr-upper",workdir="$3/usr-work" \
|
||||
"$1/usr"
|
||||
|
||||
for directory in bin lib lib64 sbin; do
|
||||
if [[ -e "/$directory" ]]; then
|
||||
mkdir -p "$1/$directory"
|
||||
mount --bind "/$directory" "$1/$directory"
|
||||
fi
|
||||
done
|
||||
|
||||
mkdir -p "$1/usr/.systemd-sysext"
|
||||
cat "/sys/class/block/${4##*/}p1/dev" >"$1/usr/.systemd-sysext/backing"
|
||||
|
||||
export container=
|
||||
export SYSTEMD_IN_INITRD=0
|
||||
export SYSTEMD_PROC_CMDLINE="root=tmpfs systemd.gpt_auto=yes"
|
||||
exec chroot "$1" "$5" \
|
||||
/run/generator-output/normal \
|
||||
/run/generator-output/early \
|
||||
/run/generator-output/late
|
||||
EOF
|
||||
|
||||
test -f "$OUTPUT/late/home.mount"
|
||||
WHAT="$(sed -n 's/^What=//p' "$OUTPUT/late/home.mount")"
|
||||
test "$(readlink --canonicalize "$WHAT")" = "$(readlink --canonicalize "${LOOP}p2")"
|
||||
Reference in New Issue
Block a user