forked from Mirrors/flatpak-builder
common: Move part of bundle install to helper functions
parent
e66e7e3f15
commit
eadb10cba7
|
@ -110,27 +110,20 @@ install_bundle (XdgAppDir *dir,
|
|||
gboolean ret = FALSE;
|
||||
g_autoptr(GFile) deploy_base = NULL;
|
||||
g_autoptr(GFile) file = NULL;
|
||||
g_autoptr(GFile) gpg_tmp_file = NULL;
|
||||
const char *filename;
|
||||
g_autofree char *ref = NULL;
|
||||
g_autofree char *origin = NULL;
|
||||
g_autofree char *metadata_contents = NULL;
|
||||
gboolean created_deploy_base = FALSE;
|
||||
gboolean added_remote = FALSE;
|
||||
g_autofree char *to_checksum = NULL;
|
||||
g_auto(GStrv) parts = NULL;
|
||||
g_autoptr(GFile) root = NULL;
|
||||
g_autoptr(GFile) metadata_file = NULL;
|
||||
g_autoptr(GBytes) gpg_data = NULL;
|
||||
g_autoptr(GInputStream) in = NULL;
|
||||
g_autofree char *remote = NULL;
|
||||
OstreeRepo *repo;
|
||||
g_autoptr(OstreeGpgVerifyResult) gpg_result = NULL;
|
||||
g_autoptr(GError) my_error = NULL;
|
||||
g_auto(GLnxLockFile) lock = GLNX_LOCK_FILE_INIT;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
g_autoptr(GVariant) gpg_value = NULL;
|
||||
gboolean metadata_valid;
|
||||
g_autofree char *basename = NULL;
|
||||
|
||||
if (argc < 2)
|
||||
return usage_error (context, "bundle filename must be specified", error);
|
||||
|
@ -142,23 +135,19 @@ install_bundle (XdgAppDir *dir,
|
|||
if (!xdg_app_supports_bundles (repo))
|
||||
return xdg_app_fail (error, "Your version of ostree is too old to support single-file bundles");
|
||||
|
||||
if (!xdg_app_dir_lock (dir, &lock,
|
||||
cancellable, error))
|
||||
goto out;
|
||||
|
||||
file = g_file_new_for_commandline_arg (filename);
|
||||
|
||||
|
||||
metadata = xdg_app_bundle_load (file, &to_checksum, error);
|
||||
|
||||
if (metadata == NULL)
|
||||
goto out;
|
||||
return FALSE;
|
||||
|
||||
if (!xdg_app_dir_lock (dir, &lock,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!g_variant_lookup (metadata, "ref", "s", &ref))
|
||||
return xdg_app_fail (error, "Invalid bundle, no ref in metadata");
|
||||
|
||||
g_variant_lookup (metadata, "metadata", "s", &metadata_contents);
|
||||
|
||||
if (!g_variant_lookup (metadata, "origin", "s", &origin))
|
||||
origin = NULL;
|
||||
|
||||
|
@ -171,14 +160,6 @@ install_bundle (XdgAppDir *dir,
|
|||
gpg_data = g_bytes_new (data, n_elements);
|
||||
}
|
||||
|
||||
parts = xdg_app_decompose_ref (ref, error);
|
||||
if (parts == NULL)
|
||||
return FALSE;
|
||||
|
||||
deploy_base = xdg_app_dir_get_deploy_dir (dir, ref);
|
||||
if (g_file_query_exists (deploy_base, cancellable))
|
||||
return xdg_app_fail (error, "%s branch %s already installed", parts[1], parts[3]);
|
||||
|
||||
if (opt_gpg_file != NULL)
|
||||
{
|
||||
/* Override gpg_data from file */
|
||||
|
@ -187,159 +168,46 @@ install_bundle (XdgAppDir *dir,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
parts = xdg_app_decompose_ref (ref, error);
|
||||
if (parts == NULL)
|
||||
return FALSE;
|
||||
|
||||
deploy_base = xdg_app_dir_get_deploy_dir (dir, ref);
|
||||
|
||||
if (g_file_query_exists (deploy_base, cancellable))
|
||||
return xdg_app_fail (error, "%s branch %s already installed", parts[1], parts[3]);
|
||||
|
||||
/* Add a remote for later updates */
|
||||
if (origin != NULL)
|
||||
{
|
||||
g_auto(GStrv) remotes = ostree_repo_remote_list (repo, NULL);
|
||||
int version = 0;
|
||||
|
||||
do
|
||||
{
|
||||
g_autofree char *name = NULL;
|
||||
if (version == 0)
|
||||
name = g_strdup_printf ("%s-origin", parts[1]);
|
||||
else
|
||||
name = g_strdup_printf ("%s-%d-origin", parts[1], version);
|
||||
version++;
|
||||
|
||||
if (remotes == NULL ||
|
||||
!g_strv_contains ((const char * const *) remotes, name))
|
||||
remote = g_steal_pointer (&name);
|
||||
}
|
||||
while (remote == NULL);
|
||||
}
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
ostree_repo_transaction_set_ref (repo, remote, ref, to_checksum);
|
||||
|
||||
if (!ostree_repo_static_delta_execute_offline (repo,
|
||||
file,
|
||||
FALSE,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (gpg_data)
|
||||
{
|
||||
g_autoptr(GFileIOStream) stream;
|
||||
GOutputStream *o;
|
||||
|
||||
gpg_tmp_file = g_file_new_tmp (".xdg-app-XXXXXX", &stream, error);
|
||||
if (gpg_tmp_file == NULL)
|
||||
return FALSE;
|
||||
o = g_io_stream_get_output_stream (G_IO_STREAM (stream));
|
||||
if (!g_output_stream_write_all (o, g_bytes_get_data (gpg_data, NULL), g_bytes_get_size (gpg_data), NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gpg_result = ostree_repo_verify_commit_ext (repo,
|
||||
to_checksum,
|
||||
NULL, gpg_tmp_file, cancellable, &my_error);
|
||||
|
||||
if (gpg_tmp_file)
|
||||
g_file_delete (gpg_tmp_file, cancellable, NULL);
|
||||
|
||||
if (gpg_result == NULL)
|
||||
{
|
||||
/* NOT_FOUND means no gpg signature, we ignore this *if* there
|
||||
* is no gpg key specified in the bundle or by the user */
|
||||
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
|
||||
gpg_data == NULL)
|
||||
g_clear_error (&my_error);
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&my_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If there is no valid gpg signature we fail, unless there is no gpg
|
||||
key specified (on the command line or in the file) because then we
|
||||
trust the source bundle. */
|
||||
if (ostree_gpg_verify_result_count_valid (gpg_result) == 0 &&
|
||||
gpg_data != NULL)
|
||||
return xdg_app_fail (error, "GPG signatures found, but none are in trusted keyring");
|
||||
}
|
||||
|
||||
if (!ostree_repo_read_commit (repo, to_checksum, &root, NULL, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* We ensure that the actual installed metadata matches the one in the
|
||||
header, because you may have made decisions on wheter to install it or not
|
||||
based on that data. */
|
||||
metadata_file = g_file_resolve_relative_path (root, "metadata");
|
||||
in = (GInputStream*)g_file_read (metadata_file, cancellable, NULL);
|
||||
if (in != NULL)
|
||||
{
|
||||
g_autoptr(GMemoryOutputStream) data_stream = (GMemoryOutputStream*)g_memory_output_stream_new_resizable ();
|
||||
|
||||
if (g_output_stream_splice (G_OUTPUT_STREAM (data_stream), in,
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
|
||||
cancellable, error) < 0)
|
||||
return FALSE;
|
||||
|
||||
/* Null terminate */
|
||||
g_output_stream_write (G_OUTPUT_STREAM (data_stream), "\0", 1, NULL, NULL);
|
||||
|
||||
metadata_valid = strcmp (metadata_contents, g_memory_output_stream_get_data (data_stream)) == 0;
|
||||
}
|
||||
else
|
||||
metadata_valid = (metadata_contents == NULL);
|
||||
|
||||
if (!metadata_valid)
|
||||
{
|
||||
/* Immediately remove this broken commit */
|
||||
ostree_repo_set_ref_immediate (repo, remote, ref, NULL, cancellable, error);
|
||||
return xdg_app_fail (error, "Metadata in header and app are inconsistent");
|
||||
}
|
||||
|
||||
if (!g_file_make_directory_with_parents (deploy_base, cancellable, error))
|
||||
basename = g_file_get_basename (file);
|
||||
remote = xdg_app_dir_create_origin_remote (dir,
|
||||
origin,
|
||||
parts[1],
|
||||
basename,
|
||||
gpg_data,
|
||||
cancellable,
|
||||
error);
|
||||
if (remote == NULL)
|
||||
return FALSE;
|
||||
|
||||
/* From here we need to goto out on error, to clean up */
|
||||
added_remote = TRUE;
|
||||
|
||||
if (!xdg_app_dir_pull_from_bundle (dir,
|
||||
file,
|
||||
remote,
|
||||
ref,
|
||||
gpg_data != NULL,
|
||||
cancellable,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (!g_file_make_directory_with_parents (deploy_base, cancellable, error))
|
||||
goto out;
|
||||
|
||||
created_deploy_base = TRUE;
|
||||
|
||||
if (remote)
|
||||
{
|
||||
g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
g_autofree char *basename = g_file_get_basename (file);
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.title",
|
||||
g_variant_new_variant (g_variant_new_string (basename)));
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.noenumerate",
|
||||
g_variant_new_variant (g_variant_new_boolean (TRUE)));
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.prio",
|
||||
g_variant_new_variant (g_variant_new_string ("0")));
|
||||
|
||||
if (!ostree_repo_remote_add (repo,
|
||||
remote, origin, g_variant_builder_end (optbuilder), cancellable, error))
|
||||
goto out;
|
||||
|
||||
added_remote = TRUE;
|
||||
|
||||
if (gpg_data)
|
||||
{
|
||||
g_autoptr(GInputStream) gpg_data_as_stream = g_memory_input_stream_new_from_bytes (gpg_data);
|
||||
|
||||
if (!ostree_repo_remote_gpg_import (repo, remote, gpg_data_as_stream,
|
||||
NULL, NULL, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!xdg_app_dir_set_origin (dir, ref, remote, cancellable, error))
|
||||
goto out;
|
||||
}
|
||||
if (!xdg_app_dir_set_origin (dir, ref, remote, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!xdg_app_dir_deploy (dir, ref, to_checksum, cancellable, error))
|
||||
goto out;
|
||||
|
|
|
@ -801,10 +801,20 @@ xdg_app_dir_pull (XdgAppDir *self,
|
|||
GSConsole *console = NULL;
|
||||
g_autoptr(OstreeAsyncProgress) console_progress = NULL;
|
||||
const char *refs[2];
|
||||
g_autofree char *url = NULL;
|
||||
|
||||
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
||||
goto out;
|
||||
|
||||
if (!ostree_repo_remote_get_url (self->repo,
|
||||
repository,
|
||||
&url,
|
||||
error))
|
||||
goto out;
|
||||
|
||||
if (*url == 0)
|
||||
return TRUE; /* Empty url, silently disables updates */
|
||||
|
||||
if (progress == NULL)
|
||||
{
|
||||
console = gs_console_get ();
|
||||
|
@ -840,6 +850,115 @@ xdg_app_dir_pull (XdgAppDir *self,
|
|||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
xdg_app_dir_pull_from_bundle (XdgAppDir *self,
|
||||
GFile *file,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
gboolean require_gpg_signature,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *metadata_contents = NULL;
|
||||
g_autofree char *to_checksum = NULL;
|
||||
g_autoptr(GFile) root = NULL;
|
||||
g_autoptr(GFile) metadata_file = NULL;
|
||||
g_autoptr(GInputStream) in = NULL;
|
||||
g_autoptr(OstreeGpgVerifyResult) gpg_result = NULL;
|
||||
g_autoptr(GError) my_error = NULL;
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
gboolean metadata_valid;
|
||||
|
||||
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!xdg_app_supports_bundles (self->repo))
|
||||
return xdg_app_fail (error, "Your version of ostree is too old to support single-file bundles");
|
||||
|
||||
metadata = xdg_app_bundle_load (file, &to_checksum, error);
|
||||
if (metadata == NULL)
|
||||
return FALSE;
|
||||
|
||||
g_variant_lookup (metadata, "metadata", "s", &metadata_contents);
|
||||
|
||||
if (!ostree_repo_prepare_transaction (self->repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
ostree_repo_transaction_set_ref (self->repo, remote, ref, to_checksum);
|
||||
|
||||
if (!ostree_repo_static_delta_execute_offline (self->repo,
|
||||
file,
|
||||
FALSE,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
gpg_result = ostree_repo_verify_commit_ext (self->repo, to_checksum,
|
||||
NULL, NULL, cancellable, &my_error);
|
||||
if (gpg_result == NULL)
|
||||
{
|
||||
/* NOT_FOUND means no gpg signature, we ignore this *if* there
|
||||
* is no gpg key specified in the bundle or by the user */
|
||||
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
|
||||
!require_gpg_signature)
|
||||
g_clear_error (&my_error);
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&my_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If there is no valid gpg signature we fail, unless there is no gpg
|
||||
key specified (on the command line or in the file) because then we
|
||||
trust the source bundle. */
|
||||
if (ostree_gpg_verify_result_count_valid (gpg_result) == 0 &&
|
||||
require_gpg_signature)
|
||||
return xdg_app_fail (error, "GPG signatures found, but none are in trusted keyring");
|
||||
}
|
||||
|
||||
if (!ostree_repo_read_commit (self->repo, to_checksum, &root, NULL, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_commit_transaction (self->repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* We ensure that the actual installed metadata matches the one in the
|
||||
header, because you may have made decisions on wheter to install it or not
|
||||
based on that data. */
|
||||
metadata_file = g_file_resolve_relative_path (root, "metadata");
|
||||
in = (GInputStream*)g_file_read (metadata_file, cancellable, NULL);
|
||||
if (in != NULL)
|
||||
{
|
||||
g_autoptr(GMemoryOutputStream) data_stream = (GMemoryOutputStream*)g_memory_output_stream_new_resizable ();
|
||||
|
||||
if (g_output_stream_splice (G_OUTPUT_STREAM (data_stream), in,
|
||||
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
|
||||
cancellable, error) < 0)
|
||||
return FALSE;
|
||||
|
||||
/* Null terminate */
|
||||
g_output_stream_write (G_OUTPUT_STREAM (data_stream), "\0", 1, NULL, NULL);
|
||||
|
||||
metadata_valid =
|
||||
metadata_contents != NULL &&
|
||||
strcmp (metadata_contents, g_memory_output_stream_get_data (data_stream)) == 0;
|
||||
}
|
||||
else
|
||||
metadata_valid = (metadata_contents == NULL);
|
||||
|
||||
if (!metadata_valid)
|
||||
{
|
||||
/* Immediately remove this broken commit */
|
||||
ostree_repo_set_ref_immediate (self->repo, remote, ref, NULL, cancellable, error);
|
||||
return xdg_app_fail (error, "Metadata in header and app are inconsistent");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
xdg_app_dir_current_ref (XdgAppDir *self,
|
||||
const char *name,
|
||||
|
@ -2728,6 +2847,72 @@ cmp_remote (gconstpointer a,
|
|||
return prio_b - prio_a;
|
||||
}
|
||||
|
||||
char *
|
||||
xdg_app_dir_create_origin_remote (XdgAppDir *self,
|
||||
const char *url,
|
||||
const char *id,
|
||||
const char *title,
|
||||
GBytes *gpg_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *remote = NULL;
|
||||
g_auto(GStrv) remotes = NULL;
|
||||
int version = 0;
|
||||
g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
if (!xdg_app_dir_ensure_repo (self, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
remotes = ostree_repo_remote_list (self->repo, NULL);
|
||||
|
||||
do
|
||||
{
|
||||
g_autofree char *name = NULL;
|
||||
if (version == 0)
|
||||
name = g_strdup_printf ("%s-origin", id);
|
||||
else
|
||||
name = g_strdup_printf ("%s-%d-origin", id, version);
|
||||
version++;
|
||||
|
||||
if (remotes == NULL ||
|
||||
!g_strv_contains ((const char * const *) remotes, name))
|
||||
remote = g_steal_pointer (&name);
|
||||
}
|
||||
while (remote == NULL);
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.title",
|
||||
g_variant_new_variant (g_variant_new_string (title)));
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.noenumerate",
|
||||
g_variant_new_variant (g_variant_new_boolean (TRUE)));
|
||||
|
||||
g_variant_builder_add (optbuilder, "{s@v}",
|
||||
"xa.prio",
|
||||
g_variant_new_variant (g_variant_new_string ("0")));
|
||||
|
||||
if (!ostree_repo_remote_add (self->repo,
|
||||
remote, url ? url : "", g_variant_builder_end (optbuilder), cancellable, error))
|
||||
return NULL;
|
||||
|
||||
if (gpg_data)
|
||||
{
|
||||
g_autoptr(GInputStream) gpg_data_as_stream = g_memory_input_stream_new_from_bytes (gpg_data);
|
||||
|
||||
if (!ostree_repo_remote_gpg_import (self->repo, remote, gpg_data_as_stream,
|
||||
NULL, NULL, cancellable, error))
|
||||
{
|
||||
ostree_repo_remote_delete (self->repo, remote,
|
||||
NULL, NULL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer (&remote);
|
||||
}
|
||||
|
||||
|
||||
char **
|
||||
xdg_app_dir_list_remotes (XdgAppDir *self,
|
||||
|
|
|
@ -147,6 +147,13 @@ gboolean xdg_app_dir_pull (XdgAppDir *self,
|
|||
OstreeAsyncProgress *progress,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean xdg_app_dir_pull_from_bundle (XdgAppDir *self,
|
||||
GFile *file,
|
||||
const char *remote,
|
||||
const char *ref,
|
||||
gboolean require_gpg_signature,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
gboolean xdg_app_dir_list_refs_for_name (XdgAppDir *self,
|
||||
const char *kind,
|
||||
const char *name,
|
||||
|
@ -241,6 +248,13 @@ gboolean xdg_app_dir_collect_deployed_refs (XdgAppDir *self,
|
|||
GHashTable *hash,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char *xdg_app_dir_create_origin_remote (XdgAppDir *self,
|
||||
const char *url,
|
||||
const char *id,
|
||||
const char *title,
|
||||
GBytes *gpg_data,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
char **xdg_app_dir_list_remotes (XdgAppDir *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
|
Loading…
Reference in New Issue