Experimental version of OCI support

This lets you export and import a runtime or an application into a tarball
that explodes to match the oci runtime spec. This goal of this is to interchange
xdg-app apps with other systems that support OCI.

Note that this is highly experimental, because the oci specs are in flux, and
in fact we should probably use the OCI image spec instead of the runtime spec,
but its not yet finished enough for us to use it. So, don't rely on this for
now other than to experiment with it.
tingping/wmclass
Alexander Larsson 2016-04-20 12:05:32 +02:00
parent acd84a454a
commit c0f2304f81
3 changed files with 852 additions and 4 deletions

View File

@ -34,16 +34,23 @@
#include "xdg-app-utils.h"
#include "xdg-app-chain-input-stream.h"
#ifdef HAVE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
#endif
static char *opt_arch;
static char *opt_repo_url;
static gboolean opt_runtime = FALSE;
static char **opt_gpg_file;
static gboolean opt_oci = FALSE;
static GOptionEntry options[] = {
{ "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, "Export runtime instead of app"},
{ "arch", 0, 0, G_OPTION_ARG_STRING, &opt_arch, "Arch to bundle for", "ARCH" },
{ "repo-url", 0, 0, G_OPTION_ARG_STRING, &opt_repo_url, "Url for repo", "URL" },
{ "gpg-keys", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, "Add GPG key from FILE (- for stdin)", "FILE" },
{ "oci", 0, 0, G_OPTION_ARG_NONE, &opt_oci, "Export oci image instead of xdg-app bundle"},
{ NULL }
};
@ -244,6 +251,623 @@ build_bundle (OstreeRepo *repo, GFile *file,
return TRUE;
}
#if defined(HAVE_LIBARCHIVE) && defined(HAVE_OSTREE_EXPORT_PATH_PREFIX)
GLNX_DEFINE_CLEANUP_FUNCTION(void*, xdg_app_local_free_read_archive, archive_read_free)
#define free_read_archive __attribute__ ((cleanup(xdg_app_local_free_read_archive)))
GLNX_DEFINE_CLEANUP_FUNCTION(void*, xdg_app_local_free_write_archive, archive_write_free)
#define free_write_archive __attribute__ ((cleanup(xdg_app_local_free_write_archive)))
GLNX_DEFINE_CLEANUP_FUNCTION(void*, xdg_app_local_free_archive_entry, archive_entry_free)
#define free_archive_entry __attribute__ ((cleanup(xdg_app_local_free_archive_entry)))
typedef struct {
GString *str;
int depth;
GList *index;
} JsonWriter;
static void
json_writer_init (JsonWriter *writer)
{
memset (writer, 0, sizeof (*writer));
writer->str = g_string_new ("");
writer->index = g_list_prepend (writer->index, 0);
}
static void
json_writer_indent (JsonWriter *writer)
{
int i;
for (i = 0; i < writer->depth; i++)
g_string_append (writer->str, " ");
}
static void
json_writer_add_bool (JsonWriter *writer, gboolean val)
{
if (val)
g_string_append (writer->str, "true");
else
g_string_append (writer->str, "false");
}
static void
json_writer_add_string (JsonWriter *writer, const gchar *str)
{
const gchar *p;
g_string_append_c (writer->str, '"');
for (p = str; *p != 0; p++)
{
if (*p == '\\' || *p == '"')
{
g_string_append_c (writer->str, '\\');
g_string_append_c (writer->str, *p);
}
else if ((*p > 0 && *p < 0x1f) || *p == 0x7f)
{
switch (*p)
{
case '\b':
g_string_append (writer->str, "\\b");
break;
case '\f':
g_string_append (writer->str, "\\f");
break;
case '\n':
g_string_append (writer->str, "\\n");
break;
case '\r':
g_string_append (writer->str, "\\r");
break;
case '\t':
g_string_append (writer->str, "\\t");
break;
default:
g_string_append_printf (writer->str, "\\u00%02x", (guint)*p);
break;
}
}
else
{
g_string_append_c (writer->str, *p);
}
}
g_string_append_c (writer->str, '"');
}
static void
json_writer_start_item (JsonWriter *writer)
{
int index = GPOINTER_TO_INT(writer->index->data);
if (index != 0)
g_string_append (writer->str, ",\n");
else
g_string_append (writer->str, "\n");
json_writer_indent (writer);
writer->index->data = GINT_TO_POINTER(index+1);
}
static void
json_writer_open_scope (JsonWriter *writer)
{
writer->depth += 1;
writer->index = g_list_prepend (writer->index, 0);
}
static void
json_writer_close_scope (JsonWriter *writer)
{
GList *l;
writer->depth -= 1;
l = writer->index;
writer->index = g_list_remove_link (writer->index, l);
g_list_free (l);
g_string_append (writer->str, "\n");
json_writer_indent (writer);
}
static void
json_writer_open_struct (JsonWriter *writer)
{
g_string_append (writer->str, "{");
json_writer_open_scope (writer);
}
static void
json_writer_close_struct (JsonWriter *writer)
{
int index;
json_writer_close_scope (writer);
g_string_append (writer->str, "}");
/* Last newline in file */
index = GPOINTER_TO_INT(writer->index->data);
if (index == 0)
g_string_append (writer->str, "\n");
}
static void
json_writer_open_array (JsonWriter *writer)
{
g_string_append (writer->str, "[");
json_writer_open_scope (writer);
}
static void
json_writer_close_array (JsonWriter *writer)
{
json_writer_close_scope (writer);
g_string_append (writer->str, "]");
}
static void
json_writer_add_property (JsonWriter *writer, const gchar *name)
{
json_writer_start_item (writer);
json_writer_add_string (writer, name);
g_string_append (writer->str, ": ");
}
static void
json_writer_add_struct_property (JsonWriter *writer, const gchar *name)
{
json_writer_add_property (writer, name);
json_writer_open_struct (writer);
}
static void
json_writer_add_array_property (JsonWriter *writer, const gchar *name)
{
json_writer_add_property (writer, name);
json_writer_open_array (writer);
}
static void
json_writer_add_string_property (JsonWriter *writer, const gchar *name, const char *value)
{
json_writer_add_property (writer, name);
json_writer_add_string (writer, value);
}
static void
json_writer_add_bool_property (JsonWriter *writer, const gchar *name, gboolean value)
{
json_writer_add_property (writer, name);
json_writer_add_bool (writer, value);
}
static void
json_writer_add_array_item (JsonWriter *writer, const gchar *string)
{
json_writer_start_item (writer);
json_writer_add_string (writer, string);
}
static void
json_writer_add_array_struct (JsonWriter *writer)
{
json_writer_start_item (writer);
json_writer_open_struct (writer);
}
static gboolean
propagate_libarchive_error (GError **error,
struct archive *a)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"%s", archive_error_string (a));
return FALSE;
}
struct archive_entry *
new_entry (struct archive *a,
const char *name,
OstreeRepoExportArchiveOptions *opts)
{
struct archive_entry *entry = archive_entry_new2 (a);
time_t ts = (time_t) opts->timestamp_secs;
archive_entry_update_pathname_utf8 (entry, name);
archive_entry_set_ctime (entry, ts, 0);
archive_entry_set_mtime (entry, ts, 0);
archive_entry_set_atime (entry, ts, 0);
archive_entry_set_uid (entry, 0);
archive_entry_set_gid (entry, 0);
return entry;
}
static gboolean
add_dir (struct archive *a,
const char *name,
OstreeRepoExportArchiveOptions *opts,
GError **error)
{
g_autofree char *full_name = g_build_filename ("rootfs", name, NULL);
free_archive_entry struct archive_entry *entry = new_entry (a, full_name, opts);
archive_entry_set_mode (entry, AE_IFDIR | 0755);
if (archive_write_header (a, entry) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
return TRUE;
}
static gboolean
add_symlink (struct archive *a,
const char *name,
const char *target,
OstreeRepoExportArchiveOptions *opts,
GError **error)
{
g_autofree char *full_name = g_build_filename ("rootfs", name, NULL);
free_archive_entry struct archive_entry *entry = new_entry (a, full_name, opts);
archive_entry_set_mode (entry, AE_IFLNK | 0755);
archive_entry_set_symlink (entry, target);
if (archive_write_header (a, entry) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
return TRUE;
}
static gboolean
add_file (struct archive *a,
const char *name,
OstreeRepo *repo,
GFile *file,
OstreeRepoExportArchiveOptions *opts,
GCancellable *cancellable,
GError **error)
{
free_archive_entry struct archive_entry *entry = new_entry (a, name, opts);
guint8 buf[8192];
g_autoptr(GInputStream) file_in = NULL;
g_autoptr(GFileInfo) file_info = NULL;
const char *checksum;
checksum = ostree_repo_file_get_checksum ((OstreeRepoFile*)file);
if (!ostree_repo_load_file (repo, checksum, &file_in, &file_info, NULL,
cancellable, error))
return FALSE;
archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::uid"));
archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::gid"));
archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (file_info, "unix::mode"));
archive_entry_set_size (entry, g_file_info_get_size (file_info));
if (archive_write_header (a, entry) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
while (TRUE)
{
ssize_t r;
gssize bytes_read = g_input_stream_read (file_in, buf, sizeof (buf),
cancellable, error);
if (bytes_read < 0)
return FALSE;;
if (bytes_read == 0)
break;
r = archive_write_data (a, buf, bytes_read);
if (r != bytes_read)
return propagate_libarchive_error (error, a);
}
if (archive_write_finish_entry (a) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
return TRUE;
}
static gboolean
add_file_from_data (struct archive *a,
const char *name,
OstreeRepo *repo,
const char *data,
gsize size,
OstreeRepoExportArchiveOptions *opts,
GCancellable *cancellable,
GError **error)
{
free_archive_entry struct archive_entry *entry = new_entry (a, name, opts);
ssize_t r;
archive_entry_set_mode (entry, AE_IFREG | 0755);
archive_entry_set_size (entry, size);
if (archive_write_header (a, entry) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
r = archive_write_data (a, data, size);
if (r != size)
return propagate_libarchive_error (error, a);
if (archive_write_finish_entry (a) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
return TRUE;
}
static const char *
get_oci_arch (const char *arch)
{
if (strcmp (arch, "x86_64") == 0)
return "amd64";
if (strcmp (arch, "aarch64") == 0)
return "arm64";
return arch;
}
static GString *
generate_config_json (const char *arch)
{
JsonWriter writer;
json_writer_init (&writer);
json_writer_open_struct (&writer);
json_writer_add_string_property (&writer, "ociVersion", "0.5.0");
json_writer_add_struct_property (&writer, "platform");
{
json_writer_add_string_property (&writer, "os", "linux");
json_writer_add_string_property (&writer, "arch", get_oci_arch (arch));
json_writer_close_struct (&writer);
}
json_writer_add_struct_property (&writer, "process");
{
json_writer_add_bool_property (&writer, "terminal", TRUE);
json_writer_add_array_property (&writer, "args");
json_writer_add_array_item (&writer, "sh");
json_writer_close_array (&writer);
json_writer_add_array_property (&writer, "envs");
json_writer_add_array_item (&writer, "PATH=/app/bin:/usr/bin");
json_writer_add_array_item (&writer, "LD_LIBRARY_PATH=/app/lib");
json_writer_add_array_item (&writer, "XDG_CONFIG_DIRS=/app/etc/xdg:/etc/xdg");
json_writer_add_array_item (&writer, "XDG_DATA_DIRS=/app/share:/usr/share");
json_writer_add_array_item (&writer, "SHELL=/bin/sh");
json_writer_close_array (&writer);
json_writer_add_string_property (&writer, "cwd", "/");
json_writer_add_bool_property (&writer, "noNewPrivileges", TRUE);
json_writer_close_struct (&writer);
}
json_writer_add_struct_property (&writer, "root");
{
json_writer_add_string_property (&writer, "path", "rootfs");
json_writer_add_bool_property (&writer, "readonly", TRUE);
json_writer_close_struct (&writer);
}
json_writer_add_array_property (&writer, "mounts");
{
json_writer_add_array_struct (&writer);
{
json_writer_add_string_property (&writer, "destination", "/proc");
json_writer_add_string_property (&writer, "type", "proc");
json_writer_add_string_property (&writer, "source", "proc");
json_writer_close_struct (&writer);
}
json_writer_add_array_struct (&writer);
{
json_writer_add_string_property (&writer, "destination", "/sys");
json_writer_add_string_property (&writer, "type", "sysfs");
json_writer_add_string_property (&writer, "source", "sysfs");
json_writer_add_array_property (&writer, "options");
{
json_writer_add_array_item (&writer, "nosuid");
json_writer_add_array_item (&writer, "noexec");
json_writer_add_array_item (&writer, "nodev");
json_writer_close_array (&writer);
}
json_writer_close_struct (&writer);
}
json_writer_add_array_struct (&writer);
{
json_writer_add_string_property (&writer, "destination", "/dev");
json_writer_add_string_property (&writer, "type", "tmpfs");
json_writer_add_string_property (&writer, "source", "tmpfs");
json_writer_add_array_property (&writer, "options");
{
json_writer_add_array_item (&writer, "nosuid");
json_writer_close_array (&writer);
}
json_writer_close_struct (&writer);
}
json_writer_close_array (&writer);
}
json_writer_add_struct_property (&writer, "linux");
{
json_writer_add_string_property (&writer, "rootfsPropagation", "slave");
json_writer_add_struct_property (&writer, "resources");
{
json_writer_close_struct (&writer);
}
json_writer_add_array_property (&writer, "namespaces");
{
json_writer_add_array_struct (&writer);
{
json_writer_add_string_property (&writer, "type", "pid");
json_writer_close_struct (&writer);
}
json_writer_add_array_struct (&writer);
{
json_writer_add_string_property (&writer, "type", "mount");
json_writer_close_struct (&writer);
}
json_writer_close_array (&writer);
}
json_writer_close_struct (&writer);
}
json_writer_add_struct_property (&writer, "annotations");
{
json_writer_close_struct (&writer);
}
json_writer_close_struct (&writer);
return writer.str;
}
#endif
static gboolean
build_oci (OstreeRepo *repo, GFile *file,
const char *name, const char *full_branch,
GCancellable *cancellable, GError **error)
{
#if !defined(HAVE_OSTREE_EXPORT_PATH_PREFIX)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"This version of ostree is to old to support OCI exports");
return FALSE;
#elif !defined(HAVE_LIBARCHIVE)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"This version of xdg-app is not compiled with libarchive support");
return FALSE;
#else
struct free_write_archive archive *a = NULL;
OstreeRepoExportArchiveOptions opts = { 0, };
g_autoptr(GFile) root = NULL;
g_autoptr(GFile) files = NULL;
g_autoptr(GFile) export = NULL;
g_autoptr(GFile) metadata = NULL;
g_autoptr(GVariant) commit_data = NULL;
g_autoptr(GVariant) commit_metadata = NULL;
g_autofree char *commit_checksum = NULL;
g_autoptr(GString) str = g_string_new ("");
g_auto(GStrv) ref_parts = g_strsplit (full_branch, "/", -1);
if (!ostree_repo_resolve_rev (repo, full_branch, FALSE, &commit_checksum, error))
return FALSE;
if (!ostree_repo_read_commit (repo, commit_checksum, &root, NULL, NULL, error))
return FALSE;
if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum, &commit_data, error))
return FALSE;
if (!ostree_repo_read_commit_detached_metadata (repo, commit_checksum, &commit_metadata, cancellable, error))
return FALSE;
a = archive_write_new ();
if (archive_write_set_format_gnutar (a) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
if (archive_write_add_filter_none (a) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
if (archive_write_open_filename (a, gs_file_get_path_cached (file)) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
opts.timestamp_secs = ostree_commit_get_timestamp (commit_data);
files = g_file_get_child (root, "files");
export = g_file_get_child (root, "export");
metadata = g_file_get_child (root, "metadata");
if (opt_runtime)
opts.path_prefix = "rootfs/usr/";
else
opts.path_prefix = "rootfs/app/";
{
const char *root_dirs[] = { "dev", "home", "proc", "run", "sys", "tmp", "var", "opt", "srv", "media", "mnt" };
const char *root_symlinks[] = {
"etc", "usr/etc",
"lib", "usr/lib",
"lib64", "usr/lib64",
"lib32", "usr/lib32",
"bin", "usr/bin",
"sbin", "usr/sbin",
"var/tmp", "/tmp",
"var/run", "/run",
};
int i;
/* Add the "other" of /app & /usr */
if (!add_dir (a, opt_runtime ? "app" : "usr", &opts, error))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (root_dirs); i++)
if (!add_dir (a, root_dirs[i], &opts, error))
return FALSE;
for (i = 0; i < G_N_ELEMENTS (root_symlinks); i += 2)
if (!add_symlink (a, root_symlinks[i], root_symlinks[i+1], &opts, error))
return FALSE;
}
if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)files, a,
cancellable, error))
return FALSE;
if (!opt_runtime && g_file_query_exists (export, NULL))
{
opts.path_prefix = "export/";
if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)export, a,
cancellable, error))
return FALSE;
}
opts.path_prefix = NULL;
if (!add_file (a, "metadata", repo, metadata, &opts, cancellable, error))
return FALSE;
if (!add_file_from_data (a, "ref",
repo,
full_branch,
strlen (full_branch),
&opts, cancellable, error))
return FALSE;
if (!add_file_from_data (a, "commit",
repo,
g_variant_get_data (commit_data),
g_variant_get_size (commit_data),
&opts, cancellable, error))
return FALSE;
if (commit_metadata != NULL)
{
if (!add_file_from_data (a, "commitmeta",
repo,
g_variant_get_data (commit_metadata),
g_variant_get_size (commit_metadata),
&opts, cancellable, error))
return FALSE;
}
str = generate_config_json (ref_parts[2]);
if (!add_file_from_data (a, "config.json",
repo,
str->str,
str->len,
&opts, cancellable, error))
return FALSE;
if (archive_write_close (a) != ARCHIVE_OK)
return propagate_libarchive_error (error, a);
g_print ("WARNING: the oci format produced by xdg-app is experimental and unstable.\n"
"Don't use this for anything but experiments for now\n");
return TRUE;
#endif
}
gboolean
xdg_app_builtin_build_bundle (int argc, char **argv, GCancellable *cancellable, GError **error)
{
@ -296,8 +920,16 @@ xdg_app_builtin_build_bundle (int argc, char **argv, GCancellable *cancellable,
if (!ostree_repo_open (repo, cancellable, error))
return FALSE;
if (!build_bundle (repo, file, name, full_branch, cancellable, error))
return FALSE;
if (opt_oci)
{
if (!build_oci (repo, file, name, full_branch, cancellable, error))
return FALSE;
}
else
{
if (!build_bundle (repo, file, name, full_branch, cancellable, error))
return FALSE;
}
return TRUE;
}

View File

@ -32,12 +32,210 @@
#include "xdg-app-utils.h"
static char *opt_ref;
static gboolean opt_oci = FALSE;
static GOptionEntry options[] = {
{ "ref", 0, 0, G_OPTION_ARG_STRING, &opt_ref, "Override the ref used for the imported bundle", "REF" },
{ "oci", 0, 0, G_OPTION_ARG_NONE, &opt_oci, "Import oci image instead of xdg-app bundle"},
{ NULL }
};
static gboolean
import_oci (OstreeRepo *repo, GFile *file,
GCancellable *cancellable, GError **error)
{
#if !defined(HAVE_OSTREE_EXPORT_PATH_PREFIX)
/* This code actually doesn't user path_prefix, but it need the support
for reading commits from the transaction that was added at the same time. */
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"This version of ostree is to old to support OCI exports");
return FALSE;
#elif !defined(HAVE_LIBARCHIVE)
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"This version of xdg-app is not compiled with libarchive support");
return FALSE;
#else
g_autoptr(OstreeMutableTree) archive_mtree = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(OstreeMutableTree) files_mtree = NULL;
g_autoptr(OstreeMutableTree) export_mtree = NULL;
g_autoptr(GFile) archive_root = NULL;
g_autoptr(GFile) root = NULL;
g_autoptr(GFile) files = NULL;
g_autoptr(GFile) export = NULL;
g_autoptr(GFile) ref = NULL;
g_autoptr(GFile) commit = NULL;
g_autoptr(GFile) commitmeta = NULL;
g_autoptr(GFile) metadata = NULL;
g_autofree char *commit_checksum = NULL;
g_autofree char *ref_data = NULL;
g_autofree char *commit_data = NULL;
gsize commit_size;
g_autofree char *commitmeta_data = NULL;
g_autofree char *parent = NULL;
const char *subject;
const char *body;
const char *target_ref;
const char *files_source;
gsize commitmeta_size;
g_autoptr(GVariant) commitv = NULL;
g_autoptr(GVariant) commitv_metadata = NULL;
g_autoptr(GVariant) commitmetav = NULL;
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
return FALSE;
/* 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 ();
if (!ostree_repo_write_archive_to_mtree (repo, file, archive_mtree, NULL,
TRUE,
cancellable, error))
return FALSE;
if (!ostree_repo_write_mtree (repo, archive_mtree, &archive_root, cancellable, error))
return FALSE;
if (!ostree_repo_file_ensure_resolved ((OstreeRepoFile*)archive_root, error))
return FALSE;
ref = g_file_get_child (archive_root, "ref");
metadata = g_file_get_child (archive_root, "metadata");
commit = g_file_get_child (archive_root, "commit");
commitmeta = g_file_get_child (archive_root, "commitmeta");
if (!g_file_query_exists (ref, NULL))
return xdg_app_fail (error, "Required file ref not in tarfile");
if (!g_file_query_exists (metadata, NULL))
return xdg_app_fail (error, "Required file metadata not in tarfile");
if (!g_file_query_exists (commit, NULL))
return xdg_app_fail (error, "Required file commit not in tarfile");
if (!g_file_load_contents (ref, cancellable,
&ref_data, NULL,
NULL, error))
return FALSE;
if (g_str_has_prefix (ref_data, "app/"))
files_source = "rootfs/app";
else
files_source = "rootfs/usr";
files = g_file_resolve_relative_path (archive_root, files_source);
if (!g_file_query_exists (files, NULL))
return xdg_app_fail (error, "Required directory %s not in tarfile", files_source);
export = g_file_get_child (archive_root, "export");
if (!g_file_load_contents (commit, cancellable,
&commit_data, &commit_size,
NULL, error))
return FALSE;
commitv = g_variant_new_from_data (OSTREE_COMMIT_GVARIANT_FORMAT,
g_steal_pointer (&commit_data), commit_size,
FALSE, g_free, commit_data);
if (!ostree_validate_structureof_commit (commitv, error))
return FALSE;
if (g_file_query_exists (commitmeta, NULL) &&
!g_file_load_contents (commitmeta, cancellable,
&commitmeta_data, &commitmeta_size,
NULL, error))
return FALSE;
if (commitmeta_data != NULL)
commitmetav = g_variant_new_from_data (G_VARIANT_TYPE ("a{sv}"),
g_steal_pointer (&commitmeta_data), commitmeta_size,
FALSE, g_free, commitmeta_data);
mtree = ostree_mutable_tree_new ();
if (!xdg_app_mtree_create_root (repo, mtree, cancellable, error))
return FALSE;
if (!ostree_mutable_tree_ensure_dir (mtree, "files", &files_mtree, error))
return FALSE;
if (!ostree_repo_write_directory_to_mtree (repo, files, files_mtree, NULL,
cancellable, error))
return FALSE;
if (g_file_query_exists (export, NULL))
{
if (!ostree_mutable_tree_ensure_dir (mtree, "export", &export_mtree, error))
return FALSE;
if (!ostree_repo_write_directory_to_mtree (repo, export, export_mtree, NULL,
cancellable, error))
return FALSE;
}
if (!ostree_mutable_tree_replace_file (mtree, "metadata",
ostree_repo_file_get_checksum ((OstreeRepoFile*) metadata),
error))
return FALSE;
if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error))
return FALSE;
/* Verify that we created the same contents */
{
g_autoptr(GVariant) tree_contents_bytes = NULL;
g_autofree char *tree_contents_checksum = NULL;
g_autoptr(GVariant) tree_metadata_bytes = NULL;
g_autofree char *tree_metadata_checksum = NULL;
tree_contents_bytes = g_variant_get_child_value (commitv, 6);
tree_contents_checksum = ostree_checksum_from_bytes_v (tree_contents_bytes);
tree_metadata_bytes = g_variant_get_child_value (commitv, 7);
tree_metadata_checksum = ostree_checksum_from_bytes_v (tree_metadata_bytes);
if (strcmp (tree_contents_checksum, ostree_repo_file_tree_get_contents_checksum ((OstreeRepoFile*) root)))
return xdg_app_fail (error, "Imported content checksum (%s) does not match original checksum (%s)",
tree_contents_checksum, ostree_repo_file_tree_get_contents_checksum ((OstreeRepoFile*) root));
if (strcmp (tree_metadata_checksum, ostree_repo_file_tree_get_metadata_checksum ((OstreeRepoFile*) root)))
return xdg_app_fail (error, "Imported metadata checksum (%s) does not match original checksum (%s)",
tree_metadata_checksum, ostree_repo_file_tree_get_metadata_checksum ((OstreeRepoFile*) root));
}
commitv_metadata = g_variant_get_child_value (commitv, 0);
parent = ostree_commit_get_parent (commitv);
g_variant_get_child (commitv, 3, "s", &subject);
g_variant_get_child (commitv, 4, "s", &body);
if (!ostree_repo_write_commit_with_time (repo,
parent,
subject,
body,
commitv_metadata,
OSTREE_REPO_FILE (root),
ostree_commit_get_timestamp (commitv),
&commit_checksum,
cancellable, error))
return FALSE;
if (commitmetav != NULL &&
!ostree_repo_write_commit_detached_metadata (repo, commit_checksum,
commitmetav, cancellable, error))
return FALSE;
if (opt_ref != NULL)
target_ref = opt_ref;
else
target_ref = ref_data;
ostree_repo_transaction_set_ref (repo, NULL, target_ref, commit_checksum);
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
return FALSE;
g_print ("Importing %s (%s)\n", target_ref, commit_checksum);
return TRUE;
#endif
}
static gboolean
import_bundle (OstreeRepo *repo, GFile *file,
GCancellable *cancellable, GError **error)
@ -104,8 +302,16 @@ xdg_app_builtin_build_import (int argc, char **argv, GCancellable *cancellable,
if (!ostree_repo_open (repo, cancellable, error))
return FALSE;
if (!import_bundle (repo, file, cancellable, error))
return FALSE;
if (opt_oci)
{
if (!import_oci (repo, file, cancellable, error))
return FALSE;
}
else
{
if (!import_bundle (repo, file, cancellable, error))
return FALSE;
}
return TRUE;
}

View File

@ -140,8 +140,18 @@ PKG_CHECK_MODULES(OSTREE, [libgsystem >= 2015.1 ostree-1 >= 2016.5])
AC_SUBST(OSTREE_CFLAGS)
AC_SUBST(OSTREE_LIBS)
save_LIBS=$LIBS
save_CFLAGS=$CFLAGS
LIBS=$OSTREE_LIBS
CFLAGS=$OSTREE_CFLAGS
AC_CHECK_MEMBER([OstreeRepoExportArchiveOptions.path_prefix],
[AC_DEFINE([HAVE_OSTREE_EXPORT_PATH_PREFIX], 1,
[Have OstreeRepoExportArchiveOptions.path_prefix])],
, [[#include <ostree.h>]])
LIBS=$save_LIBS
CFLAGS=$save_CFLAGS
PKG_CHECK_MODULES(FUSE, [fuse])
AC_SUBST(FUSE_CFLAGS)