diff --git a/apidoc/ostree-experimental-sections.txt b/apidoc/ostree-experimental-sections.txt index 16983ae8..78a50100 100644 --- a/apidoc/ostree-experimental-sections.txt +++ b/apidoc/ostree-experimental-sections.txt @@ -26,4 +26,7 @@ ostree_remote_get_name ostree_repo_get_collection_id ostree_repo_set_collection_id ostree_validate_collection_id +ostree_repo_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 dad788b7..9d2024f3 100644 --- a/src/libostree/libostree-experimental.sym +++ b/src/libostree/libostree-experimental.sym @@ -46,6 +46,9 @@ global: ostree_collection_ref_hash; ostree_collection_ref_new; ostree_repo_get_collection_id; + ostree_repo_list_collection_refs; ostree_repo_set_collection_id; + ostree_repo_set_collection_ref_immediate; + ostree_repo_transaction_set_collection_ref; ostree_validate_collection_id; } LIBOSTREE_2017.7_EXPERIMENTAL; diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index cf6d90e7..8d474d63 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -1347,6 +1347,11 @@ ensure_txn_refs (OstreeRepo *self) { if (self->txn_refs == NULL) self->txn_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + if (self->txn_collection_refs == NULL) + self->txn_collection_refs = g_hash_table_new_full (ostree_collection_ref_hash, + ostree_collection_ref_equal, + (GDestroyNotify) ostree_collection_ref_free, + g_free); } /** @@ -1410,6 +1415,41 @@ ostree_repo_transaction_set_ref (OstreeRepo *self, g_hash_table_replace (self->txn_refs, refspec, g_strdup (checksum)); } +/** + * ostree_repo_transaction_set_collection_ref: + * @self: An #OstreeRepo + * @ref: The collection–ref to write + * @checksum: (nullable): The checksum to point it to + * + * If @checksum is not %NULL, then record it as the target of local ref named + * @ref. + * + * Otherwise, if @checksum is %NULL, then record that the ref should + * be deleted. + * + * The change will not be written out immediately, but when the transaction + * is completed with ostree_repo_commit_transaction(). If the transaction + * is instead aborted with ostree_repo_abort_transaction(), no changes will + * be made to the repository. + * + * Since: 2017.8 + */ +void +ostree_repo_transaction_set_collection_ref (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum) +{ + g_return_if_fail (OSTREE_IS_REPO (self)); + g_return_if_fail (self->in_transaction == TRUE); + g_return_if_fail (ref != NULL); + g_return_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL)); + + ensure_txn_refs (self); + + g_hash_table_replace (self->txn_collection_refs, + ostree_collection_ref_dup (ref), g_strdup (checksum)); +} + /** * ostree_repo_set_ref_immediate: * @self: An #OstreeRepo @@ -1431,7 +1471,40 @@ ostree_repo_set_ref_immediate (OstreeRepo *self, GCancellable *cancellable, GError **error) { - return _ostree_repo_write_ref (self, remote, ref, checksum, + const OstreeCollectionRef _ref = { NULL, (gchar *) ref }; + return _ostree_repo_write_ref (self, remote, &_ref, checksum, + cancellable, error); +} + +/** + * ostree_repo_set_collection_ref_immediate: + * @self: An #OstreeRepo + * @ref: The collection–ref to write + * @checksum: (nullable): The checksum to point it to, or %NULL to unset + * @cancellable: GCancellable + * @error: GError + * + * This is like ostree_repo_transaction_set_collection_ref(), except it may be + * invoked outside of a transaction. This is presently safe for the + * case where we're creating or overwriting an existing ref. + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: 2017.8 + */ +gboolean +ostree_repo_set_collection_ref_immediate (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); + g_return_val_if_fail (ref != NULL, FALSE); + g_return_val_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + return _ostree_repo_write_ref (self, NULL, ref, checksum, cancellable, error); } @@ -1481,6 +1554,11 @@ ostree_repo_commit_transaction (OstreeRepo *self, return FALSE; g_clear_pointer (&self->txn_refs, g_hash_table_destroy); + if (self->txn_collection_refs) + if (!_ostree_repo_update_collection_refs (self, self->txn_collection_refs, cancellable, error)) + return FALSE; + g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy); + if (self->commit_stagedir_fd != -1) { (void) close (self->commit_stagedir_fd); @@ -1518,6 +1596,7 @@ ostree_repo_abort_transaction (OstreeRepo *self, g_hash_table_remove_all (self->loose_object_devino_hash); g_clear_pointer (&self->txn_refs, g_hash_table_destroy); + g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy); if (self->commit_stagedir_fd != -1) { diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 825c1ffc..0081eb31 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -48,6 +48,8 @@ G_BEGIN_DECLS /* Well-known keys for the additional metadata field in a summary file. */ #define OSTREE_SUMMARY_LAST_MODIFIED "ostree.summary.last-modified" #define OSTREE_SUMMARY_EXPIRES "ostree.summary.expires" +#define OSTREE_SUMMARY_COLLECTION_ID "ostree.summary.collection-id" +#define OSTREE_SUMMARY_COLLECTION_MAP "ostree.summary.collection-map" /* Well-known keys for the additional metadata field in a commit in a ref entry * in a summary file. */ @@ -96,7 +98,8 @@ struct OstreeRepo { GFile *sysroot_dir; char *remotes_config_dir; - GHashTable *txn_refs; + GHashTable *txn_refs; /* (element-type utf8 utf8) */ + GHashTable *txn_collection_refs; /* (element-type OstreeCollectionRef utf8) */ GMutex txn_stats_lock; OstreeRepoTransactionStats txn_stats; @@ -221,7 +224,13 @@ _ostree_repo_update_refs (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean +gboolean +_ostree_repo_update_collection_refs (OstreeRepo *self, + GHashTable *refs, + GCancellable *cancellable, + GError **error); + +gboolean _ostree_repo_file_replace_contents (OstreeRepo *self, int dfd, const char *path, @@ -230,13 +239,13 @@ _ostree_repo_file_replace_contents (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean -_ostree_repo_write_ref (OstreeRepo *self, - const char *remote, - const char *ref, - const char *rev, - GCancellable *cancellable, - GError **error); +gboolean +_ostree_repo_write_ref (OstreeRepo *self, + const char *remote, + const OstreeCollectionRef *ref, + const char *rev, + GCancellable *cancellable, + GError **error); OstreeRepoFile * _ostree_repo_file_new_for_commit (OstreeRepo *repo, @@ -358,6 +367,22 @@ gboolean ostree_repo_set_collection_id (OstreeRepo *self, const gchar *collection_id, GError **error); +gboolean ostree_repo_list_collection_refs (OstreeRepo *self, + const char *match_collection_id, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error); + +void ostree_repo_transaction_set_collection_ref (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum); + +gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum, + GCancellable *cancellable, + GError **error); + #endif /* !OSTREE_ENABLE_EXPERIMENTAL_API */ G_END_DECLS diff --git a/src/libostree/ostree-repo-prune.c b/src/libostree/ostree-repo-prune.c index bf0a2530..c0da7121 100644 --- a/src/libostree/ostree-repo-prune.c +++ b/src/libostree/ostree-repo-prune.c @@ -312,7 +312,6 @@ ostree_repo_prune (OstreeRepo *self, GHashTableIter hash_iter; gpointer key, value; g_autoptr(GHashTable) objects = NULL; - g_autoptr(GHashTable) all_refs = NULL; g_autoptr(GHashTable) reachable = NULL; gboolean refs_only = flags & OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY; @@ -325,12 +324,34 @@ ostree_repo_prune (OstreeRepo *self, if (refs_only) { + /* Ignoring collections. */ + g_autoptr(GHashTable) all_refs = NULL; /* (element-type utf8 utf8) */ + if (!ostree_repo_list_refs (self, NULL, &all_refs, cancellable, error)) return FALSE; g_hash_table_iter_init (&hash_iter, all_refs); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + const char *checksum = value; + + g_debug ("Finding objects to keep for commit %s", checksum); + if (!ostree_repo_traverse_commit_union (self, checksum, depth, reachable, + cancellable, error)) + return FALSE; + } + + /* Using collections. */ + g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeChecksumRef utf8) */ + + if (!ostree_repo_list_collection_refs (self, NULL, &all_collection_refs, + cancellable, error)) + return FALSE; + + g_hash_table_iter_init (&hash_iter, all_collection_refs); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *checksum = value; diff --git a/src/libostree/ostree-repo-refs.c b/src/libostree/ostree-repo-refs.c index adab50fe..849d5d8f 100644 --- a/src/libostree/ostree-repo-refs.c +++ b/src/libostree/ostree-repo-refs.c @@ -20,18 +20,25 @@ #include "config.h" +#include "ostree-core-private.h" #include "ostree-repo-private.h" #include "otutil.h" #include "ot-fs-utils.h" +/* This is polymorphic in @collection_id: if non-%NULL, @refs will be treated as of + * type OstreeCollectionRef ↦ checksum. Otherwise, it will be treated as of type + * refspec ↦ checksum. */ static gboolean add_ref_to_set (const char *remote, + const char *collection_id, int base_fd, const char *path, GHashTable *refs, GCancellable *cancellable, GError **error) { + g_return_val_if_fail (remote == NULL || collection_id == NULL, FALSE); + gsize len; char *contents = glnx_file_get_contents_utf8_at (base_fd, path, &len, cancellable, error); if (!contents) @@ -39,14 +46,21 @@ add_ref_to_set (const char *remote, g_strchomp (contents); - g_autoptr(GString) refname = g_string_new (""); - if (remote) + if (collection_id == NULL) { - g_string_append (refname, remote); - g_string_append_c (refname, ':'); + g_autoptr(GString) refname = g_string_new (""); + if (remote) + { + g_string_append (refname, remote); + g_string_append_c (refname, ':'); + } + g_string_append (refname, path); + g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents); + } + else + { + g_hash_table_insert (refs, ostree_collection_ref_new (collection_id, path), contents); } - g_string_append (refname, path); - g_hash_table_insert (refs, g_string_free (g_steal_pointer (&refname), FALSE), contents); return TRUE; } @@ -99,6 +113,8 @@ write_checksum_file_at (OstreeRepo *self, g_clear_error (&temp_error); + /* FIXME: Conflict detection needs to be extended to collection–refs + * using ostree_repo_list_collection_refs(). */ if (!ostree_repo_list_refs (self, name, &refs, cancellable, error)) return FALSE; @@ -456,6 +472,7 @@ ostree_repo_resolve_rev_ext (OstreeRepo *self, static gboolean enumerate_refs_recurse (OstreeRepo *repo, const char *remote, + const char *collection_id, int base_dfd, GString *base_path, int child_dfd, @@ -485,14 +502,14 @@ enumerate_refs_recurse (OstreeRepo *repo, { g_string_append_c (base_path, '/'); - if (!enumerate_refs_recurse (repo, remote, base_dfd, base_path, + if (!enumerate_refs_recurse (repo, remote, collection_id, base_dfd, base_path, dfd_iter.fd, dent->d_name, refs, cancellable, error)) return FALSE; } else if (dent->d_type == DT_REG) { - if (!add_ref_to_set (remote, base_dfd, base_path->str, refs, + if (!add_ref_to_set (remote, collection_id, base_dfd, base_path->str, refs, cancellable, error)) return FALSE; } @@ -554,7 +571,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self, if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error)) return FALSE; - if (!enumerate_refs_recurse (self, remote, base_fd, base_path, + if (!enumerate_refs_recurse (self, remote, NULL, base_fd, base_path, base_fd, cut_prefix ? "." : ref_prefix, ret_all_refs, cancellable, error)) return FALSE; @@ -566,7 +583,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self, if (!glnx_opendirat (self->repo_dir_fd, prefix_path, TRUE, &prefix_dfd, error)) return FALSE; - if (!add_ref_to_set (remote, prefix_dfd, ref_prefix, ret_all_refs, + if (!add_ref_to_set (remote, NULL, prefix_dfd, ref_prefix, ret_all_refs, cancellable, error)) return FALSE; } @@ -581,7 +598,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self, if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error)) return FALSE; - if (!enumerate_refs_recurse (self, NULL, refs_heads_dfd, base_path, + if (!enumerate_refs_recurse (self, NULL, NULL, refs_heads_dfd, base_path, refs_heads_dfd, ".", ret_all_refs, cancellable, error)) return FALSE; @@ -607,7 +624,7 @@ _ostree_repo_list_refs_internal (OstreeRepo *self, if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error)) return FALSE; - if (!enumerate_refs_recurse (self, dent->d_name, remote_dfd, base_path, + if (!enumerate_refs_recurse (self, dent->d_name, NULL, remote_dfd, base_path, remote_dfd, ".", ret_all_refs, cancellable, error)) @@ -741,16 +758,19 @@ ostree_repo_remote_list_refs (OstreeRepo *self, } gboolean -_ostree_repo_write_ref (OstreeRepo *self, - const char *remote, - const char *ref, - const char *rev, - GCancellable *cancellable, - GError **error) +_ostree_repo_write_ref (OstreeRepo *self, + const char *remote, + const OstreeCollectionRef *ref, + const char *rev, + GCancellable *cancellable, + GError **error) { glnx_fd_close int dfd = -1; - if (remote == NULL) + g_return_val_if_fail (remote == NULL || ref->collection_id == NULL, FALSE); + + if (remote == NULL && + (ref->collection_id == NULL || g_strcmp0 (ref->collection_id, ostree_repo_get_collection_id (self)) == 0)) { if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &dfd, error)) @@ -759,6 +779,29 @@ _ostree_repo_write_ref (OstreeRepo *self, return FALSE; } } + else if (remote == NULL && ref->collection_id != NULL) + { + glnx_fd_close int refs_mirrors_dfd = -1; + + /* refs/mirrors might not exist in older repositories, so create it. */ + if (!glnx_shutil_mkdir_p_at_open (self->repo_dir_fd, "refs/mirrors", 0777, + &refs_mirrors_dfd, cancellable, error)) + { + g_prefix_error (error, "Opening %s: ", "refs/mirrors"); + return FALSE; + } + + if (rev != NULL) + { + /* Ensure we have a dir for the collection */ + if (!glnx_shutil_mkdir_p_at (refs_mirrors_dfd, ref->collection_id, 0777, cancellable, error)) + return FALSE; + } + + dfd = glnx_opendirat_with_errno (refs_mirrors_dfd, ref->collection_id, TRUE); + if (dfd < 0 && (errno != ENOENT || rev != NULL)) + return glnx_throw_errno_prefix (error, "Opening mirrors/ dir %s", ref->collection_id); + } else { glnx_fd_close int refs_remotes_dfd = -1; @@ -786,7 +829,7 @@ _ostree_repo_write_ref (OstreeRepo *self, { if (dfd >= 0) { - if (unlinkat (dfd, ref, 0) != 0) + if (unlinkat (dfd, ref->ref_name, 0) != 0) { if (errno != ENOENT) return glnx_throw_errno (error); @@ -795,7 +838,7 @@ _ostree_repo_write_ref (OstreeRepo *self, } else { - if (!write_checksum_file_at (self, dfd, ref, rev, cancellable, error)) + if (!write_checksum_file_at (self, dfd, ref->ref_name, rev, cancellable, error)) return FALSE; } @@ -807,7 +850,7 @@ _ostree_repo_write_ref (OstreeRepo *self, gboolean _ostree_repo_update_refs (OstreeRepo *self, - GHashTable *refs, + GHashTable *refs, /* (element-type utf8 utf8) */ GCancellable *cancellable, GError **error) { @@ -820,15 +863,135 @@ _ostree_repo_update_refs (OstreeRepo *self, const char *refspec = key; const char *rev = value; g_autofree char *remote = NULL; - g_autofree char *ref = NULL; + g_autofree char *ref_name = NULL; - if (!ostree_parse_refspec (refspec, &remote, &ref, error)) + if (!ostree_parse_refspec (refspec, &remote, &ref_name, error)) return FALSE; - if (!_ostree_repo_write_ref (self, remote, ref, rev, + const OstreeCollectionRef ref = { NULL, ref_name }; + if (!_ostree_repo_write_ref (self, remote, &ref, rev, cancellable, error)) return FALSE; } return TRUE; } + +gboolean +_ostree_repo_update_collection_refs (OstreeRepo *self, + GHashTable *refs, /* (element-type OstreeCollectionRef utf8) */ + GCancellable *cancellable, + GError **error) +{ + GHashTableIter hash_iter; + gpointer key, value; + + g_hash_table_iter_init (&hash_iter, refs); + while (g_hash_table_iter_next (&hash_iter, &key, &value)) + { + const OstreeCollectionRef *ref = key; + const char *rev = value; + + if (!_ostree_repo_write_ref (self, NULL, ref, rev, + cancellable, error)) + return FALSE; + } + + return TRUE; +} + +/** + * ostree_repo_list_collection_refs: + * @self: Repo + * @match_collection_id: (nullable): If non-%NULL, only list refs from this collection + * @out_all_refs: (out) (element-type OstreeCollectionRef utf8): Mapping from collection–ref to checksum + * @cancellable: Cancellable + * @error: Error + * + * List all local and mirrored refs, mapping them to the commit checksums they + * currently point to in @out_all_refs. If @match_collection_id is specified, + * the results will be limited to those with an equal collection ID. + * + * #OstreeCollectionRefs are guaranteed to be returned with their collection ID + * set to a non-%NULL value; so no refs from `refs/heads` will be listed if no + * collection ID is configured for the repository + * (ostree_repo_get_collection_id()). + * + * Returns: %TRUE on success, %FALSE otherwise + * Since: 2017.8 + */ +gboolean +ostree_repo_list_collection_refs (OstreeRepo *self, + const char *match_collection_id, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); + g_return_val_if_fail (match_collection_id == NULL || + ostree_validate_collection_id (match_collection_id, NULL), FALSE); + g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + g_autoptr(GHashTable) ret_all_refs = NULL; + + ret_all_refs = g_hash_table_new_full (ostree_collection_ref_hash, + ostree_collection_ref_equal, + (GDestroyNotify) ostree_collection_ref_free, + g_free); + + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + g_autoptr(GString) base_path = g_string_new (""); + + const gchar *main_collection_id = ostree_repo_get_collection_id (self); + + if (main_collection_id != NULL && + (match_collection_id == NULL || g_strcmp0 (match_collection_id, main_collection_id) == 0)) + { + glnx_fd_close int refs_heads_dfd = -1; + + if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error)) + return FALSE; + + if (!enumerate_refs_recurse (self, NULL, main_collection_id, refs_heads_dfd, base_path, + refs_heads_dfd, ".", + ret_all_refs, cancellable, error)) + return FALSE; + } + + g_string_truncate (base_path, 0); + + gboolean refs_mirrors_exists = FALSE; + if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "refs/mirrors", + &dfd_iter, &refs_mirrors_exists, error)) + return FALSE; + + while (refs_mirrors_exists) + { + struct dirent *dent; + glnx_fd_close int collection_dfd = -1; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error)) + return FALSE; + if (!dent) + break; + + if (dent->d_type != DT_DIR) + continue; + + if (match_collection_id != NULL && g_strcmp0 (match_collection_id, dent->d_name) != 0) + continue; + + if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &collection_dfd, error)) + return FALSE; + + if (!enumerate_refs_recurse (self, NULL, dent->d_name, collection_dfd, base_path, + collection_dfd, ".", + ret_all_refs, + cancellable, error)) + return FALSE; + } + + ot_transfer_out_value (out_all_refs, &ret_all_refs); + return TRUE; +} diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 24e94e03..1db748cb 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -475,6 +475,7 @@ ostree_repo_finalize (GObject *object) if (self->config) g_key_file_free (self->config); g_clear_pointer (&self->txn_refs, g_hash_table_destroy); + g_clear_pointer (&self->txn_collection_refs, g_hash_table_destroy); g_clear_error (&self->writable_error); g_clear_pointer (&self->object_sizes, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&self->dirmeta_cache, (GDestroyNotify) g_hash_table_unref); @@ -1703,7 +1704,8 @@ ostree_repo_create (OstreeRepo *self, glnx_fd_close int dfd = -1; struct stat stbuf; const char *state_dirs[] = { "objects", "tmp", "extensions", "state", - "refs", "refs/heads", "refs/remotes" }; + "refs", "refs/heads", "refs/mirrors", + "refs/remotes" }; if (mkdir (repopath, 0755) != 0) { @@ -4568,6 +4570,13 @@ summary_add_ref_entry (OstreeRepo *self, * * It is regenerated automatically after a commit if * `core/commit-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 from the + * `refs/mirrors` directory 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. */ gboolean ostree_repo_regenerate_summary (OstreeRepo *self, @@ -4579,21 +4588,26 @@ ostree_repo_regenerate_summary (OstreeRepo *self, 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); + { - g_autoptr(GHashTable) refs = NULL; - if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error)) - return FALSE; - - g_autoptr(GList) ordered_keys = g_hash_table_get_keys (refs); - ordered_keys = g_list_sort (ordered_keys, (GCompareFunc)strcmp); - - for (GList *iter = ordered_keys; iter; iter = iter->next) + if (main_collection_id == NULL) { - const char *ref = iter->data; - const char *commit = g_hash_table_lookup (refs, ref); - - if (!summary_add_ref_entry (self, ref, commit, refs_builder, error)) + g_autoptr(GHashTable) refs = NULL; + if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error)) return FALSE; + + g_autoptr(GList) ordered_keys = g_hash_table_get_keys (refs); + ordered_keys = g_list_sort (ordered_keys, (GCompareFunc)strcmp); + + for (GList *iter = ordered_keys; iter; iter = iter->next) + { + const char *ref = iter->data; + const char *commit = g_hash_table_lookup (refs, ref); + + if (!summary_add_ref_entry (self, ref, commit, refs_builder, error)) + return FALSE; + } } } @@ -4640,6 +4654,88 @@ ostree_repo_regenerate_summary (OstreeRepo *self, g_variant_new_uint64 (GUINT64_TO_BE (g_get_real_time () / G_USEC_PER_SEC))); } + /* Add refs which have a collection specified. ostree_repo_list_collection_refs() + * is guaranteed to only return refs which are in refs/mirrors, or those which + * are in refs/heads if the repository configuration specifies a collection ID + * (which we put in the main refs map, rather than the collection map, for + * backwards compatibility). */ + { + g_autoptr(GHashTable) collection_refs = NULL; + if (!ostree_repo_list_collection_refs (self, NULL, &collection_refs, cancellable, error)) + return FALSE; + + gsize collection_map_size = 0; + GHashTableIter iter; + g_autoptr(GHashTable) collection_map = NULL; /* (element-type utf8 GHashTable) */ + g_hash_table_iter_init (&iter, collection_refs); + collection_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, + (GDestroyNotify) g_hash_table_unref); + + const OstreeCollectionRef *ref; + const char *checksum; + while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum)) + { + GHashTable *ref_map = g_hash_table_lookup (collection_map, ref->collection_id); + + if (ref_map == NULL) + { + ref_map = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + g_hash_table_insert (collection_map, ref->collection_id, ref_map); + } + + g_hash_table_insert (ref_map, ref->ref_name, (gpointer) checksum); + } + + g_autoptr(GVariantBuilder) collection_refs_builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sa(s(taya{sv}))}")); + + g_autoptr(GList) ordered_collection_ids = g_hash_table_get_keys (collection_map); + ordered_collection_ids = g_list_sort (ordered_collection_ids, (GCompareFunc) strcmp); + + for (GList *collection_iter = ordered_collection_ids; collection_iter; collection_iter = collection_iter->next) + { + const char *collection_id = collection_iter->data; + GHashTable *ref_map = g_hash_table_lookup (collection_map, collection_id); + + gboolean is_main_collection_id = (main_collection_id != NULL && g_str_equal (collection_id, main_collection_id)); + + if (!is_main_collection_id) + { + g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("{sa(s(taya{sv}))}")); + g_variant_builder_add (collection_refs_builder, "s", collection_id); + g_variant_builder_open (collection_refs_builder, G_VARIANT_TYPE ("a(s(taya{sv}))")); + } + + g_autoptr(GList) ordered_refs = g_hash_table_get_keys (ref_map); + ordered_refs = g_list_sort (ordered_refs, (GCompareFunc) strcmp); + + for (GList *ref_iter = ordered_refs; ref_iter != NULL; ref_iter = ref_iter->next) + { + const char *ref = ref_iter->data; + const char *commit = g_hash_table_lookup (ref_map, ref); + GVariantBuilder *builder = is_main_collection_id ? refs_builder : collection_refs_builder; + + if (!summary_add_ref_entry (self, ref, commit, builder, error)) + return FALSE; + + if (!is_main_collection_id) + collection_map_size++; + } + + if (!is_main_collection_id) + { + g_variant_builder_close (collection_refs_builder); /* array */ + g_variant_builder_close (collection_refs_builder); /* dict entry */ + } + } + + if (main_collection_id != NULL) + g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_ID, + g_variant_new_string (main_collection_id)); + if (collection_map_size > 0) + g_variant_dict_insert_value (&additional_metadata_builder, OSTREE_SUMMARY_COLLECTION_MAP, + g_variant_builder_end (collection_refs_builder)); + } + g_autoptr(GVariant) summary = NULL; { g_autoptr(GVariantBuilder) summary_builder = diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 566754d4..388d73c4 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -309,6 +309,15 @@ void ostree_repo_transaction_set_ref (OstreeRepo *self, const char *ref, const char *checksum); +#ifdef OSTREE_ENABLE_EXPERIMENTAL_API + +_OSTREE_PUBLIC +void ostree_repo_transaction_set_collection_ref (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum); + +#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ + _OSTREE_PUBLIC gboolean ostree_repo_set_ref_immediate (OstreeRepo *self, const char *remote, @@ -317,6 +326,17 @@ gboolean ostree_repo_set_ref_immediate (OstreeRepo *self, GCancellable *cancellable, GError **error); +#ifdef OSTREE_ENABLE_EXPERIMENTAL_API + +_OSTREE_PUBLIC +gboolean ostree_repo_set_collection_ref_immediate (OstreeRepo *self, + const OstreeCollectionRef *ref, + const char *checksum, + GCancellable *cancellable, + GError **error); + +#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ + _OSTREE_PUBLIC gboolean ostree_repo_has_object (OstreeRepo *self, OstreeObjectType objtype, @@ -1068,6 +1088,17 @@ gboolean ostree_repo_pull_with_options (OstreeRepo *self, GCancellable *cancellable, GError **error); +#ifdef OSTREE_ENABLE_EXPERIMENTAL_API + +_OSTREE_PUBLIC +gboolean ostree_repo_list_collection_refs (OstreeRepo *self, + const char *match_collection_id, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error); + +#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ + _OSTREE_PUBLIC void ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress, gpointer user_data); diff --git a/tests/basic-test.sh b/tests/basic-test.sh index d9b20938..6f237696 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -492,7 +492,7 @@ echo "ok pull-local with --remote arg" cd ${test_tmpdir} ${CMD_PREFIX} ostree --repo=repo3 prune find repo3/objects -name '*.commit' > objlist-before-prune -rm repo3/refs/heads/* repo3/refs/remotes/* -rf +rm repo3/refs/heads/* repo3/refs/mirrors/* repo3/refs/remotes/* -rf ${CMD_PREFIX} ostree --repo=repo3 prune --refs-only find repo3/objects -name '*.commit' > objlist-after-prune if cmp -s objlist-before-prune objlist-after-prune; then