diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index bff2047aa1..7ddcb93daf 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -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."); diff --git a/src/shared/extension-util.c b/src/shared/extension-util.c index 1f99f46e8d..e81f2a07ac 100644 --- a/src/shared/extension-util.c +++ b/src/shared/extension-util.c @@ -1,12 +1,21 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #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; +} diff --git a/src/shared/extension-util.h b/src/shared/extension-util.h index 08bdd7f33e..ad8f90f8ca 100644 --- a/src/shared/extension-util.h +++ b/src/shared/extension-util.h @@ -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); diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index cfc0d09ae6..b6cebee314 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include #include @@ -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 */ diff --git a/test/units/TEST-50-DISSECT.usr-gpt-auto-generator.sh b/test/units/TEST-50-DISSECT.usr-gpt-auto-generator.sh new file mode 100755 index 0000000000..37a85e1865 --- /dev/null +++ b/test/units/TEST-50-DISSECT.usr-gpt-auto-generator.sh @@ -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" <"$DEFINITIONS/20-home.conf" <"$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")"