dir, system-helper: Add support for installing extra-data from OCI

Extra-data usually is downloaded on the user side into an ostree repo.
For system installs, a temporary ostree repo is used on the user side
and then imported on the system side. This doesn't work for OCI images
because importing the image into an ostree repo makes it impossible for
the system side to verify the data.

So instead, the OCI image is first mirrored into a local OCI repo and
then gets imported on the system side, which can verify the image from
the index by the digest.

Closes: https://github.com/flatpak/flatpak/issues/3790
This commit is contained in:
Sebastian Wick
2025-08-29 20:51:44 +02:00
parent ac0b61a0f1
commit d4751443f5
3 changed files with 199 additions and 1 deletions

View File

@@ -1038,5 +1038,10 @@ char ** flatpak_dir_list_unused_refs (Fla
FlatpakDirFilterFlags filter_flags,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_pull_oci_extra_data (OstreeRepo *repo,
FlatpakImageSource *image_source,
const char *rev,
GCancellable *cancellable,
GError **error);
#endif /* __FLATPAK_DIR_H__ */

View File

@@ -6602,6 +6602,166 @@ flatpak_dir_pull_ostree_extra_data (FlatpakDir *self,
return TRUE;
}
/* Extra-data usually is downloaded on the user side into an ostree repo.
* For system installs, a temporary ostree repo is used on the user side
* and then imported on the system side. This doesn't work for OCI images
* because importing the image into an ostree repo makes it impossible for
* the system side to verify the data.
*
* So instead, the OCI image is first mirrored into a local OCI repo and
* then gets imported on the system side, which can verify the image from
* the index by the digest.
*/
static gboolean
flatpak_dir_mirror_oci_extra_data (FlatpakDir *self,
FlatpakOciRegistry *dst_registry,
FlatpakImageSource *image_source,
FlatpakProgress *progress,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariantBuilder) metadata_builder =
g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
g_autoptr(GVariant) commit_metadata = NULL;
g_autoptr(GVariant) extra_data_sources = NULL;
g_autoptr(GPtrArray) extra_data = NULL;
flatpak_image_source_build_commit_metadata (image_source, metadata_builder);
commit_metadata = g_variant_ref_sink (g_variant_builder_end (metadata_builder));
extra_data_sources = g_variant_lookup_value (commit_metadata,
"xa.extra-data-sources",
G_VARIANT_TYPE ("a(ayttays)"));
if (extra_data_sources == NULL)
return TRUE;
{
guint64 n_extra_data = 0;
guint64 total_download_size = 0;
compute_extra_data_download_size (extra_data_sources,
&n_extra_data,
&total_download_size);
flatpak_progress_init_extra_data (progress,
n_extra_data,
total_download_size);
}
extra_data = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
if (!flatpak_dir_pull_extra_data (self,
extra_data_sources,
progress,
extra_data,
NULL,
cancellable,
error))
return FALSE;
for (size_t i = 0; i < extra_data->len; i++)
{
GBytes *bytes = g_ptr_array_index (extra_data, i);
g_autofree char *rev = NULL;
rev = flatpak_oci_registry_store_blob (dst_registry,
bytes,
cancellable,
error);
if (rev == NULL)
return FALSE;
}
return TRUE;
}
gboolean
flatpak_dir_pull_oci_extra_data (OstreeRepo *repo,
FlatpakImageSource *image_source,
const char *rev,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariant) extra_data_sources = NULL;
g_autoptr(GVariant) detached_metadata = NULL;
g_autoptr(GVariantBuilder) extra_data_builder = NULL;
g_auto(GVariantDict) new_metadata_dict = FLATPAK_VARIANT_DICT_INITIALIZER;
g_autoptr(GVariant) new_detached_metadata = NULL;
size_t n_extra_data;
FlatpakOciRegistry *registry = flatpak_image_source_get_registry (image_source);
const char *repository = flatpak_image_source_get_oci_repository (image_source);
extra_data_sources = flatpak_repo_get_extra_data_sources (repo, rev,
cancellable, NULL);
if (extra_data_sources == NULL)
return TRUE;
n_extra_data = g_variant_n_children (extra_data_sources);
if (n_extra_data == 0)
return TRUE;
extra_data_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ayay)"));
for (size_t i = 0; i < n_extra_data; i++)
{
const char *name = NULL;
const char *uri = NULL;
const guchar *expected_sha256_bytes;
g_autofree char *expected_sha256 = NULL;
g_autofree char *digest = NULL;
g_autoptr(GBytes) bytes = NULL;
flatpak_repo_parse_extra_data_sources (extra_data_sources,
i,
&name,
NULL,
NULL,
&expected_sha256_bytes,
&uri);
if (expected_sha256_bytes == NULL)
{
return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
_("Invalid checksum for extra data uri %s"),
uri);
}
expected_sha256 = ostree_checksum_from_bytes (expected_sha256_bytes);
digest = g_strdup_printf ("sha256:%s", expected_sha256);
bytes = flatpak_oci_registry_load_blob (registry,
repository,
FALSE,
digest,
NULL,
NULL,
cancellable,
error);
if (!bytes)
return FALSE;
g_variant_builder_add (extra_data_builder,
"(^ay@ay)",
name,
g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"),
bytes, TRUE));
}
if (!ostree_repo_read_commit_detached_metadata (repo, rev, &detached_metadata,
cancellable, error))
return FALSE;
g_variant_dict_init (&new_metadata_dict, detached_metadata);
g_variant_dict_insert_value (&new_metadata_dict, "xa.extra-data",
g_variant_builder_end (extra_data_builder));
new_detached_metadata = g_variant_ref_sink (g_variant_dict_end (&new_metadata_dict));
if (!ostree_repo_write_commit_detached_metadata (repo, rev,
new_detached_metadata,
cancellable, error))
return FALSE;
return TRUE;
}
static void
oci_pull_progress_cb (guint64 total_size, guint64 pulled_size,
guint32 n_layers, guint32 pulled_layers,
@@ -6645,10 +6805,17 @@ flatpak_dir_mirror_oci (FlatpakDir *self,
res = flatpak_mirror_image_from_oci (dst_registry, image_source, state->remote_name, ref, self->repo, oci_pull_progress_cb,
progress, cancellable, error);
if (!res)
return FALSE;
if (!flatpak_dir_mirror_oci_extra_data (self,
dst_registry,
image_source,
progress,
cancellable,
error))
return FALSE;
return TRUE;
}
@@ -6708,6 +6875,23 @@ flatpak_dir_pull_oci (FlatpakDir *self,
g_info ("Imported OCI image as checksum %s", checksum);
if (!flatpak_dir_setup_extra_data (self, state, repo,
ref, checksum, token,
flatpak_flags,
progress,
cancellable,
error))
return FALSE;
if (!flatpak_dir_pull_ostree_extra_data (self, repo,
state->remote_name,
ref, checksum,
flatpak_flags,
progress,
cancellable,
error))
return FALSE;
if (repo == self->repo)
name = flatpak_dir_get_name (self);
else

View File

@@ -558,6 +558,15 @@ handle_deploy (FlatpakSystemHelper *object,
"Can't pull ref %s from child OCI registry index: %s", arg_ref, error->message);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!flatpak_dir_pull_oci_extra_data (flatpak_dir_get_repo (system), image_source,
checksum, NULL, &error))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
"Can't pull extra data for ref %s from child OCI registry index: %s",
arg_ref, error->message);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
}
else if (strlen (arg_repo_path) > 0)
{