From f35b409077dc7881e6e1a8d59bb709226e0d93c6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Mon, 7 Aug 2017 19:52:17 +0100 Subject: [PATCH] lib/repo-refs: Add ostree_repo_remote_list_collection_refs() API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This parallels ostree_repo_remote_list_refs(), but returns a map of OstreeCollectionRef → checksum, and includes refs from collection IDs other than the remote repository’s main collection ID. Use this in OstreeRepoFinderConfig to ensure that refs are matched against even if they’re stored in the repository summary file’s collection map, rather than its main ref map. This fixes false negatives when searching for refs in some situations. Signed-off-by: Philip Withnall Closes: #1058 Approved by: cgwalters --- apidoc/ostree-experimental-sections.txt | 1 + src/libostree/libostree-experimental.sym | 1 + src/libostree/ostree-repo-finder-config.c | 9 +- src/libostree/ostree-repo-refs.c | 120 ++++++++++++++++++++++ src/libostree/ostree-repo.h | 9 ++ 5 files changed, 136 insertions(+), 4 deletions(-) diff --git a/apidoc/ostree-experimental-sections.txt b/apidoc/ostree-experimental-sections.txt index a2c2c295..23412dda 100644 --- a/apidoc/ostree-experimental-sections.txt +++ b/apidoc/ostree-experimental-sections.txt @@ -82,6 +82,7 @@ ostree_repo_get_collection_id ostree_repo_set_collection_id ostree_validate_collection_id ostree_repo_list_collection_refs +ostree_repo_remote_list_collection_refs ostree_repo_set_collection_ref_immediate ostree_repo_transaction_set_collection_ref diff --git a/src/libostree/libostree-experimental.sym b/src/libostree/libostree-experimental.sym index 32ba0929..f60d4e01 100644 --- a/src/libostree/libostree-experimental.sym +++ b/src/libostree/libostree-experimental.sym @@ -70,6 +70,7 @@ global: ostree_repo_list_collection_refs; ostree_repo_pull_from_remotes_async; ostree_repo_pull_from_remotes_finish; + ostree_repo_remote_list_collection_refs; ostree_repo_resolve_keyring_for_collection; ostree_repo_set_collection_id; ostree_repo_set_collection_ref_immediate; diff --git a/src/libostree/ostree-repo-finder-config.c b/src/libostree/ostree-repo-finder-config.c index 79a63536..2f9841db 100644 --- a/src/libostree/ostree-repo-finder-config.c +++ b/src/libostree/ostree-repo-finder-config.c @@ -111,7 +111,7 @@ ostree_repo_finder_config_resolve_async (OstreeRepoFinder *find for (i = 0; i < n_remotes; i++) { g_autoptr(GError) local_error = NULL; - g_autoptr(GHashTable) remote_refs = NULL; /* (element-type utf8 utf8) */ + g_autoptr(GHashTable) remote_refs = NULL; /* (element-type OstreeCollectionRef utf8) */ const gchar *checksum; g_autofree gchar *remote_collection_id = NULL; @@ -127,8 +127,9 @@ ostree_repo_finder_config_resolve_async (OstreeRepoFinder *find continue; } - if (!ostree_repo_remote_list_refs (parent_repo, remote_name, &remote_refs, - cancellable, &local_error)) + if (!ostree_repo_remote_list_collection_refs (parent_repo, remote_name, + &remote_refs, cancellable, + &local_error)) { g_debug ("Ignoring remote ‘%s’ due to error loading its refs: %s", remote_name, local_error->message); @@ -139,7 +140,7 @@ ostree_repo_finder_config_resolve_async (OstreeRepoFinder *find for (j = 0; refs[j] != NULL; j++) { if (g_strcmp0 (refs[j]->collection_id, remote_collection_id) == 0 && - g_hash_table_lookup_extended (remote_refs, refs[j]->ref_name, NULL, (gpointer *) &checksum)) + g_hash_table_lookup_extended (remote_refs, refs[j], NULL, (gpointer *) &checksum)) { /* The requested ref is listed in the refs for this remote. Add * the remote to the results, and the ref to its diff --git a/src/libostree/ostree-repo-refs.c b/src/libostree/ostree-repo-refs.c index a180e40b..6c113fb6 100644 --- a/src/libostree/ostree-repo-refs.c +++ b/src/libostree/ostree-repo-refs.c @@ -779,6 +779,126 @@ ostree_repo_remote_list_refs (OstreeRepo *self, return TRUE; } +#ifdef OSTREE_ENABLE_EXPERIMENTAL_API +static gboolean +remote_list_collection_refs_process_refs (OstreeRepo *self, + const gchar *remote_name, + const gchar *summary_collection_id, + GVariant *summary_refs, + GHashTable *ret_all_refs, + GError **error) +{ + gsize j, n; + + for (j = 0, n = g_variant_n_children (summary_refs); j < n; j++) + { + const guchar *csum_bytes; + g_autoptr(GVariant) ref_v = NULL, csum_v = NULL; + gchar tmp_checksum[OSTREE_SHA256_STRING_LEN + 1]; + const gchar *ref_name; + + /* Check the ref name. */ + ref_v = g_variant_get_child_value (summary_refs, j); + g_variant_get_child (ref_v, 0, "&s", &ref_name); + + if (!ostree_validate_rev (ref_name, error)) + return FALSE; + + /* Check the commit checksum. */ + g_variant_get_child (ref_v, 1, "(t@ay@a{sv})", NULL, &csum_v, NULL); + + csum_bytes = ostree_checksum_bytes_peek_validate (csum_v, error); + if (csum_bytes == NULL) + return FALSE; + + ostree_checksum_inplace_from_bytes (csum_bytes, tmp_checksum); + + g_hash_table_insert (ret_all_refs, + ostree_collection_ref_new (summary_collection_id, ref_name), + g_strdup (tmp_checksum)); + } + + return TRUE; +} + +/** + * ostree_repo_remote_list_collection_refs: + * @self: Repo + * @remote_name: Name of the remote. + * @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum + * @cancellable: Cancellable + * @error: Error + * + * List refs advertised by @remote_name, including refs which are part of + * collections. If the repository at @remote_name has a collection ID set, its + * refs will be returned with that collection ID; otherwise, they will be returned + * with a %NULL collection ID in each #OstreeCollectionRef key in @out_all_refs. + * Any refs for other collections stored in the repository will also be returned. + * No filtering is performed. + * + * Since: 2017.10 + */ +gboolean +ostree_repo_remote_list_collection_refs (OstreeRepo *self, + const char *remote_name, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GBytes) summary_bytes = NULL; + g_autoptr(GHashTable) ret_all_refs = NULL; /* (element-type OstreeCollectionRef utf8) */ + g_autoptr(GVariant) summary_v = NULL; + g_autoptr(GVariant) additional_metadata_v = NULL; + g_autoptr(GVariant) summary_refs = NULL; + const char *summary_collection_id; + g_autoptr(GVariantIter) summary_collection_map = NULL; + + if (!ostree_repo_remote_fetch_summary (self, remote_name, + &summary_bytes, NULL, + cancellable, error)) + return FALSE; + + if (summary_bytes == NULL) + return glnx_throw (error, "Remote refs not available; server has no summary file"); + + ret_all_refs = g_hash_table_new_full (ostree_collection_ref_hash, + ostree_collection_ref_equal, + (GDestroyNotify) ostree_collection_ref_free, + g_free); + + summary_v = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, + summary_bytes, FALSE); + additional_metadata_v = g_variant_get_child_value (summary_v, 1); + + /* List the refs in the main map. */ + if (!g_variant_lookup (additional_metadata_v, OSTREE_SUMMARY_COLLECTION_ID, "&s", &summary_collection_id)) + summary_collection_id = NULL; + + summary_refs = g_variant_get_child_value (summary_v, 0); + + if (!remote_list_collection_refs_process_refs (self, remote_name, + summary_collection_id, summary_refs, + ret_all_refs, error)) + return FALSE; + + /* List the refs in the collection map. */ + if (!g_variant_lookup (additional_metadata_v, OSTREE_SUMMARY_COLLECTION_MAP, "a{sa(s(taya{sv}))}", &summary_collection_map)) + summary_collection_map = NULL; + + while (summary_collection_map != NULL && + g_variant_iter_loop (summary_collection_map, "{s@a(s(taya{sv}))}", &summary_collection_id, &summary_refs)) + { + if (!remote_list_collection_refs_process_refs (self, remote_name, + summary_collection_id, summary_refs, + ret_all_refs, error)) + return FALSE; + } + + ot_transfer_out_value (out_all_refs, &ret_all_refs); + return TRUE; +} +#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ + static char * relative_symlink_to (const char *relpath, const char *target) diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index decf9a4e..f01fee2a 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -482,6 +482,15 @@ gboolean ostree_repo_remote_list_refs (OstreeRepo *self, GCancellable *cancellable, GError **error); +#ifdef OSTREE_ENABLE_EXPERIMENTAL_API +_OSTREE_PUBLIC +gboolean ostree_repo_remote_list_collection_refs (OstreeRepo *self, + const char *remote_name, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error); +#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ + _OSTREE_PUBLIC gboolean ostree_repo_load_variant (OstreeRepo *self, OstreeObjectType objtype,