mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-08 05:57:26 +03:00
Merge pull request #21448 from poettering/disk-image-purpose
encode disk image purpose in extension-release.d + os-release
This commit is contained in:
commit
e18dadca24
11
TODO
11
TODO
@ -94,6 +94,17 @@ Features:
|
||||
magic into a string. Then use that to replace fstype_magic_to_name() in homed
|
||||
sources, and similar code.
|
||||
|
||||
* man: rework os-release(5), and clearly separate our extension-release.d/ and
|
||||
initrd-release parts, i.e. list explicitly which fields are about what.
|
||||
|
||||
* sysext: before applying a sysext, do a superficial validation run so that
|
||||
things are not rearranged to wildy. I.e. protect against accidental fuckups,
|
||||
such as masking out /usr/lib/ or so. We should probably refuse if existing
|
||||
inodes are replaced by other types of inodes or so.
|
||||
|
||||
* sysext: ensure one can build a sysext that can safely apply to *any* system
|
||||
(because it contains only static go binaries in /opt/ or so)
|
||||
|
||||
* userdb: when synthesizing NSS records, pick "best" password from defined
|
||||
passwords, not just the first. i.e. if there are multiple defined, prefer
|
||||
unlocked over locked and prefer non-empty over empty.
|
||||
|
@ -247,6 +247,20 @@ image. To facilitate 3 and 4 you also need to include a boot loader in the
|
||||
image. As mentioned, `mkosi -b` takes care of all of that for you, but any
|
||||
other image generator should work too.
|
||||
|
||||
The
|
||||
[os-release(5)](https://www.freedesktop.org/software/systemd/man/os-release.html)
|
||||
file may optionally be extended with a `PORTABLE_PREFIXES=` field listing all
|
||||
supported portable service prefixes for the image (see above). This is useful
|
||||
for informational purposes (as it allows recognizing portable service images
|
||||
from their contents as such), but is also useful to protect the image from
|
||||
being used under a wrong name and prefix. This is particularly relevant if the
|
||||
images are cryptographically authenticated (via Verity or a similar mechanism)
|
||||
as this way the (not necessarily authenticated) image file name can be
|
||||
validated against the (authenticated) image contents. If the field is not
|
||||
specified the image will work fine, but is not necessarily recognizable as
|
||||
portable service image, and any set of units included in the image may be
|
||||
attached, there are no restrictions enforced.
|
||||
|
||||
## Extension Images
|
||||
|
||||
Portable services can be delivered as one or multiple images that extend the base
|
||||
|
@ -407,6 +407,29 @@
|
||||
<para>Examples: <literal>SYSEXT_LEVEL=2</literal>, <literal>SYSEXT_LEVEL=15.14</literal>.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SYSEXT_SCOPE=</varname></term>
|
||||
<listitem><para>Takes a space-separated list of one or more of the strings
|
||||
<literal>system</literal>, <literal>initrd</literal> and <literal>portable</literal>. This field is
|
||||
only supported in <filename>extension-release.d/</filename> files and indicates what environments
|
||||
the system extension is applicable to: i.e. to regular systems, to initial RAM filesystems
|
||||
("initrd") or to portable service images. If unspecified, <literal>SYSEXT_SCOPE=system
|
||||
portable</literal> is implied, i.e. any system extension without this field is applicable to
|
||||
regular systems and to portable service environments, but not to initrd
|
||||
environments.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>PORTABLE_PREFIXES=</varname></term>
|
||||
<listitem><para>Takes a space-separated list of one or more valid prefix match strings for the
|
||||
<ulink url="https://systemd.io/PORTABLE_SERVICES">Portable Services</ulink> logic. This field
|
||||
serves two purposes: it's informational, identifying portable service images as such (and thus
|
||||
allowing them to be distinguished from other OS images, such as bootable system images); whenever a
|
||||
portable service image is attached the specified or implied portable service prefix is checked
|
||||
against this list, to enforce restrictions how images may be attached to a
|
||||
system.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
|
@ -1240,11 +1240,9 @@ static void read_efi_var(const char *variable, char **ret) {
|
||||
}
|
||||
|
||||
static void print_yes_no_line(bool first, bool good, const char *name) {
|
||||
printf("%s%s%s%s %s\n",
|
||||
printf("%s%s %s\n",
|
||||
first ? " Features: " : " ",
|
||||
ansi_highlight_green_red(good),
|
||||
special_glyph_check_mark(good),
|
||||
ansi_normal(),
|
||||
COLOR_MARK_BOOL(good),
|
||||
name);
|
||||
}
|
||||
|
||||
|
@ -1149,7 +1149,7 @@ static int mount_image(const MountEntry *m, const char *root_directory) {
|
||||
|
||||
r = verity_dissect_and_mount(
|
||||
mount_entry_source(m), mount_entry_path(m), m->image_options,
|
||||
host_os_release_id, host_os_release_version_id, host_os_release_sysext_level);
|
||||
host_os_release_id, host_os_release_version_id, host_os_release_sysext_level, NULL);
|
||||
if (r == -ENOENT && m->ignore)
|
||||
return 0;
|
||||
if (r == -ESTALE && host_os_release_id)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "chase-symlinks.h"
|
||||
#include "copy.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
@ -369,6 +370,46 @@ static int strv_pair_to_json(char **l, JsonVariant **ret) {
|
||||
return json_variant_new_array_strv(ret, jl);
|
||||
}
|
||||
|
||||
static void strv_pair_print(char **l, const char *prefix) {
|
||||
char **p, **q;
|
||||
|
||||
assert(prefix);
|
||||
|
||||
STRV_FOREACH_PAIR(p, q, l) {
|
||||
if (p == l)
|
||||
printf("%s %s=%s\n", prefix, *p, *q);
|
||||
else
|
||||
printf("%*s %s=%s\n", (int) strlen(prefix), "", *p, *q);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_sysext_scopes(DissectedImage *m, char ***ret_scopes) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
const char *e;
|
||||
|
||||
assert(m);
|
||||
assert(ret_scopes);
|
||||
|
||||
/* If there's no extension-release file its not a system extension. Otherwise the SYSEXT_SCOPE field
|
||||
* indicates which scope it is for — and it defaults to "system" + "portable" if unset. */
|
||||
|
||||
if (!m->extension_release) {
|
||||
*ret_scopes = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
e = strv_env_pairs_get(m->extension_release, "SYSEXT_SCOPE");
|
||||
if (e)
|
||||
l = strv_split(e, WHITESPACE);
|
||||
else
|
||||
l = strv_new("system", "portable");
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_scopes = TAKE_PTR(l);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
@ -406,47 +447,51 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire image metadata: %m");
|
||||
else if (arg_json_format_flags & JSON_FORMAT_OFF) {
|
||||
_cleanup_strv_free_ char **sysext_scopes = NULL;
|
||||
|
||||
if (m->hostname)
|
||||
printf(" Hostname: %s\n", m->hostname);
|
||||
|
||||
if (!sd_id128_is_null(m->machine_id))
|
||||
printf("Machine ID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->machine_id));
|
||||
|
||||
if (!strv_isempty(m->machine_info)) {
|
||||
char **p, **q;
|
||||
|
||||
STRV_FOREACH_PAIR(p, q, m->machine_info)
|
||||
printf("%s %s=%s\n",
|
||||
p == m->machine_info ? "Mach. Info:" : " ",
|
||||
*p, *q);
|
||||
}
|
||||
|
||||
if (!strv_isempty(m->os_release)) {
|
||||
char **p, **q;
|
||||
|
||||
STRV_FOREACH_PAIR(p, q, m->os_release)
|
||||
printf("%s %s=%s\n",
|
||||
p == m->os_release ? "OS Release:" : " ",
|
||||
*p, *q);
|
||||
}
|
||||
|
||||
if (!strv_isempty(m->extension_release)) {
|
||||
char **p, **q;
|
||||
|
||||
STRV_FOREACH_PAIR(p, q, m->extension_release)
|
||||
printf("%s %s=%s\n",
|
||||
p == m->extension_release ? "Extension Release:" : " ",
|
||||
*p, *q);
|
||||
}
|
||||
strv_pair_print(m->machine_info,
|
||||
"Mach. Info:");
|
||||
strv_pair_print(m->os_release,
|
||||
"OS Release:");
|
||||
strv_pair_print(m->extension_release,
|
||||
" Ext. Rel.:");
|
||||
|
||||
if (m->hostname ||
|
||||
!sd_id128_is_null(m->machine_id) ||
|
||||
!strv_isempty(m->machine_info) ||
|
||||
!strv_isempty(m->extension_release) ||
|
||||
!strv_isempty(m->os_release))
|
||||
!strv_isempty(m->os_release) ||
|
||||
!strv_isempty(m->extension_release))
|
||||
putc('\n', stdout);
|
||||
|
||||
printf(" Use As: %s bootable system for UEFI\n", COLOR_MARK_BOOL(m->partitions[PARTITION_ESP].found));
|
||||
|
||||
if (m->has_init_system >= 0)
|
||||
printf(" %s bootable system for container\n", COLOR_MARK_BOOL(m->has_init_system));
|
||||
|
||||
printf(" %s portable service\n",
|
||||
COLOR_MARK_BOOL(strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES")));
|
||||
|
||||
r = get_sysext_scopes(m, &sysext_scopes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m");
|
||||
|
||||
printf(" %s extension for system\n",
|
||||
COLOR_MARK_BOOL(strv_contains(sysext_scopes, "system")));
|
||||
printf(" %s extension for initrd\n",
|
||||
COLOR_MARK_BOOL(strv_contains(sysext_scopes, "initrd")));
|
||||
printf(" %s extension for portable service\n",
|
||||
COLOR_MARK_BOOL(strv_contains(sysext_scopes, "portable")));
|
||||
|
||||
putc('\n', stdout);
|
||||
} else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *mi = NULL, *osr = NULL, *exr = NULL;
|
||||
_cleanup_strv_free_ char **sysext_scopes = NULL;
|
||||
|
||||
if (!strv_isempty(m->machine_info)) {
|
||||
r = strv_pair_to_json(m->machine_info, &mi);
|
||||
@ -466,6 +511,10 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = get_sysext_scopes(m, &sysext_scopes);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse SYSEXT_SCOPE: %m");
|
||||
|
||||
r = json_build(&v, JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(basename(arg_image))),
|
||||
JSON_BUILD_PAIR("size", JSON_BUILD_INTEGER(size)),
|
||||
@ -473,7 +522,13 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(m->machine_id), "machineId", JSON_BUILD_ID128(m->machine_id)),
|
||||
JSON_BUILD_PAIR_CONDITION(mi, "machineInfo", JSON_BUILD_VARIANT(mi)),
|
||||
JSON_BUILD_PAIR_CONDITION(osr, "osRelease", JSON_BUILD_VARIANT(osr)),
|
||||
JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr))));
|
||||
JSON_BUILD_PAIR_CONDITION(exr, "extensionRelease", JSON_BUILD_VARIANT(exr)),
|
||||
JSON_BUILD_PAIR("useBootableUefi", JSON_BUILD_BOOLEAN(m->partitions[PARTITION_ESP].found)),
|
||||
JSON_BUILD_PAIR_CONDITION(m->has_init_system >= 0, "useBootableContainer", JSON_BUILD_BOOLEAN(m->has_init_system)),
|
||||
JSON_BUILD_PAIR("usePortableService", JSON_BUILD_BOOLEAN(strv_env_pairs_get(m->os_release, "PORTABLE_MATCHES"))),
|
||||
JSON_BUILD_PAIR("useSystemExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "system"))),
|
||||
JSON_BUILD_PAIR("useInitRDExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "initrd"))),
|
||||
JSON_BUILD_PAIR("usePortableExtension", JSON_BUILD_BOOLEAN(strv_contains(sysext_scopes, "portable")))));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "discover-image.h"
|
||||
#include "dissect-image.h"
|
||||
#include "env-file.h"
|
||||
#include "env-util.h"
|
||||
#include "errno-list.h"
|
||||
#include "escape.h"
|
||||
#include "extension-release.h"
|
||||
@ -509,20 +510,20 @@ static int extract_image_and_extensions(
|
||||
OrderedHashmap **ret_extension_images,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
char ***ret_valid_prefixes,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_free_ char *id = NULL, *version_id = NULL, *sysext_level = NULL;
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_strv_free_ char **valid_prefixes = NULL;
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
Image *ext;
|
||||
int r;
|
||||
|
||||
assert(name_or_path);
|
||||
assert(matches);
|
||||
assert(ret_image);
|
||||
assert(ret_extension_images);
|
||||
|
||||
r = image_find_harder(IMAGE_PORTABLE, name_or_path, NULL, &image);
|
||||
if (r < 0)
|
||||
@ -553,10 +554,12 @@ static int extract_image_and_extensions(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* If we are layering extension images on top of a runtime image, check that the os-release and extension-release metadata
|
||||
* match, otherwise reject it immediately as invalid, or it will fail when the units are started. */
|
||||
if (validate_sysext) {
|
||||
/* If we are layering extension images on top of a runtime image, check that the os-release and
|
||||
* extension-release metadata match, otherwise reject it immediately as invalid, or it will fail when
|
||||
* the units are started. Also, collect valid portable prefixes if caller requested that. */
|
||||
if (validate_sysext || ret_valid_prefixes) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *prefixes = NULL;
|
||||
|
||||
r = take_fdopen_unlocked(&os_release->fd, "r", &f);
|
||||
if (r < 0)
|
||||
@ -565,9 +568,16 @@ static int extract_image_and_extensions(
|
||||
r = parse_env_file(f, os_release->name,
|
||||
"ID", &id,
|
||||
"VERSION_ID", &version_id,
|
||||
"SYSEXT_LEVEL", &sysext_level);
|
||||
"SYSEXT_LEVEL", &sysext_level,
|
||||
"PORTABLE_PREFIXES", &prefixes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (prefixes) {
|
||||
valid_prefixes = strv_split(prefixes, WHITESPACE);
|
||||
if (!valid_prefixes)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(ext, extension_images) {
|
||||
@ -575,6 +585,7 @@ static int extract_image_and_extensions(
|
||||
_cleanup_hashmap_free_ Hashmap *extra_unit_files = NULL;
|
||||
_cleanup_strv_free_ char **extension_release = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
const char *e;
|
||||
|
||||
r = portable_extract_by_path(ext->path, /* path_is_extension= */ true, matches, &extension_release_meta, &extra_unit_files, error);
|
||||
if (r < 0)
|
||||
@ -584,7 +595,7 @@ static int extract_image_and_extensions(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!validate_sysext)
|
||||
if (!validate_sysext && !ret_valid_prefixes)
|
||||
continue;
|
||||
|
||||
r = take_fdopen_unlocked(&extension_release_meta->fd, "r", &f);
|
||||
@ -595,19 +606,40 @@ static int extract_image_and_extensions(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = extension_release_validate(ext->path, id, version_id, sysext_level, extension_release);
|
||||
if (r == 0)
|
||||
return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
|
||||
if (validate_sysext) {
|
||||
r = extension_release_validate(ext->path, id, version_id, sysext_level, "portable", extension_release);
|
||||
if (r == 0)
|
||||
return sd_bus_error_set_errnof(error, SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", ext->path);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", ext->path);
|
||||
}
|
||||
|
||||
e = strv_env_pairs_get(extension_release, "PORTABLE_PREFIXES");
|
||||
if (e) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
l = strv_split(e, WHITESPACE);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_extend_strv(&valid_prefixes, l, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*ret_image = TAKE_PTR(image);
|
||||
*ret_extension_images = TAKE_PTR(extension_images);
|
||||
strv_sort(valid_prefixes);
|
||||
|
||||
if (ret_image)
|
||||
*ret_image = TAKE_PTR(image);
|
||||
if (ret_extension_images)
|
||||
*ret_extension_images = TAKE_PTR(extension_images);
|
||||
if (ret_os_release)
|
||||
*ret_os_release = TAKE_PTR(os_release);
|
||||
if (ret_unit_files)
|
||||
*ret_unit_files = TAKE_PTR(unit_files);
|
||||
if (ret_valid_prefixes)
|
||||
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -618,23 +650,29 @@ int portable_extract(
|
||||
char **extension_image_paths,
|
||||
PortableMetadata **ret_os_release,
|
||||
Hashmap **ret_unit_files,
|
||||
char ***ret_valid_prefixes,
|
||||
sd_bus_error *error) {
|
||||
|
||||
_cleanup_(portable_metadata_unrefp) PortableMetadata *os_release = NULL;
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_(strv_freep) char **valid_prefixes = NULL;
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
int r;
|
||||
|
||||
r = extract_image_and_extensions(name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ false,
|
||||
&image,
|
||||
&extension_images,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
error);
|
||||
assert(name_or_path);
|
||||
|
||||
r = extract_image_and_extensions(
|
||||
name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ false,
|
||||
&image,
|
||||
&extension_images,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
ret_valid_prefixes ? &valid_prefixes : NULL,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -651,8 +689,12 @@ int portable_extract(
|
||||
isempty(extensions) ? "" : extensions);
|
||||
}
|
||||
|
||||
*ret_os_release = TAKE_PTR(os_release);
|
||||
*ret_unit_files = TAKE_PTR(unit_files);
|
||||
if (ret_os_release)
|
||||
*ret_os_release = TAKE_PTR(os_release);
|
||||
if (ret_unit_files)
|
||||
*ret_unit_files = TAKE_PTR(unit_files);
|
||||
if (ret_valid_prefixes)
|
||||
*ret_valid_prefixes = TAKE_PTR(valid_prefixes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1211,6 +1253,18 @@ static int install_image_and_extensions_symlinks(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool prefix_matches_compatible(char **matches, char **valid_prefixes) {
|
||||
char **m;
|
||||
|
||||
/* Checks if all 'matches' are included in the list of 'valid_prefixes' */
|
||||
|
||||
STRV_FOREACH(m, matches)
|
||||
if (!strv_contains(valid_prefixes, *m))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int portable_attach(
|
||||
sd_bus *bus,
|
||||
const char *name_or_path,
|
||||
@ -1225,33 +1279,63 @@ int portable_attach(
|
||||
_cleanup_ordered_hashmap_free_ OrderedHashmap *extension_images = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *unit_files = NULL;
|
||||
_cleanup_(lookup_paths_free) LookupPaths paths = {};
|
||||
_cleanup_strv_free_ char **valid_prefixes = NULL;
|
||||
_cleanup_(image_unrefp) Image *image = NULL;
|
||||
PortableMetadata *item;
|
||||
int r;
|
||||
|
||||
r = extract_image_and_extensions(name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ true,
|
||||
&image,
|
||||
&extension_images,
|
||||
/* os_release= */ NULL,
|
||||
&unit_files,
|
||||
error);
|
||||
r = extract_image_and_extensions(
|
||||
name_or_path,
|
||||
matches,
|
||||
extension_image_paths,
|
||||
/* validate_sysext= */ true,
|
||||
&image,
|
||||
&extension_images,
|
||||
/* os_release= */ NULL,
|
||||
&unit_files,
|
||||
&valid_prefixes,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (hashmap_isempty(unit_files)) {
|
||||
_cleanup_free_ char *extensions = strv_join(extension_image_paths, ", ");
|
||||
if (!extensions)
|
||||
if (valid_prefixes && !prefix_matches_compatible(matches, valid_prefixes)) {
|
||||
_cleanup_free_ char *matches_joined = NULL, *extensions_joined = NULL, *valid_prefixes_joined = NULL;
|
||||
|
||||
matches_joined = strv_join(matches, "', '");
|
||||
if (!matches_joined)
|
||||
return -ENOMEM;
|
||||
|
||||
return sd_bus_error_setf(error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Couldn't find any matching unit files in image '%s%s%s', refusing.",
|
||||
image->path,
|
||||
isempty(extensions) ? "" : "' or any of its extensions '",
|
||||
isempty(extensions) ? "" : extensions);
|
||||
extensions_joined = strv_join(extension_image_paths, ", ");
|
||||
if (!extensions_joined)
|
||||
return -ENOMEM;
|
||||
|
||||
valid_prefixes_joined = strv_join(valid_prefixes, ", ");
|
||||
if (!valid_prefixes_joined)
|
||||
return -ENOMEM;
|
||||
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Selected matches '%s' are not compatible with portable service image '%s%s%s', refusing. (Acceptable prefix matches are: %s)",
|
||||
matches_joined,
|
||||
image->path,
|
||||
isempty(extensions_joined) ? "" : "' or any of its extensions '",
|
||||
strempty(extensions_joined),
|
||||
valid_prefixes_joined);
|
||||
}
|
||||
|
||||
if (hashmap_isempty(unit_files)) {
|
||||
_cleanup_free_ char *extensions_joined = strv_join(extension_image_paths, ", ");
|
||||
if (!extensions_joined)
|
||||
return -ENOMEM;
|
||||
|
||||
return sd_bus_error_setf(
|
||||
error,
|
||||
SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Couldn't find any matching unit files in image '%s%s%s', refusing.",
|
||||
image->path,
|
||||
isempty(extensions_joined) ? "" : "' or any of its extensions '",
|
||||
strempty(extensions_joined));
|
||||
}
|
||||
|
||||
r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL);
|
||||
|
@ -65,7 +65,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(PortableMetadata*, portable_metadata_unref);
|
||||
|
||||
int portable_metadata_hashmap_to_sorted_array(Hashmap *unit_files, PortableMetadata ***ret);
|
||||
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, sd_bus_error *error);
|
||||
int portable_extract(const char *image, char **matches, char **extension_image_paths, PortableMetadata **ret_os_release, Hashmap **ret_unit_files, char ***ret_valid_prefixes, sd_bus_error *error);
|
||||
|
||||
int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error);
|
||||
|
@ -162,6 +162,7 @@ int bus_image_common_get_metadata(
|
||||
extension_images,
|
||||
&os_release,
|
||||
&unit_files,
|
||||
NULL,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "hostname-setup.h"
|
||||
#include "id128-util.h"
|
||||
#include "import-util.h"
|
||||
#include "io-util.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "mount-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
@ -748,10 +749,14 @@ int dissect_image(
|
||||
if (r != 0)
|
||||
return errno_or_else(EIO);
|
||||
|
||||
m = new0(DissectedImage, 1);
|
||||
m = new(DissectedImage, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
*m = (DissectedImage) {
|
||||
.has_init_system = -1,
|
||||
};
|
||||
|
||||
r = sd_device_get_sysname(d, &sysname);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get device sysname: %m");
|
||||
@ -3012,6 +3017,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
META_MACHINE_INFO,
|
||||
META_OS_RELEASE,
|
||||
META_EXTENSION_RELEASE,
|
||||
META_HAS_INIT_SYSTEM,
|
||||
_META_MAX,
|
||||
};
|
||||
|
||||
@ -3021,7 +3027,8 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
[META_MACHINE_INFO] = "/etc/machine-info\0",
|
||||
[META_OS_RELEASE] = ("/etc/os-release\0"
|
||||
"/usr/lib/os-release\0"),
|
||||
[META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
|
||||
[META_EXTENSION_RELEASE] = "extension-release\0", /* Used only for logging. */
|
||||
[META_HAS_INIT_SYSTEM] = "has-init-system\0", /* ditto */
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
|
||||
@ -3032,6 +3039,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
_cleanup_free_ char *hostname = NULL;
|
||||
unsigned n_meta_initialized = 0;
|
||||
int fds[2 * _META_MAX], r, v;
|
||||
int has_init_system = -1;
|
||||
ssize_t n;
|
||||
|
||||
BLOCK_SIGNALS(SIGCHLD);
|
||||
@ -3063,6 +3071,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r == 0) {
|
||||
/* Child in a new mount namespace */
|
||||
error_pipe[0] = safe_close(error_pipe[0]);
|
||||
|
||||
r = dissected_image_mount(
|
||||
@ -3092,7 +3101,9 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
|
||||
fds[2*k] = safe_close(fds[2*k]);
|
||||
|
||||
if (k == META_EXTENSION_RELEASE) {
|
||||
switch (k) {
|
||||
|
||||
case META_EXTENSION_RELEASE:
|
||||
/* As per the os-release spec, if the image is an extension it will have a file
|
||||
* named after the image name in extension-release.d/ - we use the image name
|
||||
* and try to resolve it with the extension-release helpers, as sometimes
|
||||
@ -3105,12 +3116,42 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
r = open_extension_release(t, m->image_name, NULL, &fd);
|
||||
if (r < 0)
|
||||
fd = r; /* Propagate the error. */
|
||||
} else
|
||||
break;
|
||||
|
||||
case META_HAS_INIT_SYSTEM: {
|
||||
bool found = false;
|
||||
const char *init;
|
||||
|
||||
FOREACH_STRING(init,
|
||||
"/usr/lib/systemd/systemd", /* systemd on /usr merged system */
|
||||
"/lib/systemd/systemd", /* systemd on /usr non-merged systems */
|
||||
"/sbin/init") { /* traditional path the Linux kernel invokes */
|
||||
|
||||
r = chase_symlinks(init, t, CHASE_PREFIX_ROOT, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to resolve %s, ignoring: %m", init);
|
||||
} else {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = loop_write(fds[2*k+1], &found, sizeof(found), false);
|
||||
if (r < 0)
|
||||
goto inner_fail;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
default:
|
||||
NULSTR_FOREACH(p, paths[k]) {
|
||||
fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (fd >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
@ -3118,15 +3159,17 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
|
||||
if (r < 0) {
|
||||
(void) write(error_pipe[1], &r, sizeof(r));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
if (r < 0)
|
||||
goto inner_fail;
|
||||
|
||||
fds[2*k+1] = safe_close(fds[2*k+1]);
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
|
||||
inner_fail:
|
||||
(void) write(error_pipe[1], &r, sizeof(r));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
error_pipe[1] = safe_close(error_pipe[1]);
|
||||
@ -3194,7 +3237,20 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
log_debug_errno(r, "Failed to read extension release file: %m");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case META_HAS_INIT_SYSTEM: {
|
||||
bool b = false;
|
||||
size_t nr;
|
||||
|
||||
errno = 0;
|
||||
nr = fread(&b, 1, sizeof(b), f);
|
||||
if (nr != sizeof(b))
|
||||
log_debug_errno(errno_or_else(EIO), "Failed to read has-init-system boolean: %m");
|
||||
else
|
||||
has_init_system = b;
|
||||
|
||||
break;
|
||||
}}
|
||||
}
|
||||
|
||||
r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
|
||||
@ -3218,6 +3274,7 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
strv_free_and_replace(m->machine_info, machine_info);
|
||||
strv_free_and_replace(m->os_release, os_release);
|
||||
strv_free_and_replace(m->extension_release, extension_release);
|
||||
m->has_init_system = has_init_system;
|
||||
|
||||
finish:
|
||||
for (unsigned k = 0; k < n_meta_initialized; k++)
|
||||
@ -3468,7 +3525,8 @@ int verity_dissect_and_mount(
|
||||
const MountOptions *options,
|
||||
const char *required_host_os_release_id,
|
||||
const char *required_host_os_release_version_id,
|
||||
const char *required_host_os_release_sysext_level) {
|
||||
const char *required_host_os_release_sysext_level,
|
||||
const char *required_sysext_scope) {
|
||||
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
|
||||
@ -3554,11 +3612,12 @@ int verity_dissect_and_mount(
|
||||
return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
|
||||
|
||||
r = extension_release_validate(
|
||||
dissected_image->image_name,
|
||||
required_host_os_release_id,
|
||||
required_host_os_release_version_id,
|
||||
required_host_os_release_sysext_level,
|
||||
extension_release);
|
||||
dissected_image->image_name,
|
||||
required_host_os_release_id,
|
||||
required_host_os_release_version_id,
|
||||
required_host_os_release_sysext_level,
|
||||
required_sysext_scope,
|
||||
extension_release);
|
||||
if (r == 0)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
|
||||
if (r < 0)
|
||||
|
@ -163,6 +163,7 @@ struct DissectedImage {
|
||||
char **machine_info;
|
||||
char **os_release;
|
||||
char **extension_release;
|
||||
int has_init_system;
|
||||
};
|
||||
|
||||
struct MountOptions {
|
||||
@ -227,4 +228,4 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
|
||||
|
||||
int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image);
|
||||
|
||||
int verity_dissect_and_mount(const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level);
|
||||
int verity_dissect_and_mount(const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope);
|
||||
|
@ -12,6 +12,7 @@ int extension_release_validate(
|
||||
const char *host_os_release_id,
|
||||
const char *host_os_release_version_id,
|
||||
const char *host_os_release_sysext_level,
|
||||
const char *host_sysext_scope,
|
||||
char **extension_release) {
|
||||
|
||||
const char *extension_release_id = NULL, *extension_release_sysext_level = NULL;
|
||||
@ -25,6 +26,28 @@ int extension_release_validate(
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (host_sysext_scope) {
|
||||
_cleanup_strv_free_ char **extension_sysext_scope_list = NULL;
|
||||
const char *extension_sysext_scope;
|
||||
bool valid;
|
||||
|
||||
extension_sysext_scope = strv_env_pairs_get(extension_release, "SYSEXT_SCOPE");
|
||||
if (extension_sysext_scope) {
|
||||
extension_sysext_scope_list = strv_split(extension_sysext_scope, WHITESPACE);
|
||||
if (!extension_sysext_scope_list)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* by default extension are good for attachment in portable service and on the system */
|
||||
valid = strv_contains(
|
||||
extension_sysext_scope_list ?: STRV_MAKE("system", "portable"),
|
||||
host_sysext_scope);
|
||||
if (!valid) {
|
||||
log_debug("Extension '%s' is not suitable for scope %s, ignoring extension.", name, host_sysext_scope);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extension_release_id = strv_env_pairs_get(extension_release, "ID");
|
||||
if (isempty(extension_release_id)) {
|
||||
log_debug("Extension '%s' does not contain ID in extension-release but requested to match '%s'",
|
||||
|
@ -9,6 +9,7 @@ int extension_release_validate(
|
||||
const char *host_os_release_id,
|
||||
const char *host_os_release_version_id,
|
||||
const char *host_os_release_sysext_level,
|
||||
const char *host_sysext_scope,
|
||||
char **extension_release);
|
||||
|
||||
/* Parse SYSTEMD_SYSEXT_HIERARCHIES and if not set, return "/usr /opt" */
|
||||
|
@ -874,7 +874,7 @@ static int mount_in_namespace(
|
||||
mount_tmp_created = true;
|
||||
|
||||
if (is_image)
|
||||
r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL);
|
||||
r = verity_dissect_and_mount(FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, options, NULL, NULL, NULL, NULL);
|
||||
else
|
||||
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
|
||||
if (r < 0)
|
||||
|
@ -1,6 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "glyph-util.h"
|
||||
#include "terminal-util.h"
|
||||
|
||||
void print_separator(void);
|
||||
|
||||
int file_url_from_path(const char *path, char **ret);
|
||||
@ -17,3 +20,23 @@ typedef enum CatFlags {
|
||||
|
||||
int cat_files(const char *file, char **dropins, CatFlags flags);
|
||||
int conf_files_cat(const char *root, const char *name);
|
||||
|
||||
#define RED_CROSS_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_RED) + STRLEN("✗") + STRLEN(ANSI_NORMAL) + 1)
|
||||
#define GREEN_CHECK_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_GREEN) + STRLEN("✓") + STRLEN(ANSI_NORMAL) + 1)
|
||||
|
||||
static inline const char *red_cross_mark_internal(char buffer[static RED_CROSS_MARK_MAX]) {
|
||||
assert(buffer);
|
||||
assert_se(stpcpy(stpcpy(stpcpy(buffer, ansi_highlight_red()), special_glyph(SPECIAL_GLYPH_CROSS_MARK)), ansi_normal()) < buffer + RED_CROSS_MARK_MAX);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static inline const char *green_check_mark_internal(char buffer[static GREEN_CHECK_MARK_MAX]) {
|
||||
assert(buffer);
|
||||
assert_se(stpcpy(stpcpy(stpcpy(buffer, ansi_highlight_green()), special_glyph(SPECIAL_GLYPH_CHECK_MARK)), ansi_normal()) < buffer + GREEN_CHECK_MARK_MAX);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#define RED_CROSS_MARK() red_cross_mark_internal((char[RED_CROSS_MARK_MAX]) {})
|
||||
#define GREEN_CHECK_MARK() green_check_mark_internal((char[GREEN_CHECK_MARK_MAX]) {})
|
||||
|
||||
#define COLOR_MARK_BOOL(b) ((b) ? GREEN_CHECK_MARK() : RED_CROSS_MARK())
|
||||
|
@ -432,12 +432,17 @@ static int validate_version(
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Extension image contains /usr/lib/os-release file, which is not allowed (it may carry /etc/os-release), refusing.");
|
||||
|
||||
return extension_release_validate(
|
||||
r = extension_release_validate(
|
||||
img->name,
|
||||
host_os_release_id,
|
||||
host_os_release_version_id,
|
||||
host_os_release_sysext_level,
|
||||
in_initrd() ? "initrd" : "system",
|
||||
img->extension_release);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to validate extension release information: %m");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "glyph-util.h"
|
||||
#include "gpt.h"
|
||||
#include "log.h"
|
||||
#include "pretty-print.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
#include "tests.h"
|
||||
@ -32,11 +33,11 @@ static void test_gpt_types_against_architectures(void) {
|
||||
|
||||
r = gpt_partition_type_uuid_from_string(joined, &id);
|
||||
if (r < 0) {
|
||||
printf("%s%s%s %s\n", ansi_highlight_red(), special_glyph(SPECIAL_GLYPH_CROSS_MARK), ansi_normal(), joined);
|
||||
printf("%s %s\n", RED_CROSS_MARK(), joined);
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s%s%s %s\n", ansi_highlight_green(), special_glyph(SPECIAL_GLYPH_CHECK_MARK), ansi_normal(), joined);
|
||||
printf("%s %s\n", GREEN_CHECK_MARK(), joined);
|
||||
|
||||
if (streq(prefix, "root-") && streq(suffix, ""))
|
||||
assert_se(gpt_partition_type_is_root(id));
|
||||
|
@ -31,11 +31,25 @@ static void test_cat_files(void) {
|
||||
assert_se(cat_files("/etc/fstab", STRV_MAKE("/etc/fstab", "/etc/fstab"), 0) == 0);
|
||||
}
|
||||
|
||||
static void test_red_green_cross_check_mark(void) {
|
||||
bool b = false;
|
||||
|
||||
printf("yeah: <%s>\n", GREEN_CHECK_MARK());
|
||||
printf("nay: <%s>\n", RED_CROSS_MARK());
|
||||
|
||||
printf("%s → %s → %s → %s\n",
|
||||
COLOR_MARK_BOOL(b),
|
||||
COLOR_MARK_BOOL(!b),
|
||||
COLOR_MARK_BOOL(!!b),
|
||||
COLOR_MARK_BOOL(!!!b));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_INFO);
|
||||
|
||||
test_terminal_urlify();
|
||||
test_cat_files();
|
||||
test_red_green_cross_check_mark();
|
||||
|
||||
print_separator();
|
||||
|
||||
|
@ -594,6 +594,7 @@ install_verity_minimal() {
|
||||
touch "$initdir/etc/machine-id" "$initdir/etc/resolv.conf"
|
||||
touch "$initdir/opt/some_file"
|
||||
echo MARKER=1 >>"$initdir/usr/lib/os-release"
|
||||
echo "PORTABLE_PREFIXES=app0 minimal" >>"$initdir/usr/lib/os-release"
|
||||
echo -e "[Service]\nExecStartPre=cat /usr/lib/os-release\nExecStart=sleep 120" >"$initdir/usr/lib/systemd/system/app0.service"
|
||||
cp "$initdir/usr/lib/systemd/system/app0.service" "$initdir/usr/lib/systemd/system/app0-foo.service"
|
||||
|
||||
@ -638,7 +639,9 @@ EOF
|
||||
export initdir="$TESTDIR/app1"
|
||||
mkdir -p "$initdir/usr/lib/extension-release.d" "$initdir/usr/lib/systemd/system" "$initdir/opt"
|
||||
grep "^ID=" "$os_release" >"$initdir/usr/lib/extension-release.d/extension-release.app2"
|
||||
echo "${version_id}" >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
|
||||
( echo "${version_id}"
|
||||
echo "SYSEXT_SCOPE=portable"
|
||||
echo "PORTABLE_PREFIXES=app1" ) >>"$initdir/usr/lib/extension-release.d/extension-release.app2"
|
||||
setfattr -n user.extension-release.strict -v false "$initdir/usr/lib/extension-release.d/extension-release.app2"
|
||||
cat >"$initdir/usr/lib/systemd/system/app1.service" <<EOF
|
||||
[Service]
|
||||
|
@ -12,6 +12,11 @@ if [[ -v ASAN_OPTIONS || -v UBSAN_OPTIONS ]]; then
|
||||
ARGS+=(--profile=trusted)
|
||||
fi
|
||||
|
||||
systemd-dissect --no-pager /usr/share/minimal_0.raw | grep -q '✓ portable service'
|
||||
systemd-dissect --no-pager /usr/share/minimal_1.raw | grep -q '✓ portable service'
|
||||
systemd-dissect --no-pager /usr/share/app0.raw | grep -q '✓ extension for portable service'
|
||||
systemd-dissect --no-pager /usr/share/app1.raw | grep -q '✓ extension for portable service'
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
mkdir -p /run/systemd/system/systemd-portabled.service.d/
|
||||
cat <<EOF >/run/systemd/system/systemd-portabled.service.d/override.conf
|
||||
@ -98,10 +103,19 @@ portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/mi
|
||||
|
||||
# portablectl also works with directory paths rather than images
|
||||
|
||||
mkdir /tmp/rootdir /tmp/app1 /tmp/overlay
|
||||
mkdir /tmp/rootdir /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc
|
||||
mount /usr/share/app1.raw /tmp/app1
|
||||
mount /usr/share/minimal_0.raw /tmp/rootdir
|
||||
mount -t overlay overlay -o lowerdir=/tmp/app1:/tmp/rootdir /tmp/overlay
|
||||
|
||||
# Fix up os-release to drop the valid PORTABLE_SERVICES field (because we are
|
||||
# bypassing the sysext logic in portabled here it will otherwise not see the
|
||||
# extensions additional valid prefix)
|
||||
grep -v "^PORTABLE_PREFIXES=" /tmp/rootdir/etc/os-release > /tmp/os-release-fix/etc/os-release
|
||||
|
||||
mount -t overlay overlay -o lowerdir=/tmp/os-release-fix:/tmp/app1:/tmp/rootdir /tmp/overlay
|
||||
|
||||
grep . /tmp/overlay/usr/lib/extension-release.d/*
|
||||
grep . /tmp/overlay/etc/os-release
|
||||
|
||||
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/overlay app1
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user