From 8257508c58aad823b02689d9ea0868412623ceb2 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 21 Feb 2024 20:00:29 +0000 Subject: [PATCH] portable: support vpick Resolve at attach/detach/inspect time, so that the image is pinned and requires re-attaching on update, given files are extracted from it so just passing img.v/ to RootImage= is not enough to get a portable image updated --- man/org.freedesktop.portable1.xml | 3 ++ man/portablectl.xml | 4 ++ src/portable/portable.c | 65 +++++++++++++++++++++++++++++-- test/units/testsuite-29.sh | 20 ++++++++++ 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/man/org.freedesktop.portable1.xml b/man/org.freedesktop.portable1.xml index c68995d9524..4de3da2905d 100644 --- a/man/org.freedesktop.portable1.xml +++ b/man/org.freedesktop.portable1.xml @@ -259,6 +259,9 @@ node /org/freedesktop/portable1 { on the system. Note that this method returns only after all the listed operations are completed, and due to the I/O involved it might take some time. + + + AttachImageWithExtensions() attaches a portable image to the system. This method is a superset of AttachImage() with the addition of a list of extensions as input parameter, which will be overlaid on top of the main diff --git a/man/portablectl.xml b/man/portablectl.xml index 5e88eb309dc..92d8ff03aa7 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -141,6 +141,8 @@ immediately started (blocking operation unless is passed) and/or enabled after attaching the image. + + @@ -421,6 +423,8 @@ Note that the same extensions have to be specified, in the same order, when attaching and detaching. + + diff --git a/src/portable/portable.c b/src/portable/portable.c index 60dc98c5baf..53418c417b5 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -43,6 +43,7 @@ #include "strv.h" #include "tmpfile-util.h" #include "user-util.h" +#include "vpick.h" /* Markers used in the first line of our 20-portable.conf unit file drop-in to determine, that a) the unit file was * dropped there by the portable service logic and b) for which image it was dropped there. */ @@ -564,6 +565,7 @@ static int extract_image_and_extensions( _cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL, *confext_level = NULL; _cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL; _cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL, *extension_releases = NULL; + _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; _cleanup_hashmap_free_ Hashmap *unit_files = NULL; _cleanup_strv_free_ char **valid_prefixes = NULL; _cleanup_(image_unrefp) Image *image = NULL; @@ -572,7 +574,27 @@ static int extract_image_and_extensions( assert(name_or_path); - r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image); + /* If we get a path, then check if it can be resolved with vpick. We need this as we might just + * get a simple image name, which would make vpick error out. */ + if (path_is_absolute(name_or_path)) { + r = path_pick(/* toplevel_path= */ NULL, + /* toplevel_fd= */ AT_FDCWD, + name_or_path, + &pick_filter_image_any, + PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, + &result); + if (r < 0) + return r; + if (!result.path) + return log_debug_errno( + SYNTHETIC_ERRNO(ENOENT), + "No matching entry in .v/ directory %s found.", + name_or_path); + + name_or_path = result.path; + } + + r = image_find_harder(IMAGE_PORTABLE, name_or_path, /* root= */ NULL, &image); if (r < 0) return r; @@ -588,9 +610,29 @@ static int extract_image_and_extensions( } STRV_FOREACH(p, extension_image_paths) { + _cleanup_(pick_result_done) PickResult ext_result = PICK_RESULT_NULL; _cleanup_(image_unrefp) Image *new = NULL; + const char *path = *p; + + if (path_is_absolute(*p)) { + r = path_pick(/* toplevel_path= */ NULL, + /* toplevel_fd= */ AT_FDCWD, + *p, + &pick_filter_image_any, + PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, + &ext_result); + if (r < 0) + return r; + if (!ext_result.path) + return log_debug_errno( + SYNTHETIC_ERRNO(ENOENT), + "No matching entry in .v/ directory %s found.", + *p); + + path = ext_result.path; + } - r = image_find_harder(IMAGE_PORTABLE, *p, NULL, &new); + r = image_find_harder(IMAGE_PORTABLE, path, NULL, &new); if (r < 0) return r; @@ -1691,6 +1733,7 @@ static bool marker_matches_images(const char *marker, const char *name_or_path, while (!isempty(marker)) STRV_FOREACH(image_name_or_path, root_and_extensions) { _cleanup_free_ char *image = NULL, *base_image = NULL, *base_image_name_or_path = NULL; + _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); if (r < 0) @@ -1702,9 +1745,23 @@ static bool marker_matches_images(const char *marker, const char *name_or_path, if (r < 0) return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image); - r = path_extract_image_name(*image_name_or_path, &base_image_name_or_path); + r = path_pick(/* toplevel_path= */ NULL, + /* toplevel_fd= */ AT_FDCWD, + *image_name_or_path, + &pick_filter_image_any, + PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE, + &result); + if (r < 0) + return r; + if (!result.path) + return log_debug_errno( + SYNTHETIC_ERRNO(ENOENT), + "No matching entry in .v/ directory %s found.", + *image_name_or_path); + + r = path_extract_image_name(result.path, &base_image_name_or_path); if (r < 0) - return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", *image_name_or_path); + return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", result.path); if (!streq(base_image, base_image_name_or_path)) { if (match_all) diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index 977d76e22f0..4c0f1ba3293 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -183,6 +183,26 @@ status="$(portablectl is-attached --extension app1 minimal_0)" portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 +# Ensure vpick works, including reattaching to a new image +mkdir -p /tmp/app1.v/ +cp /usr/share/app1.raw /tmp/app1.v/app1_1.0.raw +cp /tmp/app1_2.raw /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +rm -f /tmp/app1.v/app1_2.0.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_1.0.raw minimal_1)" +[[ "${status}" == "running-runtime" ]] + +portablectl detach --now --runtime --extension /tmp/app1.v/ /usr/share/minimal_0.raw app1 +rm -f /tmp/app1.v/app1_1.0.raw + # Ensure that the combination of read-only images, state directory and dynamic user works, and that # state is retained. Check after detaching, as on slow systems (eg: sanitizers) it might take a while # after the service is attached before the file appears. -- 2.47.3