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;
}
static gboolean
export_dir (int           source_parent_fd,
            const char   *source_name,
            const char   *source_relpath,
            int           destination_parent_fd,
            const char   *destination_name,
            const char   *required_prefix,
            GCancellable *cancellable,
            GError      **error)
{
  int res;

  g_auto(GLnxDirFdIterator) source_iter = {0};
  glnx_fd_close int destination_dfd = -1;
  struct dirent *dent;

  if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error))
    return FALSE;

  do
    res = mkdirat (destination_parent_fd, destination_name, 0755);
  while (G_UNLIKELY (res == -1 && errno == EINTR));
  if (res == -1)
    {
      if (errno != EEXIST)
        {
          glnx_set_error_from_errno (error);
          return FALSE;
        }
    }

  if (!glnx_opendirat (destination_parent_fd, destination_name, TRUE,
                       &destination_dfd, error))
    return FALSE;

  while (TRUE)
    {
      struct stat stbuf;
      g_autofree char *source_printable = NULL;

      if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error))
        return FALSE;

      if (dent == NULL)
        break;

      if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
        {
          if (errno == ENOENT)
            {
              continue;
            }
          else
            {
              glnx_set_error_from_errno (error);
              return FALSE;
            }
        }

      /* Don't export any hidden files or backups */
      if (g_str_has_prefix (dent->d_name, ".") ||
          g_str_has_suffix (dent->d_name, "~"))
        continue;

      if (S_ISDIR (stbuf.st_mode))
        {
          g_autofree gchar *child_relpath = g_build_filename (source_relpath, dent->d_name, NULL);

          if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name,
                           required_prefix, cancellable, error))
            return FALSE;
        }
      else if (S_ISREG (stbuf.st_mode))
        {
          source_printable = g_build_filename (source_relpath, dent->d_name, NULL);


          if (!flatpak_has_name_prefix (dent->d_name, required_prefix))
            {
              g_print ("Not exporting %s, wrong prefix\n", source_printable);
              continue;
            }

          g_print ("Exporting %s\n", source_printable);

          if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf,
                                  destination_dfd, dent->d_name,
                                  GLNX_FILE_COPY_NOXATTRS,
                                  cancellable,
                                  error))
            return FALSE;
        }
      else
        {
          source_printable = g_build_filename (source_relpath, dent->d_name, NULL);
          g_debug ("Not exporting non-regular file %s\n", source_printable);
        }
    }

  /* Try to remove the directory, as we don't want to export empty directories.
   * However, don't fail if the unlink fails due to the directory not being empty */
  do
    res = unlinkat (destination_parent_fd, destination_name, AT_REMOVEDIR);
  while (G_UNLIKELY (res == -1 && errno == EINTR));
  if (res == -1)
    {
      if (errno != ENOTEMPTY)
        {
          glnx_set_error_from_errno (error);
          return FALSE;
        }
    }

  return TRUE;
}
static gboolean
export_dir (int            source_parent_fd,
            const char    *source_name,
            const char    *source_relpath,
            int            destination_parent_fd,
            const char    *destination_name,
            const char    *required_prefix,
            GCancellable  *cancellable,
            GError       **error)
{
  gboolean ret = FALSE;
  int res;
  g_auto(GLnxDirFdIterator) source_iter = {0};
  glnx_fd_close int destination_dfd = -1;
  struct dirent *dent;

  if (!glnx_dirfd_iterator_init_at (source_parent_fd, source_name, FALSE, &source_iter, error))
    goto out;

  do
    res = mkdirat (destination_parent_fd, destination_name, 0777);
  while (G_UNLIKELY (res == -1 && errno == EINTR));
  if (res == -1)
    {
      if (errno != EEXIST)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }

  if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name,
                               &destination_dfd,
                               cancellable, error))
    goto out;

  while (TRUE)
    {
      struct stat stbuf;
      g_autofree char *source_printable = NULL;

      if (!glnx_dirfd_iterator_next_dent (&source_iter, &dent, cancellable, error))
        goto out;

      if (dent == NULL)
        break;

      if (fstatat (source_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1)
        {
          if (errno == ENOENT)
            continue;
          else
            {
              glnx_set_error_from_errno (error);
              goto out;
            }
        }

      if (S_ISDIR (stbuf.st_mode))
        {
          g_autofree gchar *child_relpath = g_build_filename(source_relpath, dent->d_name, NULL);

          if (!export_dir (source_iter.fd, dent->d_name, child_relpath, destination_dfd, dent->d_name,
                           required_prefix, cancellable, error))
            goto out;
        }
      else if (S_ISREG (stbuf.st_mode))
        {
          source_printable = g_build_filename (source_relpath, dent->d_name, NULL);


          if (!xdg_app_has_name_prefix (dent->d_name, required_prefix))
            {
              g_print ("Not exporting %s, wrong prefix\n", source_printable);
              continue;
            }

          g_print ("Exporting %s\n", source_printable);

          if (!glnx_file_copy_at (source_iter.fd, dent->d_name, &stbuf,
                                  destination_dfd, dent->d_name,
                                  GLNX_FILE_COPY_NOXATTRS,
                                  cancellable,
                                  error))
            goto out;
        }
      else
        {
          source_printable = g_build_filename (source_relpath, dent->d_name, NULL);
          g_print ("Not exporting non-regular file %s\n", source_printable);
        }
    }

  ret = TRUE;
 out:

  return ret;
}