<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>
</variablelist>
</refsect2>
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)
if (r < 0)
return r;
- r = extension_release_validate(ext->path, id, version_id, sysext_level, extension_release);
+ 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)
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;
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)
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);
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;
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'",
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" */
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)
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) {