Skip to content

Commit

Permalink
Merge pull request systemd#31435 from bluca/portable_fix_versioned
Browse files Browse the repository at this point in the history
portable: assorted bug fixes
  • Loading branch information
keszybz authored Apr 5, 2024
2 parents 1eeae73 + 373a1e4 commit c1e7f93
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 64 deletions.
55 changes: 52 additions & 3 deletions src/basic/os-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,39 @@ bool image_name_is_valid(const char *s) {
return true;
}

int path_extract_image_name(const char *path, char **ret) {
_cleanup_free_ char *fn = NULL;
int r;

assert(path);
assert(ret);

/* Extract last component from path, without any "/" suffixes. */
r = path_extract_filename(path, &fn);
if (r < 0)
return r;

if (r != O_DIRECTORY) {
/* Chop off any image suffixes we recognize (unless we already know this must refer to some dir */
FOREACH_STRING(suffix, ".sysext.raw", ".confext.raw", ".raw") {
char *m = endswith(fn, suffix);
if (m) {
*m = 0;
break;
}
}
}

/* Truncate the version/counting suffixes */
fn[strcspn(fn, "_+")] = 0;

if (!image_name_is_valid(fn))
return -EINVAL;

*ret = TAKE_PTR(fn);
return 0;
}

int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check) {
int r;

Expand Down Expand Up @@ -230,9 +263,25 @@ int open_extension_release_at(
continue;
}

if (!relax_extension_release_check &&
extension_release_strict_xattr_value(fd, dir_path, de->d_name) != 0)
continue;
if (!relax_extension_release_check) {
_cleanup_free_ char *base_image_name = NULL, *base_extension = NULL;

r = path_extract_image_name(image_name, &base_image_name);
if (r < 0) {
log_debug_errno(r, "Failed to extract image name from %s/%s, ignoring: %m", dir_path, de->d_name);
continue;
}

r = path_extract_image_name(extension, &base_extension);
if (r < 0) {
log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", extension);
continue;
}

if (!streq(base_image_name, base_extension) &&
extension_release_strict_xattr_value(fd, dir_path, image_name) != 0)
continue;
}

/* We already found what we were looking for, but there's another candidate? We treat this as
* an error, as we want to enforce that there are no ambiguities in case we are in the
Expand Down
1 change: 1 addition & 0 deletions src/basic/os-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ImageClass image_class_from_string(const char *s) _pure_;
* in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */

bool image_name_is_valid(const char *s) _pure_;
int path_extract_image_name(const char *path, char **ret);

int path_is_extension_tree(ImageClass image_class, const char *path, const char *extension, bool relax_extension_release_check);
static inline int path_is_os_tree(const char *path) {
Expand Down
88 changes: 27 additions & 61 deletions src/portable/portable.c
Original file line number Diff line number Diff line change
Expand Up @@ -1664,9 +1664,8 @@ int portable_attach(
return 0;
}

static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) {
static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths, bool match_all) {
_cleanup_strv_free_ char **root_and_extensions = NULL;
const char *a;
int r;

assert(marker);
Expand All @@ -1676,7 +1675,9 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
* list of images/paths. We enforce strict 1:1 matching, so that we are sure
* we are detaching exactly what was attached.
* For each image, starting with the root, we look for a token in the marker,
* and return a negative answer on any non-matching combination. */
* and return a negative answer on any non-matching combination.
* If a partial match is allowed, then return immediately once it is found, otherwise
* ensure that everything matches. */

root_and_extensions = strv_new(name_or_path);
if (!root_and_extensions)
Expand All @@ -1686,70 +1687,33 @@ static bool marker_matches_images(const char *marker, const char *name_or_path,
if (r < 0)
return r;

STRV_FOREACH(image_name_or_path, root_and_extensions) {
_cleanup_free_ char *image = NULL;
/* Ensure the number of images passed matches the number of images listed in the marker */
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;

r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return log_debug_errno(r, "Failed to parse marker: %s", marker);
if (r == 0)
return false;

a = last_path_component(image);

if (image_name_is_valid(*image_name_or_path)) {
const char *e, *underscore;

/* We shall match against an image name. In that case let's compare the last component, and optionally
* allow either a suffix of ".raw" or a series of "/".
* But allow matching on a different version of the same image, when a "_" is used as a separator. */
underscore = strchr(*image_name_or_path, '_');
if (underscore) {
if (strneq(a, *image_name_or_path, underscore - *image_name_or_path))
continue;
return false;
}

e = startswith(a, *image_name_or_path);
if (!e)
return false;

if(!(e[strspn(e, "/")] == 0 || streq(e, ".raw")))
return false;
} else {
const char *b, *underscore;
size_t l;

/* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to
* reach the same file. However, in this mode, let's validate any file suffix.
* But also ensure that we don't fail if both components don't have a '/' at all
* (strcspn returns the full length of the string in that case, which might not
* match as the versions might differ). */

l = strcspn(a, "/");
b = last_path_component(*image_name_or_path);

if ((a[l] != '/') != !strchr(b, '/')) /* One is a directory, the other is not */
r = extract_first_word(&marker, &image, ":", EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return log_debug_errno(r, "Failed to parse marker: %s", marker);
if (r == 0)
return false;

if (a[l] != 0 && strcspn(b, "/") != l)
return false;
r = path_extract_image_name(image, &base_image);
if (r < 0)
return log_debug_errno(r, "Failed to extract image name from %s, ignoring: %m", image);

underscore = strchr(b, '_');
if (underscore)
l = underscore - b;
else { /* Either component could be versioned */
underscore = strchr(a, '_');
if (underscore)
l = underscore - a;
}
r = path_extract_image_name(*image_name_or_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);

if (!strneq(a, b, l))
return false;
if (!streq(base_image, base_image_name_or_path)) {
if (match_all)
return false;
} else if (!match_all)
return true;
}
}

return true;
return match_all;
}

static int test_chroot_dropin(
Expand Down Expand Up @@ -1804,7 +1768,9 @@ static int test_chroot_dropin(
if (!name_or_path)
r = true;
else
r = marker_matches_images(marker, name_or_path, extension_image_paths);
/* When detaching we want to match exactly on all images, but when inspecting we only need
* to get the state of one component */
r = marker_matches_images(marker, name_or_path, extension_image_paths, ret_marker != NULL);

if (ret_marker)
*ret_marker = TAKE_PTR(marker);
Expand Down
43 changes: 43 additions & 0 deletions test/units/testsuite-29.sh
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,19 @@ grep -q -F "LogExtraFields=PORTABLE_EXTENSION_NAME_AND_VERSION=app" /run/systemd

portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0

# Ensure versioned images are accepted without needing to use --force to override the extension-release
# matching

cp /usr/share/app0.raw /tmp/app0_1.0.raw
portablectl "${ARGS[@]}" attach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_0.raw app0

systemctl is-active app0.service
status="$(portablectl is-attached --extension app0_1 minimal_0)"
[[ "${status}" == "running-runtime" ]]

portablectl detach --now --runtime --extension /tmp/app0_1.0.raw /usr/share/minimal_1.raw app0
rm -f /tmp/app0_1.0.raw

portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1

systemctl is-active app1.service
Expand Down Expand Up @@ -211,6 +224,36 @@ test -f /run/systemd/system.attached/app0.service
test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0

# Ensure that when two portables share the same base image, removing one doesn't remove the other too

portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0
portablectl "${ARGS[@]}" attach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1

status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]

(! portablectl detach --runtime /usr/share/minimal_0.raw app)

status="$(portablectl is-attached --extension app0 minimal_0)"
[[ "${status}" == "attached-runtime" ]]
status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]

# Ensure 'portablectl list' shows the correct status for both images
portablectl list
portablectl list | grep -F "minimal_0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app0" | grep -q -F "attached-runtime"
portablectl list | grep -F "app1" | grep -q -F "attached-runtime"

portablectl detach --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app

status="$(portablectl is-attached --extension app1 minimal_0)"
[[ "${status}" == "attached-runtime" ]]

portablectl detach --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app

# portablectl also works with directory paths rather than images

mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
Expand Down

0 comments on commit c1e7f93

Please sign in to comment.