#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. */
_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;
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;
}
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;
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)
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)
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.