static gboolean
vardict_sort_and_insert_pkgs (GVariantDict *dict,
                              const char   *key_prefix,
                              GUnixFDList  *fd_list,
                              const char *const* pkgs,
                              GError      **error)
{
  g_autoptr(GVariant) fd_idxs = NULL;
  g_autoptr(GPtrArray) repo_pkgs = NULL;

  if (!rpmostree_sort_pkgs_strv (pkgs, fd_list, &repo_pkgs, &fd_idxs, error))
    return FALSE;

  /* for grep: here we insert install-packages/override-replace-packages */
  if (repo_pkgs != NULL && repo_pkgs->len > 0)
    g_variant_dict_insert_value (dict, glnx_strjoina (key_prefix, "-packages"),
      g_variant_new_strv ((const char *const*)repo_pkgs->pdata,
                                              repo_pkgs->len));

  /* for grep: here we insert install-local-packages/override-replace-local-packages */
  if (fd_idxs != NULL)
    g_variant_dict_insert_value (dict, glnx_strjoina (key_prefix, "-local-packages"),
                                 fd_idxs);
  return TRUE;
}
GVariant *
rpmostreed_commit_generate_cached_details_variant (OstreeDeployment *deployment,
                                                   OstreeRepo *repo,
                                                   const gchar *refspec,
						   GError **error)
{
  g_autoptr(GVariant) commit = NULL;
  g_autofree gchar *origin_refspec = NULL;
  g_autofree gchar *head = NULL;
  gboolean gpg_enabled;
  const gchar *osname;
  GVariant *sigs = NULL; /* floating variant */
  GVariantDict dict;

  osname = ostree_deployment_get_osname (deployment);

  if (refspec)
    origin_refspec = g_strdup (refspec);
  else
    {
      g_autoptr(RpmOstreeOrigin) origin = NULL;
  
      origin = rpmostree_origin_parse_deployment (deployment, error);
      if (!origin)
        return NULL;
      origin_refspec = g_strdup (rpmostree_origin_get_refspec (origin));
    }

  g_assert (origin_refspec);

  /* allow_noent=TRUE since the ref may have been deleted for a
   * rebase.
   */
  if (!ostree_repo_resolve_rev (repo, origin_refspec,
                                TRUE, &head, error))
    return NULL;

  if (head == NULL)
    head = g_strdup (ostree_deployment_get_csum (deployment));

  if (!ostree_repo_load_variant (repo,
				 OSTREE_OBJECT_TYPE_COMMIT,
				 head,
				 &commit,
				 error))
    return NULL;

  sigs = rpmostreed_deployment_gpg_results (repo, origin_refspec, head, &gpg_enabled);

  g_variant_dict_init (&dict, NULL);
  if (osname != NULL)
    g_variant_dict_insert (&dict, "osname", "s", osname);
  g_variant_dict_insert (&dict, "checksum", "s", head);
  variant_add_commit_details (&dict, commit);
  g_variant_dict_insert (&dict, "origin", "s", origin_refspec);
  if (sigs != NULL)
    g_variant_dict_insert_value (&dict, "signatures", sigs);
  g_variant_dict_insert (&dict, "gpg-enabled", "b", gpg_enabled);
  return g_variant_dict_end (&dict);
}
static inline void
insert_integer_value (TrackerSparqlCursor *const cursor,
                      GVariantDict        *const dict,
                      gchar const         *const key,
                      gint const                 column)
{

  GVariant *const value = g_variant_new_int64 (
    tracker_sparql_cursor_get_integer (cursor, column)
  );

  g_variant_dict_insert_value (dict, key, value);

}
static inline void
insert_string_value (TrackerSparqlCursor *const cursor,
                     GVariantDict        *const dict,
                     gchar const         *const key,
                     gint const                 column)
{

  GVariant *const value = g_variant_new_printf (
    "%s",
    tracker_sparql_cursor_get_string (cursor, column, NULL)
  );

  g_variant_dict_insert_value (dict, key, value);

}
GVariant *
rpmostreed_deployment_generate_variant (OstreeDeployment *deployment,
                                        OstreeRepo *repo)
{
  g_autoptr(GVariant) commit = NULL;

  g_autofree gchar *origin_refspec = NULL;
  g_autofree gchar *version_commit = NULL;
  g_autofree gchar *id = NULL;

  GVariant *sigs = NULL; /* floating variant */
  GError *error = NULL;

  GVariantDict dict;
  guint64 timestamp = 0;

  const gchar *osname = ostree_deployment_get_osname (deployment);
  const gchar *csum = ostree_deployment_get_csum (deployment);
  gint serial = ostree_deployment_get_deployserial (deployment);
  id = rpmostreed_deployment_generate_id (deployment);

  if (ostree_repo_load_variant (repo,
                                OSTREE_OBJECT_TYPE_COMMIT,
                                csum,
                                &commit,
                                &error))
    {
      g_autoptr(GVariant) metadata = NULL;
      timestamp = ostree_commit_get_timestamp (commit);
      metadata = g_variant_get_child_value (commit, 0);
      if (metadata != NULL)
          g_variant_lookup (metadata, "version", "s", &version_commit);
    }
  else
    {
      g_warning ("Error loading commit %s", error->message);
    }
  g_clear_error (&error);

  origin_refspec = rpmostreed_deployment_get_refspec (deployment);
  if (origin_refspec)
    sigs = rpmostreed_deployment_gpg_results (repo, origin_refspec, csum);

  g_variant_dict_init (&dict, NULL);

  g_variant_dict_insert (&dict, "id", "s", id);
  if (osname != NULL)
    g_variant_dict_insert (&dict, "osname", "s", osname);
  g_variant_dict_insert (&dict, "serial", "i", serial);
  g_variant_dict_insert (&dict, "checksum", "s", csum);
  if (version_commit != NULL)
    g_variant_dict_insert (&dict, "version", "s", version_commit);
  if (timestamp > 0)
    g_variant_dict_insert (&dict, "timestamp", "t", timestamp);
  if (origin_refspec != NULL)
    g_variant_dict_insert (&dict, "origin", "s", origin_refspec);
  if (sigs != NULL)
    g_variant_dict_insert_value (&dict, "signatures", sigs);

  return g_variant_dict_end (&dict);
}
GVariant *
rpmostreed_deployment_generate_variant (OstreeDeployment *deployment,
                                        const char *booted_id,
                                        OstreeRepo *repo,
					GError **error)
{
  g_autoptr(GVariant) commit = NULL;
  g_autoptr(RpmOstreeOrigin) origin = NULL;
  g_autofree gchar *id = NULL;

  GVariant *sigs = NULL; /* floating variant */

  GVariantDict dict;

  const gchar *osname = ostree_deployment_get_osname (deployment);
  const gchar *csum = ostree_deployment_get_csum (deployment);
  gint serial = ostree_deployment_get_deployserial (deployment);
  gboolean gpg_enabled = FALSE;

  if (!ostree_repo_load_variant (repo,
				 OSTREE_OBJECT_TYPE_COMMIT,
				 csum,
				 &commit,
				 error))
    return NULL;
  
  id = rpmostreed_deployment_generate_id (deployment);

  origin = rpmostree_origin_parse_deployment (deployment, error);
  if (!origin)
    return NULL;

  g_variant_dict_init (&dict, NULL);

  g_variant_dict_insert (&dict, "id", "s", id);
  if (osname != NULL)
    g_variant_dict_insert (&dict, "osname", "s", osname);
  g_variant_dict_insert (&dict, "serial", "i", serial);
  g_variant_dict_insert (&dict, "checksum", "s", csum);
  if (rpmostree_origin_is_locally_assembled (origin))
    {
      const char *parent = ostree_commit_get_parent (commit);
      g_assert (parent);
      g_variant_dict_insert (&dict, "base-checksum", "s", parent);
      sigs = rpmostreed_deployment_gpg_results (repo, rpmostree_origin_get_refspec (origin),
                                                parent, &gpg_enabled);
    }
  else
    sigs = rpmostreed_deployment_gpg_results (repo, rpmostree_origin_get_refspec (origin),
                                              csum, &gpg_enabled);

  variant_add_commit_details (&dict, commit);
  g_variant_dict_insert (&dict, "origin", "s", rpmostree_origin_get_refspec (origin));
  if (rpmostree_origin_get_packages (origin) != NULL)
    g_variant_dict_insert (&dict, "packages", "^as", rpmostree_origin_get_packages (origin));
  if (sigs != NULL)
    g_variant_dict_insert_value (&dict, "signatures", sigs);
  g_variant_dict_insert (&dict, "gpg-enabled", "b", gpg_enabled);

  g_variant_dict_insert (&dict, "unlocked", "s",
			 ostree_deployment_unlocked_state_to_string (ostree_deployment_get_unlocked (deployment)));

  if (booted_id != NULL)
    g_variant_dict_insert (&dict, "booted", "b", g_strcmp0 (booted_id, id) == 0);

  return g_variant_dict_end (&dict);
}
static gboolean
rewrite_delta (OstreeRepo *src_repo,
               const char *src_commit,
               OstreeRepo *dst_repo,
               const char *dst_commit,
               GVariant   *dst_commitv,
               const char *from,
               GError    **error)
{
  g_autoptr(GFile) src_delta_file = NULL;
  g_autoptr(GFile) dst_delta_file = NULL;
  g_autofree char *src_detached_key = _ostree_get_relative_static_delta_path (from, src_commit, "commitmeta");
  g_autofree char *dst_detached_key = _ostree_get_relative_static_delta_path (from, dst_commit, "commitmeta");
  g_autofree char *src_delta_dir = _ostree_get_relative_static_delta_path (from, src_commit, NULL);
  g_autofree char *dst_delta_dir = _ostree_get_relative_static_delta_path (from, dst_commit, NULL);
  g_autofree char *src_superblock_path = _ostree_get_relative_static_delta_path (from, src_commit, "superblock");
  g_autofree char *dst_superblock_path = _ostree_get_relative_static_delta_path (from, dst_commit, "superblock");
  GMappedFile *mfile = NULL;
  g_auto(GVariantBuilder) superblock_builder = FLATPAK_VARIANT_BUILDER_INITIALIZER;
  g_autoptr(GVariant) src_superblock = NULL;
  g_autoptr(GVariant) dst_superblock = NULL;
  g_autoptr(GBytes) bytes = NULL;
  g_autoptr(GVariant) dst_detached = NULL;
  g_autoptr(GVariant) src_metadata = NULL;
  g_autoptr(GVariant) src_recurse = NULL;
  g_autoptr(GVariant) src_parts = NULL;
  g_auto(GVariantDict) dst_metadata_dict = FLATPAK_VARIANT_DICT_INITIALIZER;
  int i;

  src_delta_file = g_file_resolve_relative_path (ostree_repo_get_path (src_repo), src_superblock_path);
  mfile = g_mapped_file_new (flatpak_file_get_path_cached (src_delta_file), FALSE, NULL);
  if (mfile == NULL)
    return TRUE; /* No superblock, not an error */

  bytes = g_mapped_file_get_bytes (mfile);
  g_mapped_file_unref (mfile);

  src_superblock = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT), bytes, FALSE));

  src_metadata = g_variant_get_child_value (src_superblock, 0);
  src_recurse = g_variant_get_child_value (src_superblock, 5);
  src_parts = g_variant_get_child_value (src_superblock, 6);

  if (g_variant_n_children (src_recurse) != 0)
    return flatpak_fail (error, "Recursive deltas not supported, ignoring");

  g_variant_builder_init (&superblock_builder, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT));

  g_variant_dict_init (&dst_metadata_dict, src_metadata);
  g_variant_dict_remove (&dst_metadata_dict, src_detached_key);
  if (ostree_repo_read_commit_detached_metadata (dst_repo, dst_commit, &dst_detached, NULL, NULL) &&
      dst_detached != NULL)
    g_variant_dict_insert_value (&dst_metadata_dict, dst_detached_key, dst_detached);

  g_variant_builder_add_value (&superblock_builder, g_variant_dict_end (&dst_metadata_dict));
  g_variant_builder_add_value (&superblock_builder, g_variant_get_child_value (src_superblock, 1)); /* timestamp */
  g_variant_builder_add_value (&superblock_builder, from ? ostree_checksum_to_bytes_v (from) : new_bytearray ((guchar *) "", 0));
  g_variant_builder_add_value (&superblock_builder, ostree_checksum_to_bytes_v (dst_commit));
  g_variant_builder_add_value (&superblock_builder, dst_commitv);
  g_variant_builder_add_value (&superblock_builder, src_recurse);
  g_variant_builder_add_value (&superblock_builder, src_parts);
  g_variant_builder_add_value (&superblock_builder, g_variant_get_child_value (src_superblock, 7)); /* fallback */

  dst_superblock = g_variant_ref_sink (g_variant_builder_end (&superblock_builder));

  if (!glnx_shutil_mkdir_p_at (ostree_repo_get_dfd (dst_repo), dst_delta_dir, 0755, NULL, error))
    return FALSE;

  for (i = 0; i < g_variant_n_children (src_parts); i++)
    {
      g_autofree char *src_part_path = g_strdup_printf ("%s/%d", src_delta_dir, i);
      g_autofree char *dst_part_path = g_strdup_printf ("%s/%d", dst_delta_dir, i);

      if (!glnx_file_copy_at (ostree_repo_get_dfd (src_repo),
                              src_part_path,
                              NULL,
                              ostree_repo_get_dfd (dst_repo),
                              dst_part_path,
                              GLNX_FILE_COPY_OVERWRITE | GLNX_FILE_COPY_NOXATTRS,
                              NULL, error))
        return FALSE;
    }

  dst_delta_file = g_file_resolve_relative_path (ostree_repo_get_path (dst_repo), dst_superblock_path);
  if (!flatpak_variant_save (dst_delta_file, dst_superblock, NULL, error))
    return FALSE;

  return TRUE;
}
/**
 * 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;
}