flatpak_dir_create_child_repo (FlatpakDir   *self,
                               GFile        *cache_dir,
                               GLnxLockFile *file_lock,
                               const char   *optional_commit,
                               GError      **error)
{
  g_autoptr(GFile) repo_dir = NULL;
  g_autoptr(GFile) repo_dir_config = NULL;
  g_autoptr(OstreeRepo) repo = NULL;
  g_autofree char *tmpdir_name = NULL;
  g_autoptr(OstreeRepo) new_repo = NULL;
  g_autoptr(GKeyFile) config = NULL;
  g_autofree char *current_mode = NULL;
  GKeyFile *orig_config = NULL;
  g_autofree char *orig_min_free_space_percent = NULL;
  g_autofree char *orig_min_free_space_size = NULL;

  /* We use bare-user-only here now, which means we don't need xattrs
   * for the child repo. This only works as long as the pulled repo
   * is valid in a bare-user-only repo, i.e. doesn't have xattrs or
   * weird permissions, because then the pull into the system repo
   * would complain that the checksum was wrong. However, by now all
   * flatpak builds are likely to be valid, so this is fine.
   */
  OstreeRepoMode mode = OSTREE_REPO_MODE_BARE_USER_ONLY;
  const char *mode_str = "bare-user-only";

  if (!flatpak_dir_ensure_repo (self, NULL, error))
    return NULL;

  orig_config = ostree_repo_get_config (self->repo);

  if (!flatpak_allocate_tmpdir (AT_FDCWD,
                                flatpak_file_get_path_cached (cache_dir),
                                "repo-", &tmpdir_name,
                                NULL,
                                file_lock,
                                NULL,
                                NULL, error))
    return NULL;

  repo_dir = g_file_get_child (cache_dir, tmpdir_name);

  new_repo = ostree_repo_new (repo_dir);

  repo_dir_config = g_file_get_child (repo_dir, "config");
  if (!g_file_query_exists (repo_dir_config, NULL))
    {
      if (!ostree_repo_create (new_repo, mode, NULL, error))
        return NULL;
    }
  else
    {
      /* Try to open, but on failure, re-create */
      if (!ostree_repo_open (new_repo, NULL, NULL))
        {
          flatpak_rm_rf (repo_dir, NULL, NULL);
          if (!ostree_repo_create (new_repo, mode, NULL, error))
            return NULL;
        }
    }

  config = ostree_repo_copy_config (new_repo);

  /* Verify that the mode is the expected one; if it isn't, recreate the repo */
  current_mode = g_key_file_get_string (config, "core", "mode", NULL);
  if (current_mode == NULL || g_strcmp0 (current_mode, mode_str) != 0)
    {
      flatpak_rm_rf (repo_dir, NULL, NULL);

      /* Re-initialize the object because its dir's contents have been deleted (and it
       * holds internal references to them) */
      g_object_unref (new_repo);
      new_repo = ostree_repo_new (repo_dir);

      if (!ostree_repo_create (new_repo, mode, NULL, error))
        return NULL;

      /* Reload the repo config */
      g_key_file_free (config);
      config = ostree_repo_copy_config (new_repo);
    }

  /* Ensure the config is updated */
  g_key_file_set_string (config, "core", "parent",
                         flatpak_file_get_path_cached (ostree_repo_get_path (self->repo)));

  /* Copy the min space percent value so it affects the temporary repo too */
  orig_min_free_space_percent = g_key_file_get_value (orig_config, "core", "min-free-space-percent", NULL);
  if (orig_min_free_space_percent)
    g_key_file_set_value (config, "core", "min-free-space-percent", orig_min_free_space_percent);

  /* Copy the min space size value so it affects the temporary repo too */
  orig_min_free_space_size = g_key_file_get_value (orig_config, "core", "min-free-space-size", NULL);
  if (orig_min_free_space_size)
    g_key_file_set_value (config, "core", "min-free-space-size", orig_min_free_space_size);

  if (!ostree_repo_write_config (new_repo, config, error))
    return NULL;

  /* We need to reopen to apply the parent config */
  repo = ostree_repo_new (repo_dir);

  if (!ostree_repo_open (repo, NULL, error))
    return NULL;

  /* We don't need to sync the child repos, they are never used for stable storage, and we
     verify + fsync when importing to stable storage */
  ostree_repo_set_disable_fsync (repo, TRUE);

  g_autoptr(GFile) user_cache_dir = flatpak_ensure_user_cache_dir_location (error);
  if (user_cache_dir == NULL)
    return FALSE;

  if (!ostree_repo_set_cache_dir (repo, AT_FDCWD,
                                  flatpak_file_get_path_cached (user_cache_dir),
                                  NULL, error))
    return FALSE;

  /* Create a commitpartial in the child repo if needed to ensure we download everything, because
     any commitpartial state in the parent will not otherwise be inherited */
  if (optional_commit)
    {
      g_autofree char *commitpartial_basename = g_strconcat (optional_commit, ".commitpartial", NULL);
      g_autoptr(GFile) orig_commitpartial =
        flatpak_build_file (ostree_repo_get_path (self->repo),
                            "state", commitpartial_basename, NULL);
      if (g_file_query_exists (orig_commitpartial, NULL))
        {
          g_autoptr(GFile) commitpartial =
            flatpak_build_file (ostree_repo_get_path (repo),
                                "state", commitpartial_basename, NULL);
          g_file_replace_contents (commitpartial, "", 0, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, NULL, NULL);
        }
    }
  return g_steal_pointer (&repo);
}