/** * rpmostreed_repo_lookup_checksum: * @repo: Repo * @refspec: Repository branch * @checksum: Checksum to look for * @progress: (allow-none): Progress * @cancellable: Cancellable * @error: Error * * Tries to determine if the checksum given belongs on the remote and branch * given by @refspec. This may require pulling commit objects from a remote * repository. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_lookup_checksum (OstreeRepo *repo, const char *refspec, const char *checksum, OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error) { ChecksumVisitorClosure closure = { checksum, FALSE }; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); g_return_val_if_fail (checksum != NULL, FALSE); if (!rpmostreed_repo_pull_ancestry (repo, refspec, checksum_visitor, &closure, progress, cancellable, error)) return FALSE; if (!closure.found) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Checksum %s not found in %s", checksum, refspec); return FALSE; } return TRUE; }
/** * rpmostreed_repo_lookup_cached_version: * @repo: Repo * @refspec: Repository branch * @version: Version to look for * @cancellable: Cancellable * @out_checksum: (out) (allow-none): Commit checksum, or %NULL * @error: Error * * Similar to rpmostreed_repo_lookup_version(), except without pulling * from a remote repository. It traverses whatever commits are available * locally in @repo. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_lookup_cached_version (OstreeRepo *repo, const char *refspec, const char *version, GCancellable *cancellable, char **out_checksum, GError **error) { VersionVisitorClosure closure = { version, NULL }; g_autofree char *checksum = NULL; gboolean ret = FALSE; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); g_return_val_if_fail (version != NULL, FALSE); if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &checksum, error)) goto out; while (checksum != NULL) { g_autoptr(GVariant) commit = NULL; gboolean stop = FALSE; if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error)) goto out; if (!version_visitor (repo, checksum, commit, &closure, &stop, error)) goto out; g_clear_pointer (&checksum, g_free); if (!stop) checksum = ostree_commit_get_parent (commit); } if (closure.checksum == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Version %s not cached in %s", version, refspec); goto out; } if (out_checksum != NULL) *out_checksum = g_steal_pointer (&closure.checksum); g_free (closure.checksum); ret = TRUE; out: return ret; }
gboolean is_checksum_an_update (OstreeRepo *repo, const gchar *checksum, GVariant **commit, GError **error) { g_autofree gchar *cur = NULL; g_autoptr(GVariant) current_commit = NULL; g_autoptr(GVariant) update_commit = NULL; gboolean is_newer; guint64 update_timestamp, current_timestamp; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (checksum != NULL, FALSE); g_return_val_if_fail (commit != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); cur = eos_updater_get_booted_checksum (error); if (cur == NULL) return FALSE; g_debug ("%s: current: %s, update: %s", G_STRFUNC, cur, checksum); if (!ostree_repo_load_commit (repo, cur, ¤t_commit, NULL, error)) return FALSE; if (!ostree_repo_load_commit (repo, checksum, &update_commit, NULL, error)) return FALSE; /* Determine if the new commit is newer than the old commit to prevent * inadvertent (or malicious) attempts to downgrade the system. */ update_timestamp = ostree_commit_get_timestamp (update_commit); current_timestamp = ostree_commit_get_timestamp (current_commit); g_debug ("%s: current_timestamp: %" G_GUINT64_FORMAT ", " "update_timestamp: %" G_GUINT64_FORMAT, G_STRFUNC, update_timestamp, current_timestamp); is_newer = update_timestamp > current_timestamp; /* if we have a checksum for the remote upgrade candidate * and it's ≠ what we're currently booted into, advertise it as such. */ if (is_newer && g_strcmp0 (cur, checksum) != 0) *commit = g_steal_pointer (&update_commit); else *commit = NULL; return TRUE; }
gboolean fetch_latest_commit (OstreeRepo *repo, GCancellable *cancellable, const gchar *remote_name, const gchar *ref, const gchar *url_override, gchar **out_checksum, EosExtensions **out_extensions, GError **error) { g_autoptr(GBytes) summary_bytes = NULL; g_autoptr(GVariant) summary = NULL; g_autofree gchar *checksum = NULL; g_autoptr(GVariant) options = NULL; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (remote_name != NULL, FALSE); g_return_val_if_fail (ref != NULL, FALSE); g_return_val_if_fail (out_checksum != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); options = get_repo_pull_options (url_override, ref); if (!ostree_repo_pull_with_options (repo, remote_name, options, NULL, cancellable, error)) return FALSE; return fetch_commit_checksum (repo, cancellable, remote_name, ref, url_override, out_checksum, out_extensions, error); }
/** * rpmostreed_repo_lookup_version: * @repo: Repo * @refspec: Repository branch * @version: Version to look for * @progress: (allow-none): Progress * @cancellable: Cancellable * @out_checksum: (out) (allow-none): Commit checksum, or %NULL * @error: Error * * Tries to determine the commit checksum for @version on @refspec. * This may require pulling commit objects from a remote repository. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_lookup_version (OstreeRepo *repo, const char *refspec, const char *version, OstreeAsyncProgress *progress, GCancellable *cancellable, char **out_checksum, GError **error) { VersionVisitorClosure closure = { version, NULL }; gboolean ret = FALSE; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); g_return_val_if_fail (version != NULL, FALSE); if (!rpmostreed_repo_pull_ancestry (repo, refspec, version_visitor, &closure, progress, cancellable, error)) goto out; if (closure.checksum == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Version %s not found in %s", version, refspec); goto out; } if (out_checksum != NULL) *out_checksum = g_steal_pointer (&closure.checksum); g_free (closure.checksum); ret = TRUE; out: return ret; }
/** * rpmostreed_repo_pull_ancestry: * @repo: Repo * @refspec: Repository branch * @visitor: (allow-none): Visitor function to call on each commit * @visitor_data: (allow-none): User data for @visitor * @progress: (allow-none): Progress * @cancellable: Cancellable * @error: Error * * Downloads an ancestry of commit objects starting from @refspec. * * If a @visitor function pointer is given, commit objects are downloaded * in batches and the @visitor function is called for each commit object. * The @visitor function can stop the recursion, such as when looking for * a particular commit. * * Returns: %TRUE on success, %FALSE on failure */ gboolean rpmostreed_repo_pull_ancestry (OstreeRepo *repo, const char *refspec, RpmostreedCommitVisitor visitor, gpointer visitor_data, OstreeAsyncProgress *progress, GCancellable *cancellable, GError **error) { OstreeRepoPullFlags flags; GVariantDict options; GVariant *refs_value; const char *refs_array[] = { NULL, NULL }; g_autofree char *remote = NULL; g_autofree char *ref = NULL; g_autofree char *checksum = NULL; int depth, ii; gboolean ret = FALSE; g_return_val_if_fail (OSTREE_IS_REPO (repo), FALSE); g_return_val_if_fail (refspec != NULL, FALSE); if (!ostree_parse_refspec (refspec, &remote, &ref, error)) goto out; /* If no visitor function was provided then we won't be short-circuiting * the recursion, so pull everything in one shot. Otherwise pull commits * in increasingly large batches. */ depth = (visitor != NULL) ? 10 : -1; flags = OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY; /* It's important to use the ref name instead of a checksum on the first * pass because we want to search from the latest available commit on the * remote server, which is not necessarily what the ref name is currently * pointing at in our local repo. */ refs_array[0] = ref; while (TRUE) { /* Floating reference, transferred to dictionary. */ refs_value = g_variant_new_strv ((const char * const *) refs_array, -1); g_variant_dict_init (&options, NULL); g_variant_dict_insert (&options, "depth", "i", depth); g_variant_dict_insert (&options, "flags", "i", flags); g_variant_dict_insert_value (&options, "refs", refs_value); if (!ostree_repo_pull_with_options (repo, remote, g_variant_dict_end (&options), progress, cancellable, error)) goto out; /* First pass only. Now we can resolve the ref to a checksum. */ if (checksum == NULL) { if (!ostree_repo_resolve_rev (repo, ref, FALSE, &checksum, error)) goto out; } /* If depth is negative (no visitor), this loop is skipped. */ for (ii = 0; ii < depth && checksum != NULL; ii++) { g_autoptr(GVariant) commit = NULL; gboolean stop = FALSE; if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error)) goto out; if (!visitor (repo, checksum, commit, visitor_data, &stop, error)) goto out; g_clear_pointer (&checksum, g_free); if (!stop) checksum = ostree_commit_get_parent (commit); } /* Break if no visitor, or visitor told us to stop. */ if (depth < 0 || checksum == NULL) break; /* Pull the next batch of commits, twice as many. */ refs_array[0] = checksum; depth = depth * 2; } ret = TRUE; out: return ret; }