forked from Mirrors/flatpak-builder
Merge branch 'wip/oci'
commit
f65f0cb37c
|
@ -7,8 +7,6 @@ flatpak_SOURCES = \
|
|||
app/flatpak-builtins.h \
|
||||
app/flatpak-builtins-utils.h \
|
||||
app/flatpak-builtins-utils.c \
|
||||
app/flatpak-oci.h \
|
||||
app/flatpak-oci.c \
|
||||
app/flatpak-transaction.h \
|
||||
app/flatpak-transaction.c \
|
||||
app/flatpak-builtins-add-remote.c \
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#include "flatpak-builtins.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "flatpak-oci.h"
|
||||
#include "flatpak-oci-registry.h"
|
||||
#include "flatpak-chain-input-stream.h"
|
||||
#include "flatpak-builtins-utils.h"
|
||||
|
||||
|
@ -268,102 +268,6 @@ timestamp_to_iso8601 (guint64 timestamp)
|
|||
return g_time_val_to_iso8601 (&stamp);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
generate_config_json (guint64 timestamp,
|
||||
const char *layer_sha256,
|
||||
const char *arch)
|
||||
{
|
||||
g_autoptr(FlatpakJsonWriter) writer = flatpak_json_writer_new ();
|
||||
g_autofree char *created = timestamp_to_iso8601 (timestamp);
|
||||
g_autofree char *layer_digest = g_strdup_printf ("sha256:%s", layer_sha256);
|
||||
|
||||
flatpak_json_writer_add_string_property (writer, "created", created);
|
||||
flatpak_json_writer_add_string_property (writer, "architecture", flatpak_arch_to_oci_arch (arch));
|
||||
flatpak_json_writer_add_string_property (writer, "os", "linux");
|
||||
|
||||
flatpak_json_writer_add_struct_property (writer, "rootfs");
|
||||
{
|
||||
flatpak_json_writer_add_array_property (writer, "diff_ids");
|
||||
{
|
||||
flatpak_json_writer_add_array_string (writer, layer_digest);
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
flatpak_json_writer_add_string_property (writer, "type", "layers");
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
|
||||
return flatpak_json_writer_get_result (writer);
|
||||
}
|
||||
|
||||
static GBytes *
|
||||
generate_manifest_json (guint64 config_size,
|
||||
const char *config_sha256,
|
||||
guint64 layer_size,
|
||||
const char *layer_sha256,
|
||||
const char *ref,
|
||||
const char *checksum,
|
||||
GVariant *commit)
|
||||
{
|
||||
g_autoptr(FlatpakJsonWriter) writer = flatpak_json_writer_new ();
|
||||
g_autofree char *config_digest = g_strdup_printf ("sha256:%s", config_sha256);
|
||||
g_autofree char *layer_digest = g_strdup_printf ("sha256:%s", layer_sha256);
|
||||
|
||||
flatpak_json_writer_add_uint64_property (writer, "schemaVersion", 2);
|
||||
flatpak_json_writer_add_string_property (writer, "mediaType", "application/vnd.oci.image.manifest.v1+json");
|
||||
flatpak_json_writer_add_struct_property (writer, "config");
|
||||
{
|
||||
flatpak_json_writer_add_string_property (writer, "mediaType", "application/vnd.oci.image.config.v1+json");
|
||||
flatpak_json_writer_add_uint64_property (writer, "size", config_size);
|
||||
flatpak_json_writer_add_string_property (writer, "digest", config_digest);
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
|
||||
flatpak_json_writer_add_array_property (writer, "layers");
|
||||
{
|
||||
flatpak_json_writer_add_array_struct (writer);
|
||||
{
|
||||
flatpak_json_writer_add_string_property (writer, "mediaType", "application/vnd.oci.image.layer.v1.tar+gzip");
|
||||
flatpak_json_writer_add_uint64_property (writer, "size", layer_size);
|
||||
flatpak_json_writer_add_string_property (writer, "digest", layer_digest);
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
|
||||
flatpak_json_writer_add_struct_property (writer, "annotations");
|
||||
{
|
||||
g_autofree char *parent = NULL;
|
||||
g_autofree char *subject = NULL;
|
||||
g_autofree char *body = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autofree char *metadata_base64 = NULL;
|
||||
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.Ref", ref);
|
||||
|
||||
parent = ostree_commit_get_parent (commit);
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.ParentCommit", parent);
|
||||
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.Commit", checksum);
|
||||
|
||||
metadata = g_variant_get_child_value (commit, 0);
|
||||
if (g_variant_get_size (metadata) > 0)
|
||||
{
|
||||
metadata_base64 = g_base64_encode (g_variant_get_data (metadata), g_variant_get_size (metadata));
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.Metadata", metadata_base64);
|
||||
}
|
||||
|
||||
g_variant_get_child (commit, 3, "s", &subject);
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.Subject", subject);
|
||||
|
||||
g_variant_get_child (commit, 4, "s", &body);
|
||||
flatpak_json_writer_add_string_property (writer, "org.flatpak.Body", body);
|
||||
|
||||
flatpak_json_writer_close (writer);
|
||||
}
|
||||
|
||||
return flatpak_json_writer_get_result (writer);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
export_commit_to_archive (OstreeRepo *repo,
|
||||
GFile *root,
|
||||
|
@ -388,24 +292,25 @@ build_oci (OstreeRepo *repo, GFile *dir,
|
|||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autoptr(GFile) root = NULL;
|
||||
g_autoptr(GFile) refs_tag = NULL;
|
||||
g_autoptr(GFile) refs = NULL;
|
||||
g_autoptr(GVariant) commit_data = NULL;
|
||||
g_autoptr(GVariant) commit_metadata = NULL;
|
||||
g_autofree char *commit_checksum = NULL;
|
||||
g_auto(GStrv) ref_parts = g_strsplit (ref, "/", -1);
|
||||
glnx_fd_close int dfd = -1;
|
||||
g_autofree char *layer_compressed_sha256 = NULL;
|
||||
g_autofree char *layer_uncompressed_sha256 = NULL;
|
||||
guint64 layer_compressed_size;
|
||||
guint64 layer_uncompressed_size;
|
||||
g_autoptr(GBytes) config = NULL;
|
||||
g_autofree char *config_sha256 = NULL;
|
||||
g_autoptr(GBytes) manifest = NULL;
|
||||
g_autoptr(FlatpakOciDir) oci_dir = NULL;
|
||||
g_autofree char *dir_uri = NULL;
|
||||
g_autoptr(FlatpakOciRegistry) registry = NULL;
|
||||
g_autoptr(FlatpakOciLayerWriter) layer_writer = NULL;
|
||||
g_autofree char *manifest_sha256 = NULL;
|
||||
struct archive *archive;
|
||||
g_autofree char *uncompressed_digest = NULL;
|
||||
g_autofree char *timestamp = NULL;
|
||||
g_autoptr(FlatpakOciImage) image = NULL;
|
||||
g_autoptr(FlatpakOciRef) layer_ref = NULL;
|
||||
g_autoptr(FlatpakOciRef) image_ref = NULL;
|
||||
g_autoptr(FlatpakOciRef) manifest_ref = NULL;
|
||||
g_autoptr(FlatpakOciManifest) manifest = NULL;
|
||||
g_autoptr(GFile) metadata_file = NULL;
|
||||
guint64 installed_size = 0;
|
||||
GHashTable *annotations;
|
||||
gsize metadata_size;
|
||||
g_autofree char *metadata_contents = NULL;
|
||||
|
||||
if (!ostree_repo_resolve_rev (repo, ref, FALSE, &commit_checksum, error))
|
||||
return FALSE;
|
||||
|
@ -419,50 +324,69 @@ build_oci (OstreeRepo *repo, GFile *dir,
|
|||
if (!ostree_repo_read_commit_detached_metadata (repo, commit_checksum, &commit_metadata, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
oci_dir = flatpak_oci_dir_new ();
|
||||
|
||||
if (!flatpak_oci_dir_ensure (oci_dir, dir, cancellable, error))
|
||||
dir_uri = g_file_get_uri (dir);
|
||||
registry = flatpak_oci_registry_new (dir_uri, TRUE, -1, cancellable, error);
|
||||
if (registry == NULL)
|
||||
return FALSE;
|
||||
|
||||
layer_writer = flatpak_oci_layer_writer_new (oci_dir);
|
||||
|
||||
archive = flatpak_oci_layer_writer_open (layer_writer, cancellable, error);
|
||||
if (archive == NULL)
|
||||
layer_writer = flatpak_oci_registry_write_layer (registry, cancellable, error);
|
||||
if (layer_writer == NULL)
|
||||
return FALSE;
|
||||
|
||||
archive = flatpak_oci_layer_writer_get_archive (layer_writer);
|
||||
|
||||
if (!export_commit_to_archive (repo, root, ostree_commit_get_timestamp (commit_data),
|
||||
archive, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!flatpak_oci_layer_writer_close (layer_writer,
|
||||
&layer_uncompressed_sha256,
|
||||
&layer_uncompressed_size,
|
||||
&layer_compressed_sha256,
|
||||
&layer_compressed_size,
|
||||
cancellable, error))
|
||||
&uncompressed_digest,
|
||||
&layer_ref,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
config = generate_config_json (ostree_commit_get_timestamp (commit_data),
|
||||
layer_uncompressed_sha256,
|
||||
ref_parts[2]);
|
||||
config_sha256 = flatpak_oci_dir_write_blob (oci_dir, config, cancellable, error);
|
||||
if (config_sha256 == NULL)
|
||||
|
||||
image = flatpak_oci_image_new ();
|
||||
flatpak_oci_image_set_layer (image, uncompressed_digest);
|
||||
|
||||
timestamp = timestamp_to_iso8601 (ostree_commit_get_timestamp (commit_data));
|
||||
flatpak_oci_image_set_created (image, timestamp);
|
||||
|
||||
image_ref = flatpak_oci_registry_store_json (registry, FLATPAK_JSON (image), cancellable, error);
|
||||
if (image_ref == NULL)
|
||||
return FALSE;
|
||||
|
||||
manifest = generate_manifest_json (g_bytes_get_size (config), config_sha256,
|
||||
layer_compressed_size, layer_compressed_sha256,
|
||||
ref, commit_checksum, commit_data);
|
||||
manifest_sha256 = flatpak_oci_dir_write_blob (oci_dir, manifest, cancellable, error);
|
||||
if (manifest_sha256 == NULL)
|
||||
manifest = flatpak_oci_manifest_new ();
|
||||
flatpak_oci_manifest_set_config (manifest, image_ref);
|
||||
flatpak_oci_manifest_set_layer (manifest, layer_ref);
|
||||
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
flatpak_oci_add_annotations_for_commit (annotations, ref, commit_checksum, commit_data);
|
||||
|
||||
metadata_file = g_file_get_child (root, "metadata");
|
||||
if (g_file_load_contents (metadata_file, cancellable, &metadata_contents, &metadata_size, NULL, NULL) &&
|
||||
g_utf8_validate (metadata_contents, -1, NULL))
|
||||
{
|
||||
g_hash_table_replace (annotations,
|
||||
g_strdup ("org.flatpak.Metadata"),
|
||||
g_steal_pointer (&metadata_contents));
|
||||
}
|
||||
|
||||
if (!flatpak_repo_collect_sizes (repo, root, &installed_size, NULL, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!flatpak_oci_dir_set_ref (oci_dir, "latest",
|
||||
g_bytes_get_size (manifest), manifest_sha256,
|
||||
cancellable, error))
|
||||
g_hash_table_replace (annotations,
|
||||
g_strdup ("org.flatpak.InstalledSize"),
|
||||
g_strdup_printf ("%" G_GUINT64_FORMAT, installed_size));
|
||||
|
||||
manifest_ref = flatpak_oci_registry_store_json (registry, FLATPAK_JSON (manifest), cancellable, error);
|
||||
if (manifest_ref == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_print ("WARNING: the oci format produced by flatpak is experimental and unstable.\n"
|
||||
"Don't use this for anything but experiments for now\n");
|
||||
if (!flatpak_oci_registry_set_ref (registry, "latest", manifest_ref,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
#include "flatpak-builtins.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "flatpak-oci.h"
|
||||
#include "flatpak-oci-registry.h"
|
||||
|
||||
static char *opt_ref;
|
||||
static gboolean opt_oci = FALSE;
|
||||
|
@ -50,138 +50,46 @@ static GOptionEntry options[] = {
|
|||
{ NULL }
|
||||
};
|
||||
|
||||
GLNX_DEFINE_CLEANUP_FUNCTION (void *, flatpak_local_free_read_archive, archive_read_free)
|
||||
#define free_read_archive __attribute__((cleanup (flatpak_local_free_read_archive)))
|
||||
|
||||
static void
|
||||
propagate_libarchive_error (GError **error,
|
||||
struct archive *a)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"%s", archive_error_string (a));
|
||||
}
|
||||
|
||||
static char *
|
||||
import_oci (OstreeRepo *repo, GFile *file,
|
||||
GCancellable *cancellable, GError **error)
|
||||
{
|
||||
g_autoptr(OstreeMutableTree) archive_mtree = NULL;
|
||||
g_autoptr(GFile) archive_root = NULL;
|
||||
g_autofree char *commit_checksum = NULL;
|
||||
g_autofree char *config_digest = NULL;
|
||||
const char *parent = NULL;
|
||||
const char *metadata_base64;
|
||||
const char *subject;
|
||||
const char *body;
|
||||
const char *target_ref;
|
||||
guint64 timestamp;
|
||||
g_autoptr(FlatpakOciDir) oci_dir = NULL;
|
||||
g_autoptr(JsonObject) manifest = NULL;
|
||||
g_autoptr(JsonObject) config = NULL;
|
||||
g_autoptr(GHashTable) annotations = NULL;
|
||||
g_autoptr(GVariant) metadatav = NULL;
|
||||
g_auto(GStrv) layers = NULL;
|
||||
int i;
|
||||
g_autofree char *dir_uri = NULL;
|
||||
g_autofree char *target_ref = NULL;
|
||||
g_autofree char *oci_digest = NULL;
|
||||
g_autoptr(FlatpakOciRegistry) registry = NULL;
|
||||
g_autoptr(FlatpakOciManifest) manifest = NULL;
|
||||
GHashTable *annotations;
|
||||
|
||||
oci_dir = flatpak_oci_dir_new ();
|
||||
|
||||
if (!flatpak_oci_dir_open (oci_dir, file, cancellable, error))
|
||||
dir_uri = g_file_get_uri (file);
|
||||
registry = flatpak_oci_registry_new (dir_uri, FALSE, -1, cancellable, error);
|
||||
if (registry == NULL)
|
||||
return NULL;
|
||||
|
||||
manifest = flatpak_oci_dir_find_manifest (oci_dir, "latest", "linux", "amd64",
|
||||
cancellable, error);
|
||||
manifest = flatpak_oci_registry_chose_image (registry, "latest", &oci_digest,
|
||||
cancellable, error);
|
||||
if (manifest == NULL)
|
||||
return NULL;
|
||||
|
||||
if (opt_ref)
|
||||
target_ref = g_strdup (opt_ref);
|
||||
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
if (annotations)
|
||||
flatpak_oci_parse_commit_annotations (annotations, NULL, NULL, NULL,
|
||||
&target_ref, NULL, NULL, NULL);
|
||||
|
||||
if (opt_ref != NULL)
|
||||
target_ref = opt_ref;
|
||||
else
|
||||
if (target_ref == NULL)
|
||||
{
|
||||
target_ref = g_hash_table_lookup (annotations, "org.flatpak.Ref");
|
||||
if (target_ref == NULL)
|
||||
{
|
||||
flatpak_fail (error, "No flatpak ref specified in image, must manually specify");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
subject = g_hash_table_lookup (annotations, "org.flatpak.Subject");
|
||||
body = g_hash_table_lookup (annotations, "org.flatpak.Body");
|
||||
parent = g_hash_table_lookup (annotations, "org.flatpak.ParentCommit");
|
||||
metadata_base64 = g_hash_table_lookup (annotations, "org.flatpak.Metadata");
|
||||
if (metadata_base64)
|
||||
{
|
||||
gsize data_len;
|
||||
guchar *data = g_base64_decode (metadata_base64, &data_len);
|
||||
metadatav = g_variant_new_from_data (G_VARIANT_TYPE("a{sv}"), data, data_len,
|
||||
FALSE, g_free, data);
|
||||
}
|
||||
|
||||
config_digest = flatpak_oci_manifest_get_config (manifest);
|
||||
if (config_digest == NULL)
|
||||
{
|
||||
flatpak_fail (error, "No oci config specified");
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
||||
"The OCI image didn't specify a ref, use --ref to specify one");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config = flatpak_oci_dir_load_json (oci_dir, config_digest, cancellable, error);
|
||||
if (config == NULL)
|
||||
return NULL;
|
||||
|
||||
timestamp = flatpak_oci_config_get_created (config);
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
/* There is no way to write a subset of the archive to a mtree, so instead
|
||||
we write all of it and then build a new mtree with the subset */
|
||||
archive_mtree = ostree_mutable_tree_new ();
|
||||
|
||||
layers = flatpak_oci_manifest_get_layers (manifest);
|
||||
for (i = 0; layers[i] != NULL; i++)
|
||||
{
|
||||
OstreeRepoImportArchiveOptions opts = { 0, };
|
||||
free_read_archive struct archive *a = NULL;
|
||||
|
||||
opts.autocreate_parents = TRUE;
|
||||
|
||||
a = flatpak_oci_dir_load_layer (oci_dir, layers[i],
|
||||
cancellable, error);
|
||||
if (a == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!ostree_repo_import_archive_to_mtree (repo, &opts, a, archive_mtree, NULL, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
if (archive_read_close (a) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, a);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ostree_repo_write_mtree (repo, archive_mtree, &archive_root, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile *) archive_root, error))
|
||||
return NULL;
|
||||
|
||||
if (!ostree_repo_write_commit_with_time (repo,
|
||||
parent,
|
||||
subject,
|
||||
body,
|
||||
metadatav,
|
||||
OSTREE_REPO_FILE (archive_root),
|
||||
timestamp,
|
||||
&commit_checksum,
|
||||
cancellable, error))
|
||||
return NULL;
|
||||
|
||||
ostree_repo_transaction_set_ref (repo, NULL, target_ref, commit_checksum);
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
commit_checksum = flatpak_pull_from_oci (repo, registry, oci_digest, manifest,
|
||||
target_ref, cancellable, error);
|
||||
if (commit_checksum == NULL)
|
||||
return NULL;
|
||||
|
||||
g_print ("Importing %s (%s)\n", target_ref, commit_checksum);
|
||||
|
|
|
@ -49,6 +49,7 @@ static gboolean opt_runtime;
|
|||
static gboolean opt_app;
|
||||
static gboolean opt_bundle;
|
||||
static gboolean opt_from;
|
||||
static gboolean opt_oci;
|
||||
|
||||
static GOptionEntry options[] = {
|
||||
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, N_("Arch to install for"), N_("ARCH") },
|
||||
|
@ -60,6 +61,7 @@ static GOptionEntry options[] = {
|
|||
{ "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, N_("Look for app with the specified name"), NULL },
|
||||
{ "bundle", 0, 0, G_OPTION_ARG_NONE, &opt_bundle, N_("Install from local bundle file"), NULL },
|
||||
{ "from", 0, 0, G_OPTION_ARG_NONE, &opt_from, N_("Load options from file or uri"), NULL },
|
||||
{ "oci", 0, 0, G_OPTION_ARG_NONE, &opt_oci, N_("Install from oci image"), NULL },
|
||||
{ "gpg-file", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, N_("Check bundle signatures with GPG key from FILE (- for stdin)"), N_("FILE") },
|
||||
{ "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, N_("Only install this subpath"), N_("PATH") },
|
||||
{ NULL }
|
||||
|
@ -310,6 +312,48 @@ install_from (FlatpakDir *dir,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
install_oci (FlatpakDir *dir,
|
||||
GOptionContext *context,
|
||||
int argc, char **argv,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
const char *registry_arg;
|
||||
g_autoptr(GFile) registry_file = NULL;
|
||||
g_autofree char *registry_uri = NULL;
|
||||
g_autoptr(FlatpakTransaction) transaction = NULL;
|
||||
char *default_tags[] = { "latest", NULL };
|
||||
char **tags;
|
||||
int i;
|
||||
|
||||
if (argc < 2)
|
||||
return usage_error (context, _("OCI repo Filename or uri must be specified"), error);
|
||||
|
||||
registry_arg = argv[1];
|
||||
if (argc > 2)
|
||||
tags = &argv[2];
|
||||
else
|
||||
tags = default_tags;
|
||||
|
||||
registry_file = g_file_new_for_commandline_arg (registry_arg);
|
||||
registry_uri = g_file_get_uri (registry_file);
|
||||
|
||||
transaction = flatpak_transaction_new (dir, opt_no_pull, opt_no_deploy,
|
||||
!opt_no_deps, !opt_no_related);
|
||||
|
||||
for (i = 0; tags[i] != NULL; i++)
|
||||
{
|
||||
if (!flatpak_transaction_add_install_oci (transaction, registry_uri, tags[i], error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!flatpak_transaction_run (transaction, TRUE, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error)
|
||||
{
|
||||
|
@ -337,6 +381,9 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
|
|||
if (opt_from)
|
||||
return install_from (dir, context, argc, argv, cancellable, error);
|
||||
|
||||
if (opt_oci)
|
||||
return install_oci (dir, context, argc, argv, cancellable, error);
|
||||
|
||||
if (argc < 3)
|
||||
return usage_error (context, _("REMOTE and REF must be specified"), error);
|
||||
|
||||
|
|
|
@ -162,13 +162,14 @@ print_installed_refs (gboolean app, gboolean runtime, gboolean print_system, gbo
|
|||
|
||||
if (opt_show_details)
|
||||
{
|
||||
g_autofree char *active = flatpak_dir_read_active (dir, ref, NULL);
|
||||
const char *active = flatpak_deploy_data_get_commit (deploy_data);
|
||||
const char *alt_id = flatpak_deploy_data_get_alt_id (deploy_data);
|
||||
g_autofree char *latest = NULL;
|
||||
g_autofree char *size_s = NULL;
|
||||
guint64 size = 0;
|
||||
g_autofree const char **subpaths = NULL;
|
||||
|
||||
latest = flatpak_dir_read_latest (dir, repo, ref, NULL, NULL);
|
||||
latest = flatpak_dir_read_latest (dir, repo, ref, NULL, NULL, NULL);
|
||||
if (latest)
|
||||
{
|
||||
if (strcmp (active, latest) == 0)
|
||||
|
@ -176,10 +177,6 @@ print_installed_refs (gboolean app, gboolean runtime, gboolean print_system, gbo
|
|||
g_free (latest);
|
||||
latest = g_strdup ("-");
|
||||
}
|
||||
else
|
||||
{
|
||||
latest[MIN (strlen (latest), 12)] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -189,9 +186,8 @@ print_installed_refs (gboolean app, gboolean runtime, gboolean print_system, gbo
|
|||
flatpak_table_printer_add_column (printer, partial_ref);
|
||||
flatpak_table_printer_add_column (printer, repo);
|
||||
|
||||
active[MIN (strlen (active), 12)] = 0;
|
||||
flatpak_table_printer_add_column (printer, active);
|
||||
flatpak_table_printer_add_column (printer, latest);
|
||||
flatpak_table_printer_add_column_len (printer, active, 12);
|
||||
flatpak_table_printer_add_column_len (printer, latest, 12);
|
||||
|
||||
size = flatpak_deploy_data_get_installed_size (deploy_data);
|
||||
size_s = g_format_size (size);
|
||||
|
@ -202,6 +198,12 @@ print_installed_refs (gboolean app, gboolean runtime, gboolean print_system, gbo
|
|||
if (print_user && print_system)
|
||||
flatpak_table_printer_append_with_comma (printer, is_user ? "user" : "system");
|
||||
|
||||
if (alt_id)
|
||||
{
|
||||
g_autofree char *alt_id_str = g_strdup_printf ("alt-id=%.12s", alt_id);
|
||||
flatpak_table_printer_append_with_comma (printer, alt_id_str);
|
||||
}
|
||||
|
||||
if (strcmp (parts[0], "app") == 0)
|
||||
{
|
||||
g_autofree char *current;
|
||||
|
|
1195
app/flatpak-oci.c
1195
app/flatpak-oci.c
File diff suppressed because it is too large
Load Diff
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2016 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __FLATPAK_OCI_H__
|
||||
#define __FLATPAK_OCI_H__
|
||||
|
||||
#include "libglnx/libglnx.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
#include <archive.h>
|
||||
#include <archive_entry.h>
|
||||
|
||||
#define FLATPAK_TYPE_OCI_DIR flatpak_oci_dir_get_type ()
|
||||
#define FLATPAK_OCI_DIR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_OCI_DIR, FlatpakOciDir))
|
||||
#define FLATPAK_IS_OCI_DIR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLATPAK_TYPE_OCI_DIR))
|
||||
|
||||
GType flatpak_oci_dir_get_type (void);
|
||||
|
||||
typedef struct FlatpakOciDir FlatpakOciDir;
|
||||
|
||||
#define FLATPAK_TYPE_OCI_LAYER_WRITER flatpak_oci_layer_writer_get_type ()
|
||||
#define FLATPAK_OCI_LAYER_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_OCI_LAYER_WRITER, FlatpakOciLayerWriter))
|
||||
#define FLATPAK_IS_OCI_LAYER_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLATPAK_TYPE_OCI_LAYER_WRITER))
|
||||
|
||||
GType flatpak_oci_layer_writer_get_type (void);
|
||||
|
||||
typedef struct FlatpakOciLayerWriter FlatpakOciLayerWriter;
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakOciDir, g_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakOciLayerWriter, g_object_unref)
|
||||
|
||||
const char * flatpak_arch_to_oci_arch (const char *flatpak_arch);
|
||||
|
||||
FlatpakOciDir *flatpak_oci_dir_new (void);
|
||||
gboolean flatpak_oci_dir_open (FlatpakOciDir *self,
|
||||
GFile *dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_dir_ensure (FlatpakOciDir *self,
|
||||
GFile *dir,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char * flatpak_oci_dir_write_blob (FlatpakOciDir *self,
|
||||
GBytes *data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_dir_set_ref (FlatpakOciDir *self,
|
||||
const char *ref,
|
||||
guint64 object_size,
|
||||
const char *object_sha256,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_dir_load_ref (FlatpakOciDir *self,
|
||||
const char *ref,
|
||||
guint64 *size_out,
|
||||
char **digest_out,
|
||||
char **mediatype_out,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GBytes * flatpak_oci_dir_load_object (FlatpakOciDir *self,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
struct archive *flatpak_oci_dir_load_layer (FlatpakOciDir *self,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
JsonObject * flatpak_oci_dir_load_json (FlatpakOciDir *self,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
JsonObject * flatpak_oci_dir_find_manifest (FlatpakOciDir *self,
|
||||
const char *ref,
|
||||
const char *os,
|
||||
const char *arch,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
char * flatpak_oci_manifest_get_config (JsonObject *manifest);
|
||||
char ** flatpak_oci_manifest_get_layers (JsonObject *manifest);
|
||||
GHashTable *flatpak_oci_manifest_get_annotations (JsonObject *manifest);
|
||||
|
||||
guint64 flatpak_oci_config_get_created (JsonObject *config);
|
||||
|
||||
FlatpakOciLayerWriter *flatpak_oci_layer_writer_new (FlatpakOciDir *dir);
|
||||
struct archive * flatpak_oci_layer_writer_open (FlatpakOciLayerWriter *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_layer_writer_close (FlatpakOciLayerWriter *self,
|
||||
char **uncompressed_sha256_out,
|
||||
guint64 *uncompressed_size_out,
|
||||
char **compressed_sha256_out,
|
||||
guint64 *compressed_size_out,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
|
||||
typedef struct FlatpakJsonWriter FlatpakJsonWriter;
|
||||
|
||||
FlatpakJsonWriter *flatpak_json_writer_new (void);
|
||||
GBytes *flatpak_json_writer_get_result (FlatpakJsonWriter *self);
|
||||
void flatpak_json_writer_free (FlatpakJsonWriter *self);
|
||||
|
||||
void flatpak_json_writer_open_struct (FlatpakJsonWriter *writer);
|
||||
void flatpak_json_writer_open_array (FlatpakJsonWriter *writer);
|
||||
void flatpak_json_writer_close (FlatpakJsonWriter *writer);
|
||||
void flatpak_json_writer_add_struct_property (FlatpakJsonWriter *writer, const gchar *name);
|
||||
void flatpak_json_writer_add_array_property (FlatpakJsonWriter *writer, const gchar *name);
|
||||
void flatpak_json_writer_add_string_property (FlatpakJsonWriter *writer, const gchar *name, const char *value);
|
||||
void flatpak_json_writer_add_uint64_property (FlatpakJsonWriter *writer, const gchar *name, guint64 value);
|
||||
void flatpak_json_writer_add_bool_property (FlatpakJsonWriter *writer, const gchar *name, gboolean value);
|
||||
void flatpak_json_writer_add_array_string (FlatpakJsonWriter *writer, const gchar *string);
|
||||
void flatpak_json_writer_add_array_struct (FlatpakJsonWriter *writer);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakJsonWriter, flatpak_json_writer_free)
|
||||
|
||||
|
||||
#endif /* __FLATPAK_OCI_H__ */
|
|
@ -25,6 +25,7 @@
|
|||
#include "flatpak-transaction.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "flatpak-builtins-utils.h"
|
||||
#include "flatpak-oci-registry.h"
|
||||
#include "flatpak-error.h"
|
||||
|
||||
typedef struct FlatpakTransactionOp FlatpakTransactionOp;
|
||||
|
@ -458,6 +459,66 @@ flatpak_transaction_add_install (FlatpakTransaction *self,
|
|||
return flatpak_transaction_add_ref (self, remote, ref, subpaths, NULL, FALSE, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_transaction_add_install_oci (FlatpakTransaction *self,
|
||||
const char *uri,
|
||||
const char *tag,
|
||||
GError **error)
|
||||
{
|
||||
GHashTable *annotations;
|
||||
g_autofree char *ref = NULL;
|
||||
g_autofree char *checksum = NULL;
|
||||
g_autoptr(FlatpakOciManifest) manifest = NULL;
|
||||
g_autoptr(FlatpakOciRegistry) registry = NULL;
|
||||
const char *all_paths[] = { NULL };
|
||||
g_autofree char *remote = NULL;
|
||||
g_autofree char *title = NULL;
|
||||
g_autofree char **parts = NULL;
|
||||
g_autofree char *id = NULL;
|
||||
|
||||
registry = flatpak_oci_registry_new (uri, FALSE, -1, NULL, error);
|
||||
if (registry == NULL)
|
||||
return FALSE;
|
||||
|
||||
manifest = flatpak_oci_registry_chose_image (registry, tag, NULL,
|
||||
NULL, error);
|
||||
if (manifest == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* TODO: Extract runtime dependencies and related refs */
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
if (annotations)
|
||||
flatpak_oci_parse_commit_annotations (annotations, NULL,
|
||||
NULL, NULL,
|
||||
&ref, &checksum, NULL,
|
||||
NULL);
|
||||
|
||||
if (ref == NULL)
|
||||
return flatpak_fail (error, _("OCI image is not a flatpak (missing ref)"));
|
||||
|
||||
parts = flatpak_decompose_ref (ref, error);
|
||||
if (parts == NULL)
|
||||
return FALSE;
|
||||
|
||||
title = g_strdup_printf ("OCI remote for %s", parts[1]);
|
||||
|
||||
id = g_strdup_printf ("oci-%s", parts[1]);
|
||||
|
||||
remote = flatpak_dir_create_origin_remote (self->dir, NULL,
|
||||
id, title,
|
||||
ref, uri, tag, NULL,
|
||||
NULL, error);
|
||||
if (remote == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!flatpak_dir_recreate_repo (self->dir, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_debug ("Added OCI origin remote %s", remote);
|
||||
|
||||
return flatpak_transaction_add_ref (self, remote, ref, all_paths, checksum, FALSE, error);
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_transaction_add_update (FlatpakTransaction *self,
|
||||
const char *ref,
|
||||
|
@ -468,7 +529,6 @@ flatpak_transaction_add_update (FlatpakTransaction *self,
|
|||
return flatpak_transaction_add_ref (self, NULL, ref, subpaths, commit, TRUE, error);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
flatpak_transaction_run (FlatpakTransaction *self,
|
||||
gboolean stop_on_first_error,
|
||||
|
|
|
@ -43,6 +43,10 @@ gboolean flatpak_transaction_add_install (FlatpakTransaction *self,
|
|||
const char *ref,
|
||||
const char **subpaths,
|
||||
GError **error);
|
||||
gboolean flatpak_transaction_add_install_oci (FlatpakTransaction *self,
|
||||
const char *uri,
|
||||
const char *tag,
|
||||
GError **error);
|
||||
gboolean flatpak_transaction_add_update (FlatpakTransaction *self,
|
||||
const char *ref,
|
||||
const char **subpaths,
|
||||
|
|
|
@ -230,25 +230,7 @@ SoupSession *
|
|||
builder_context_get_soup_session (BuilderContext *self)
|
||||
{
|
||||
if (self->soup_session == NULL)
|
||||
{
|
||||
const char *http_proxy;
|
||||
|
||||
self->soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "flatpak-builder ",
|
||||
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
|
||||
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
|
||||
SOUP_SESSION_TIMEOUT, 60,
|
||||
SOUP_SESSION_IDLE_TIMEOUT, 60,
|
||||
NULL);
|
||||
http_proxy = g_getenv ("http_proxy");
|
||||
if (http_proxy)
|
||||
{
|
||||
g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy);
|
||||
if (!proxy_uri)
|
||||
g_warning ("Invalid proxy URI '%s'", http_proxy);
|
||||
else
|
||||
g_object_set (self->soup_session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
|
||||
}
|
||||
}
|
||||
self->soup_session = flatpak_create_soup_session ("flatpak-builder");
|
||||
|
||||
return self->soup_session;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,12 @@ libflatpak_common_la_SOURCES = \
|
|||
common/gvdb/gvdb-builder.c \
|
||||
common/flatpak-db.c \
|
||||
common/flatpak-db.h \
|
||||
common/flatpak-json.c \
|
||||
common/flatpak-json.h \
|
||||
common/flatpak-json-oci.c \
|
||||
common/flatpak-json-oci.h \
|
||||
common/flatpak-oci-registry.c \
|
||||
common/flatpak-oci-registry.h \
|
||||
$(NULL)
|
||||
|
||||
libflatpak_common_la_CFLAGS = \
|
||||
|
@ -61,4 +67,4 @@ libflatpak_common_la_CFLAGS = \
|
|||
$(LIBSECCOMP_CFLAGS) \
|
||||
-I$(srcdir)/dbus-proxy \
|
||||
$(NULL)
|
||||
libflatpak_common_la_LIBADD = libglnx.la $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) $(XAUTH_LIBS) $(LIBSECCOMP_LIBS)
|
||||
libflatpak_common_la_LIBADD = libglnx.la $(BASE_LIBS) $(OSTREE_LIBS) $(SOUP_LIBS) $(JSON_LIBS) $(XAUTH_LIBS) $(LIBSECCOMP_LIBS)
|
||||
|
|
|
@ -29,5 +29,7 @@ typedef enum {
|
|||
typedef struct FlatpakDir FlatpakDir;
|
||||
typedef struct FlatpakDeploy FlatpakDeploy;
|
||||
typedef struct FlatpakContext FlatpakContext;
|
||||
typedef struct FlatpakOciRegistry FlatpakOciRegistry;
|
||||
typedef struct _FlatpakOciManifest FlatpakOciManifest;
|
||||
|
||||
#endif /* __FLATPAK_COMMON_TYPES_H__ */
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
#include "flatpak-dir.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "flatpak-oci-registry.h"
|
||||
#include "flatpak-run.h"
|
||||
|
||||
#include "errno.h"
|
||||
|
@ -686,6 +687,17 @@ flatpak_deploy_data_get_commit (GVariant *deploy_data)
|
|||
return commit;
|
||||
}
|
||||
|
||||
const char *
|
||||
flatpak_deploy_data_get_alt_id (GVariant *deploy_data)
|
||||
{
|
||||
g_autoptr(GVariant) metadata = g_variant_get_child_value (deploy_data, 4);
|
||||
const char *alt_id = NULL;
|
||||
|
||||
g_variant_lookup (metadata, "alt-id", "&s", &alt_id);
|
||||
|
||||
return alt_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* flatpak_deploy_data_get_subpaths:
|
||||
*
|
||||
|
@ -1342,29 +1354,11 @@ repo_pull_one_dir (OstreeRepo *self,
|
|||
static void
|
||||
ensure_soup_session (FlatpakDir *self)
|
||||
{
|
||||
const char *http_proxy;
|
||||
|
||||
if (g_once_init_enter (&self->soup_session))
|
||||
{
|
||||
SoupSession *soup_session;
|
||||
|
||||
soup_session =
|
||||
soup_session_new_with_options (SOUP_SESSION_USER_AGENT, "ostree ",
|
||||
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
|
||||
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
|
||||
SOUP_SESSION_TIMEOUT, 60,
|
||||
SOUP_SESSION_IDLE_TIMEOUT, 60,
|
||||
NULL);
|
||||
http_proxy = g_getenv ("http_proxy");
|
||||
if (http_proxy)
|
||||
{
|
||||
g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy);
|
||||
|
||||
if (!proxy_uri)
|
||||
g_warning ("Invalid proxy URI '%s'", http_proxy);
|
||||
else
|
||||
g_object_set (soup_session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
|
||||
}
|
||||
soup_session = flatpak_create_soup_session ("ostree");
|
||||
|
||||
if (g_getenv ("OSTREE_DEBUG_HTTP"))
|
||||
soup_session_add_feature (soup_session, (SoupSessionFeature *) soup_logger_new (SOUP_LOGGER_LOG_BODY, 500));
|
||||
|
@ -1550,6 +1544,70 @@ flatpak_dir_pull_extra_data (FlatpakDir *self,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_dir_pull_oci (FlatpakDir *self,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
OstreeRepo *repo,
|
||||
FlatpakPullFlags flatpak_flags,
|
||||
OstreeRepoPullFlags flags,
|
||||
OstreeAsyncProgress *progress,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GHashTable *annotations;
|
||||
g_autoptr(FlatpakOciManifest) manifest = NULL;
|
||||
g_autoptr(FlatpakOciRegistry) registry = NULL;
|
||||
g_autofree char *oci_ref = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
g_autofree char *oci_tag = NULL;
|
||||
g_autofree char *oci_digest = NULL;
|
||||
g_autofree char *full_ref = NULL;
|
||||
g_autofree char *checksum = NULL;
|
||||
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, remote);
|
||||
g_assert (oci_uri != NULL);
|
||||
|
||||
oci_tag = flatpak_dir_get_remote_oci_tag (self, remote);
|
||||
if (oci_tag == NULL)
|
||||
oci_tag = g_strdup ("latest");
|
||||
|
||||
registry = flatpak_oci_registry_new (oci_uri, FALSE, -1, NULL, error);
|
||||
if (registry == NULL)
|
||||
return FALSE;
|
||||
|
||||
manifest = flatpak_oci_registry_chose_image (registry, oci_tag, &oci_digest,
|
||||
NULL, error);
|
||||
if (manifest == NULL)
|
||||
return FALSE;
|
||||
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
if (annotations)
|
||||
flatpak_oci_parse_commit_annotations (annotations, NULL,
|
||||
NULL, NULL,
|
||||
&oci_ref, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
if (oci_ref == NULL)
|
||||
return flatpak_fail (error, _("OCI image is not a flatpak (missing ref)"));
|
||||
|
||||
if (strcmp (ref, oci_ref) != 0)
|
||||
return flatpak_fail (error, _("OCI image specifies the wrong app id"));
|
||||
|
||||
full_ref = g_strdup_printf ("%s:%s", remote, ref);
|
||||
|
||||
if (repo == NULL)
|
||||
repo = self->repo;
|
||||
|
||||
checksum = flatpak_pull_from_oci (repo, registry, oci_digest, manifest, full_ref, cancellable, error);
|
||||
if (checksum == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_debug ("Imported OCI image as checksum %s\n", checksum);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_dir_pull (FlatpakDir *self,
|
||||
const char *repository,
|
||||
|
@ -1568,6 +1626,7 @@ flatpak_dir_pull (FlatpakDir *self,
|
|||
g_autofree char *url = NULL;
|
||||
g_autoptr(GBytes) summary_bytes = NULL;
|
||||
g_autofree char *latest_rev = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
g_auto(GLnxConsoleRef) console = { 0, };
|
||||
g_autoptr(OstreeAsyncProgress) console_progress = NULL;
|
||||
g_autoptr(GPtrArray) subdirs_arg = NULL;
|
||||
|
@ -1575,6 +1634,11 @@ flatpak_dir_pull (FlatpakDir *self,
|
|||
if (!flatpak_dir_ensure_repo (self, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, repository);
|
||||
if (oci_uri != NULL)
|
||||
return flatpak_dir_pull_oci (self, repository, ref, repo, flatpak_flags,
|
||||
flags, progress, cancellable, error);
|
||||
|
||||
if (!ostree_repo_remote_get_url (self->repo,
|
||||
repository,
|
||||
&url,
|
||||
|
@ -2204,10 +2268,12 @@ char *
|
|||
flatpak_dir_read_latest (FlatpakDir *self,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
char **out_alt_id,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *remote_and_ref = NULL;
|
||||
g_autofree char *alt_id = NULL;
|
||||
char *res = NULL;
|
||||
|
||||
/* There may be several remotes with the same branch (if we for
|
||||
|
@ -2222,6 +2288,21 @@ flatpak_dir_read_latest (FlatpakDir *self,
|
|||
if (!ostree_repo_resolve_rev (self->repo, remote_and_ref, FALSE, &res, error))
|
||||
return NULL;
|
||||
|
||||
if (out_alt_id)
|
||||
{
|
||||
g_autoptr(GVariant) commit_data = NULL;
|
||||
g_autoptr(GVariant) commit_metadata = NULL;
|
||||
g_autofree char *tmp_dir_path = NULL;
|
||||
|
||||
if (!ostree_repo_load_commit (self->repo, res, &commit_data, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
commit_metadata = g_variant_get_child_value (commit_data, 0);
|
||||
g_variant_lookup (commit_metadata, "xa.alt-id", "s", &alt_id);
|
||||
|
||||
*out_alt_id = g_steal_pointer (&alt_id);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -3232,8 +3313,12 @@ flatpak_dir_deploy (FlatpakDir *self,
|
|||
const char *checksum;
|
||||
glnx_fd_close int checkoutdir_dfd = -1;
|
||||
g_autoptr(GFile) tmp_dir_template = NULL;
|
||||
g_autoptr(GVariant) commit_data = NULL;
|
||||
g_autofree char *tmp_dir_path = NULL;
|
||||
g_autofree char *alt_id = NULL;
|
||||
gboolean created_extra_data = FALSE;
|
||||
g_autoptr(GVariant) commit_metadata = NULL;
|
||||
GVariantBuilder metadata_builder;
|
||||
|
||||
if (!flatpak_dir_ensure_repo (self, cancellable, error))
|
||||
return FALSE;
|
||||
|
@ -3244,7 +3329,7 @@ flatpak_dir_deploy (FlatpakDir *self,
|
|||
{
|
||||
g_debug ("No checksum specified, getting tip of %s", ref);
|
||||
|
||||
resolved_ref = flatpak_dir_read_latest (self, origin, ref, cancellable, error);
|
||||
resolved_ref = flatpak_dir_read_latest (self, origin, ref, NULL, cancellable, error);
|
||||
if (resolved_ref == NULL)
|
||||
{
|
||||
g_prefix_error (error, _("While trying to resolve ref %s: "), ref);
|
||||
|
@ -3265,6 +3350,12 @@ flatpak_dir_deploy (FlatpakDir *self,
|
|||
return flatpak_fail (error, _("%s is not available"), ref);
|
||||
}
|
||||
|
||||
if (!ostree_repo_load_commit (self->repo, checksum, &commit_data, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
commit_metadata = g_variant_get_child_value (commit_data, 0);
|
||||
g_variant_lookup (commit_metadata, "xa.alt-id", "s", &alt_id);
|
||||
|
||||
real_checkoutdir = g_file_get_child (deploy_base, checksum);
|
||||
if (g_file_query_exists (real_checkoutdir, cancellable))
|
||||
{
|
||||
|
@ -3459,11 +3550,16 @@ flatpak_dir_deploy (FlatpakDir *self,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
g_variant_builder_init (&metadata_builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
if (alt_id)
|
||||
g_variant_builder_add (&metadata_builder, "{s@v}", "alt-id",
|
||||
g_variant_new_variant (g_variant_new_string (alt_id)));
|
||||
|
||||
deploy_data = flatpak_dir_new_deploy_data (origin,
|
||||
checksum,
|
||||
(char **) subpaths,
|
||||
installed_size,
|
||||
NULL);
|
||||
g_variant_builder_end (&metadata_builder));
|
||||
|
||||
deploy_data_file = g_file_get_child (checkoutdir, "deploy");
|
||||
if (!flatpak_variant_save (deploy_data_file, deploy_data, cancellable, error))
|
||||
|
@ -3910,6 +4006,7 @@ flatpak_dir_install_bundle (FlatpakDir *self,
|
|||
parts[1],
|
||||
basename,
|
||||
ref,
|
||||
NULL, NULL,
|
||||
gpg_data,
|
||||
cancellable,
|
||||
error);
|
||||
|
@ -4023,6 +4120,7 @@ flatpak_dir_update (FlatpakDir *self,
|
|||
gboolean is_local;
|
||||
g_autofree char *latest_rev = NULL;
|
||||
const char *rev = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
|
||||
deploy_data = flatpak_dir_get_deploy_data (self, ref,
|
||||
cancellable, NULL);
|
||||
|
@ -4039,7 +4137,9 @@ flatpak_dir_update (FlatpakDir *self,
|
|||
if (!ostree_repo_remote_get_url (self->repo, remote_name, &url, error))
|
||||
return FALSE;
|
||||
|
||||
if (*url == 0)
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, remote_name);
|
||||
|
||||
if (*url == 0 && oci_uri == NULL)
|
||||
return TRUE; /* Empty URL => disabled */
|
||||
|
||||
rev = checksum_or_latest;
|
||||
|
@ -4052,6 +4152,7 @@ flatpak_dir_update (FlatpakDir *self,
|
|||
_g_strv_equal0 ((char **)subpaths, (char **)old_subpaths))
|
||||
{
|
||||
const char *installed_commit = flatpak_deploy_data_get_commit (deploy_data);
|
||||
const char *installed_alt_id = flatpak_deploy_data_get_alt_id (deploy_data);
|
||||
|
||||
if (checksum_or_latest != NULL)
|
||||
{
|
||||
|
@ -4074,7 +4175,8 @@ flatpak_dir_update (FlatpakDir *self,
|
|||
ref,
|
||||
&latest_rev))
|
||||
{
|
||||
if (strcmp (latest_rev, installed_commit) == 0)
|
||||
if (g_strcmp0 (latest_rev, installed_commit) == 0 ||
|
||||
g_strcmp0 (latest_rev, installed_alt_id) == 0)
|
||||
{
|
||||
g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_ALREADY_INSTALLED,
|
||||
_("%s branch %s already installed"), ref, installed_commit);
|
||||
|
@ -4970,6 +5072,101 @@ flatpak_dir_cache_summary (FlatpakDir *self,
|
|||
G_UNLOCK (cache);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_dir_remote_make_oci_summary (FlatpakDir *self,
|
||||
const char *remote,
|
||||
GBytes **out_summary,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(FlatpakOciRegistry) registry = NULL;
|
||||
g_autofree char *oci_ref = NULL;
|
||||
g_autofree char *remote_oci_ref = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
g_autofree char *oci_tag = NULL;
|
||||
g_autofree char *oci_digest = NULL;
|
||||
g_autoptr(GVariantBuilder) refs_builder = NULL;
|
||||
g_autoptr(GVariantBuilder) additional_metadata_builder = NULL;
|
||||
g_autoptr(GVariantBuilder) summary_builder = NULL;
|
||||
g_autoptr(FlatpakOciManifest) manifest = NULL;
|
||||
g_autoptr(GVariant) summary = NULL;
|
||||
GHashTable *annotations;
|
||||
g_autoptr(GVariantBuilder) ref_data_builder = NULL;
|
||||
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, remote);
|
||||
g_assert (oci_uri != NULL);
|
||||
|
||||
oci_tag = flatpak_dir_get_remote_oci_tag (self, remote);
|
||||
if (oci_tag == NULL)
|
||||
oci_tag = g_strdup ("latest");
|
||||
|
||||
oci_ref = flatpak_dir_get_remote_main_ref (self, remote);
|
||||
|
||||
registry = flatpak_oci_registry_new (oci_uri, FALSE, -1, NULL, error);
|
||||
if (registry == NULL)
|
||||
return FALSE;
|
||||
|
||||
manifest = flatpak_oci_registry_chose_image (registry, oci_tag, &oci_digest,
|
||||
NULL, error);
|
||||
if (manifest == NULL)
|
||||
return FALSE;
|
||||
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
if (annotations)
|
||||
flatpak_oci_parse_commit_annotations (annotations, NULL,
|
||||
NULL, NULL,
|
||||
&remote_oci_ref, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(s(taya{sv}))"));
|
||||
ref_data_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{s(tts)}"));
|
||||
additional_metadata_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
if (remote_oci_ref != NULL && g_strcmp0 (remote_oci_ref, oci_ref) == 0 && g_str_has_prefix (oci_digest, "sha256:"))
|
||||
{
|
||||
const char *fake_commit = oci_digest + strlen ("sha256:");
|
||||
guint64 installed_size = 0;
|
||||
guint64 download_size = 0;
|
||||
const char *installed_size_str;
|
||||
const char *metadata_contents = NULL;
|
||||
int i;
|
||||
|
||||
g_variant_builder_add_value (refs_builder,
|
||||
g_variant_new ("(s(t@ay@a{sv}))", oci_ref,
|
||||
0,
|
||||
ostree_checksum_to_bytes_v (fake_commit),
|
||||
flatpak_gvariant_new_empty_string_dict ()));
|
||||
|
||||
for (i = 0; manifest->layers != NULL && manifest->layers[i] != NULL; i++)
|
||||
download_size += manifest->layers[i]->size;
|
||||
|
||||
installed_size_str = g_hash_table_lookup (annotations, "org.flatpak.InstalledSize");
|
||||
if (installed_size_str)
|
||||
installed_size = g_ascii_strtoull (installed_size_str, NULL, 10);
|
||||
|
||||
metadata_contents = g_hash_table_lookup (annotations, "org.flatpak.Metadata");
|
||||
|
||||
g_variant_builder_add (ref_data_builder, "{s(tts)}",
|
||||
oci_ref,
|
||||
GUINT64_TO_BE (installed_size),
|
||||
GUINT64_TO_BE (download_size),
|
||||
metadata_contents ? metadata_contents : "");
|
||||
}
|
||||
|
||||
g_variant_builder_add (additional_metadata_builder, "{sv}", "xa.cache",
|
||||
g_variant_new_variant (g_variant_builder_end (ref_data_builder)));
|
||||
|
||||
summary_builder = g_variant_builder_new (OSTREE_SUMMARY_GVARIANT_FORMAT);
|
||||
|
||||
g_variant_builder_add_value (summary_builder, g_variant_builder_end (refs_builder));
|
||||
g_variant_builder_add_value (summary_builder, g_variant_builder_end (additional_metadata_builder));
|
||||
|
||||
summary = g_variant_ref_sink (g_variant_builder_end (summary_builder));
|
||||
|
||||
*out_summary = g_variant_get_data_as_bytes (summary);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
flatpak_dir_remote_fetch_summary (FlatpakDir *self,
|
||||
const char *name,
|
||||
|
@ -4980,6 +5177,7 @@ flatpak_dir_remote_fetch_summary (FlatpakDir *self,
|
|||
g_autofree char *url = NULL;
|
||||
gboolean is_local;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
GBytes *summary;
|
||||
|
||||
if (!ostree_repo_remote_get_url (self->repo, name, &url, error))
|
||||
|
@ -5002,11 +5200,24 @@ flatpak_dir_remote_fetch_summary (FlatpakDir *self,
|
|||
if (error == NULL)
|
||||
error = &local_error;
|
||||
|
||||
if (!ostree_repo_remote_fetch_summary (self->repo, name,
|
||||
&summary, NULL,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, name);
|
||||
|
||||
if (oci_uri != NULL)
|
||||
{
|
||||
if (!flatpak_dir_remote_make_oci_summary (self, name,
|
||||
&summary,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ostree_repo_remote_fetch_summary (self->repo, name,
|
||||
&summary, NULL,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (summary == NULL)
|
||||
return flatpak_fail (error, "Remote listing for %s not available; server has no summary file\n" \
|
||||
|
@ -5501,6 +5712,45 @@ flatpak_dir_get_remote_title (FlatpakDir *self,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_dir_get_remote_oci_uri (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
{
|
||||
GKeyFile *config = ostree_repo_get_config (self->repo);
|
||||
g_autofree char *group = get_group (remote_name);
|
||||
|
||||
if (config)
|
||||
return g_key_file_get_string (config, group, "xa.oci-uri", NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_dir_get_remote_main_ref (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
{
|
||||
GKeyFile *config = ostree_repo_get_config (self->repo);
|
||||
g_autofree char *group = get_group (remote_name);
|
||||
|
||||
if (config)
|
||||
return g_key_file_get_string (config, group, "xa.main-ref", NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_dir_get_remote_oci_tag (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
{
|
||||
GKeyFile *config = ostree_repo_get_config (self->repo);
|
||||
g_autofree char *group = get_group (remote_name);
|
||||
|
||||
if (config)
|
||||
return g_key_file_get_string (config, group, "xa.oci-tag", NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_dir_get_remote_default_branch (FlatpakDir *self,
|
||||
const char *remote_name)
|
||||
|
@ -5560,13 +5810,18 @@ flatpak_dir_get_remote_disabled (FlatpakDir *self,
|
|||
GKeyFile *config = ostree_repo_get_config (self->repo);
|
||||
g_autofree char *group = get_group (remote_name);
|
||||
g_autofree char *url = NULL;
|
||||
g_autofree char *oci_uri = NULL;
|
||||
|
||||
if (config &&
|
||||
g_key_file_get_boolean (config, group, "xa.disable", NULL))
|
||||
return TRUE;
|
||||
|
||||
if (ostree_repo_remote_get_url (self->repo, remote_name, &url, NULL) && *url == 0)
|
||||
return TRUE; /* Empty URL => disabled */
|
||||
{
|
||||
oci_uri = flatpak_dir_get_remote_oci_uri (self, remote_name);
|
||||
if (oci_uri == NULL)
|
||||
return TRUE; /* Empty URL => disabled */
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -5593,6 +5848,8 @@ create_origin_remote_config (OstreeRepo *repo,
|
|||
const char *id,
|
||||
const char *title,
|
||||
const char *main_ref,
|
||||
const char *oci_uri,
|
||||
const char *oci_tag,
|
||||
GKeyFile *new_config)
|
||||
{
|
||||
g_autofree char *remote = NULL;
|
||||
|
@ -5627,6 +5884,10 @@ create_origin_remote_config (OstreeRepo *repo,
|
|||
g_key_file_set_string (new_config, group, "gpg-verify-summary", "true");
|
||||
if (main_ref)
|
||||
g_key_file_set_string (new_config, group, "xa.main-ref", main_ref);
|
||||
if (oci_uri)
|
||||
g_key_file_set_string (new_config, group, "xa.oci-uri", oci_uri);
|
||||
if (oci_tag)
|
||||
g_key_file_set_string (new_config, group, "xa.oci-tag", oci_tag);
|
||||
|
||||
return g_steal_pointer (&remote);
|
||||
}
|
||||
|
@ -5637,6 +5898,8 @@ flatpak_dir_create_origin_remote (FlatpakDir *self,
|
|||
const char *id,
|
||||
const char *title,
|
||||
const char *main_ref,
|
||||
const char *oci_uri,
|
||||
const char *oci_tag,
|
||||
GBytes *gpg_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
|
@ -5644,7 +5907,7 @@ flatpak_dir_create_origin_remote (FlatpakDir *self,
|
|||
g_autoptr(GKeyFile) new_config = g_key_file_new ();
|
||||
g_autofree char *remote = NULL;
|
||||
|
||||
remote = create_origin_remote_config (self->repo, url, id, title, main_ref, new_config);
|
||||
remote = create_origin_remote_config (self->repo, url, id, title, main_ref, oci_uri, oci_tag, new_config);
|
||||
|
||||
if (!flatpak_dir_modify_remote (self, remote, new_config,
|
||||
gpg_data, cancellable, error))
|
||||
|
@ -5856,7 +6119,7 @@ flatpak_dir_create_remote_for_ref_file (FlatpakDir *self,
|
|||
|
||||
if (remote == NULL)
|
||||
{
|
||||
remote = flatpak_dir_create_origin_remote (self, url, name, title, ref,
|
||||
remote = flatpak_dir_create_origin_remote (self, url, name, title, ref, NULL, NULL,
|
||||
gpg_data, NULL, error);
|
||||
if (remote == NULL)
|
||||
return FALSE;
|
||||
|
@ -6633,6 +6896,7 @@ flatpak_dir_find_remote_related (FlatpakDir *self,
|
|||
int i;
|
||||
g_auto(GStrv) parts = NULL;
|
||||
g_autoptr(GPtrArray) related = g_ptr_array_new_with_free_func ((GDestroyNotify)flatpak_related_free);
|
||||
g_autofree char *url = NULL;
|
||||
|
||||
parts = flatpak_decompose_ref (ref, error);
|
||||
if (parts == NULL)
|
||||
|
@ -6641,6 +6905,15 @@ flatpak_dir_find_remote_related (FlatpakDir *self,
|
|||
if (!flatpak_dir_ensure_repo (self, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
if (!ostree_repo_remote_get_url (self->repo,
|
||||
remote_name,
|
||||
&url,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (*url == 0)
|
||||
return g_steal_pointer (&related); /* Empty url, silently disables updates */
|
||||
|
||||
if (!flatpak_dir_remote_fetch_summary (self, remote_name,
|
||||
&summary_bytes,
|
||||
cancellable, error))
|
||||
|
|
|
@ -130,6 +130,7 @@ const char * flatpak_deploy_data_get_origin (GVariant *deploy_data);
|
|||
const char * flatpak_deploy_data_get_commit (GVariant *deploy_data);
|
||||
const char ** flatpak_deploy_data_get_subpaths (GVariant *deploy_data);
|
||||
guint64 flatpak_deploy_data_get_installed_size (GVariant *deploy_data);
|
||||
const char * flatpak_deploy_data_get_alt_id (GVariant *deploy_data);
|
||||
|
||||
GFile * flatpak_deploy_get_dir (FlatpakDeploy *deploy);
|
||||
GFile * flatpak_deploy_get_files (FlatpakDeploy *deploy);
|
||||
|
@ -289,6 +290,7 @@ gboolean flatpak_dir_list_refs (FlatpakDir *self,
|
|||
char * flatpak_dir_read_latest (FlatpakDir *self,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
char **out_alt_id,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char * flatpak_dir_read_active (FlatpakDir *self,
|
||||
|
@ -426,6 +428,8 @@ char *flatpak_dir_create_origin_remote (FlatpakDir *self,
|
|||
const char *id,
|
||||
const char *title,
|
||||
const char *main_ref,
|
||||
const char *oci_uri,
|
||||
const char *oci_tag,
|
||||
GBytes *gpg_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
@ -462,6 +466,12 @@ gboolean flatpak_dir_remove_remote (FlatpakDir *self,
|
|||
GError **error);
|
||||
char *flatpak_dir_get_remote_title (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
char *flatpak_dir_get_remote_main_ref (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
char *flatpak_dir_get_remote_oci_uri (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
char *flatpak_dir_get_remote_oci_tag (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
char *flatpak_dir_get_remote_default_branch (FlatpakDir *self,
|
||||
const char *remote_name);
|
||||
int flatpak_dir_get_remote_prio (FlatpakDir *self,
|
||||
|
|
|
@ -0,0 +1,716 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "flatpak-json-oci.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "libglnx.h"
|
||||
|
||||
const char *
|
||||
flatpak_arch_to_oci_arch (const char *flatpak_arch)
|
||||
{
|
||||
if (strcmp (flatpak_arch, "x86_64") == 0)
|
||||
return "amd64";
|
||||
if (strcmp (flatpak_arch, "aarch64") == 0)
|
||||
return "arm64";
|
||||
if (strcmp (flatpak_arch, "i386") == 0)
|
||||
return "386";
|
||||
return flatpak_arch;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_descriptor_destroy (FlatpakOciDescriptor *self)
|
||||
{
|
||||
g_free (self->mediatype);
|
||||
g_free (self->digest);
|
||||
g_strfreev (self->urls);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_descriptor_free (FlatpakOciDescriptor *self)
|
||||
{
|
||||
flatpak_oci_descriptor_destroy (self);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static FlatpakJsonProp flatpak_oci_descriptor_props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciDescriptor, mediatype, "mediaType"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciDescriptor, digest, "digest"),
|
||||
FLATPAK_JSON_INT64_PROP (FlatpakOciDescriptor, size, "size"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciDescriptor, urls, "urls"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_platform_destroy (FlatpakOciManifestPlatform *self)
|
||||
{
|
||||
g_free (self->architecture);
|
||||
g_free (self->os);
|
||||
g_free (self->os_version);
|
||||
g_strfreev (self->os_features);
|
||||
g_free (self->variant);
|
||||
g_strfreev (self->features);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_manifest_descriptor_destroy (FlatpakOciManifestDescriptor *self)
|
||||
{
|
||||
flatpak_oci_manifest_platform_destroy (&self->platform);
|
||||
flatpak_oci_descriptor_destroy (&self->parent);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_manifest_descriptor_free (FlatpakOciManifestDescriptor *self)
|
||||
{
|
||||
flatpak_oci_manifest_descriptor_destroy (self);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static FlatpakJsonProp flatpak_oci_manifest_platform_props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, architecture, "architecture"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, os, "os"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, os_version, "os.version"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciManifestPlatform, variant, "variant"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciManifestPlatform, os_features, "os.features"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciManifestPlatform, features, "features"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
static FlatpakJsonProp flatpak_oci_manifest_descriptor_props[] = {
|
||||
FLATPAK_JSON_PARENT_PROP (FlatpakOciManifestDescriptor, parent, flatpak_oci_descriptor_props),
|
||||
FLATPAK_JSON_STRUCT_PROP (FlatpakOciManifestDescriptor, platform, "platform", flatpak_oci_manifest_platform_props),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (FlatpakOciRef, flatpak_oci_ref, FLATPAK_TYPE_JSON);
|
||||
|
||||
static void
|
||||
flatpak_oci_ref_finalize (GObject *object)
|
||||
{
|
||||
FlatpakOciRef *self = FLATPAK_OCI_REF (object);
|
||||
|
||||
flatpak_oci_descriptor_destroy (&self->descriptor);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_ref_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_ref_class_init (FlatpakOciRefClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
|
||||
static FlatpakJsonProp props[] = {
|
||||
FLATPAK_JSON_PARENT_PROP (FlatpakOciRef, descriptor, flatpak_oci_descriptor_props),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
object_class->finalize = flatpak_oci_ref_finalize;
|
||||
json_class->props = props;
|
||||
json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_DESCRIPTOR;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_ref_init (FlatpakOciRef *self)
|
||||
{
|
||||
}
|
||||
|
||||
FlatpakOciRef *
|
||||
flatpak_oci_ref_new (const char *mediatype,
|
||||
const char *digest,
|
||||
gint64 size)
|
||||
{
|
||||
FlatpakOciRef *ref;
|
||||
|
||||
ref = g_object_new (FLATPAK_TYPE_OCI_REF, NULL);
|
||||
ref->descriptor.mediatype = g_strdup (mediatype);
|
||||
ref->descriptor.digest = g_strdup (digest);
|
||||
ref->descriptor.size = size;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
const char *
|
||||
flatpak_oci_ref_get_mediatype (FlatpakOciRef *self)
|
||||
{
|
||||
return self->descriptor.mediatype;
|
||||
}
|
||||
|
||||
const char *
|
||||
flatpak_oci_ref_get_digest (FlatpakOciRef *self)
|
||||
{
|
||||
return self->descriptor.digest;
|
||||
}
|
||||
|
||||
gint64
|
||||
flatpak_oci_ref_get_size (FlatpakOciRef *self)
|
||||
{
|
||||
return self->descriptor.size;
|
||||
}
|
||||
|
||||
const char **
|
||||
flatpak_oci_ref_get_urls (FlatpakOciRef *self)
|
||||
{
|
||||
return (const char **)self->descriptor.urls;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_ref_set_urls (FlatpakOciRef *self,
|
||||
const char **urls)
|
||||
{
|
||||
g_strfreev (self->descriptor.urls);
|
||||
self->descriptor.urls = g_strdupv ((char **)urls);
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (FlatpakOciVersioned, flatpak_oci_versioned, FLATPAK_TYPE_JSON);
|
||||
|
||||
static void
|
||||
flatpak_oci_versioned_finalize (GObject *object)
|
||||
{
|
||||
FlatpakOciVersioned *self = FLATPAK_OCI_VERSIONED (object);
|
||||
|
||||
g_free (self->mediatype);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_versioned_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_versioned_class_init (FlatpakOciVersionedClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
|
||||
static FlatpakJsonProp props[] = {
|
||||
FLATPAK_JSON_INT64_PROP (FlatpakOciVersioned, version, "schemaVersion"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciVersioned, mediatype, "mediaType"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
object_class->finalize = flatpak_oci_versioned_finalize;
|
||||
json_class->props = props;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_versioned_init (FlatpakOciVersioned *self)
|
||||
{
|
||||
}
|
||||
|
||||
FlatpakOciVersioned *
|
||||
flatpak_oci_versioned_from_json (GBytes *bytes, GError **error)
|
||||
{
|
||||
g_autoptr(JsonParser) parser = NULL;
|
||||
JsonNode *root = NULL;
|
||||
const gchar *mediatype;
|
||||
JsonObject *object;
|
||||
|
||||
parser = json_parser_new ();
|
||||
if (!json_parser_load_from_data (parser,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes),
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
root = json_parser_get_root (parser);
|
||||
object = json_node_get_object (root);
|
||||
|
||||
mediatype = json_object_get_string_member (object, "mediaType");
|
||||
if (mediatype == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Versioned object lacks mediatype");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strcmp (mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST) == 0)
|
||||
return (FlatpakOciVersioned *) flatpak_json_from_node (root, FLATPAK_TYPE_OCI_MANIFEST, error);
|
||||
|
||||
if (strcmp (mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFESTLIST) == 0)
|
||||
return (FlatpakOciVersioned *) flatpak_json_from_node (root, FLATPAK_TYPE_OCI_MANIFEST_LIST, error);
|
||||
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Unsupported media type %s", mediatype);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
flatpak_oci_versioned_get_mediatype (FlatpakOciVersioned *self)
|
||||
{
|
||||
return self->mediatype;
|
||||
}
|
||||
|
||||
gint64
|
||||
flatpak_oci_versioned_get_version (FlatpakOciVersioned *self)
|
||||
{
|
||||
return self->version;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (FlatpakOciManifest, flatpak_oci_manifest, FLATPAK_TYPE_OCI_VERSIONED);
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_finalize (GObject *object)
|
||||
{
|
||||
FlatpakOciManifest *self = (FlatpakOciManifest *) object;
|
||||
int i;
|
||||
|
||||
for (i = 0; self->layers != NULL && self->layers[i] != NULL; i++)
|
||||
flatpak_oci_descriptor_free (self->layers[i]);
|
||||
g_free (self->layers);
|
||||
flatpak_oci_descriptor_destroy (&self->config);
|
||||
if (self->annotations)
|
||||
g_hash_table_destroy (self->annotations);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_manifest_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_class_init (FlatpakOciManifestClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
|
||||
static FlatpakJsonProp props[] = {
|
||||
FLATPAK_JSON_STRUCT_PROP(FlatpakOciManifest, config, "config", flatpak_oci_descriptor_props),
|
||||
FLATPAK_JSON_STRUCTV_PROP(FlatpakOciManifest, layers, "layers", flatpak_oci_descriptor_props),
|
||||
FLATPAK_JSON_STRMAP_PROP(FlatpakOciManifest, annotations, "annotations"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
object_class->finalize = flatpak_oci_manifest_finalize;
|
||||
json_class->props = props;
|
||||
json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_init (FlatpakOciManifest *self)
|
||||
{
|
||||
}
|
||||
|
||||
FlatpakOciManifest *
|
||||
flatpak_oci_manifest_new (void)
|
||||
{
|
||||
FlatpakOciManifest *manifest;
|
||||
|
||||
manifest = g_object_new (FLATPAK_TYPE_OCI_MANIFEST, NULL);
|
||||
manifest->parent.version = 2;
|
||||
manifest->parent.mediatype = g_strdup (FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST);
|
||||
|
||||
manifest->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
|
||||
return manifest;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_manifest_set_config (FlatpakOciManifest *self,
|
||||
FlatpakOciRef *ref)
|
||||
{
|
||||
g_free (self->config.mediatype);
|
||||
self->config.mediatype = g_strdup (ref->descriptor.mediatype);
|
||||
g_free (self->config.digest);
|
||||
self->config.digest = g_strdup (ref->descriptor.digest);
|
||||
self->config.size = ref->descriptor.size;
|
||||
}
|
||||
|
||||
static int
|
||||
ptrv_count (gpointer *ptrs)
|
||||
{
|
||||
int count;
|
||||
|
||||
for (count = 0; ptrs != NULL && ptrs[count] != NULL; count++)
|
||||
;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_manifest_set_layer (FlatpakOciManifest *self,
|
||||
FlatpakOciRef *ref)
|
||||
{
|
||||
FlatpakOciRef *refs[2] = { ref, NULL };
|
||||
flatpak_oci_manifest_set_layers (self, refs);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_manifest_set_layers (FlatpakOciManifest *self,
|
||||
FlatpakOciRef **refs)
|
||||
{
|
||||
int i, count;
|
||||
|
||||
for (i = 0; self->layers != NULL && self->layers[i] != NULL; i++)
|
||||
flatpak_oci_descriptor_free (self->layers[i]);
|
||||
g_free (self->layers);
|
||||
|
||||
count = ptrv_count ((gpointer *)refs);
|
||||
|
||||
self->layers = g_new0 (FlatpakOciDescriptor *, count + 1);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
self->layers[i] = g_new0 (FlatpakOciDescriptor, 1);
|
||||
self->layers[i]->mediatype = g_strdup (refs[i]->descriptor.mediatype);
|
||||
self->layers[i]->digest = g_strdup (refs[i]->descriptor.digest);
|
||||
self->layers[i]->size = refs[i]->descriptor.size;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
flatpak_oci_manifest_get_n_layers (FlatpakOciManifest *self)
|
||||
{
|
||||
return ptrv_count ((gpointer *)self->layers);
|
||||
}
|
||||
|
||||
const char *
|
||||
flatpak_oci_manifest_get_layer_digest (FlatpakOciManifest *self,
|
||||
int i)
|
||||
{
|
||||
return self->layers[i]->digest;
|
||||
}
|
||||
|
||||
GHashTable *
|
||||
flatpak_oci_manifest_get_annotations (FlatpakOciManifest *self)
|
||||
{
|
||||
return self->annotations;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (FlatpakOciManifestList, flatpak_oci_manifest_list, FLATPAK_TYPE_OCI_VERSIONED);
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_list_finalize (GObject *object)
|
||||
{
|
||||
FlatpakOciManifestList *self = (FlatpakOciManifestList *) object;
|
||||
int i;
|
||||
|
||||
for (i = 0; self->manifests != NULL && self->manifests[i] != NULL; i++)
|
||||
flatpak_oci_manifest_descriptor_free (self->manifests[i]);
|
||||
g_free (self->manifests);
|
||||
|
||||
if (self->annotations)
|
||||
g_hash_table_destroy (self->annotations);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_manifest_list_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_list_class_init (FlatpakOciManifestListClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
|
||||
static FlatpakJsonProp props[] = {
|
||||
FLATPAK_JSON_STRUCTV_PROP(FlatpakOciManifestList, manifests, "manifests", flatpak_oci_manifest_descriptor_props),
|
||||
FLATPAK_JSON_STRMAP_PROP(FlatpakOciManifestList, annotations, "annotations"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
object_class->finalize = flatpak_oci_manifest_list_finalize;
|
||||
json_class->props = props;
|
||||
json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFESTLIST;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_manifest_list_init (FlatpakOciManifestList *self)
|
||||
{
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE (FlatpakOciImage, flatpak_oci_image, FLATPAK_TYPE_JSON);
|
||||
|
||||
static void
|
||||
flatpak_oci_image_rootfs_destroy (FlatpakOciImageRootfs *self)
|
||||
{
|
||||
g_free (self->type);
|
||||
g_strfreev (self->diff_ids);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_image_config_destroy (FlatpakOciImageConfig *self)
|
||||
{
|
||||
g_free (self->user);
|
||||
g_free (self->working_dir);
|
||||
g_strfreev (self->env);
|
||||
g_strfreev (self->cmd);
|
||||
g_strfreev (self->entrypoint);
|
||||
g_strfreev (self->exposed_ports);
|
||||
g_strfreev (self->volumes);
|
||||
if (self->labels)
|
||||
g_hash_table_destroy (self->labels);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_image_history_free (FlatpakOciImageHistory *self)
|
||||
{
|
||||
g_free (self->created);
|
||||
g_free (self->created_by);
|
||||
g_free (self->author);
|
||||
g_free (self->comment);
|
||||
g_free (self);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_image_finalize (GObject *object)
|
||||
{
|
||||
FlatpakOciImage *self = (FlatpakOciImage *) object;
|
||||
int i;
|
||||
|
||||
g_free (self->created);
|
||||
g_free (self->author);
|
||||
g_free (self->architecture);
|
||||
g_free (self->os);
|
||||
flatpak_oci_image_rootfs_destroy (&self->rootfs);
|
||||
flatpak_oci_image_config_destroy (&self->config);
|
||||
|
||||
for (i = 0; self->history != NULL && self->history[i] != NULL; i++)
|
||||
flatpak_oci_image_history_free (self->history[i]);
|
||||
g_free (self->history);
|
||||
|
||||
G_OBJECT_CLASS (flatpak_oci_image_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_image_class_init (FlatpakOciImageClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
|
||||
static FlatpakJsonProp config_props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageConfig, user, "User"),
|
||||
FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, memory, "Memory"),
|
||||
FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, memory_swap, "MemorySwap"),
|
||||
FLATPAK_JSON_INT64_PROP (FlatpakOciImageConfig, cpu_shares, "CpuShares"),
|
||||
FLATPAK_JSON_BOOLMAP_PROP (FlatpakOciImageConfig, exposed_ports, "ExposedPorts"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, env, "Env"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, entrypoint, "Entrypoint"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciImageConfig, cmd, "Cmd"),
|
||||
FLATPAK_JSON_BOOLMAP_PROP (FlatpakOciImageConfig, volumes, "Volumes"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageConfig, working_dir, "WorkingDir"),
|
||||
FLATPAK_JSON_STRMAP_PROP(FlatpakOciImageConfig, labels, "Labels"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
static FlatpakJsonProp rootfs_props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageRootfs, type, "type"),
|
||||
FLATPAK_JSON_STRV_PROP (FlatpakOciImageRootfs, diff_ids, "diff_ids"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
static FlatpakJsonProp history_props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, created, "created"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, created_by, "created_by"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, author, "author"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImageHistory, comment, "comment"),
|
||||
FLATPAK_JSON_BOOL_PROP (FlatpakOciImageHistory, empty_layer, "empty_layer"),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
static FlatpakJsonProp props[] = {
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImage, created, "created"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImage, author, "author"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImage, architecture, "architecture"),
|
||||
FLATPAK_JSON_STRING_PROP (FlatpakOciImage, os, "os"),
|
||||
FLATPAK_JSON_STRUCT_PROP (FlatpakOciImage, config, "config", config_props),
|
||||
FLATPAK_JSON_STRUCT_PROP (FlatpakOciImage, rootfs, "rootfs", rootfs_props),
|
||||
FLATPAK_JSON_STRUCTV_PROP (FlatpakOciImage, history, "history", history_props),
|
||||
FLATPAK_JSON_LAST_PROP
|
||||
};
|
||||
|
||||
object_class->finalize = flatpak_oci_image_finalize;
|
||||
json_class->props = props;
|
||||
json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_CONFIG;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_oci_image_init (FlatpakOciImage *self)
|
||||
{
|
||||
}
|
||||
|
||||
FlatpakOciImage *
|
||||
flatpak_oci_image_new (void)
|
||||
{
|
||||
FlatpakOciImage *image;
|
||||
GTimeVal stamp;
|
||||
|
||||
stamp.tv_sec = time (NULL);
|
||||
stamp.tv_usec = 0;
|
||||
|
||||
image = g_object_new (FLATPAK_TYPE_OCI_IMAGE, NULL);
|
||||
|
||||
/* Some default values */
|
||||
image->created = g_time_val_to_iso8601 (&stamp);
|
||||
image->architecture = g_strdup ("arm64");
|
||||
image->os = g_strdup ("linux");
|
||||
|
||||
image->rootfs.type = g_strdup ("layers");
|
||||
image->rootfs.diff_ids = g_new0 (char *, 1);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_image_set_created (FlatpakOciImage *image,
|
||||
const char *created)
|
||||
{
|
||||
g_free (image->created);
|
||||
image->created = g_strdup (created);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_image_set_architecture (FlatpakOciImage *image,
|
||||
const char *arch)
|
||||
{
|
||||
g_free (image->architecture);
|
||||
image->architecture = g_strdup (arch);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_image_set_os (FlatpakOciImage *image,
|
||||
const char *os)
|
||||
{
|
||||
g_free (image->os);
|
||||
image->os = g_strdup (os);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_image_set_layers (FlatpakOciImage *image,
|
||||
const char **layers)
|
||||
{
|
||||
g_strfreev (image->rootfs.diff_ids);
|
||||
image->rootfs.diff_ids = g_strdupv ((char **)layers);
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_image_set_layer (FlatpakOciImage *image,
|
||||
const char *layer)
|
||||
{
|
||||
const char *layers[] = {layer, NULL};
|
||||
|
||||
flatpak_oci_image_set_layers (image, layers);
|
||||
}
|
||||
|
||||
static void
|
||||
add_annotation (GHashTable *annotations, const char *key, const char *value)
|
||||
{
|
||||
g_hash_table_replace (annotations,
|
||||
g_strdup (key),
|
||||
g_strdup (value));
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_add_annotations_for_commit (GHashTable *annotations,
|
||||
const char *ref,
|
||||
const char *commit,
|
||||
GVariant *commit_data)
|
||||
{
|
||||
if (ref)
|
||||
add_annotation (annotations,"org.flatpak.Ostree.Ref", ref);
|
||||
|
||||
if (commit)
|
||||
add_annotation (annotations,"org.flatpak.Ostree.Commit", commit);
|
||||
|
||||
if (commit_data)
|
||||
{
|
||||
g_autofree char *parent = NULL;
|
||||
g_autofree char *subject = NULL;
|
||||
g_autofree char *body = NULL;
|
||||
g_autofree char *timestamp = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
int i;
|
||||
|
||||
parent = ostree_commit_get_parent (commit_data);
|
||||
if (parent)
|
||||
add_annotation (annotations, "org.flatpak.Ostree.ParentCommit", parent);
|
||||
|
||||
metadata = g_variant_get_child_value (commit_data, 0);
|
||||
for (i = 0; i < g_variant_n_children (metadata); i++)
|
||||
{
|
||||
g_autoptr(GVariant) elm = g_variant_get_child_value (metadata, i);
|
||||
g_autoptr(GVariant) value = g_variant_get_child_value (elm, 1);
|
||||
g_autofree char *key = NULL;
|
||||
g_autofree char *full_key = NULL;
|
||||
g_autofree char *value_base64 = NULL;
|
||||
|
||||
g_variant_get_child (elm, 0, "s", &key);
|
||||
full_key = g_strdup_printf ("org.flatpak.Ostree.Metadata.%s", key);
|
||||
|
||||
value_base64 = g_base64_encode (g_variant_get_data (value), g_variant_get_size (value));
|
||||
add_annotation (annotations, full_key, value_base64);
|
||||
}
|
||||
|
||||
timestamp = g_strdup_printf ("%"G_GUINT64_FORMAT, ostree_commit_get_timestamp (commit_data));
|
||||
add_annotation (annotations, "org.flatpak.Ostree.Timestamp", timestamp);
|
||||
|
||||
g_variant_get_child (commit_data, 3, "s", &subject);
|
||||
add_annotation (annotations, "org.flatpak.Ostree.Subject", subject);
|
||||
|
||||
g_variant_get_child (commit_data, 4, "s", &body);
|
||||
add_annotation (annotations, "org.flatpak.Ostree.Body", body);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_oci_parse_commit_annotations (GHashTable *annotations,
|
||||
guint64 *out_timestamp,
|
||||
char **out_subject,
|
||||
char **out_body,
|
||||
char **out_ref,
|
||||
char **out_commit,
|
||||
char **out_parent_commit,
|
||||
GVariantBuilder *metadata_builder)
|
||||
{
|
||||
const char *oci_timestamp, *oci_subject, *oci_body, *oci_parent_commit, *oci_commit, *oci_ref;
|
||||
GHashTableIter iter;
|
||||
gpointer _key, _value;
|
||||
|
||||
oci_ref = g_hash_table_lookup (annotations, "org.flatpak.Ostree.Ref");
|
||||
if (oci_ref != NULL && out_ref != NULL && *out_ref == NULL)
|
||||
*out_ref = g_strdup (oci_ref);
|
||||
|
||||
oci_commit = g_hash_table_lookup (annotations, "org.flatpak.Ostree.Commit");
|
||||
if (oci_commit != NULL && out_commit != NULL && *out_commit == NULL)
|
||||
*out_commit = g_strdup (oci_commit);
|
||||
|
||||
oci_parent_commit = g_hash_table_lookup (annotations, "org.flatpak.Ostree.ParentCommit");
|
||||
if (oci_parent_commit != NULL && out_parent_commit != NULL && *out_parent_commit == NULL)
|
||||
*out_parent_commit = g_strdup (oci_parent_commit);
|
||||
|
||||
oci_timestamp = g_hash_table_lookup (annotations, "org.flatpak.Ostree.Timestamp");
|
||||
if (oci_timestamp != NULL && out_timestamp != NULL && *out_timestamp == 0)
|
||||
*out_timestamp = g_ascii_strtoull (oci_timestamp, NULL, 10);
|
||||
|
||||
oci_subject = g_hash_table_lookup (annotations, "org.flatpak.Ostree.Subject");
|
||||
if (oci_subject != NULL && out_subject != NULL && *out_subject == NULL)
|
||||
*out_subject = g_strdup (oci_subject);
|
||||
|
||||
oci_body = g_hash_table_lookup (annotations, "org.flatpak.Ostree.Body");
|
||||
if (oci_body != NULL && out_body != NULL && *out_body == NULL)
|
||||
*out_body = g_strdup (oci_body);
|
||||
|
||||
if (metadata_builder)
|
||||
{
|
||||
g_hash_table_iter_init (&iter, annotations);
|
||||
while (g_hash_table_iter_next (&iter, &_key, &_value))
|
||||
{
|
||||
const char *key = _key;
|
||||
const char *value = _value;
|
||||
guchar *bin;
|
||||
gsize bin_len;
|
||||
g_autoptr(GVariant) data = NULL;
|
||||
|
||||
if (!g_str_has_prefix (key, "org.flatpak.Ostree.Metadata."))
|
||||
continue;
|
||||
key += strlen ("org.flatpak.Ostree.Metadata.");
|
||||
|
||||
bin = g_base64_decode (value, &bin_len);
|
||||
data = g_variant_ref_sink (g_variant_new_from_data (G_VARIANT_TYPE("v"), bin, bin_len, FALSE,
|
||||
g_free, bin));
|
||||
g_variant_builder_add (metadata_builder, "{s@v}", key, data);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright © 2016 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __FLATPAK_JSON_OCI_H__
|
||||
#define __FLATPAK_JSON_OCI_H__
|
||||
|
||||
#include "flatpak-json.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_DESCRIPTOR "application/vnd.oci.descriptor.v1+json"
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST "application/vnd.oci.image.manifest.v1+json"
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFESTLIST "application/vnd.oci.image.manifest.list.v1+json"
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_IMAGE_LAYER "application/vnd.oci.image.layer.v1.tar+gzip"
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_IMAGE_LAYER_NONDISTRIBUTABLE "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
|
||||
#define FLATPAK_OCI_MEDIA_TYPE_IMAGE_CONFIG "application/vnd.oci.image.config.v1+json"
|
||||
|
||||
const char * flatpak_arch_to_oci_arch (const char *flatpak_arch);
|
||||
|
||||
typedef struct {
|
||||
char *mediatype;
|
||||
char *digest;
|
||||
gint64 size;
|
||||
char **urls;
|
||||
} FlatpakOciDescriptor;
|
||||
|
||||
void flatpak_oci_descriptor_destroy (FlatpakOciDescriptor *self);
|
||||
void flatpak_oci_descriptor_free (FlatpakOciDescriptor *self);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *architecture;
|
||||
char *os;
|
||||
char *os_version;
|
||||
char **os_features;
|
||||
char *variant;
|
||||
char **features;
|
||||
} FlatpakOciManifestPlatform;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FlatpakOciDescriptor parent;
|
||||
FlatpakOciManifestPlatform platform;
|
||||
} FlatpakOciManifestDescriptor;
|
||||
|
||||
void flatpak_oci_manifest_descriptor_destroy (FlatpakOciManifestDescriptor *self);
|
||||
void flatpak_oci_manifest_descriptor_free (FlatpakOciManifestDescriptor *self);
|
||||
|
||||
#define FLATPAK_TYPE_OCI_REF flatpak_oci_ref_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (FlatpakOciRef, flatpak_oci_ref, FLATPAK_OCI, REF, FlatpakJson)
|
||||
|
||||
struct _FlatpakOciRef {
|
||||
FlatpakJson parent;
|
||||
|
||||
FlatpakOciDescriptor descriptor;
|
||||
};
|
||||
|
||||
struct _FlatpakOciRefClass {
|
||||
FlatpakJsonClass parent_class;
|
||||
};
|
||||
|
||||
FlatpakOciRef *flatpak_oci_ref_new (const char *mediatype,
|
||||
const char *digest,
|
||||
gint64 size);
|
||||
const char * flatpak_oci_ref_get_mediatype (FlatpakOciRef *self);
|
||||
const char * flatpak_oci_ref_get_digest (FlatpakOciRef *self);
|
||||
gint64 flatpak_oci_ref_get_size (FlatpakOciRef *self);
|
||||
const char ** flatpak_oci_ref_get_urls (FlatpakOciRef *self);
|
||||
void flatpak_oci_ref_set_urls (FlatpakOciRef *self,
|
||||
const char **urls);
|
||||
|
||||
|
||||
#define FLATPAK_TYPE_OCI_VERSIONED flatpak_oci_versioned_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (FlatpakOciVersioned, flatpak_oci_versioned, FLATPAK_OCI, VERSIONED, FlatpakJson)
|
||||
|
||||
struct _FlatpakOciVersioned {
|
||||
FlatpakJson parent;
|
||||
|
||||
int version;
|
||||
char *mediatype;
|
||||
};
|
||||
|
||||
struct _FlatpakOciVersionedClass {
|
||||
FlatpakJsonClass parent_class;
|
||||
};
|
||||
|
||||
FlatpakOciVersioned *flatpak_oci_versioned_from_json (GBytes *bytes,
|
||||
GError **error);
|
||||
const char * flatpak_oci_versioned_get_mediatype (FlatpakOciVersioned *self);
|
||||
gint64 flatpak_oci_versioned_get_version (FlatpakOciVersioned *self);
|
||||
|
||||
#define FLATPAK_TYPE_OCI_MANIFEST flatpak_oci_manifest_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (FlatpakOciManifest, flatpak_oci_manifest, FLATPAK, OCI_MANIFEST, FlatpakOciVersioned)
|
||||
|
||||
struct _FlatpakOciManifest
|
||||
{
|
||||
FlatpakOciVersioned parent;
|
||||
|
||||
FlatpakOciDescriptor config;
|
||||
FlatpakOciDescriptor **layers;
|
||||
GHashTable *annotations;
|
||||
};
|
||||
|
||||
struct _FlatpakOciManifestClass
|
||||
{
|
||||
FlatpakOciVersionedClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
FlatpakOciManifest *flatpak_oci_manifest_new (void);
|
||||
void flatpak_oci_manifest_set_config (FlatpakOciManifest *self,
|
||||
FlatpakOciRef *ref);
|
||||
void flatpak_oci_manifest_set_layers (FlatpakOciManifest *self,
|
||||
FlatpakOciRef **refs);
|
||||
void flatpak_oci_manifest_set_layer (FlatpakOciManifest *self,
|
||||
FlatpakOciRef *ref);
|
||||
int flatpak_oci_manifest_get_n_layers (FlatpakOciManifest *self);
|
||||
const char * flatpak_oci_manifest_get_layer_digest (FlatpakOciManifest *self,
|
||||
int i);
|
||||
GHashTable * flatpak_oci_manifest_get_annotations (FlatpakOciManifest *self);
|
||||
|
||||
#define FLATPAK_TYPE_OCI_MANIFEST_LIST flatpak_oci_manifest_list_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (FlatpakOciManifestList, flatpak_oci_manifest_list, FLATPAK, OCI_MANIFEST_LIST, FlatpakOciVersioned)
|
||||
|
||||
struct _FlatpakOciManifestList
|
||||
{
|
||||
FlatpakOciVersioned parent;
|
||||
|
||||
FlatpakOciManifestDescriptor **manifests;
|
||||
GHashTable *annotations;
|
||||
};
|
||||
|
||||
struct _FlatpakOciManifestListClass
|
||||
{
|
||||
FlatpakOciVersionedClass parent_class;
|
||||
};
|
||||
|
||||
#define FLATPAK_TYPE_OCI_IMAGE flatpak_oci_image_get_type ()
|
||||
G_DECLARE_FINAL_TYPE (FlatpakOciImage, flatpak_oci_image, FLATPAK, OCI_IMAGE, FlatpakJson)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *type;
|
||||
char **diff_ids;
|
||||
} FlatpakOciImageRootfs;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *user;
|
||||
char *working_dir;
|
||||
gint64 memory;
|
||||
gint64 memory_swap;
|
||||
gint64 cpu_shares;
|
||||
char **env;
|
||||
char **cmd;
|
||||
char **entrypoint;
|
||||
char **exposed_ports;
|
||||
char **volumes;
|
||||
GHashTable *labels;
|
||||
} FlatpakOciImageConfig;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *created;
|
||||
char *created_by;
|
||||
char *author;
|
||||
char *comment;
|
||||
gboolean empty_layer;
|
||||
} FlatpakOciImageHistory;
|
||||
|
||||
struct _FlatpakOciImage
|
||||
{
|
||||
FlatpakJson parent;
|
||||
|
||||
char *created;
|
||||
char *author;
|
||||
char *architecture;
|
||||
char *os;
|
||||
FlatpakOciImageRootfs rootfs;
|
||||
FlatpakOciImageConfig config;
|
||||
FlatpakOciImageHistory **history;
|
||||
};
|
||||
|
||||
struct _FlatpakOciImageClass
|
||||
{
|
||||
FlatpakJsonClass parent_class;
|
||||
};
|
||||
|
||||
FlatpakOciImage *flatpak_oci_image_new (void);
|
||||
void flatpak_oci_image_set_created (FlatpakOciImage *image,
|
||||
const char *created);
|
||||
void flatpak_oci_image_set_architecture (FlatpakOciImage *image,
|
||||
const char *arch);
|
||||
void flatpak_oci_image_set_os (FlatpakOciImage *image,
|
||||
const char *os);
|
||||
void flatpak_oci_image_set_layers (FlatpakOciImage *image,
|
||||
const char **layers);
|
||||
void flatpak_oci_image_set_layer (FlatpakOciImage *image,
|
||||
const char *layer);
|
||||
|
||||
void flatpak_oci_add_annotations_for_commit (GHashTable *annotations,
|
||||
const char *ref,
|
||||
const char *commit,
|
||||
GVariant *commit_data);
|
||||
void flatpak_oci_parse_commit_annotations (GHashTable *annotations,
|
||||
guint64 *out_timestamp,
|
||||
char **out_subject,
|
||||
char **out_body,
|
||||
char **out_ref,
|
||||
char **out_commit,
|
||||
char **out_parent_commit,
|
||||
GVariantBuilder *metadata_builder);
|
||||
|
||||
#endif /* __FLATPAK_JSON_OCI_H__ */
|
|
@ -0,0 +1,597 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Red Hat, Inc
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "flatpak-json.h"
|
||||
#include "flatpak-utils.h"
|
||||
#include "libglnx.h"
|
||||
|
||||
G_DEFINE_TYPE (FlatpakJson, flatpak_json, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
flatpak_json_finalize (GObject *object)
|
||||
{
|
||||
G_OBJECT_CLASS (flatpak_json_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_json_class_init (FlatpakJsonClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = flatpak_json_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
flatpak_json_init (FlatpakJson *self)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
demarshal (JsonNode *parent_node,
|
||||
const char *name,
|
||||
gpointer dest,
|
||||
FlatpakJsonPropType type,
|
||||
gpointer type_data,
|
||||
gpointer type_data2,
|
||||
GError **error)
|
||||
{
|
||||
JsonObject *parent_object;
|
||||
JsonNode *node;
|
||||
|
||||
if (type != FLATPAK_JSON_PROP_TYPE_PARENT)
|
||||
{
|
||||
parent_object = json_node_get_object (parent_node);
|
||||
node = json_object_get_member (parent_object, name);
|
||||
}
|
||||
else
|
||||
node = parent_node;
|
||||
|
||||
if (node == NULL || JSON_NODE_TYPE (node) == JSON_NODE_NULL)
|
||||
return TRUE;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FLATPAK_JSON_PROP_TYPE_STRING:
|
||||
if (!JSON_NODE_HOLDS_VALUE (node) ||
|
||||
json_node_get_value_type (node) != G_TYPE_STRING)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting string for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
*(char **)dest = g_strdup (json_node_get_string (node));
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_INT64:
|
||||
if (!JSON_NODE_HOLDS_VALUE (node) ||
|
||||
json_node_get_value_type (node) != G_TYPE_INT64)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting int64 for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
*(gint64 *)dest = json_node_get_int (node);
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_BOOL:
|
||||
if (!JSON_NODE_HOLDS_VALUE (node) ||
|
||||
json_node_get_value_type (node) != G_TYPE_BOOLEAN)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting bool for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
*(gboolean *)dest = json_node_get_boolean (node);
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRV:
|
||||
if (!JSON_NODE_HOLDS_ARRAY (node))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting array for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
JsonArray *array = json_node_get_array (node);
|
||||
guint i, array_len = json_array_get_length (array);
|
||||
g_autoptr(GPtrArray) str_array = g_ptr_array_sized_new (array_len + 1);
|
||||
|
||||
for (i = 0; i < array_len; i++)
|
||||
{
|
||||
JsonNode *val = json_array_get_element (array, i);
|
||||
|
||||
if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE)
|
||||
continue;
|
||||
|
||||
if (json_node_get_string (val) != NULL)
|
||||
g_ptr_array_add (str_array, (gpointer) g_strdup (json_node_get_string (val)));
|
||||
}
|
||||
|
||||
g_ptr_array_add (str_array, NULL);
|
||||
*(char ***)dest = (char **)g_ptr_array_free (g_steal_pointer (&str_array), FALSE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_PARENT:
|
||||
case FLATPAK_JSON_PROP_TYPE_STRUCT:
|
||||
if (!JSON_NODE_HOLDS_OBJECT (node))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting object for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
FlatpakJsonProp *struct_props = type_data;
|
||||
int i;
|
||||
|
||||
for (i = 0; struct_props[i].name != NULL; i++)
|
||||
{
|
||||
if (!demarshal (node, struct_props[i].name,
|
||||
G_STRUCT_MEMBER_P (dest, struct_props[i].offset),
|
||||
struct_props[i].type, struct_props[i].type_data, struct_props[i].type_data2,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRUCTV:
|
||||
if (!JSON_NODE_HOLDS_ARRAY (node))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting array for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
JsonArray *array = json_node_get_array (node);
|
||||
guint array_len = json_array_get_length (array);
|
||||
FlatpakJsonProp *struct_props = type_data;
|
||||
g_autoptr(GPtrArray) obj_array = g_ptr_array_sized_new (array_len + 1);
|
||||
int i, j;
|
||||
gboolean res = TRUE;
|
||||
|
||||
for (j = 0; res && j < array_len; j++)
|
||||
{
|
||||
JsonNode *val = json_array_get_element (array, j);
|
||||
gpointer new_element;
|
||||
|
||||
if (!JSON_NODE_HOLDS_OBJECT (val))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting object elemen for property %s", name);
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
new_element = g_malloc0 ((gsize)type_data2);
|
||||
g_ptr_array_add (obj_array, new_element);
|
||||
|
||||
for (i = 0; struct_props[i].name != NULL; i++)
|
||||
{
|
||||
if (!demarshal (val, struct_props[i].name,
|
||||
G_STRUCT_MEMBER_P (new_element, struct_props[i].offset),
|
||||
struct_props[i].type, struct_props[i].type_data, struct_props[i].type_data2,
|
||||
error))
|
||||
{
|
||||
res = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* NULL terminate */
|
||||
g_ptr_array_add (obj_array, NULL);
|
||||
|
||||
/* We always set the array, even if it is partial, because we don't know how
|
||||
to free what we demarshalled so far */
|
||||
*(gpointer *)dest = (gpointer *)g_ptr_array_free (g_steal_pointer (&obj_array), FALSE);
|
||||
return res;
|
||||
}
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRMAP:
|
||||
if (!JSON_NODE_HOLDS_OBJECT (node))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting object for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
g_autoptr(GHashTable) h = NULL;
|
||||
JsonObject *object = json_node_get_object (node);
|
||||
g_autoptr(GList) members = NULL;
|
||||
GList *l;
|
||||
|
||||
h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
|
||||
|
||||
members = json_object_get_members (object);
|
||||
for (l = members; l != NULL; l = l->next)
|
||||
{
|
||||
const char *member_name = l->data;
|
||||
JsonNode *val;
|
||||
const char *val_str;
|
||||
|
||||
val = json_object_get_member (object, member_name);
|
||||
val_str = json_node_get_string (val);
|
||||
if (val_str == NULL)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Wrong type for string member %s", member_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_hash_table_insert (h, g_strdup (member_name), g_strdup (val_str));
|
||||
}
|
||||
|
||||
*(GHashTable **)dest = g_steal_pointer (&h);
|
||||
}
|
||||
break;
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_BOOLMAP:
|
||||
if (!JSON_NODE_HOLDS_OBJECT (node))
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting object for property %s", name);
|
||||
return FALSE;
|
||||
}
|
||||
{
|
||||
JsonObject *object = json_node_get_object (node);
|
||||
g_autoptr(GPtrArray) res = g_ptr_array_new_with_free_func (g_free);
|
||||
g_autoptr(GList) members = NULL;
|
||||
GList *l;
|
||||
|
||||
members = json_object_get_members (object);
|
||||
for (l = members; l != NULL; l = l->next)
|
||||
{
|
||||
const char *member_name = l->data;
|
||||
|
||||
g_ptr_array_add (res, g_strdup (member_name));
|
||||
}
|
||||
|
||||
g_ptr_array_add (res, NULL);
|
||||
|
||||
*(char ***)dest = (char **)g_ptr_array_free (g_steal_pointer (&res), FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FlatpakJson *
|
||||
flatpak_json_from_node (JsonNode *node, GType type, GError **error)
|
||||
{
|
||||
g_autoptr(FlatpakJson) json = NULL;
|
||||
FlatpakJsonProp *props = NULL;
|
||||
gpointer class;
|
||||
int i;
|
||||
|
||||
/* We should handle these before we get here */
|
||||
g_assert (node != NULL);
|
||||
g_assert (JSON_NODE_TYPE (node) != JSON_NODE_NULL);
|
||||
|
||||
if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"Expecting a JSON object, but the node is of type `%s'",
|
||||
json_node_type_name (node));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json = g_object_new (type, NULL);
|
||||
|
||||
class = FLATPAK_JSON_GET_CLASS (json);
|
||||
while (FLATPAK_JSON_CLASS (class)->props != NULL)
|
||||
{
|
||||
props = FLATPAK_JSON_CLASS (class)->props;
|
||||
for (i = 0; props[i].name != NULL; i++)
|
||||
{
|
||||
if (!demarshal (node, props[i].name,
|
||||
G_STRUCT_MEMBER_P (json, props[i].offset),
|
||||
props[i].type, props[i].type_data, props[i].type_data2,
|
||||
error))
|
||||
return NULL;
|
||||
}
|
||||
class = g_type_class_peek_parent (class);
|
||||
}
|
||||
|
||||
return g_steal_pointer (&json);
|
||||
}
|
||||
|
||||
FlatpakJson *
|
||||
flatpak_json_from_bytes (GBytes *bytes,
|
||||
GType type,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(JsonParser) parser = NULL;
|
||||
JsonNode *root = NULL;
|
||||
|
||||
parser = json_parser_new ();
|
||||
if (!json_parser_load_from_data (parser,
|
||||
g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes),
|
||||
error))
|
||||
return NULL;
|
||||
|
||||
root = json_parser_get_root (parser);
|
||||
|
||||
return flatpak_json_from_node (root, type, error);
|
||||
}
|
||||
|
||||
static JsonNode *
|
||||
marshal (JsonObject *parent,
|
||||
const char *name,
|
||||
gpointer src,
|
||||
FlatpakJsonPropType type,
|
||||
gpointer type_data)
|
||||
{
|
||||
JsonNode *retval = NULL;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FLATPAK_JSON_PROP_TYPE_STRING:
|
||||
{
|
||||
const char *str = *(const char **)src;
|
||||
if (str != NULL)
|
||||
retval = json_node_init_string (json_node_alloc (), str);
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_INT64:
|
||||
{
|
||||
retval = json_node_init_int (json_node_alloc (), *(gint64 *)src);
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_BOOL:
|
||||
{
|
||||
gboolean val = *(gboolean *)src;
|
||||
if (val)
|
||||
retval = json_node_init_boolean (json_node_alloc (), val);
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRV:
|
||||
{
|
||||
char **strv = *(char ***)src;
|
||||
int i;
|
||||
JsonArray *array;
|
||||
|
||||
if (strv != NULL && strv[0] != NULL)
|
||||
{
|
||||
array = json_array_sized_new (g_strv_length (strv));
|
||||
for (i = 0; strv[i] != NULL; i++)
|
||||
{
|
||||
JsonNode *str = json_node_new (JSON_NODE_VALUE);
|
||||
|
||||
json_node_set_string (str, strv[i]);
|
||||
json_array_add_element (array, str);
|
||||
}
|
||||
|
||||
retval = json_node_init_array (json_node_alloc (), array);
|
||||
json_array_unref (array);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_PARENT:
|
||||
case FLATPAK_JSON_PROP_TYPE_STRUCT:
|
||||
{
|
||||
FlatpakJsonProp *struct_props = type_data;
|
||||
JsonObject *obj;
|
||||
int i;
|
||||
|
||||
if (type == FLATPAK_JSON_PROP_TYPE_PARENT)
|
||||
obj = parent;
|
||||
else
|
||||
obj = json_object_new ();
|
||||
|
||||
for (i = 0; struct_props[i].name != NULL; i++)
|
||||
{
|
||||
JsonNode *val = marshal (obj, struct_props[i].name,
|
||||
G_STRUCT_MEMBER_P (src, struct_props[i].offset),
|
||||
struct_props[i].type, struct_props[i].type_data);
|
||||
|
||||
if (val == NULL)
|
||||
continue;
|
||||
|
||||
json_object_set_member (obj, struct_props[i].name, val);
|
||||
}
|
||||
|
||||
if (type != FLATPAK_JSON_PROP_TYPE_PARENT)
|
||||
{
|
||||
retval = json_node_new (JSON_NODE_OBJECT);
|
||||
json_node_take_object (retval, obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRUCTV:
|
||||
{
|
||||
FlatpakJsonProp *struct_props = type_data;
|
||||
gpointer *structv = *(gpointer **)src;
|
||||
int i, j;
|
||||
JsonArray *array;
|
||||
|
||||
if (structv != NULL && structv[0] != NULL)
|
||||
{
|
||||
array = json_array_new ();
|
||||
for (j = 0; structv[j] != NULL; j++)
|
||||
{
|
||||
JsonObject *obj = json_object_new ();
|
||||
JsonNode *node;
|
||||
|
||||
for (i = 0; struct_props[i].name != NULL; i++)
|
||||
{
|
||||
JsonNode *val = marshal (obj, struct_props[i].name,
|
||||
G_STRUCT_MEMBER_P (structv[j], struct_props[i].offset),
|
||||
struct_props[i].type, struct_props[i].type_data);
|
||||
|
||||
if (val == NULL)
|
||||
continue;
|
||||
|
||||
json_object_set_member (obj, struct_props[i].name, val);
|
||||
}
|
||||
|
||||
node = json_node_new (JSON_NODE_OBJECT);
|
||||
json_node_take_object (node, obj);
|
||||
json_array_add_element (array, node);
|
||||
}
|
||||
|
||||
retval = json_node_init_array (json_node_alloc (), array);
|
||||
json_array_unref (array);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_STRMAP:
|
||||
{
|
||||
GHashTable *map = *(GHashTable **)src;
|
||||
|
||||
if (map != NULL && g_hash_table_size (map) > 0)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer _key, _value;
|
||||
JsonObject *object;
|
||||
|
||||
object = json_object_new ();
|
||||
|
||||
g_hash_table_iter_init (&iter, map);
|
||||
while (g_hash_table_iter_next (&iter, &_key, &_value))
|
||||
{
|
||||
const char *key = _key;
|
||||
const char *value = _value;
|
||||
JsonNode *str = json_node_new (JSON_NODE_VALUE);
|
||||
|
||||
json_node_set_string (str, value);
|
||||
json_object_set_member (object, key, str);
|
||||
}
|
||||
|
||||
retval = json_node_init_object (json_node_alloc (), object);
|
||||
json_object_unref (object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case FLATPAK_JSON_PROP_TYPE_BOOLMAP:
|
||||
{
|
||||
char **map = *(char ***)src;
|
||||
|
||||
if (map != NULL && map[0] != NULL)
|
||||
{
|
||||
JsonObject *object;
|
||||
int i;
|
||||
|
||||
object = json_object_new ();
|
||||
|
||||
for (i = 0; map[i] != NULL; i++)
|
||||
{
|
||||
const char *element = map[i];
|
||||
JsonObject *empty_o = json_object_new ();
|
||||
JsonNode *empty = json_node_init_object (json_node_alloc (), empty_o);
|
||||
json_object_unref (empty_o);
|
||||
|
||||
json_object_set_member (object, element, empty);
|
||||
}
|
||||
|
||||
retval = json_node_init_object (json_node_alloc (), object);
|
||||
json_object_unref (object);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
marshal_props_for_class (FlatpakJson *self,
|
||||
FlatpakJsonClass *class,
|
||||
JsonObject *obj)
|
||||
{
|
||||
FlatpakJsonProp *props = NULL;
|
||||
int i;
|
||||
gpointer parent_class;
|
||||
|
||||
parent_class = g_type_class_peek_parent (class);
|
||||
|
||||
if (FLATPAK_JSON_CLASS (parent_class)->props != NULL)
|
||||
marshal_props_for_class (self,
|
||||
FLATPAK_JSON_CLASS(parent_class),
|
||||
obj);
|
||||
|
||||
props = FLATPAK_JSON_CLASS (class)->props;
|
||||
for (i = 0; props[i].name != NULL; i++)
|
||||
{
|
||||
JsonNode *val = marshal (obj, props[i].name,
|
||||
G_STRUCT_MEMBER_P (self, props[i].offset),
|
||||
props[i].type, props[i].type_data);
|
||||
|
||||
if (val == NULL)
|
||||
continue;
|
||||
|
||||
json_object_set_member (obj, props[i].name, val);
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode *
|
||||
flatpak_json_to_node (FlatpakJson *self)
|
||||
{
|
||||
JsonNode *retval;
|
||||
JsonObject *obj;
|
||||
gpointer class;
|
||||
|
||||
if (self == NULL)
|
||||
json_node_new (JSON_NODE_NULL);
|
||||
|
||||
obj = json_object_new ();
|
||||
|
||||
class = FLATPAK_JSON_GET_CLASS (self);
|
||||
marshal_props_for_class (self, FLATPAK_JSON_CLASS (class), obj);
|
||||
|
||||
retval = json_node_new (JSON_NODE_OBJECT);
|
||||
json_node_take_object (retval, obj);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
GBytes *
|
||||
flatpak_json_to_bytes (FlatpakJson *self)
|
||||
{
|
||||
g_autoptr(JsonNode) node = NULL;
|
||||
char *str;
|
||||
|
||||
node = flatpak_json_to_node (FLATPAK_JSON (self));
|
||||
|
||||
str = json_to_string (node, TRUE);
|
||||
return g_bytes_new_take (str, strlen (str));
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright © 2016 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __FLATPAK_JSON_H__
|
||||
#define __FLATPAK_JSON_H__
|
||||
|
||||
#include <json-glib/json-glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FLATPAK_TYPE_JSON flatpak_json_get_type ()
|
||||
|
||||
typedef struct _FlatpakJsonProp FlatpakJsonProp;
|
||||
|
||||
typedef enum {
|
||||
FLATPAK_JSON_PROP_TYPE_PARENT,
|
||||
FLATPAK_JSON_PROP_TYPE_INT64,
|
||||
FLATPAK_JSON_PROP_TYPE_BOOL,
|
||||
FLATPAK_JSON_PROP_TYPE_STRING,
|
||||
FLATPAK_JSON_PROP_TYPE_STRUCT,
|
||||
FLATPAK_JSON_PROP_TYPE_STRUCTV,
|
||||
FLATPAK_JSON_PROP_TYPE_STRV,
|
||||
FLATPAK_JSON_PROP_TYPE_STRMAP,
|
||||
FLATPAK_JSON_PROP_TYPE_BOOLMAP,
|
||||
} FlatpakJsonPropType;
|
||||
|
||||
struct _FlatpakJsonProp {
|
||||
const char *name;
|
||||
gsize offset;
|
||||
FlatpakJsonPropType type;
|
||||
gpointer type_data;
|
||||
gpointer type_data2;
|
||||
} ;
|
||||
|
||||
#define FLATPAK_JSON_STRING_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_STRING }
|
||||
#define FLATPAK_JSON_INT64_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_INT64 }
|
||||
#define FLATPAK_JSON_BOOL_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_BOOL }
|
||||
#define FLATPAK_JSON_STRV_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_STRV }
|
||||
#define FLATPAK_JSON_STRMAP_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_STRMAP }
|
||||
#define FLATPAK_JSON_BOOLMAP_PROP(_struct, _field, _name) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_BOOLMAP }
|
||||
#define FLATPAK_JSON_STRUCT_PROP(_struct, _field, _name, _props) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_STRUCT, (gpointer)_props}
|
||||
#define FLATPAK_JSON_PARENT_PROP(_struct, _field, _props) \
|
||||
{ "parent", G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_PARENT, (gpointer)_props}
|
||||
#define FLATPAK_JSON_STRUCTV_PROP(_struct, _field, _name, _props) \
|
||||
{ _name, G_STRUCT_OFFSET (_struct, _field), FLATPAK_JSON_PROP_TYPE_STRUCTV, (gpointer)_props, (gpointer) sizeof (**((_struct *) 0)->_field) }
|
||||
#define FLATPAK_JSON_LAST_PROP { NULL }
|
||||
|
||||
G_DECLARE_DERIVABLE_TYPE (FlatpakJson, flatpak_json, FLATPAK, JSON, GObject)
|
||||
|
||||
struct _FlatpakJsonClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
FlatpakJsonProp *props;
|
||||
const char *mediatype;
|
||||
};
|
||||
|
||||
FlatpakJson *flatpak_json_from_node (JsonNode *node,
|
||||
GType type,
|
||||
GError **error);
|
||||
JsonNode *flatpak_json_to_node (FlatpakJson *self);
|
||||
FlatpakJson *flatpak_json_from_bytes (GBytes *bytes,
|
||||
GType type,
|
||||
GError **error);
|
||||
GBytes *flatpak_json_to_bytes (FlatpakJson *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __FLATPAK_JSON_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright © 2016 Red Hat, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* Alexander Larsson <alexl@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __FLATPAK_OCI_REGISTRY_H__
|
||||
#define __FLATPAK_OCI_REGISTRY_H__
|
||||
|
||||
#include "libglnx/libglnx.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include <archive.h>
|
||||
#include "flatpak-json-oci.h"
|
||||
#include "flatpak-utils.h"
|
||||
|
||||
#define FLATPAK_TYPE_OCI_REGISTRY flatpak_oci_registry_get_type ()
|
||||
#define FLATPAK_OCI_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_OCI_REGISTRY, FlatpakOciRegistry))
|
||||
#define FLATPAK_IS_OCI_REGISTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLATPAK_TYPE_OCI_REGISTRY))
|
||||
|
||||
GType flatpak_oci_registry_get_type (void);
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakOciRegistry, g_object_unref)
|
||||
|
||||
#define FLATPAK_TYPE_OCI_LAYER_WRITER flatpak_oci_layer_writer_get_type ()
|
||||
#define FLATPAK_OCI_LAYER_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FLATPAK_TYPE_OCI_LAYER_WRITER, FlatpakOciLayerWriter))
|
||||
#define FLATPAK_IS_OCI_LAYER_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FLATPAK_TYPE_OCI_LAYER_WRITER))
|
||||
|
||||
GType flatpak_oci_layer_writer_get_type (void);
|
||||
|
||||
typedef struct FlatpakOciLayerWriter FlatpakOciLayerWriter;
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakOciLayerWriter, g_object_unref)
|
||||
|
||||
FlatpakOciRegistry * flatpak_oci_registry_new (const char *uri,
|
||||
gboolean for_write,
|
||||
int tmp_dfd,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakOciRef * flatpak_oci_registry_load_ref (FlatpakOciRegistry *self,
|
||||
const char *ref,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_oci_registry_set_ref (FlatpakOciRegistry *self,
|
||||
const char *ref,
|
||||
FlatpakOciRef *data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
int flatpak_oci_registry_download_blob (FlatpakOciRegistry *self,
|
||||
const char *digest,
|
||||
FlatpakLoadUriProgress progress_cb,
|
||||
gpointer user_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GBytes * flatpak_oci_registry_load_blob (FlatpakOciRegistry *self,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char * flatpak_oci_registry_store_blob (FlatpakOciRegistry *self,
|
||||
GBytes *data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakOciRef * flatpak_oci_registry_store_json (FlatpakOciRegistry *self,
|
||||
FlatpakJson *json,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakOciVersioned * flatpak_oci_registry_load_versioned (FlatpakOciRegistry *self,
|
||||
const char *digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakOciLayerWriter *flatpak_oci_registry_write_layer (FlatpakOciRegistry *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
FlatpakOciManifest *flatpak_oci_registry_chose_image (FlatpakOciRegistry *self,
|
||||
const char *tag,
|
||||
char **out_digest,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
struct archive *flatpak_oci_layer_writer_get_archive (FlatpakOciLayerWriter *self);
|
||||
gboolean flatpak_oci_layer_writer_close (FlatpakOciLayerWriter *self,
|
||||
char **uncompressed_digest_out,
|
||||
FlatpakOciRef **ref_out,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
#endif /* __FLATPAK_OCI_REGISTRY_H__ */
|
|
@ -24,6 +24,7 @@
|
|||
#include "lib/flatpak-error.h"
|
||||
#include "flatpak-dir.h"
|
||||
#include "flatpak-portal-error.h"
|
||||
#include "flatpak-oci-registry.h"
|
||||
#include "flatpak-run.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
@ -49,6 +50,18 @@ static const GDBusErrorEntry flatpak_error_entries[] = {
|
|||
{FLATPAK_ERROR_NOT_INSTALLED, "org.freedesktop.Flatpak.Error.NotInstalled"},
|
||||
};
|
||||
|
||||
|
||||
GLNX_DEFINE_CLEANUP_FUNCTION (void *, flatpak_local_free_read_archive, archive_read_free)
|
||||
#define free_read_archive __attribute__((cleanup (flatpak_local_free_read_archive)))
|
||||
|
||||
static void
|
||||
propagate_libarchive_error (GError **error,
|
||||
struct archive *a)
|
||||
{
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
||||
"%s", archive_error_string (a));
|
||||
}
|
||||
|
||||
GQuark
|
||||
flatpak_error_quark (void)
|
||||
{
|
||||
|
@ -1355,6 +1368,14 @@ flatpak_table_printer_add_column (FlatpakTablePrinter *printer,
|
|||
g_ptr_array_add (printer->current, text ? g_strdup (text) : g_strdup (""));
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_table_printer_add_column_len (FlatpakTablePrinter *printer,
|
||||
const char *text,
|
||||
gsize len)
|
||||
{
|
||||
g_ptr_array_add (printer->current, text ? g_strndup (text, len) : g_strdup (""));
|
||||
}
|
||||
|
||||
void
|
||||
flatpak_table_printer_append_with_comma (FlatpakTablePrinter *printer,
|
||||
const char *text)
|
||||
|
@ -2159,6 +2180,14 @@ flatpak_open_in_tmpdir_at (int tmpdir_fd,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
GVariant *
|
||||
flatpak_gvariant_new_empty_string_dict (void)
|
||||
{
|
||||
g_auto(GVariantBuilder) builder = FLATPAK_VARIANT_BUILDER_INITIALIZER;
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_variant_save (GFile *dest,
|
||||
GVariant *variant,
|
||||
|
@ -3981,6 +4010,116 @@ flatpak_pull_from_bundle (OstreeRepo *repo,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
char *
|
||||
flatpak_pull_from_oci (OstreeRepo *repo,
|
||||
FlatpakOciRegistry *registry,
|
||||
const char *digest,
|
||||
FlatpakOciManifest *manifest,
|
||||
const char *ref,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(OstreeMutableTree) archive_mtree = NULL;
|
||||
g_autoptr(GFile) archive_root = NULL;
|
||||
g_autofree char *commit_checksum = NULL;
|
||||
g_autofree char *dir_uri = NULL;
|
||||
const char *parent = NULL;
|
||||
g_autofree char *subject = NULL;
|
||||
g_autofree char *body = NULL;
|
||||
guint64 timestamp = 0;
|
||||
g_autoptr(GVariantBuilder) metadata_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
GHashTable *annotations;
|
||||
int i;
|
||||
|
||||
g_assert (ref != NULL);
|
||||
g_assert (g_str_has_prefix (digest, "sha256:"));
|
||||
|
||||
annotations = flatpak_oci_manifest_get_annotations (manifest);
|
||||
if (annotations)
|
||||
flatpak_oci_parse_commit_annotations (annotations, ×tamp,
|
||||
&subject, &body,
|
||||
NULL, NULL, NULL,
|
||||
metadata_builder);
|
||||
|
||||
g_variant_builder_add (metadata_builder, "{s@v}", "xa.alt-id",
|
||||
g_variant_new_variant (g_variant_new_string (digest + strlen("sha256:"))));
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
/* There is no way to write a subset of the archive to a mtree, so instead
|
||||
we write all of it and then build a new mtree with the subset */
|
||||
archive_mtree = ostree_mutable_tree_new ();
|
||||
|
||||
for (i = 0; manifest->layers[i] != NULL; i++)
|
||||
{
|
||||
FlatpakOciDescriptor *layer = manifest->layers[i];
|
||||
OstreeRepoImportArchiveOptions opts = { 0, };
|
||||
free_read_archive struct archive *a = NULL;
|
||||
glnx_fd_close int layer_fd = -1;
|
||||
|
||||
opts.autocreate_parents = TRUE;
|
||||
opts.ignore_unsupported_content = TRUE;
|
||||
|
||||
layer_fd = flatpak_oci_registry_download_blob (registry, layer->digest,
|
||||
NULL, NULL,
|
||||
cancellable, error);
|
||||
if (layer_fd == -1)
|
||||
goto error;
|
||||
|
||||
a = archive_read_new ();
|
||||
#ifdef HAVE_ARCHIVE_READ_SUPPORT_FILTER_ALL
|
||||
archive_read_support_filter_all (a);
|
||||
#else
|
||||
archive_read_support_compression_all (a);
|
||||
#endif
|
||||
archive_read_support_format_all (a);
|
||||
if (archive_read_open_fd (a, layer_fd, 8192) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, a);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!ostree_repo_import_archive_to_mtree (repo, &opts, a, archive_mtree, NULL, cancellable, error))
|
||||
goto error;
|
||||
|
||||
if (archive_read_close (a) != ARCHIVE_OK)
|
||||
{
|
||||
propagate_libarchive_error (error, a);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ostree_repo_write_mtree (repo, archive_mtree, &archive_root, cancellable, error))
|
||||
goto error;
|
||||
|
||||
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile *) archive_root, error))
|
||||
goto error;
|
||||
|
||||
if (!ostree_repo_write_commit_with_time (repo,
|
||||
parent,
|
||||
subject,
|
||||
body,
|
||||
g_variant_builder_end (metadata_builder),
|
||||
OSTREE_REPO_FILE (archive_root),
|
||||
timestamp,
|
||||
&commit_checksum,
|
||||
cancellable, error))
|
||||
goto error;
|
||||
|
||||
ostree_repo_transaction_set_ref (repo, NULL, ref, commit_checksum);
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
return NULL;
|
||||
|
||||
return g_steal_pointer (&commit_checksum);
|
||||
|
||||
error:
|
||||
|
||||
ostree_repo_abort_transaction (repo, cancellable, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This allocates and locks a subdir of the tmp dir, using an existing
|
||||
* one with the same prefix if it is not in use already. */
|
||||
gboolean
|
||||
|
@ -4192,6 +4331,8 @@ flatpak_number_prompt (int min, int max, const char *prompt, ...)
|
|||
typedef struct {
|
||||
GMainLoop *loop;
|
||||
GError *error;
|
||||
GOutputStream *out;
|
||||
guint64 downloaded_bytes;
|
||||
GString *content;
|
||||
char buffer[16*1024];
|
||||
FlatpakLoadUriProgress progress;
|
||||
|
@ -4222,18 +4363,40 @@ load_uri_read_cb (GObject *source, GAsyncResult *res, gpointer user_data)
|
|||
nread = g_input_stream_read_finish (stream, res, &data->error);
|
||||
if (nread == -1 || nread == 0)
|
||||
{
|
||||
data->progress (data->content->len, data->user_data);
|
||||
if (data->progress)
|
||||
data->progress (data->downloaded_bytes, data->user_data);
|
||||
g_input_stream_close_async (stream,
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
stream_closed, data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_string_append_len (data->content, data->buffer, nread);
|
||||
if (data->out != NULL)
|
||||
{
|
||||
gsize n_written;
|
||||
|
||||
if (!g_output_stream_write_all (data->out, data->buffer, nread, &n_written,
|
||||
NULL, &data->error))
|
||||
{
|
||||
data->downloaded_bytes += n_written;
|
||||
g_input_stream_close_async (stream,
|
||||
G_PRIORITY_DEFAULT, NULL,
|
||||
stream_closed, data);
|
||||
return;
|
||||
}
|
||||
|
||||
data->downloaded_bytes += n_written;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->downloaded_bytes += nread;
|
||||
g_string_append_len (data->content, data->buffer, nread);
|
||||
}
|
||||
|
||||
if (g_get_monotonic_time () - data->last_progress_time > 1 * G_USEC_PER_SEC)
|
||||
{
|
||||
data->progress (data->content->len, data->user_data);
|
||||
if (data->progress)
|
||||
data->progress (data->downloaded_bytes, data->user_data);
|
||||
data->last_progress_time = g_get_monotonic_time ();
|
||||
}
|
||||
|
||||
|
@ -4248,7 +4411,6 @@ load_uri_callback (GObject *source_object,
|
|||
gpointer user_data)
|
||||
{
|
||||
SoupRequestHTTP *request = SOUP_REQUEST_HTTP(source_object);
|
||||
g_autoptr(GError) error = NULL;
|
||||
g_autoptr(GInputStream) in = NULL;
|
||||
LoadUriData *data = user_data;
|
||||
|
||||
|
@ -4287,6 +4449,32 @@ load_uri_callback (GObject *source_object,
|
|||
load_uri_read_cb, data);
|
||||
}
|
||||
|
||||
SoupSession *
|
||||
flatpak_create_soup_session (const char *user_agent)
|
||||
{
|
||||
SoupSession *soup_session;
|
||||
const char *http_proxy;
|
||||
|
||||
soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent,
|
||||
SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
|
||||
SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
|
||||
SOUP_SESSION_TIMEOUT, 60,
|
||||
SOUP_SESSION_IDLE_TIMEOUT, 60,
|
||||
NULL);
|
||||
soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_CONTENT_DECODER);
|
||||
http_proxy = g_getenv ("http_proxy");
|
||||
if (http_proxy)
|
||||
{
|
||||
g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy);
|
||||
if (!proxy_uri)
|
||||
g_warning ("Invalid proxy URI '%s'", http_proxy);
|
||||
else
|
||||
g_object_set (soup_session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
|
||||
}
|
||||
|
||||
return soup_session;
|
||||
}
|
||||
|
||||
GBytes *
|
||||
flatpak_load_http_uri (SoupSession *soup_session,
|
||||
const char *uri,
|
||||
|
@ -4296,7 +4484,6 @@ flatpak_load_http_uri (SoupSession *soup_session,
|
|||
GError **error)
|
||||
{
|
||||
GBytes *bytes = NULL;
|
||||
g_autoptr(SoupMessage) msg = NULL;
|
||||
g_autoptr(SoupRequestHTTP) request = NULL;
|
||||
g_autoptr(GMainLoop) loop = NULL;
|
||||
g_autoptr(GString) content = g_string_new ("");
|
||||
|
@ -4329,13 +4516,54 @@ flatpak_load_http_uri (SoupSession *soup_session,
|
|||
}
|
||||
|
||||
bytes = g_string_free_to_bytes (g_steal_pointer (&content));
|
||||
g_debug ("Received %" G_GSIZE_FORMAT " bytes", g_bytes_get_size (bytes));
|
||||
g_debug ("Received %" G_GSIZE_FORMAT " bytes", data.downloaded_bytes);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
gboolean
|
||||
flatpak_download_http_uri (SoupSession *soup_session,
|
||||
const char *uri,
|
||||
GOutputStream *out,
|
||||
FlatpakLoadUriProgress progress,
|
||||
gpointer user_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autoptr(SoupRequestHTTP) request = NULL;
|
||||
g_autoptr(GMainLoop) loop = NULL;
|
||||
LoadUriData data = { NULL };
|
||||
|
||||
g_debug ("Loading %s using libsoup", uri);
|
||||
|
||||
loop = g_main_loop_new (NULL, TRUE);
|
||||
data.loop = loop;
|
||||
data.out = out;
|
||||
data.progress = progress;
|
||||
data.user_data = user_data;
|
||||
data.last_progress_time = g_get_monotonic_time ();
|
||||
|
||||
request = soup_session_request_http (soup_session, "GET",
|
||||
uri, error);
|
||||
if (request == NULL)
|
||||
return FALSE;
|
||||
|
||||
soup_request_send_async (SOUP_REQUEST(request),
|
||||
cancellable,
|
||||
load_uri_callback, &data);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
if (data.error)
|
||||
{
|
||||
g_propagate_error (error, data.error);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_debug ("Received %" G_GSIZE_FORMAT " bytes", data.downloaded_bytes);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Uncomment to get debug traces in /tmp/flatpak-completion-debug.txt (nice
|
||||
* to not have it interfere with stdout/stderr)
|
||||
|
|
|
@ -37,6 +37,13 @@ typedef enum {
|
|||
} FlatpakHostCommandFlags;
|
||||
|
||||
|
||||
/* https://bugzilla.gnome.org/show_bug.cgi?id=766370 */
|
||||
#if !GLIB_CHECK_VERSION(2, 49, 3)
|
||||
#define FLATPAK_VARIANT_BUILDER_INITIALIZER {{0,}}
|
||||
#else
|
||||
#define FLATPAK_VARIANT_BUILDER_INITIALIZER {{{0,}}}
|
||||
#endif
|
||||
|
||||
gboolean flatpak_fail (GError **error,
|
||||
const char *format,
|
||||
...);
|
||||
|
@ -69,6 +76,7 @@ gboolean flatpak_variant_save (GFile *dest,
|
|||
GVariant *variant,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
GVariant * flatpak_gvariant_new_empty_string_dict (void);
|
||||
void flatpak_variant_builder_init_from_variant (GVariantBuilder *builder,
|
||||
const char *type,
|
||||
GVariant *variant);
|
||||
|
@ -238,6 +246,9 @@ FlatpakTablePrinter *flatpak_table_printer_new (void);
|
|||
void flatpak_table_printer_free (FlatpakTablePrinter *printer);
|
||||
void flatpak_table_printer_add_column (FlatpakTablePrinter *printer,
|
||||
const char *text);
|
||||
void flatpak_table_printer_add_column_len (FlatpakTablePrinter *printer,
|
||||
const char *text,
|
||||
gsize len);
|
||||
void flatpak_table_printer_append_with_comma (FlatpakTablePrinter *printer,
|
||||
const char *text);
|
||||
void flatpak_table_printer_finish_row (FlatpakTablePrinter *printer);
|
||||
|
@ -294,6 +305,13 @@ gboolean flatpak_pull_from_bundle (OstreeRepo *repo,
|
|||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
char * flatpak_pull_from_oci (OstreeRepo *repo,
|
||||
FlatpakOciRegistry *registry,
|
||||
const char *digest,
|
||||
FlatpakOciManifest *manifest,
|
||||
const char *ref,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
@ -520,12 +538,20 @@ long flatpak_number_prompt (int min, int max, const char *prompt, ...);
|
|||
typedef void (*FlatpakLoadUriProgress) (guint64 downloaded_bytes,
|
||||
gpointer user_data);
|
||||
|
||||
SoupSession * flatpak_create_soup_session (const char *user_agent);
|
||||
GBytes * flatpak_load_http_uri (SoupSession *soup_session,
|
||||
const char *uri,
|
||||
FlatpakLoadUriProgress progress,
|
||||
gpointer user_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean flatpak_download_http_uri (SoupSession *soup_session,
|
||||
const char *uri,
|
||||
GOutputStream *out,
|
||||
FlatpakLoadUriProgress progress,
|
||||
gpointer user_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
typedef struct {
|
||||
char *shell_cur;
|
||||
|
|
|
@ -44,6 +44,16 @@
|
|||
</group>
|
||||
<arg choice="plain">FILENAME</arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>flatpak install --oci</command>
|
||||
<arg choice="opt" rep="repeat">OPTION</arg>
|
||||
<group choice="opt">
|
||||
<arg choice="plain">--bundle</arg>
|
||||
<arg choice="plain">--from</arg>
|
||||
</group>
|
||||
<arg choice="plain">REGISTRYURI</arg>
|
||||
<arg choice="opt">TAG</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
|
@ -120,6 +130,14 @@
|
|||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--oci</option></term>
|
||||
|
||||
<listitem><para>
|
||||
Install from a oci registry with a given uri and optionally a tag (defaults to latest).
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--user</option></term>
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ libflatpak_la_LIBADD = \
|
|||
$(BASE_LIBS) \
|
||||
$(OSTREE_LIBS) \
|
||||
$(SOUP_LIBS) \
|
||||
$(JSON_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
test_libflatpak_SOURCES = \
|
||||
|
|
|
@ -372,6 +372,8 @@ get_ref (FlatpakDir *dir,
|
|||
g_auto(GStrv) parts = NULL;
|
||||
const char *origin = NULL;
|
||||
const char *commit = NULL;
|
||||
const char *alt_id = NULL;
|
||||
g_autofree char *latest_alt_id = NULL;
|
||||
g_autoptr(GFile) deploy_dir = NULL;
|
||||
g_autoptr(GFile) deploy_subdir = NULL;
|
||||
g_autofree char *deploy_path = NULL;
|
||||
|
@ -388,6 +390,7 @@ get_ref (FlatpakDir *dir,
|
|||
return NULL;
|
||||
origin = flatpak_deploy_data_get_origin (deploy_data);
|
||||
commit = flatpak_deploy_data_get_commit (deploy_data);
|
||||
alt_id = flatpak_deploy_data_get_alt_id (deploy_data);
|
||||
subpaths = flatpak_deploy_data_get_subpaths (deploy_data);
|
||||
installed_size = flatpak_deploy_data_get_installed_size (deploy_data);
|
||||
|
||||
|
@ -403,11 +406,11 @@ get_ref (FlatpakDir *dir,
|
|||
is_current = TRUE;
|
||||
}
|
||||
|
||||
latest_commit = flatpak_dir_read_latest (dir, origin, full_ref, NULL, NULL);
|
||||
latest_commit = flatpak_dir_read_latest (dir, origin, full_ref, &latest_alt_id, NULL, NULL);
|
||||
|
||||
return flatpak_installed_ref_new (full_ref,
|
||||
commit,
|
||||
latest_commit,
|
||||
alt_id ? alt_id : commit,
|
||||
latest_alt_id ? latest_alt_id : latest_commit,
|
||||
origin, subpaths,
|
||||
deploy_path,
|
||||
installed_size,
|
||||
|
|
|
@ -117,6 +117,7 @@ dist_test_scripts = \
|
|||
tests/test-bundle-system.sh \
|
||||
tests/test-builder.sh \
|
||||
tests/test-builder-python.sh \
|
||||
tests/test-oci.sh \
|
||||
$(NULL)
|
||||
|
||||
test_programs = testdb test-doc-portal testlibrary
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2011 Colin Walters <walters@verbum.org>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
. $(dirname $0)/libtest.sh
|
||||
|
||||
skip_without_bwrap
|
||||
skip_without_user_xattrs
|
||||
|
||||
echo "1..4"
|
||||
|
||||
setup_repo
|
||||
|
||||
${FLATPAK} ${U} install test-repo org.test.Platform master
|
||||
|
||||
${FLATPAK} build-bundle --oci repo oci-dir org.test.Hello
|
||||
|
||||
assert_has_file oci-dir/oci-layout
|
||||
assert_has_dir oci-dir/blobs/sha256
|
||||
assert_has_dir oci-dir/refs
|
||||
assert_file_has_content oci-dir/refs/latest "application/vnd.oci.image.manifest.v1+json"
|
||||
|
||||
for i in oci-dir/blobs/sha256/*; do
|
||||
echo $(basename $i) $i >> sums
|
||||
done
|
||||
sha256sum -c sums
|
||||
|
||||
echo "ok export oci"
|
||||
|
||||
ostree --repo=repo2 init --mode=archive-z2
|
||||
|
||||
$FLATPAK build-import-bundle --oci repo2 oci-dir
|
||||
|
||||
ostree checkout -U --repo=repo2 app/org.test.Hello/$ARCH/master checked-out
|
||||
|
||||
assert_has_dir checked-out/files
|
||||
assert_has_file checked-out/files/bin/hello.sh
|
||||
assert_has_file checked-out/metadata
|
||||
|
||||
echo "ok commit oci"
|
||||
|
||||
${FLATPAK} install ${U} --oci oci-dir latest
|
||||
|
||||
run org.test.Hello > hello_out
|
||||
assert_file_has_content hello_out '^Hello world, from a sandbox$'
|
||||
|
||||
echo "ok install oci"
|
||||
|
||||
make_updated_app
|
||||
${FLATPAK} build-bundle --oci repo oci-dir org.test.Hello
|
||||
|
||||
${FLATPAK} update ${U} org.test.Hello
|
||||
run org.test.Hello > hello_out
|
||||
assert_file_has_content hello_out '^Hello world, from a sandboxUPDATED$'
|
||||
|
||||
echo "ok update oci"
|
Loading…
Reference in New Issue