static char *
_ostree_get_relative_static_delta_path (const char *from,
                                        const char *to,
                                        const char *target)
{
  guint8 csum_to[OSTREE_SHA256_DIGEST_LEN];
  char to_b64[44];
  guint8 csum_to_copy[OSTREE_SHA256_DIGEST_LEN];
  GString *ret = g_string_new ("deltas/");

  ostree_checksum_inplace_to_bytes (to, csum_to);
  ostree_checksum_b64_inplace_from_bytes (csum_to, to_b64);
  ostree_checksum_b64_inplace_to_bytes (to_b64, csum_to_copy);

  g_assert (memcmp (csum_to, csum_to_copy, OSTREE_SHA256_DIGEST_LEN) == 0);

  if (from != NULL)
    {
      guint8 csum_from[OSTREE_SHA256_DIGEST_LEN];
      char from_b64[44];

      ostree_checksum_inplace_to_bytes (from, csum_from);
      ostree_checksum_b64_inplace_from_bytes (csum_from, from_b64);

      g_string_append_c (ret, from_b64[0]);
      g_string_append_c (ret, from_b64[1]);
      g_string_append_c (ret, '/');
      g_string_append (ret, from_b64 + 2);
      g_string_append_c (ret, '-');
    }

  g_string_append_c (ret, to_b64[0]);
  g_string_append_c (ret, to_b64[1]);
  if (from == NULL)
    g_string_append_c (ret, '/');
  g_string_append (ret, to_b64 + 2);

  if (target != NULL)
    {
      g_string_append_c (ret, '/');
      g_string_append (ret, target);
    }

  return g_string_free (ret, FALSE);
}
/**
 * ostree_repo_list_static_delta_names:
 * @self: Repo
 * @out_deltas: (out) (element-type utf8) (transfer container): String name of deltas (checksum-checksum.delta)
 * @cancellable: Cancellable
 * @error: Error
 *
 * This function synchronously enumerates all static deltas in the
 * repository, returning its result in @out_deltas.
 */ 
gboolean
ostree_repo_list_static_delta_names (OstreeRepo                  *self,
                                     GPtrArray                  **out_deltas,
                                     GCancellable                *cancellable,
                                     GError                     **error)
{
  g_autoptr(GPtrArray) ret_deltas = g_ptr_array_new_with_free_func (g_free);

  g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
  gboolean exists;
  if (!ot_dfd_iter_init_allow_noent (self->repo_dir_fd, "deltas", &dfd_iter,
                                     &exists, error))
    return FALSE;
  if (!exists)
    {
      /* Note early return */
      ot_transfer_out_value (out_deltas, &ret_deltas);
      return TRUE;
    }

  while (TRUE)
    {
      g_auto(GLnxDirFdIterator) sub_dfd_iter = { 0, };
      struct dirent *dent;

      if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
        return FALSE;
      if (dent == NULL)
        break;
      if (dent->d_type != DT_DIR)
        continue;

      if (!glnx_dirfd_iterator_init_at (dfd_iter.fd, dent->d_name, FALSE,
                                        &sub_dfd_iter, error))
        return FALSE;

      while (TRUE)
        {
          struct dirent *sub_dent;
          const char *name1;
          const char *name2;
          g_autofree char *superblock_subpath = NULL;
          struct stat stbuf;

          if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&sub_dfd_iter, &sub_dent,
                                                           cancellable, error))
            return FALSE;
          if (sub_dent == NULL)
            break;
          if (dent->d_type != DT_DIR)
            continue;

          name1 = dent->d_name;
          name2 = sub_dent->d_name;

          superblock_subpath = g_strconcat (name2, "/superblock", NULL);
          if (fstatat (sub_dfd_iter.fd, superblock_subpath, &stbuf, 0) < 0)
            {
              if (errno != ENOENT)
                {
                  glnx_set_error_from_errno (error);
                  return FALSE;
                }
            }
          else
            {
              g_autofree char *buf = g_strconcat (name1, name2, NULL);
              GString *out = g_string_new ("");
              char checksum[OSTREE_SHA256_STRING_LEN+1];
              guchar csum[OSTREE_SHA256_DIGEST_LEN];
              const char *dash = strchr (buf, '-');

              ostree_checksum_b64_inplace_to_bytes (buf, csum);
              ostree_checksum_inplace_from_bytes (csum, checksum);
              g_string_append (out, checksum);
              if (dash)
                {
                  g_string_append_c (out, '-');
                  ostree_checksum_b64_inplace_to_bytes (dash+1, csum);
                  ostree_checksum_inplace_from_bytes (csum, checksum);
                  g_string_append (out, checksum);
                }

              g_ptr_array_add (ret_deltas, g_string_free (out, FALSE));
            }
        }
    }

  ot_transfer_out_value (out_deltas, &ret_deltas);
  return TRUE;
}
/**
 * ostree_repo_list_static_delta_names:
 * @self: Repo
 * @out_deltas: (out) (element-type utf8) (transfer container): String name of deltas (checksum-checksum.delta)
 * @cancellable: Cancellable
 * @error: Error
 *
 * This function synchronously enumerates all static deltas in the
 * repository, returning its result in @out_deltas.
 */
gboolean
ostree_repo_list_static_delta_names (OstreeRepo                  *self,
                                     GPtrArray                  **out_deltas,
                                     GCancellable                *cancellable,
                                     GError                     **error)
{
    gboolean ret = FALSE;
    g_autoptr(GPtrArray) ret_deltas = NULL;
    g_autoptr(GFileEnumerator) dir_enum = NULL;

    ret_deltas = g_ptr_array_new_with_free_func (g_free);

    if (g_file_query_exists (self->deltas_dir, NULL))
    {
        dir_enum = g_file_enumerate_children (self->deltas_dir, OSTREE_GIO_FAST_QUERYINFO,
                                              G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                              NULL, error);
        if (!dir_enum)
            goto out;

        while (TRUE)
        {
            g_autoptr(GFileEnumerator) dir_enum2 = NULL;
            GFileInfo *file_info;
            GFile *child;

            if (!g_file_enumerator_iterate (dir_enum, &file_info, &child,
                                            NULL, error))
                goto out;
            if (file_info == NULL)
                break;

            if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY)
                continue;


            dir_enum2 = g_file_enumerate_children (child, OSTREE_GIO_FAST_QUERYINFO,
                                                   G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
                                                   NULL, error);
            if (!dir_enum2)
                goto out;

            while (TRUE)
            {
                GFileInfo *file_info2;
                GFile *child2;
                const char *name1;
                const char *name2;

                if (!g_file_enumerator_iterate (dir_enum2, &file_info2, &child2,
                                                NULL, error))
                    goto out;
                if (file_info2 == NULL)
                    break;

                if (g_file_info_get_file_type (file_info2) != G_FILE_TYPE_DIRECTORY)
                    continue;

                name1 = g_file_info_get_name (file_info);
                name2 = g_file_info_get_name (file_info2);

                {
                    g_autoptr(GFile) meta_path = g_file_get_child (child2, "superblock");

                    if (g_file_query_exists (meta_path, NULL))
                    {
                        g_autofree char *buf = g_strconcat (name1, name2, NULL);
                        GString *out = g_string_new ("");
                        char checksum[OSTREE_SHA256_STRING_LEN+1];
                        guchar csum[OSTREE_SHA256_DIGEST_LEN];
                        const char *dash = strchr (buf, '-');

                        ostree_checksum_b64_inplace_to_bytes (buf, csum);
                        ostree_checksum_inplace_from_bytes (csum, checksum);
                        g_string_append (out, checksum);
                        if (dash)
                        {
                            g_string_append_c (out, '-');
                            ostree_checksum_b64_inplace_to_bytes (dash+1, csum);
                            ostree_checksum_inplace_from_bytes (csum, checksum);
                            g_string_append (out, checksum);
                        }

                        g_ptr_array_add (ret_deltas, g_string_free (out, FALSE));
                    }
                }
            }
        }
    }

    ret = TRUE;
    if (out_deltas)
        *out_deltas = g_steal_pointer (&ret_deltas);
out:
    return ret;
}