mirror of
https://github.com/ostreedev/ostree.git
synced 2025-03-09 12:58:38 +03:00
Merge pull request #1946 from dbnicholson/ostree-metadata-api
ostree-metadata commit API
This commit is contained in:
commit
8025a83b6d
@ -173,9 +173,9 @@ endif # USE_GPGME
|
||||
symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
|
||||
|
||||
# Uncomment this include when adding new development symbols.
|
||||
# if BUILDOPT_IS_DEVEL_BUILD
|
||||
# symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
|
||||
# endif
|
||||
if BUILDOPT_IS_DEVEL_BUILD
|
||||
symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
|
||||
endif
|
||||
|
||||
# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
|
||||
wl_versionscript_arg = -Wl,--version-script=
|
||||
@ -251,6 +251,7 @@ libostree_1_la_SOURCES += \
|
||||
src/libostree/ostree-sign-dummy.h \
|
||||
src/libostree/ostree-sign-ed25519.c \
|
||||
src/libostree/ostree-sign-ed25519.h \
|
||||
src/libostree/ostree-sign-private.h \
|
||||
$(NULL)
|
||||
|
||||
if USE_LIBSODIUM
|
||||
|
@ -483,6 +483,7 @@ ostree_repo_verify_commit
|
||||
ostree_repo_verify_commit_ext
|
||||
ostree_repo_verify_commit_for_remote
|
||||
ostree_repo_verify_summary
|
||||
ostree_repo_regenerate_metadata
|
||||
ostree_repo_regenerate_summary
|
||||
<SUBSECTION Standard>
|
||||
OSTREE_REPO
|
||||
|
@ -20,6 +20,11 @@
|
||||
- uncomment the include in Makefile-libostree.am
|
||||
*/
|
||||
|
||||
LIBOSTREE_2023.1 {
|
||||
global:
|
||||
ostree_repo_regenerate_metadata;
|
||||
} LIBOSTREE_2022.7;
|
||||
|
||||
/* Stub section for the stable release *after* this development one; don't
|
||||
* edit this other than to update the year. This is just a copy/paste
|
||||
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
|
||||
|
@ -2350,7 +2350,8 @@ ostree_repo_commit_transaction (OstreeRepo *self,
|
||||
/* Update the summary if auto-update-summary is set, because doing so was
|
||||
* delayed for each ref change during the transaction.
|
||||
*/
|
||||
if ((self->txn.refs || self->txn.collection_refs) &&
|
||||
if (!self->txn.disable_auto_summary &&
|
||||
(self->txn.refs || self->txn.collection_refs) &&
|
||||
!_ostree_repo_maybe_regenerate_summary (self, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@ -4870,3 +4871,72 @@ ostree_repo_transaction_stats_free (OstreeRepoTransactionStats *stats)
|
||||
G_DEFINE_BOXED_TYPE(OstreeRepoTransactionStats, ostree_repo_transaction_stats,
|
||||
ostree_repo_transaction_stats_copy,
|
||||
ostree_repo_transaction_stats_free);
|
||||
|
||||
|
||||
gboolean
|
||||
_ostree_repo_transaction_write_repo_metadata (OstreeRepo *self,
|
||||
GVariant *additional_metadata,
|
||||
char **out_checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (self != NULL);
|
||||
g_assert (OSTREE_IS_REPO (self));
|
||||
g_assert (self->in_transaction == TRUE);
|
||||
|
||||
const char *collection_id = ostree_repo_get_collection_id (self);
|
||||
if (collection_id == NULL)
|
||||
return glnx_throw (error, "Repository must have collection ID to write repo metadata");
|
||||
|
||||
OstreeCollectionRef collection_ref = { (gchar *) collection_id,
|
||||
(gchar *) OSTREE_REPO_METADATA_REF };
|
||||
g_autofree char *old_checksum = NULL;
|
||||
if (!ostree_repo_resolve_rev (self, OSTREE_REPO_METADATA_REF, TRUE,
|
||||
&old_checksum, error))
|
||||
return FALSE;
|
||||
|
||||
/* Add bindings to the commit metadata. */
|
||||
g_autoptr(GVariantDict) metadata_dict = g_variant_dict_new (additional_metadata);
|
||||
g_variant_dict_insert (metadata_dict, OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
|
||||
"s", collection_ref.collection_id);
|
||||
g_variant_dict_insert_value (metadata_dict, OSTREE_COMMIT_META_KEY_REF_BINDING,
|
||||
g_variant_new_strv ((const gchar * const *) &collection_ref.ref_name, 1));
|
||||
g_autoptr(GVariant) metadata = g_variant_dict_end (metadata_dict);
|
||||
|
||||
/* Set up an empty mtree. */
|
||||
g_autoptr(OstreeMutableTree) mtree = ostree_mutable_tree_new ();
|
||||
|
||||
glnx_unref_object GFileInfo *fi = g_file_info_new ();
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::mode", (0755 | S_IFDIR));
|
||||
|
||||
g_autoptr(GVariant) dirmeta = ostree_create_directory_metadata (fi, NULL /* xattrs */);
|
||||
|
||||
g_autofree guchar *csum_raw = NULL;
|
||||
if (!ostree_repo_write_metadata (self, OSTREE_OBJECT_TYPE_DIR_META, NULL,
|
||||
dirmeta, &csum_raw, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *csum = ostree_checksum_from_bytes (csum_raw);
|
||||
ostree_mutable_tree_set_metadata_checksum (mtree, csum);
|
||||
|
||||
g_autoptr(OstreeRepoFile) repo_file = NULL;
|
||||
if (!ostree_repo_write_mtree (self, mtree, (GFile **) &repo_file, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree gchar *new_checksum = NULL;
|
||||
if (!ostree_repo_write_commit (self, old_checksum,
|
||||
NULL /* subject */, NULL /* body */,
|
||||
metadata, repo_file,
|
||||
&new_checksum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
ostree_repo_transaction_set_collection_ref (self, &collection_ref, new_checksum);
|
||||
|
||||
if (out_checksum != NULL)
|
||||
*out_checksum = g_steal_pointer (&new_checksum);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ typedef struct {
|
||||
/* Implementation of min-free-space-percent */
|
||||
gulong blocksize;
|
||||
fsblkcnt_t max_blocks;
|
||||
gboolean disable_auto_summary;
|
||||
} OstreeRepoTxn;
|
||||
|
||||
typedef struct {
|
||||
@ -522,6 +523,13 @@ ostree_repo_list_objects_set (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_transaction_write_repo_metadata (OstreeRepo *self,
|
||||
GVariant *additional_metadata,
|
||||
char **out_checksum,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
/**
|
||||
* OstreeRepoAutoTransaction:
|
||||
*
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "ostree-repo-static-delta-private.h"
|
||||
#include "ot-fs-utils.h"
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-sign-private.h"
|
||||
|
||||
#include <locale.h>
|
||||
#include <glib/gstdio.h>
|
||||
@ -5592,18 +5593,9 @@ ostree_repo_sign_delta (OstreeRepo *self,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_add_gpg_signature_summary:
|
||||
* @self: Self
|
||||
* @key_id: (array zero-terminated=1) (element-type utf8): NULL-terminated array of GPG keys.
|
||||
* @homedir: (allow-none): GPG home directory, or %NULL
|
||||
* @cancellable: A #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Add a GPG signature to a summary file.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
static gboolean
|
||||
_ostree_repo_add_gpg_signature_summary_at (OstreeRepo *self,
|
||||
int dir_fd,
|
||||
const gchar **key_id,
|
||||
const gchar *homedir,
|
||||
GCancellable *cancellable,
|
||||
@ -5611,7 +5603,7 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
{
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
glnx_autofd int fd = -1;
|
||||
if (!glnx_openat_rdonly (self->repo_dir_fd, "summary", TRUE, &fd, error))
|
||||
if (!glnx_openat_rdonly (dir_fd, "summary", TRUE, &fd, error))
|
||||
return FALSE;
|
||||
g_autoptr(GBytes) summary_data = ot_fd_readall_or_mmap (fd, 0, error);
|
||||
if (!summary_data)
|
||||
@ -5620,7 +5612,7 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
if (!ot_openat_ignore_enoent (self->repo_dir_fd, "summary.sig", &fd, error))
|
||||
if (!ot_openat_ignore_enoent (dir_fd, "summary.sig", &fd, error))
|
||||
return FALSE;
|
||||
if (fd >= 0)
|
||||
{
|
||||
@ -5644,7 +5636,7 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
g_autoptr(GVariant) normalized = g_variant_get_normal_form (metadata);
|
||||
|
||||
if (!_ostree_repo_file_replace_contents (self,
|
||||
self->repo_dir_fd,
|
||||
dir_fd,
|
||||
"summary.sig",
|
||||
g_variant_get_data (normalized),
|
||||
g_variant_get_size (normalized),
|
||||
@ -5652,6 +5644,35 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
return glnx_throw (error, "GPG feature is disabled at build time");
|
||||
#endif /* OSTREE_DISABLE_GPGME */
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_add_gpg_signature_summary:
|
||||
* @self: Self
|
||||
* @key_id: (array zero-terminated=1) (element-type utf8): NULL-terminated array of GPG keys.
|
||||
* @homedir: (allow-none): GPG home directory, or %NULL
|
||||
* @cancellable: A #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Add a GPG signature to a summary file.
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
|
||||
const gchar **key_id,
|
||||
const gchar *homedir,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
return _ostree_repo_add_gpg_signature_summary_at (self,
|
||||
self->repo_dir_fd,
|
||||
key_id,
|
||||
homedir,
|
||||
cancellable,
|
||||
error);
|
||||
#else
|
||||
return glnx_throw (error, "GPG feature is disabled in a build time");
|
||||
#endif /* OSTREE_DISABLE_GPGME */
|
||||
@ -6235,35 +6256,11 @@ summary_add_ref_entry (OstreeRepo *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_regenerate_summary:
|
||||
* @self: Repo
|
||||
* @additional_metadata: (allow-none): A GVariant of type a{sv}, or %NULL
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* An OSTree repository can contain a high level "summary" file that
|
||||
* describes the available branches and other metadata.
|
||||
*
|
||||
* If the timetable for making commits and updating the summary file is fairly
|
||||
* regular, setting the `ostree.summary.expires` key in @additional_metadata
|
||||
* will aid clients in working out when to check for updates.
|
||||
*
|
||||
* It is regenerated automatically after any ref is
|
||||
* added, removed, or updated if `core/auto-update-summary` is set.
|
||||
*
|
||||
* If the `core/collection-id` key is set in the configuration, it will be
|
||||
* included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs that
|
||||
* have associated collection IDs will be included in the generated summary
|
||||
* file, listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs
|
||||
* and refs in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in
|
||||
* lexicographic order.
|
||||
*
|
||||
* Locking: shared (Prior to 2021.7, this was exclusive)
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
static gboolean
|
||||
regenerate_metadata (OstreeRepo *self,
|
||||
gboolean do_metadata_commit,
|
||||
GVariant *additional_metadata,
|
||||
GVariant *options,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@ -6275,12 +6272,99 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
if (!lock)
|
||||
return FALSE;
|
||||
|
||||
/* Parse options vardict. */
|
||||
g_autofree char **gpg_key_ids = NULL;
|
||||
const char *gpg_homedir = NULL;
|
||||
g_autoptr(GVariant) sign_keys = NULL;
|
||||
const char *sign_type = NULL;
|
||||
g_autoptr(OstreeSign) sign = NULL;
|
||||
|
||||
if (options != NULL)
|
||||
{
|
||||
if (!g_variant_is_of_type (options, G_VARIANT_TYPE_VARDICT))
|
||||
return glnx_throw (error, "Invalid options doesn't match variant type '%s'",
|
||||
(const char *) G_VARIANT_TYPE_VARDICT);
|
||||
|
||||
g_variant_lookup (options, "gpg-key-ids", "^a&s", &gpg_key_ids);
|
||||
g_variant_lookup (options, "gpg-homedir", "&s", &gpg_homedir);
|
||||
sign_keys = g_variant_lookup_value (options, "sign-keys", G_VARIANT_TYPE_ARRAY);
|
||||
g_variant_lookup (options, "sign-type", "&s", &sign_type);
|
||||
|
||||
if (sign_keys != NULL)
|
||||
{
|
||||
if (sign_type == NULL)
|
||||
sign_type = OSTREE_SIGN_NAME_ED25519;
|
||||
|
||||
sign = ostree_sign_get_by_name (sign_type, error);
|
||||
if (sign == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
const gchar *main_collection_id = ostree_repo_get_collection_id (self);
|
||||
|
||||
/* Write out a new metadata commit for the repository when it has a collection ID. */
|
||||
if (do_metadata_commit && main_collection_id != NULL)
|
||||
{
|
||||
g_autoptr(OstreeRepoAutoTransaction) txn =
|
||||
_ostree_repo_auto_transaction_start (self, cancellable, error);
|
||||
if (!txn)
|
||||
return FALSE;
|
||||
|
||||
/* Disable automatic summary updating since we're already doing it */
|
||||
self->txn.disable_auto_summary = TRUE;
|
||||
|
||||
g_autofree gchar *new_ostree_metadata_checksum = NULL;
|
||||
if (!_ostree_repo_transaction_write_repo_metadata (self,
|
||||
additional_metadata,
|
||||
&new_ostree_metadata_checksum,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* Sign the new commit. */
|
||||
if (gpg_key_ids != NULL)
|
||||
{
|
||||
for (const char * const *iter = (const char * const *) gpg_key_ids;
|
||||
iter != NULL && *iter != NULL; iter++)
|
||||
{
|
||||
const char *gpg_key_id = *iter;
|
||||
|
||||
if (!ostree_repo_sign_commit (self,
|
||||
new_ostree_metadata_checksum,
|
||||
gpg_key_id,
|
||||
gpg_homedir,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (sign_keys != NULL)
|
||||
{
|
||||
GVariantIter *iter;
|
||||
GVariant *key;
|
||||
|
||||
g_variant_get (sign_keys, "av", &iter);
|
||||
while (g_variant_iter_loop (iter, "v", &key))
|
||||
{
|
||||
if (!ostree_sign_set_sk (sign, key, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sign_commit (sign, self, new_ostree_metadata_checksum,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_ostree_repo_auto_transaction_commit (txn, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_auto(GVariantDict) additional_metadata_builder = OT_VARIANT_BUILDER_INITIALIZER;
|
||||
g_variant_dict_init (&additional_metadata_builder, additional_metadata);
|
||||
g_autoptr(GVariantBuilder) refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(s(taya{sv}))"));
|
||||
|
||||
const gchar *main_collection_id = ostree_repo_get_collection_id (self);
|
||||
|
||||
{
|
||||
if (main_collection_id == NULL)
|
||||
{
|
||||
@ -6460,8 +6544,17 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
if (!ostree_repo_static_delta_reindex (self, 0, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Create the summary and signature in a temporary directory so that
|
||||
* the summary isn't published without a matching signature.
|
||||
*/
|
||||
g_auto(GLnxTmpDir) summary_tmpdir = { 0, };
|
||||
if (!glnx_mkdtempat (self->tmp_dir_fd, "summary-XXXXXX", 0777,
|
||||
&summary_tmpdir, error))
|
||||
return FALSE;
|
||||
g_debug ("Using summary tmpdir %s", summary_tmpdir.path);
|
||||
|
||||
if (!_ostree_repo_file_replace_contents (self,
|
||||
self->repo_dir_fd,
|
||||
summary_tmpdir.fd,
|
||||
"summary",
|
||||
g_variant_get_data (summary),
|
||||
g_variant_get_size (summary),
|
||||
@ -6469,12 +6562,124 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
|
||||
if (gpg_key_ids != NULL &&
|
||||
!_ostree_repo_add_gpg_signature_summary_at (self, summary_tmpdir.fd,
|
||||
(const char **) gpg_key_ids, gpg_homedir,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (sign_keys != NULL &&
|
||||
!_ostree_sign_summary_at (sign, self, summary_tmpdir.fd, sign_keys,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Rename them into place */
|
||||
if (!glnx_renameat (summary_tmpdir.fd, "summary",
|
||||
self->repo_dir_fd, "summary",
|
||||
error))
|
||||
return glnx_prefix_error (error, "Unable to rename summary file: ");
|
||||
|
||||
if (gpg_key_ids != NULL || sign_keys != NULL)
|
||||
{
|
||||
if (!glnx_renameat (summary_tmpdir.fd, "summary.sig",
|
||||
self->repo_dir_fd, "summary.sig",
|
||||
error))
|
||||
{
|
||||
/* Delete an existing signature since it no longer corresponds
|
||||
* to the published summary.
|
||||
*/
|
||||
g_debug ("Deleting existing unmatched summary.sig file");
|
||||
(void) ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", NULL);
|
||||
|
||||
return glnx_prefix_error (error, "Unable to rename summary signature file: ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_debug ("Deleting existing unmatched summary.sig file");
|
||||
if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
|
||||
return glnx_prefix_error (error, "Unable to delete summary signature file: ");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_regenerate_summary:
|
||||
* @self: Repo
|
||||
* @additional_metadata: (allow-none): A GVariant of type a{sv}, or %NULL
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* An OSTree repository can contain a high level "summary" file that
|
||||
* describes the available branches and other metadata.
|
||||
*
|
||||
* If the timetable for making commits and updating the summary file is fairly
|
||||
* regular, setting the `ostree.summary.expires` key in @additional_metadata
|
||||
* will aid clients in working out when to check for updates.
|
||||
*
|
||||
* It is regenerated automatically after any ref is
|
||||
* added, removed, or updated if `core/auto-update-summary` is set.
|
||||
*
|
||||
* If the `core/collection-id` key is set in the configuration, it will be
|
||||
* included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs that
|
||||
* have associated collection IDs will be included in the generated summary
|
||||
* file, listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs
|
||||
* and refs in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in
|
||||
* lexicographic order.
|
||||
*
|
||||
* Locking: shared (Prior to 2021.7, this was exclusive)
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
GVariant *additional_metadata,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return regenerate_metadata (self, FALSE, additional_metadata, NULL, cancellable, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_repo_regenerate_metadata:
|
||||
* @self: Repo
|
||||
* @additional_metadata: (nullable): A GVariant `a{sv}`, or %NULL
|
||||
* @options: (nullable): A GVariant `a{sv}` with an extensible set of flags
|
||||
* @cancellable: Cancellable
|
||||
* @error: Error
|
||||
*
|
||||
* Regenerate the OSTree repository metadata used by clients to describe
|
||||
* available branches and other metadata.
|
||||
*
|
||||
* The repository metadata currently consists of the `summary` file. See
|
||||
* ostree_repo_regenerate_summary() and %OSTREE_SUMMARY_GVARIANT_FORMAT for
|
||||
* additional details on its contents.
|
||||
*
|
||||
* Additionally, if the `core/collection-id` key is set in the configuration, a
|
||||
* %OSTREE_REPO_METADATA_REF commit will be created.
|
||||
*
|
||||
* The following @options are currently defined:
|
||||
*
|
||||
* * `gpg-key-ids` (`as`): Array of GPG key IDs to sign the metadata with.
|
||||
* * `gpg-homedir` (`s`): GPG home directory.
|
||||
* * `sign-keys` (`av`): Array of keys to sign the metadata with. The key
|
||||
* type is specific to the sign engine used.
|
||||
* * `sign-type` (`s`): Sign engine type to use. If not specified,
|
||||
* %OSTREE_SIGN_NAME_ED25519 is used.
|
||||
*
|
||||
* Locking: shared
|
||||
*
|
||||
* Since: 2023.1
|
||||
*/
|
||||
gboolean
|
||||
ostree_repo_regenerate_metadata (OstreeRepo *self,
|
||||
GVariant *additional_metadata,
|
||||
GVariant *options,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return regenerate_metadata (self, TRUE, additional_metadata, options, cancellable, error);
|
||||
}
|
||||
|
||||
/* Regenerate the summary if `core/auto-update-summary` is set. We default to FALSE for
|
||||
* this setting because OSTree supports multiple processes committing to the same repo (but
|
||||
* different refs) concurrently, and in fact gnome-continuous actually does this. In that
|
||||
|
@ -1585,6 +1585,13 @@ gboolean ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
_OSTREE_PUBLIC
|
||||
gboolean ostree_repo_regenerate_metadata (OstreeRepo *self,
|
||||
GVariant *additional_metadata,
|
||||
GVariant *options,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
|
||||
/**
|
||||
* OstreeRepoLockType:
|
||||
|
39
src/libostree/ostree-sign-private.h
Normal file
39
src/libostree/ostree-sign-private.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright © 2023 Endless OS Foundation LLC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.0+
|
||||
*
|
||||
* 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, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors:
|
||||
* - Dan Nicholson <dbn@endlessos.org>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "ostree-sign.h"
|
||||
#include "ostree-types.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
gboolean _ostree_sign_summary_at (OstreeSign *self,
|
||||
OstreeRepo *repo,
|
||||
int dir_fd,
|
||||
GVariant *keys,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
@ -40,6 +40,7 @@
|
||||
#include "ostree-autocleanups.h"
|
||||
#include "ostree-core.h"
|
||||
#include "ostree-sign.h"
|
||||
#include "ostree-sign-private.h"
|
||||
#include "ostree-sign-dummy.h"
|
||||
#ifdef HAVE_LIBSODIUM
|
||||
#include "ostree-sign-ed25519.h"
|
||||
@ -601,24 +602,10 @@ ostree_sign_get_by_name (const gchar *name, GError **error)
|
||||
return sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sign_summary:
|
||||
* @self: Self
|
||||
* @repo: ostree repository
|
||||
* @keys: keys -- GVariant containing keys as GVarints specific to signature type.
|
||||
* @cancellable: A #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Add a signature to a summary file.
|
||||
* Based on ostree_repo_add_gpg_signature_summary implementation.
|
||||
*
|
||||
* Returns: @TRUE if summary file has been signed with all provided keys
|
||||
*
|
||||
* Since: 2020.2
|
||||
*/
|
||||
gboolean
|
||||
ostree_sign_summary (OstreeSign *self,
|
||||
_ostree_sign_summary_at (OstreeSign *self,
|
||||
OstreeRepo *repo,
|
||||
int dir_fd,
|
||||
GVariant *keys,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
@ -631,7 +618,7 @@ ostree_sign_summary (OstreeSign *self,
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
|
||||
glnx_autofd int fd = -1;
|
||||
if (!glnx_openat_rdonly (repo->repo_dir_fd, "summary", TRUE, &fd, error))
|
||||
if (!glnx_openat_rdonly (dir_fd, "summary", TRUE, &fd, error))
|
||||
return FALSE;
|
||||
summary_data = ot_fd_readall_or_mmap (fd, 0, error);
|
||||
if (!summary_data)
|
||||
@ -640,7 +627,7 @@ ostree_sign_summary (OstreeSign *self,
|
||||
/* Note that fd is reused below */
|
||||
glnx_close_fd (&fd);
|
||||
|
||||
if (!ot_openat_ignore_enoent (repo->repo_dir_fd, "summary.sig", &fd, error))
|
||||
if (!ot_openat_ignore_enoent (dir_fd, "summary.sig", &fd, error))
|
||||
return FALSE;
|
||||
|
||||
if (fd >= 0)
|
||||
@ -681,7 +668,7 @@ ostree_sign_summary (OstreeSign *self,
|
||||
|
||||
normalized = g_variant_get_normal_form (metadata);
|
||||
if (!_ostree_repo_file_replace_contents (repo,
|
||||
repo->repo_dir_fd,
|
||||
dir_fd,
|
||||
"summary.sig",
|
||||
g_variant_get_data (normalized),
|
||||
g_variant_get_size (normalized),
|
||||
@ -690,3 +677,29 @@ ostree_sign_summary (OstreeSign *self,
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ostree_sign_summary:
|
||||
* @self: Self
|
||||
* @repo: ostree repository
|
||||
* @keys: keys -- GVariant containing keys as GVarints specific to signature type.
|
||||
* @cancellable: A #GCancellable
|
||||
* @error: a #GError
|
||||
*
|
||||
* Add a signature to a summary file.
|
||||
* Based on ostree_repo_add_gpg_signature_summary implementation.
|
||||
*
|
||||
* Returns: @TRUE if summary file has been signed with all provided keys
|
||||
*
|
||||
* Since: 2020.2
|
||||
*/
|
||||
gboolean
|
||||
ostree_sign_summary (OstreeSign *self,
|
||||
OstreeRepo *repo,
|
||||
GVariant *keys,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
return _ostree_sign_summary_at (self, repo, repo->repo_dir_fd, keys,
|
||||
cancellable, error);
|
||||
}
|
||||
|
@ -146,152 +146,40 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char *collection_id = ostree_repo_get_collection_id (repo);
|
||||
|
||||
/* Write out a new metadata commit for the repository. */
|
||||
if (collection_id != NULL)
|
||||
{
|
||||
OstreeCollectionRef collection_ref = { (gchar *) collection_id, (gchar *) OSTREE_REPO_METADATA_REF };
|
||||
g_autofree char *old_ostree_metadata_checksum = NULL;
|
||||
g_autofree gchar *new_ostree_metadata_checksum = NULL;
|
||||
g_autoptr(OstreeMutableTree) mtree = NULL;
|
||||
g_autoptr(OstreeRepoFile) repo_file = NULL;
|
||||
g_autoptr(GVariantDict) new_summary_commit_dict = NULL;
|
||||
g_autoptr(GVariant) new_summary_commit = NULL;
|
||||
|
||||
if (!ostree_repo_resolve_rev (repo, OSTREE_REPO_METADATA_REF,
|
||||
TRUE, &old_ostree_metadata_checksum, error))
|
||||
return FALSE;
|
||||
|
||||
/* Add bindings to the metadata. */
|
||||
new_summary_commit_dict = g_variant_dict_new (additional_metadata);
|
||||
g_variant_dict_insert (new_summary_commit_dict, OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
|
||||
"s", collection_ref.collection_id);
|
||||
g_variant_dict_insert_value (new_summary_commit_dict, OSTREE_COMMIT_META_KEY_REF_BINDING,
|
||||
g_variant_new_strv ((const gchar * const *) &collection_ref.ref_name, 1));
|
||||
new_summary_commit = g_variant_dict_end (new_summary_commit_dict);
|
||||
|
||||
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* Set up an empty mtree. */
|
||||
mtree = ostree_mutable_tree_new ();
|
||||
|
||||
glnx_unref_object GFileInfo *fi = g_file_info_new ();
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
|
||||
g_file_info_set_attribute_uint32 (fi, "unix::mode", (0755 | S_IFDIR));
|
||||
|
||||
g_autofree guchar *csum_raw = NULL;
|
||||
g_autofree char *csum = NULL;
|
||||
|
||||
g_autoptr(GVariant) dirmeta = ostree_create_directory_metadata (fi, NULL /* xattrs */);
|
||||
|
||||
if (!ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, NULL,
|
||||
dirmeta, &csum_raw, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
csum = ostree_checksum_from_bytes (csum_raw);
|
||||
ostree_mutable_tree_set_metadata_checksum (mtree, csum);
|
||||
|
||||
if (!ostree_repo_write_mtree (repo, mtree, (GFile **) &repo_file, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_write_commit (repo, old_ostree_metadata_checksum,
|
||||
NULL /* subject */, NULL /* body */,
|
||||
new_summary_commit, repo_file, &new_ostree_metadata_checksum,
|
||||
NULL, error))
|
||||
return FALSE;
|
||||
/* Regenerate and sign the repo metadata. */
|
||||
g_auto(GVariantBuilder) metadata_opts_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
|
||||
g_autoptr(GVariant) metadata_opts = NULL;
|
||||
if (opt_gpg_key_ids != NULL)
|
||||
g_variant_builder_add (&metadata_opts_builder, "{sv}", "gpg-key-ids",
|
||||
g_variant_new_strv ((const char * const *) opt_gpg_key_ids, -1));
|
||||
if (opt_gpg_homedir != NULL)
|
||||
g_variant_builder_add (&metadata_opts_builder, "{sv}", "gpg-homedir",
|
||||
g_variant_new_string (opt_gpg_homedir));
|
||||
if (opt_key_ids != NULL)
|
||||
{
|
||||
for (const char * const *iter = (const char * const *) opt_gpg_key_ids;
|
||||
g_auto(GVariantBuilder) sk_builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_ARRAY);
|
||||
|
||||
/* Currently only strings are used as keys for supported
|
||||
* signature types. */
|
||||
for (const char * const *iter = (const char * const *) opt_key_ids;
|
||||
iter != NULL && *iter != NULL; iter++)
|
||||
{
|
||||
const char *key_id = *iter;
|
||||
g_variant_builder_add (&sk_builder, "v", g_variant_new_string (key_id));
|
||||
}
|
||||
|
||||
if (!ostree_repo_sign_commit (repo,
|
||||
new_ostree_metadata_checksum,
|
||||
key_id,
|
||||
opt_gpg_homedir,
|
||||
cancellable,
|
||||
error))
|
||||
g_variant_builder_add (&metadata_opts_builder, "{sv}", "sign-keys",
|
||||
g_variant_builder_end (&sk_builder));
|
||||
}
|
||||
if (opt_sign_name != NULL)
|
||||
g_variant_builder_add (&metadata_opts_builder, "{sv}", "sign-type",
|
||||
g_variant_new_string (opt_sign_name));
|
||||
|
||||
metadata_opts = g_variant_ref_sink (g_variant_builder_end (&metadata_opts_builder));
|
||||
if (!ostree_repo_regenerate_metadata (repo, additional_metadata, metadata_opts,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_key_ids)
|
||||
{
|
||||
char **iter;
|
||||
for (iter = opt_key_ids; iter && *iter; iter++)
|
||||
{
|
||||
const char *keyid = *iter;
|
||||
g_autoptr (GVariant) secret_key = NULL;
|
||||
|
||||
secret_key = g_variant_new_string (keyid);
|
||||
if (!ostree_sign_set_sk (sign, secret_key, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_sign_commit (sign,
|
||||
repo,
|
||||
new_ostree_metadata_checksum,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
ostree_repo_transaction_set_collection_ref (repo, &collection_ref,
|
||||
new_ostree_metadata_checksum);
|
||||
|
||||
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Regenerate and sign the conventional summary file. */
|
||||
if (!ostree_repo_regenerate_summary (repo, additional_metadata, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
#ifndef OSTREE_DISABLE_GPGME
|
||||
if (opt_gpg_key_ids)
|
||||
{
|
||||
if (!ostree_repo_add_gpg_signature_summary (repo,
|
||||
(const gchar **) opt_gpg_key_ids,
|
||||
opt_gpg_homedir,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
if (opt_key_ids)
|
||||
{
|
||||
g_autoptr (GVariant) secret_keys = NULL;
|
||||
g_autoptr (GVariantBuilder) sk_builder = NULL;
|
||||
|
||||
sk_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
|
||||
|
||||
char **iter;
|
||||
for (iter = opt_key_ids; iter && *iter; iter++)
|
||||
{
|
||||
const char *keyid = *iter;
|
||||
GVariant *secret_key = NULL;
|
||||
|
||||
/* Currently only strings are used as keys
|
||||
* for supported signature types */
|
||||
secret_key = g_variant_new_string (keyid);
|
||||
|
||||
g_variant_builder_add (sk_builder, "v", secret_key);
|
||||
}
|
||||
|
||||
secret_keys = g_variant_builder_end (sk_builder);
|
||||
|
||||
if (! ostree_sign_summary (sign,
|
||||
repo,
|
||||
secret_keys,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (opt_view || opt_raw)
|
||||
{
|
||||
g_autoptr(GBytes) summary_data = NULL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user