Files
flatpak/common/flatpak-json-oci.c
Alexander Larsson 49b5304589 Update OCI support to latest version of spec
This is a major change in the OCI support, as the format of the OCI image
registries changed. Instead of now having a "ref" file for each image
in the repo it has a single index json file, where the ref name is now
a per-image annotation.

This allows us to support OCI much better, as we can now use the actual
flatpak ref as the OCI ref name, and we can find all the flatpak refs
in a remote.

So, with this you can just use:
 flatpak remote-add --oci remote-name URL

and then you can use the regular flatpak operations on the remote.
2017-03-13 14:31:36 +01:00

858 lines
25 KiB
C

/*
* 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;
}
FlatpakOciDescriptor *
flatpak_oci_descriptor_new (const char *mediatype,
const char *digest,
gint64 size)
{
FlatpakOciDescriptor *desc = g_new0 (FlatpakOciDescriptor, 1);
desc->mediatype = g_strdup (mediatype);
desc->digest = g_strdup (digest);
desc->size = size;
desc->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
return desc;
}
void
flatpak_oci_descriptor_copy (FlatpakOciDescriptor *source,
FlatpakOciDescriptor *dest)
{
flatpak_oci_descriptor_destroy (dest);
dest->mediatype = g_strdup (source->mediatype);
dest->digest = g_strdup (source->digest);
dest->size = source->size;
dest->urls = g_strdupv ((char **)source->urls);
dest->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
if (source->annotations)
flatpak_oci_copy_annotations (source->annotations, dest->annotations);
}
void
flatpak_oci_descriptor_destroy (FlatpakOciDescriptor *self)
{
g_free (self->mediatype);
g_free (self->digest);
g_strfreev (self->urls);
if (self->annotations)
g_hash_table_destroy (self->annotations);
}
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_STRMAP_PROP(FlatpakOciDescriptor, annotations, "annotations"),
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);
}
FlatpakOciManifestDescriptor *
flatpak_oci_manifest_descriptor_new (void)
{
return g_new0 (FlatpakOciManifestDescriptor, 1);
}
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_OPT_STRUCT_PROP (FlatpakOciManifestDescriptor, platform, "platform", flatpak_oci_manifest_platform_props),
FLATPAK_JSON_LAST_PROP
};
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_INDEX) == 0)
return (FlatpakOciVersioned *) flatpak_json_from_node (root, FLATPAK_TYPE_OCI_INDEX, 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,
FlatpakOciDescriptor *desc)
{
g_free (self->config.mediatype);
self->config.mediatype = g_strdup (desc->mediatype);
g_free (self->config.digest);
self->config.digest = g_strdup (desc->digest);
self->config.size = desc->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,
FlatpakOciDescriptor *desc)
{
FlatpakOciDescriptor *descs[2] = { desc, NULL };
flatpak_oci_manifest_set_layers (self, descs);
}
void
flatpak_oci_manifest_set_layers (FlatpakOciManifest *self,
FlatpakOciDescriptor **descs)
{
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 *)descs);
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 (descs[i]->mediatype);
self->layers[i]->digest = g_strdup (descs[i]->digest);
self->layers[i]->size = descs[i]->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 (FlatpakOciIndex, flatpak_oci_index, FLATPAK_TYPE_OCI_VERSIONED);
static void
flatpak_oci_index_finalize (GObject *object)
{
FlatpakOciIndex *self = (FlatpakOciIndex *) 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_index_parent_class)->finalize (object);
}
static void
flatpak_oci_index_class_init (FlatpakOciIndexClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
FlatpakJsonClass *json_class = FLATPAK_JSON_CLASS (klass);
static FlatpakJsonProp props[] = {
FLATPAK_JSON_STRUCTV_PROP(FlatpakOciIndex, manifests, "manifests", flatpak_oci_manifest_descriptor_props),
FLATPAK_JSON_STRMAP_PROP(FlatpakOciIndex, annotations, "annotations"),
FLATPAK_JSON_LAST_PROP
};
object_class->finalize = flatpak_oci_index_finalize;
json_class->props = props;
json_class->mediatype = FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX;
}
static void
flatpak_oci_index_init (FlatpakOciIndex *self)
{
}
FlatpakOciIndex *
flatpak_oci_index_new (void)
{
FlatpakOciIndex *index;
index = g_object_new (FLATPAK_TYPE_OCI_INDEX, NULL);
index->parent.version = 2;
index->parent.mediatype = g_strdup (FLATPAK_OCI_MEDIA_TYPE_IMAGE_INDEX);
index->annotations = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
return index;
}
static FlatpakOciManifestDescriptor *
manifest_desc_for_desc (FlatpakOciDescriptor *src_descriptor)
{
FlatpakOciManifestDescriptor *desc;
desc = flatpak_oci_manifest_descriptor_new ();
flatpak_oci_descriptor_copy (src_descriptor, &desc->parent);
return desc;
}
int
flatpak_oci_index_get_n_manifests (FlatpakOciIndex *self)
{
return ptrv_count ((gpointer *)self->manifests);
}
void
flatpak_oci_index_add_manifest (FlatpakOciIndex *self,
FlatpakOciDescriptor *desc)
{
FlatpakOciManifestDescriptor *m;
const char *m_ref = NULL;
int count;
if (desc->annotations != NULL)
m_ref = g_hash_table_lookup (desc->annotations, "org.opencontainers.ref.name");
if (m_ref != NULL)
flatpak_oci_index_remove_manifest (self, m_ref);
count = flatpak_oci_index_get_n_manifests (self);
m = manifest_desc_for_desc (desc);
self->manifests = g_renew (FlatpakOciManifestDescriptor *, self->manifests, count + 2);
self->manifests[count] = m;
self->manifests[count+1] = NULL;
}
const char *
flatpak_oci_manifest_descriptor_get_ref (FlatpakOciManifestDescriptor *m)
{
if (m->parent.mediatype == NULL ||
strcmp (m->parent.mediatype, FLATPAK_OCI_MEDIA_TYPE_IMAGE_MANIFEST) != 0)
return NULL;
if (m->parent.annotations == NULL)
return NULL;
return g_hash_table_lookup (m->parent.annotations, "org.opencontainers.ref.name");
}
static int
index_find_ref (FlatpakOciIndex *self,
const char *ref)
{
int i;
if (self->manifests == NULL)
return -1;
for (i = 0; self->manifests[i] != NULL; i++)
{
const char *m_ref = flatpak_oci_manifest_descriptor_get_ref (self->manifests[i]);
if (m_ref == NULL)
continue;
if (strcmp (ref, m_ref) == 0)
return i;
}
return -1;
}
FlatpakOciManifestDescriptor *
flatpak_oci_index_get_manifest (FlatpakOciIndex *self,
const char *ref)
{
int i = index_find_ref (self, ref);
if (i >= 0)
return self->manifests[i];
return NULL;
}
FlatpakOciManifestDescriptor *
flatpak_oci_index_get_only_manifest (FlatpakOciIndex *self)
{
int i, found = -1;
if (self->manifests == NULL)
return NULL;
for (i = 0; self->manifests[i] != NULL; i++)
{
const char *m_ref = flatpak_oci_manifest_descriptor_get_ref (self->manifests[i]);
if (m_ref == NULL)
continue;
if (found == -1)
found = i;
else
return NULL;
}
if (found >= 0)
return self->manifests[found];
return NULL;
}
gboolean
flatpak_oci_index_remove_manifest (FlatpakOciIndex *self,
const char *ref)
{
int i = index_find_ref (self, ref);
if (i < 0)
return FALSE;
for (; self->manifests[i] != NULL; i++)
self->manifests[i] = self->manifests[i+1];
return TRUE;
}
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);
}
void
flatpak_oci_export_annotations (GHashTable *source,
GHashTable *dest)
{
const char *keys[] = {
"org.opencontainers.ref.name",
"org.flatpak.installed-size",
"org.flatpak.download-size",
"org.flatpak.metadata",
};
int i;
for (i = 0; i < G_N_ELEMENTS (keys); i++)
{
const char *key = keys[i];
const char *value = g_hash_table_lookup (source, key);
if (value)
g_hash_table_replace (dest,
g_strdup (key),
g_strdup (value));
}
}
void
flatpak_oci_copy_annotations (GHashTable *source,
GHashTable *dest)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, source);
while (g_hash_table_iter_next (&iter, &key, &value))
g_hash_table_replace (dest,
g_strdup ((char *)key),
g_strdup ((char *)value));
}
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.opencontainers.ref.name", ref);
if (commit)
add_annotation (annotations,"org.flatpak.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.parent-commit", 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.commit-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.timestamp", timestamp);
g_variant_get_child (commit_data, 3, "s", &subject);
add_annotation (annotations, "org.flatpak.subject", subject);
g_variant_get_child (commit_data, 4, "s", &body);
add_annotation (annotations, "org.flatpak.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.opencontainers.ref.name");
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.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.parent-commit");
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.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.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.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.commit-metadata."))
continue;
key += strlen ("org.flatpak.commit-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);
}
}
}